面向嵌入合作伙伴的 token exchange#
功能可用性
- 适用于 Enterprise 计划。
- 通过将
N8N_ENV_FEAT_TOKEN_EXCHANGE环境变量设为true启用。自托管实例可以直接设置。在 Cloud 上,请联系 n8n 支持请求启用。 - 面向运行外部身份提供程序(IdP)或可签发已签名 JWT 的后端的嵌入合作伙伴。
预览功能
Token exchange 是通过环境标志开启的预览功能。在功能正式可用前,环境变量、端点路径和 JWT claim 合约都可能发生变化。请固定 n8n 版本,并在每次升级后重新测试你的集成。
OAuth 2.0 Token Exchange(RFC 8693)允许嵌入合作伙伴在嵌入式 n8n 实例中验证用户并代表他们执行操作。Token exchange 支持两种用例:
- Iframe SSO:将外部 JWT 交换为 n8n 会话 cookie,让用户在 iframe 中嵌入 n8n 时无缝登录。
- 委托 API 访问:将外部 JWT 交换为 n8n access token,以便代表用户调用 n8n API,例如触发工作流或管理凭据。
两个流程的起点相同。你的后端会签发一个使用私钥签名的短期 JWT。n8n 使用已注册的公钥验证它,解析用户,并返回会话 cookie 或 access token。
开始之前#
你需要:
- 一个已启用 token exchange 功能的 Enterprise 许可证。
- 在实例上设置预览功能标志:
N8N_ENV_FEAT_TOKEN_EXCHANGE=true。 - 一个 RSA 或 EC 密钥对,或你的 IdP 已发布的 JWKS 端点。请参阅生成密钥对。
- 一个通过 HTTPS 提供服务的 n8n 实例。浏览器会拒绝纯 HTTP 上的
SameSite=None; Secure会话 cookie,因此没有 HTTPS 时 iframe SSO 流程会静默失败。如果你在 TLS 终止代理后运行 n8n,请确保代理设置X-Forwarded-Protoheader。
生成密钥对#
你需要一个非对称密钥对。你的后端使用私钥签名 JWT,n8n 使用公钥验证它们。
1 2 3 4 5 | |
1 2 3 4 5 | |
将 private.pem 作为后端密钥妥善保管。你会在 n8n 的受信任密钥配置中注册 public.pem 的内容。
如果你的 IdP 已经发布 JWKS 端点(大多数 OAuth 2.0 和 OIDC 提供商都会这样做),可以跳过密钥生成,直接让 n8n 指向 JWKS URL。请参阅 JWKS 密钥源。
环境变量#
所有设置都必需#
1 2 3 4 5 | |
按流程启用#
两个流程相互独立。只启用你使用的端点:
1 2 3 4 5 | |
嵌入登录端点不需要 N8N_TOKEN_EXCHANGE_ENABLED,token exchange 端点也不需要 N8N_EMBED_LOGIN_ENABLED。
基于文件的配置
您可以在单个变量后添加 _FILE 以在单独的文件中提供其配置。有关更多详细信息,请参阅在单独文件中保存敏感数据。
例如,对于大型或多行 JSON,可将受信任密钥存储在文件中,并设置 N8N_TOKEN_EXCHANGE_TRUSTED_KEYS_FILE=/path/to/trusted-keys.json。
可选调优设置#
这些设置有合理默认值。通常不需要更改:
| 变量 | 默认值 | 说明 |
|---|---|---|
N8N_TOKEN_EXCHANGE_MAX_TOKEN_TTL |
900(15 分钟) |
已签发 n8n token 的最长生命周期(秒)。实际过期时间是此值、subject token 剩余生命周期,以及 actor token 剩余生命周期(如果存在)中的最小值。 |
N8N_TOKEN_EXCHANGE_KEY_REFRESH_INTERVAL_SECONDS |
300(5 分钟) |
当 JWKS 端点未提供缓存生命周期时,刷新 JWKS 密钥的回退间隔。每个实例都会在启动时刷新密钥;在多主部署中,只有 leader 实例会定期刷新。 |
N8N_TOKEN_EXCHANGE_JTI_CLEANUP_INTERVAL_SECONDS |
60(1 分钟) |
n8n 清理过期重放保护记录的频率。 |
N8N_TOKEN_EXCHANGE_JTI_CLEANUP_BATCH_SIZE |
1000 |
每次清理运行删除的最大过期记录数。 |
N8N_TOKEN_EXCHANGE_EMBED_LOGIN_PER_MINUTE |
20 |
嵌入登录端点的速率限制(每 IP 每分钟请求数)。 |
N8N_TOKEN_EXCHANGE_TOKEN_EXCHANGE_PER_MINUTE |
20 |
token exchange 端点的速率限制(每 IP 每分钟请求数)。 |
配置受信任密钥#
N8N_TOKEN_EXCHANGE_TRUSTED_KEYS 环境变量接受一个受信任密钥源的 JSON 数组。每个条目告诉 n8n 如何验证来自你的 IdP 的 JWT。
有两种源类型:static(内联公钥)和 jwks(远程 JWKS 端点)。你可以在同一个数组中混合使用两种类型。
静态密钥源#
当你生成了自己的密钥对,并希望直接嵌入公钥时使用此方式。
1 2 3 4 5 6 7 8 9 | |
| 字段 | 必填 | 说明 |
|---|---|---|
type |
是 | 必须为 "static"。 |
kid |
是 | Key ID。必须匹配传入 JWT header 中的 kid。 |
algorithms |
是 | 允许的签名算法数组,例如 ["RS256"]。请参阅支持的算法。 |
key |
是 | PEM 编码的公钥。在 JSON 中使用 \n 表示换行。 |
issuer |
是 | 传入 JWT 中预期的 iss claim。 |
expectedAudience |
否 | 如果设置,JWT aud claim 必须匹配此值。如果未设置,n8n 会完全跳过 audience 验证。生产环境中始终应设置它,并使用实例特定值,例如你的 n8n 基础 URL。 |
allowedRoles |
否 | 如果设置,只有这些角色可以通过 role claim 分配。只有在你确实希望集成管理 admin 用户时,才包含 global:admin。请参阅角色处理。 |
JWKS 密钥源#
当你的 IdP 发布 JWKS 端点时使用此方式。
1 2 3 4 5 6 7 | |
| 字段 | 必填 | 说明 |
|---|---|---|
type |
是 | 必须为 "jwks"。 |
url |
是 | JWKS 端点的 URL。 |
issuer |
是 | 传入 JWT 中预期的 iss claim。 |
expectedAudience |
否 | 如果设置,JWT aud claim 必须匹配此值。如果未设置,n8n 会完全跳过 audience 验证。生产环境中始终应设置它,并使用实例特定值,例如你的 n8n 基础 URL。 |
allowedRoles |
否 | 如果设置,只有这些角色可以通过 role claim 分配。请参阅角色处理。 |
cacheTtlSeconds |
否 | 当 JWKS 端点未发送 Cache-Control: max-age header 时使用的回退缓存时长。当 header 存在时,以 header 为准。默认为 3600 秒。有效值限制在 60 到 86400 秒之间。 |
完整示例#
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | |
支持的算法#
n8n 仅接受非对称算法。它会排除 HMAC 和 none。
| 系列 | 算法 |
|---|---|
| RSA | RS256、RS384、RS512 |
| RSA-PSS | PS256、PS384、PS512 |
| 椭圆曲线 | ES256、ES384、ES512 |
| Edwards 曲线 | EdDSA |
对于静态密钥,配置中的算法必须全部属于同一系列,并与密钥类型匹配。对于 JWKS 密钥,n8n 会自动从 JWK alg 和 kty 或 crv 字段推断算法。
必填和可选 JWT claim#
必填 claim#
你的 IdP token 必须包含以下 claim,n8n 才会接受:
| Claim | 类型 | 说明 |
|---|---|---|
sub |
string | Subject 标识符。IdP 中的唯一用户 ID。 |
iss |
string(URL) | Issuer。必须匹配受信任密钥配置中的 issuer。 |
aud |
string 或 string 数组 | Audience。必须存在。只有在受信任密钥源配置了 expectedAudience 时,n8n 才会验证该值。 |
iat |
number | Issued-at 时间戳(Unix epoch 秒)。 |
exp |
number | 过期时间戳(Unix epoch 秒)。 |
jti |
string | 唯一 token ID。n8n 只接受每个值一次(重放保护)。 |
可选 claim#
| Claim | 类型 | 说明 |
|---|---|---|
email |
string(有效邮箱) | 用户邮箱,用于匹配现有 n8n 用户。JIT 配置时,即 n8n 不认识该用户的首次登录时,此字段必需。除非你确定每个用户都已存在,否则请始终发送。 |
given_name |
string | 名,已同步到 n8n 用户资料。 |
family_name |
string | 姓,已同步到 n8n 用户资料。 |
role |
string | 要分配的 n8n 角色,例如 global:member 或 global:admin。请参阅用户配置。 |
nbf |
number | Not-before 时间戳。 |
Iframe SSO 流程#
在 iframe 中嵌入 n8n 时使用此流程。n8n 使用会话 cookie 透明登录用户。
sequenceDiagram
participant Browser as 用户浏览器
participant Backend as 你的后端
participant n8n as n8n 实例
Browser->>Backend: 1. 加载 iframe
Backend->>Backend: 2. 签发 JWT(私钥)
Backend-->>Browser: 3. 返回自动提交表单(token)
Browser->>n8n: 4. POST /rest/auth/embed(正文中包含 token)
n8n->>n8n: 5. 验证 JWT
n8n->>n8n: 6. 解析用户
n8n->>n8n: 7. 设置会话 cookie
n8n-->>Browser: 8. 重定向(带会话 cookie)
Browser->>n8n: 9. 用户已登录,加载 n8n UI
第 1 步:在后端签发 JWT#
你的后端创建一个使用私钥签名的短期 JWT。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | |
最长 60 秒生命周期
对于嵌入登录流程,JWT 生命周期(exp - iat)不得超过 60 秒。n8n 会在服务端强制执行此限制。
第 2 步:将 token 发送到嵌入端点#
通过表单提交将 token 发送到 POST /rest/auth/embed。将 iframe src 指向你后端上的一个页面,该页面返回自动提交表单:
1 2 3 4 5 | |
可选的 redirectTo 字段仅接受以 / 开头的相对路径。对于绝对 URL 或其他任何内容,n8n 会回退到 /。
还有一个 GET /rest/auth/embed?token=<jwt>&redirectTo=/workflow/abc123 变体。优先使用 POST 表单:使用 GET 时,token 会出现在服务器和代理日志、浏览器历史记录,以及可能的 Referer header 中。如果必须使用 GET,请确保你的基础设施从日志中清理查询字符串。
第 3 步:n8n 验证并签发会话#
n8n 验证 JWT 签名,解析或配置用户(请参阅用户配置),设置安全会话 cookie(SameSite=None; Secure),并重定向到指定路径。
第三方 cookie 限制#
当 n8n 与你的产品运行在不同的可注册域名上时,会话 cookie 是第三方 cookie。Safari 的 Intelligent Tracking Prevention 和 Chrome 的第三方 cookie 限制等浏览器隐私功能可能会阻止或分区它,从而破坏 iframe 登录。请将 n8n 托管在你产品站点的子域名上,例如 automation.your-product.example.com,以避免此问题。
委托 API 访问流程#
当你的后端需要代表用户调用 n8n API 时(例如触发工作流或以编程方式管理凭据),请使用此流程。
此流程支持用于委托的可选 actor token。Actor(例如服务账号或 admin)代表 subject(终端用户)执行操作。这会启用审计归因,因此 n8n 会同时记录谁执行了操作,以及代表谁执行。
当你提供 actor token 时,n8n 会使用 actor 的身份和权限授权 API 调用。n8n 只记录 subject 用于审计归因,subject 不会限制 token 可执行的操作。这与 RFC 8693 不同,在 RFC 8693 中 subject 通常仍是有效主体。请使用 n8n 角色只授予集成所需权限的 actor。
sequenceDiagram
participant Backend as 你的后端
participant n8n as n8n 实例
Backend->>Backend: 1. 签发 subject JWT(+ 可选 actor JWT)
Backend->>n8n: 2. POST /rest/auth/oauth/token(token-exchange grant)
n8n->>n8n: 3. 验证 JWT
n8n->>n8n: 4. 解析用户
n8n->>n8n: 5. 签发 n8n access token
n8n-->>Backend: 6. access_token、token_type、expires_in
Backend->>n8n: 7. 使用 Bearer token 调用 n8n API
第 1 步:在后端签发 JWT#
创建一个代表终端用户的 subject token。嵌入流程中的 60 秒限制不适用于此处。已签发的 n8n access token 会在 subject token 剩余生命周期、actor token 剩余生命周期(如果存在)和 N8N_TOKEN_EXCHANGE_MAX_TOKEN_TTL(默认 15 分钟)中的最小值后过期。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
对于委托,还要签发一个代表执行操作的服务或 admin 的 actor token。Actor 的 n8n 角色决定已签发 token 的权限,因此应尽可能保持低权限:
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
第 2 步:交换 n8n access token#
1 2 3 4 5 | |
请求字段(application/x-www-form-urlencoded):
| 字段 | 必填 | 说明 |
|---|---|---|
grant_type |
是 | 必须为 urn:ietf:params:oauth:grant-type:token-exchange。 |
subject_token |
是 | 代表终端用户的 JWT。 |
subject_token_type |
否 | Token 类型标识符。n8n 接受并忽略此字段。RFC 8693 要求该字段,因此如果你使用符合规范的客户端库,请发送 urn:ietf:params:oauth:token-type:jwt。 |
actor_token |
否 | 代表 actor 的 JWT(用于委托)。 |
actor_token_type |
否 | Actor token 类型标识符。与 subject_token_type 一样,会被接受并忽略。 |
requested_token_type |
否 | 请求的 token 类型标识符。会被接受并忽略。n8n 始终签发 access token。 |
scope |
否 | 请求的 scope(最大 1024 字符)。记录在已签发 token 和审计事件中,但不会强制执行。 |
audience |
否 | 目标 audience(最大 1024 字符)。会被接受并忽略。 |
resource |
否 | 目标资源 URI,使用空格分隔(最大 2048 字符)。记录在已签发 token 和审计事件中,但不会强制执行。 |
Scope 不会被强制执行
已签发的 access token 携带执行用户的完整权限:发送 actor token 时为 actor 权限,否则为 subject 权限。n8n 只记录 scope 和 resource 用于审计,不会据此限制 token。要限制集成可执行的操作,请使用低权限 actor 和 allowedRoles 设置。
成功响应(200 OK):
1 2 3 4 5 6 | |
第 3 步:使用 access token#
在后续 n8n API 调用中包含该 token:
1 2 | |
Token 会在 expires_in 秒后过期。过期后请求新的 token。不要复用原始外部 JWT,因为每个 jti 只能使用一次。
用户配置#
交换 token 时,n8n 会按以下顺序将外部身份解析为 n8n 用户:
- 已知身份:n8n 在其身份存储中查找
subclaim。如果之前的交换已经将此sub链接到某个 n8n 用户,n8n 会返回该用户。 - 邮箱回退:如果
sub未知,但 JWT 包含emailclaim,n8n 会搜索具有该邮箱的现有用户。如果找到,n8n 会将外部身份链接到该用户,供后续使用。 - 即时(JIT)配置:如果两者都不匹配,n8n 会自动创建新用户。JWT 必须包含
emailclaim 才能执行此操作。n8n 会为新用户禁用密码登录,因此他们只能通过 token exchange 进行身份验证。
角色处理#
如果包含 role claim,你的 IdP 会成为该用户全局角色的事实来源:n8n 会在每次交换时应用该角色,覆盖 n8n UI 中分配的角色。除非你在 IdP 中管理角色,否则请省略此 claim。
| 场景 | 行为 |
|---|---|
新用户,无 role claim |
分配 global:member。 |
新用户,存在 role claim |
分配 claim 中的角色。如果角色无法识别、为 global:owner,或不在 allowedRoles 中,交换会失败。 |
现有用户,无 role claim |
角色保持不变。 |
现有用户,有效 role claim |
如果不同则更新角色。如果角色不在 allowedRoles 中,交换会失败。 |
现有用户,无法识别或 global:owner 角色 claim |
忽略该 claim,并在服务端发出 warning。登录会继续,角色保持不变。 |
现有用户是 global:owner |
完全跳过角色同步。Owner 角色不能通过 token exchange 更改。 |
用户资料同步#
n8n 会在每次登录时将 given_name 和 family_name claim 同步到用户资料。仅当值与存储的内容不同时才会应用变更,并截断超过 32 个字符的值。
安全注意事项#
短生命周期 token#
- 嵌入流程的外部 JWT 生命周期最多为 60 秒(
exp - iat <= 60)。 - 对于 token exchange 流程,已签发 n8n token 的过期时间是 subject token 剩余生命周期、actor token 剩余生命周期(如果存在)和
N8N_TOKEN_EXCHANGE_MAX_TOKEN_TTL(默认 900 秒)中的最小值。 - 在 token exchange 流程中,如果计算出的已签发 token 过期时间少于五秒,n8n 会拒绝请求。
重放保护#
每个外部 JWT 都必须包含唯一的 jti(JWT ID)claim。n8n 会记录每个 jti,并拒绝任何 jti 已被使用的 token。n8n 会自动清理过期的 jti 记录。
仅支持非对称签名#
n8n 仅接受非对称算法(RSA、EC、EdDSA)。它会有意拒绝 HS256 和 none 等 HMAC 算法。这确保 n8n 永远不需要访问你的签名密钥。
Audience 限制#
始终在每个受信任密钥源上设置 expectedAudience。没有它,n8n 会跳过 audience 验证,来自已配置 issuer 的任何有效 token 都可以被交换,包括你的 IdP 为其他服务或其他 n8n 实例签发的 token。请使用实例特定值,例如你的 n8n 基础 URL。
角色约束#
受信任密钥源上的 allowedRoles 字段限制哪些角色可以通过 token exchange 分配。使用它来执行最小权限,例如将嵌入集成限制为只能配置 global:member 用户。OAuth scope 和 resource 请求字段不会限制权限。请参阅第 2 步:交换 n8n access token。
限制嵌入页面#
嵌入会话 cookie 使用 SameSite=None,因此浏览器会在任何第三方 iframe 上下文中发送它。请限制哪些站点可以嵌入你的 n8n 实例:配置反向代理发送 Content-Security-Policy: frame-ancestors header,并只列出你产品的 origin。不要整体禁用 framing 保护。
密钥轮换与泄露#
要在不中断服务的情况下轮换密钥,请向 N8N_TOKEN_EXCHANGE_TRUSTED_KEYS 添加带有新 kid 的新条目,将后端切换为使用新密钥签名,然后移除旧条目。如果私钥泄露,请立即移除其条目。这会停止新的交换,但已签发的 n8n token 和会话会在过期前保持有效,受短 token 生命周期限制。
审计归因#
n8n 会为所有 token exchange 活动发出审计事件:
| 事件 | 触发时机 |
|---|---|
n8n.audit.token-exchange.succeeded |
token exchange 成功。 |
n8n.audit.token-exchange.failed |
token exchange 失败(包含失败原因)。 |
n8n.audit.token-exchange.embed-login |
嵌入登录成功。 |
n8n.audit.token-exchange.embed-login-failed |
嵌入登录失败(包含失败原因)。 |
n8n.audit.token-exchange.user-provisioned |
通过 JIT 配置创建新用户。 |
n8n.audit.token-exchange.identity-linked |
现有用户链接到新的外部身份。 |
n8n.audit.token-exchange.role-updated |
用户角色通过 token exchange 更改。 |
使用 actor token 流程时,n8n 会同时记录 subject(代表谁)和 actor(谁执行了操作),从而在审计日志中启用完整归因。
验证设置#
签发一次性 JWT 并交换它。将第 1 步示例保存为 mint-token.js,在末尾添加 console.log(token);,然后运行:
1 | |
然后调用 token exchange 端点:
1 2 3 4 | |
返回包含 access_token 的 200 响应,说明你的密钥、claim 和配置正常工作。对于嵌入流程,请在浏览器中向 POST /rest/auth/embed 提交一个新 token,并预期 n8n UI 加载成功。
故障排查#
| 现象 | 可能原因 |
|---|---|
两个端点都返回 404 |
预览标志 N8N_ENV_FEAT_TOKEN_EXCHANGE 不是 true,或你的许可证不包含 token exchange 功能。n8n 根本不会加载该模块。 |
501 - Token exchange is not enabled on this instance |
N8N_TOKEN_EXCHANGE_ENABLED 不是 true。 |
501 - Embed login is not enabled on this instance |
N8N_EMBED_LOGIN_ENABLED 不是 true。 |
400 - unsupported_grant_type |
缺少 grant_type 字段,或其值不是精确的 urn:ietf:params:oauth:grant-type:token-exchange。 |
400 - invalid_grant 且描述为 Token exchange failed |
token exchange 端点上的任意 token 验证失败:签名无效、未知 kid、缺少 kid header、重放的 jti、token 过期或即将过期、audience 不匹配,或角色不被允许。n8n 会有意返回通用描述。请检查 n8n 服务器日志获取具体原因。 |
400 - invalid_request 且描述为 Token claims validation failed |
在 token exchange 端点上:JWT 缺少必填 claim(sub、iss、aud、iat、exp、jti),或 claim 类型错误。 |
嵌入端点返回 401 |
响应会包含具体失败消息,例如 Token has already been used(重放的 jti)、Token lifetime exceeds maximum allowed(嵌入 token 必须满足 exp - iat <= 60 秒),或 Token header missing kid。 |
嵌入端点返回 500 |
JWT 缺少必填 claim,或 claim 类型错误。 |
| iframe 中显示原始 JSON 错误 | 嵌入登录失败会返回没有错误重定向的 JSON 响应。请根据上方各行检查消息。 |
| 嵌入登录后未设置会话 cookie | 实例未通过 HTTPS 提供服务,或 TLS 终止代理未转发 X-Forwarded-Proto。浏览器会拒绝纯 HTTP 上的 SameSite=None; Secure cookie。第三方 cookie 限制也可能阻止该 cookie,请参阅第三方 cookie 限制。 |
| 首次登录时未创建用户 | JWT 缺少 email claim,而 JIT 配置需要该 claim。 |
| 未应用角色 | 对于现有用户,n8n 会忽略无法识别和 global:owner 角色 claim,并在服务端发出 warning。缺少于 allowedRoles 中的有效角色会导致交换被拒绝。请参阅角色处理。 |
相关资源#
- OEM 部署概览:在你的产品中嵌入并呈现 n8n 界面。
- 设置 SSO:通过 SAML 或 OIDC 实现组织范围内的 Single Sign-On。
- HTTP Request 凭据:使用 OAuth2:设置通用 OAuth 2.0 凭据。
- OAuth 2.0 Token Exchange (RFC 8693):token exchange 规范。