Embed login wired end-to-end for iframe SSO

The OAuth 2.0 Token Exchange initiative hits a major milestone: iframe-based single sign-on now works end-to-end with cross-origin cookie support, audit logging, and an independent feature flag for safe rollout.
The embed login flow was wired to real implementations, completing Phase 2a of the OAuth 2.0 Token Exchange initiative. Previously a skeleton, the endpoints now handle full authentication with cross-origin cookie overrides, audit event emission, and a feature gate for independent control.
Embedding n8n in iframes requires cookies that work across origins—a technical constraint that blocked this use case until now. By passing SameSite=None; Secure to the session cookie when login originates from an embed context, authenticated sessions now work inside cross-origin iframes. When users authenticate through an embedded n8n instance, an embed-login event fires with subject, issuer, and client IP for audit trail visibility. A new N8N_EMBED_LOGIN_ENABLED environment variable lets operators enable embed endpoints independently from the broader token exchange feature, allowing gradual rollout without full commitment.
The changes live in the @n8n/cli package's auth and token exchange modules.
View Original GitHub Description
Summary
This PR completes the embed login flow by wiring real implementations into the skeleton endpoints. It is the final integration step for Phase 2a of the OAuth 2.0 Token Exchange initiative — after this, iframe SSO works end-to-end.
What changed:
- Cookie overrides for iframe context —
AuthService.issueCookie()now accepts an optionalcookieOverridesparameter. The embed controller passesSameSite=None; Secureso the session cookie works inside cross-origin iframes (previously a TODO). - Audit event emission — Successful embed logins emit an
embed-loginevent withsubject,issuer, andclientIpfor audit trail visibility. - Structured return from
embedLogin()—TokenExchangeService.embedLogin()now returns{ user, subject, issuer }instead of just the user, so the controller has the claims it needs for audit logging without re-parsing the token. - Feature gate — Added
N8N_EMBED_LOGIN_ENABLEDconfig flag so the embed endpoints can be toggled independently of the token exchange feature. Returns 501 when disabled. - Integration tests — Full integration test suite covering: valid GET/POST login with cookie assertions, expired/replayed/bad-signature/long-lived token rejection, JIT user provisioning, and returning-user profile sync.
How to test
- Set env vars:
N8N_ENV_FEAT_TOKEN_EXCHANGE=true
N8N_EMBED_LOGIN_ENABLED=true
- Configure a trusted key via
N8N_TOKEN_EXCHANGE_TRUSTED_KEYSwith an RSA public key - Sign a JWT with the corresponding private key containing
sub,email,iss,aud,iat,exp,jticlaims GET /rest/auth/embed?token=<jwt>— should set anHttpOnly; Secure; SameSite=Nonecookie and redirect to/- Verify expired tokens, replayed JTIs, and invalid signatures all return 401
- First login with a new
subshould JIT-provision the user; second login should sync profile fields
See https://n8nio.slack.com/archives/C0ACFN1G7NK/p1775812851354889 for test scripts to help set this up.
Related
closes https://linear.app/n8n/issue/IAM-468
Review / Merge checklist
- I have seen this code, I have run this code, and I take responsibility for this code.
- PR title and summary are descriptive. (conventions) <!-- **Remember, the title automatically goes into the changelog. Use `(no-changelog)` otherwise.** -->
- Docs updated or follow-up ticket created.
- Tests included. <!-- A bug is not considered fixed, unless a test is added to prevent it from happening again. A feature is not complete without tests. -->
- PR Labeled with
Backport to Beta,Backport to Stable, orBackport to v1(if the PR is an urgent fix that needs to be backported)