Merged
Size
L
Change Breakdown
Feature75%
Maintenance15%
Refactor10%
#28009feat(core): Add identity resolution for token exchange (no-changelog)

Token exchange identity resolution goes live

Token exchange identity resolution goes live

External tokens can now map directly to n8n users — first via identity lookup, then email matching, and as a last resort, automatic provisioning with personal projects included.

Token exchange in n8n just became functional. Previously a stub that threw "not implemented," the IdentityResolutionService now handles the full workflow for mapping external identity claims to internal users.

The resolution algorithm tries three paths in order: first, it checks if the external sub claim is already linked to a user via the token-exchange provider. If not found, it falls back to matching by email — useful when a user already exists in n8n but hasn't connected this external identity yet. When neither match succeeds, the service provisions a new user, creates a personal project, and links the identity — all in a single database transaction.

Role handling adds guardrails without blocking logins. Existing owners can never have their role changed via token exchange. Other existing users keep their current role if the claimed role isn't in the key's allowedRoles whitelist. New users default to global:member unless a valid role claim is present and permitted — if not, an error is thrown rather than leaving the user in an ambiguous state.

Three new audit events track the lifecycle: token-exchange-identity-linked when email fallback connects a new sub to an existing user, token-exchange-user-provisioned when JIT creates a fresh user, and token-exchange-role-updated when role changes propagate.

This lands in the @n8n/cli package as part of the OAuth 2.0 Token Exchange initiative (Phase 2a — Embed flow). External systems that issue tokens can now authenticate users into n8n without passwords, managing identity their way while n8n handles the user record.

View Original GitHub Description

Summary

Implements IdentityResolutionService that resolves verified external token claims (sub) to an n8n user. This replaces the skeleton stub that previously threw "not implemented".

Part of the OAuth 2.0 Token Exchange initiative (Phase 2a — Embed flow). External systems identify users by their own IDs (sub claim). This service maps those to internal n8n users — linking on first encounter and keeping profiles in sync on subsequent exchanges.

Resolution algorithm

  1. Known subAuthIdentity lookup by sub + token-exchange provider → return linked user
  2. Email fallback — no identity match but email matches existing user → create AuthIdentity link, return user
  3. JIT provision — no match at all → create user + personal project + AuthIdentity in a single transaction

Role handling

  • Role claim validated against the key's allowedRoles whitelist
  • global:owner can never be assigned via token exchange (existing owners keep their role, new users are rejected)
  • Existing users keep their current role when the claimed role is invalid or not allowed — login is never blocked due to role mismatch
  • New users get global:member by default when no role claim is present

Other changes

  • TokenExchangeService.verifyToken now returns { claims, resolvedKey } so embedLogin can pass allowedRoles to identity resolution
  • Three new audit events: token-exchange-identity-linked, token-exchange-user-provisioned, token-exchange-role-updated

How to test manually

  1. Configure a trusted key with allowedRoles: ['global:member', 'global:admin']
  2. Generate a JWT with sub, email, and optional role claim signed by the trusted key
  3. Call POST /rest/auth/embed with { "token": "<jwt>" }
  4. Verify:
    • First call with a new sub + known email links the identity and returns the existing user
    • First call with a new sub + unknown email creates a new user with personal project
    • Subsequent calls with the same sub resolve via identity lookup (Path 1)
    • Role updates are applied when the claim is in allowedRoles
    • global:owner role claim is silently ignored for existing users and rejected for new users
  5. Check audit events are emitted for linking, provisioning, and role changes

Related tickets

Review checklist

  • PR title and summary are descriptive. (conventions)
  • Docs updated or follow-up ticket created.
  • Tests included.
  • PR Labeled with Backport to Beta, Backport to Stable, or Backport to v1 (if the PR is an urgent fix that needs to be backported)
© 2026 · via Gitpulse