Skip to content

Conversation

@xfalcox
Copy link
Member

@xfalcox xfalcox commented Dec 11, 2025

Summary

Adds a new fullApp option to the embed snippet that renders the complete Discourse Ember application in the iframe instead of the simplified Rails-templated view. This allows embedded comments to have the full Discourse experience including likes, reactions, and inline replies.

When fullApp: true is set:

  • /embed/comments redirects to the topic URL with ?embed_mode=true
  • Header, sidebar, and footer are hidden
  • All links open in new tabs (to avoid navigating away within the iframe)
  • Iframe is scrollable with configurable height (default 600px)

Demo

https://discourse-full-embed.pages.dev/embed-test

Usage

DiscourseEmbed = {
  discourseUrl: 'https://forum.example.com/',
  discourseEmbedUrl: 'EMBED_URL',
  fullApp: true,
  // embedHeight: '800px',  // optional, defaults to 600px
};

Security

The embed_mode parameter only removes the X-Frame-Options header if:

  • embed_any_origin site setting is enabled, OR
  • The request referer matches a configured embeddable host

This matches the existing security model for /embed/comments.

@xfalcox xfalcox marked this pull request as draft December 11, 2025 01:33
@xfalcox xfalcox force-pushed the full-embeds branch 4 times, most recently from 8d3912a to b05da07 Compare December 11, 2025 17:07
xfalcox and others added 6 commits December 12, 2025 10:51
Adds a new `fullApp` option to the embed snippet that renders the
complete Discourse Ember application in the iframe instead of the
simplified Rails-templated view.

When enabled:
- Redirects /embed/comments to the topic with ?embed_mode=true
- Hides header, sidebar, and footer in embed mode
- Forces all links to open in new tabs
- Uses scrollable iframe with configurable height (default 600px)

This allows embedded comments to have the full Discourse experience
including likes, reactions, and inline replies.

Usage:
```js
DiscourseEmbed = {
  discourseUrl: 'https://forum.example.com/',
  discourseEmbedUrl: 'EMBED_URL',
  fullApp: true,
  // embedHeight: '800px',  // optional
};
```
Full app embed mode loads Discourse in an iframe on a different origin.
jQuery considers these requests cross-domain and skips the CSRF token
header, causing 403 errors on POST/PUT/DELETE requests.

This change:
- Sends CSRF token even on cross-domain requests when in embed mode
- Adds X-Discourse-Embed-Mode header for server-side detection
- Allows redirect to topic URL across hosts (needed for dev proxy)
In full app embed mode, clicking links that navigate away from the
current topic (user profiles, other topics, etc.) should open in a
new tab rather than navigating within the iframe.

Uses routeWillChange to intercept Ember transitions and redirect
non-topic routes to a new browser window.
The previous approach using routeWillChange didn't work because
link clicks were causing full page reloads instead of going through
Ember's router.

This fix intercepts navigation at two key points:
- intercept-click.js: For cooked HTML links, explicitly call
  preventDefault() and window.open() in embed mode
- DiscourseURL.routeTo(): For programmatic navigation, open in
  new tab except for same-topic navigation (post scrolling)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Safari's Intelligent Tracking Prevention (ITP) blocks third-party
cookies in iframes, preventing logged-in users from being recognized
when viewing embedded comments.

This adds a prompt for Safari users in embed mode to request storage
access via the Storage Access API. Once granted, the page reloads
and the session cookie becomes available.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
When users click actions requiring authentication (like, reply, etc.)
while in embed mode, the login and signup pages now open in a new tab
instead of navigating within the iframe. This is essential for SSO
configurations where login redirects to external identity providers.
- Fix logic conflict between intercept-click.js and url.js: removed
  EmbedMode handling from intercept-click.js so clicks properly flow
  to DiscourseURL.routeTo() where same-topic navigation is allowed
  while other navigation opens in new tabs

- Extract hardcoded strings in embed-storage-access.js to locale file
  for i18n support

- Replace innerHTML with proper DOM element creation for security

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@github-actions github-actions bot added the i18n PRs which update English locale files or i18n related code label Dec 12, 2025
Request specs:
- Verify X-Frame-Options header is kept without embed_mode param
- Verify X-Frame-Options header is kept with invalid referer
- Verify X-Frame-Options header is stripped with valid embeddable host
- Verify X-Frame-Options header is stripped with embed_any_origin setting

System specs:
- Verify embed-mode class is applied to body with embed_mode=true
- Verify embed-mode class is not applied without the param
- Verify suggested topics are hidden in embed mode
- Verify topic content loads without JS errors

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <[email protected]>
@xfalcox xfalcox marked this pull request as ready for review December 12, 2025 18:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

i18n PRs which update English locale files or i18n related code

Development

Successfully merging this pull request may close these issues.

2 participants