嵌入合作伙伴的令牌交换
功能可用范围
令牌交换可用于:
- **n8n Cloud:**Enterprise。
- **自托管:**企业版。
将 N8N_ENV_FEAT_TOKEN_EXCHANGE 环境变量设为 true 即可启用。自托管实例可直接设置,n8n Cloud 用户请联系 n8n 支持团队申请启用。
面向运行外部身份提供商(IdP),或能签发已签名 JWT 的后端的嵌入合作伙伴。
预览状态
令牌交换是通过环境变量开关控制的预览功能。正式发布前,环境变量、端点路径和 JWT 声明约定都可能更改。请固定 n8n 版本,并在每次升级后重新测试集成。
OAuth 2.0 Token Exchange(RFC 8693)允许嵌入合作伙伴在嵌入式 n8n 实例中验证用户,并代表用户操作。令牌交换支持两个场景:
- Iframe SSO:将外部 JWT 交换为 n8n 会话 Cookie,让用户在 iframe 嵌入的 n8n 中无缝登录。
- 委托 API 访问:将外部 JWT 交换为 n8n 访问令牌,代表用户调用 n8n API,例如触发工作流或管理凭据。
两种流程的起点相同:后端使用私钥签发短期 JWT,n8n 用已注册公钥验证它,解析用户,并返回会话 Cookie 或访问令牌。
开始之前
你需要:
- 已启用令牌交换功能的 Enterprise 许可证。
- 在实例上设置预览功能开关:
N8N_ENV_FEAT_TOKEN_EXCHANGE=true。 - RSA 或 EC 密钥对,或 IdP 已发布的 JWKS 端点。参阅生成密钥对。
- 通过 HTTPS 提供服 务的 n8n 实例。浏览器会拒绝通过普通 HTTP 设置的
SameSite=None; Secure会话 Cookie,缺少 HTTPS 时,iframe SSO 流程会无提示失败。如果 n8n 位于终止 TLS 的代理之后,请确保代理设置X-Forwarded-Proto标头。
生成密钥对
需要非对称密钥对。后端使用私钥签名 JWT,n8n 使用公钥验证。
RSA(RS256)
# Generate a 2048-bit RSA private key
openssl genrsa -out private.pem 2048
# Extract the public key
openssl rsa -in private.pem -pubout -out public.pem
EC(ES256)
# Generate an EC private key using P-256
openssl ecparam -name prime256v1 -genkey -noout -out private.pem
# Extract the public key
openssl ec -in private.pem -pubout -out public.pem
在后端妥善保密 private.pem,将 public.pem 的内容注册到 n8n 的可信密钥配置中。
如果 IdP 已发布 JWKS 端点,大多数 OAuth 2.0 和 OIDC 提供商都是如此,就可以跳过密钥生成,直接让 n8n 指向 JWKS URL。参阅 JWKS 密钥源。
环境变量
所有配置都需要的变量
# Enable the preview module
N8N_ENV_FEAT_TOKEN_EXCHANGE=true
# Register your public key(s) - see Configure trusted keys
N8N_TOKEN_EXCHANGE_TRUSTED_KEYS='[{ ... }]'
各流程独立开关
两种流程相互独立,请仅启用所需端点:
# Iframe SSO: enable the embed login endpoint (POST or GET /rest/auth/embed)
N8N_EMBED_LOGIN_ENABLED=true
# Delegated API access: enable the token exchange endpoint (POST /rest/auth/oauth/token)
N8N_TOKEN_EXCHANGE_ENABLED=true
嵌入登录端点不需要 N8N_TOKEN_EXCHANGE_ENABLED,令牌交换端点也不需要 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 令牌的最长有效期,单位为秒。实际过期时间取此值、主体令牌剩余有效期和操作者令牌剩余有效期(如果提供)三者中的最小值。 |
N8N_TOKEN_EXCHANGE_KEY_REFRESH_INTERVAL_SECONDS | 300,5 分钟 | 端点未提供缓存有效期时,刷新 JWKS 密钥的回退间隔。每个实例都在启动时刷新密钥;多主实例部署中,仅领导者实例定期刷新。 |
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 | 令牌交换端点的速率限制,每个 IP 每分钟请求数。 |
配置可信密钥
N8N_TOKEN_EXCHANGE_TRUSTED_KEYS 环境变量接受可信密钥源的 JSON 数组。每个条目告知 n8n 如何验证来自 IdP 的 JWT。
有两种源类型:static,内联公钥;jwks,远程 JWKS 端点。可以在同一数组中混合使用。
静态密钥源
自行生成密钥对,希望直接嵌入公钥时使用。
{
"type": "static",
"kid": "my-key-1",
"algorithms": ["RS256"],
"key": "-----BEGIN PUBLIC KEY-----\nMIIBIjAN...contents-of-public.pem...\n-----END PUBLIC KEY-----",
"issuer": "https://your-backend.example.com",
"expectedAudience": "https://your-n8n.example.com",
"allowedRoles": ["global:member"]
}
| 字段 | 必填 | 说明 |
|---|---|---|
type | 是 | 必须为 "static"。 |
kid | 是 | 密钥 ID,必须与传入 JWT 中的 kid 标头匹配。 |
algorithms | 是 | 允许的签名算法数组,例如 ["RS256"]。参阅支持的算法。 |
key | 是 | PEM 编码的公钥。在 JSON 中使用 \n 表示换行。 |
issuer | 是 | 传入 JWT 中预期的 iss 声明。 |
expectedAudience | 否 | 设置后,JWT 的 aud 声明必须与此值匹配。未设置时,n8n 完全跳过受众验证。生产环境始终应设置为实例特有的值,例如 n8n 基础 URL。 |
allowedRoles | 否 | 设置后,只允许通过 role 声明分配这些角色。只有明确希望集成管理管理员用户时,才包含 global:admin。参阅角色处理。 |
JWKS 密钥源
IdP 发布了 JWKS 端点时使用。
{
"type": "jwks",
"url": "https://idp.example.com/.well-known/jwks.json",
"issuer": "https://idp.example.com",
"expectedAudience": "https://your-n8n.example.com",
"allowedRoles": ["global:member"]
}
| 字段 | 必填 | 说明 |
|---|---|---|
type | 是 | 必须为 "jwks"。 |
url | 是 | JWKS 端点的 URL。 |
issuer | 是 | 传入 JWT 中预期的 iss 声明。 |
expectedAudience | 否 | 设置后,JWT 的 aud 声明必须与此值匹配。未设置时,n8n 完全跳过受众验证。生产环境始终应设置为实例特有的值,例如 n8n 基础 URL。 |
allowedRoles | 否 | 设置后,只允许通过 role 声明分配这些角色。参阅角色处理。 |
cacheTtlSeconds | 否 | JWKS 端点未发送 Cache-Control: max-age 标头时的回退缓存时长。存在该标头时,优先采用标头。默认 3600 秒,有效值限制在 60 到 86400 秒之间。 |
完整示例
[
{
"type": "static",
"kid": "my-static-key-1",
"algorithms": ["RS256"],
"key": "-----BEGIN PUBLIC KEY-----\nMIIBIjAN...your-key-here...\n-----END PUBLIC KEY-----",
"issuer": "https://your-backend.example.com",
"expectedAudience": "https://your-n8n.example.com",
"allowedRoles": ["global:member"]
},
{
"type": "jwks",
"url": "https://idp.example.com/.well-known/jwks.json",
"issuer": "https://idp.example.com",
"expectedAudience": "https://your-n8n.example.com"
}
]
支持的算法
n8n 仅接受非对称算法,排除 HMAC 和 none。
| 算法族 | 算法 |
|---|---|
| RSA | RS256、RS384、RS512 |
| RSA-PSS | PS256、PS384、PS512 |
| 椭圆曲线 | ES256、ES384、ES512 |
| Edwards 曲线 | EdDSA |
静态密钥配置中的算法必须属于同一算法族,并与密钥类型匹配。对于 JWKS 密钥,n8n 根据 JWK 的 alg 和 kty 或 crv 字段自动推断算法。
必需和可选 JWT 声明
必需声明
IdP 令牌必须包含以下声明,n8n 才会接受:
| 声明 | 类型 | 说明 |
|---|---|---|
sub | 字符串 | 主体标识符,即 IdP 中的唯一用户 ID。 |
iss | 字符串,URL | 签发者,必须与可信密钥配置中的 issuer 匹配。 |
aud | 字符串或字符串数组 | 受众,必须存在。只有可信密钥源配置了 expectedAudience 时,n8n 才验证其值。 |
iat | 数字 | 签发时间戳,Unix 纪元秒数。 |
exp | 数字 | 过期时间戳,Unix 纪元秒数。 |
jti | 字符串 | 唯一令牌 ID。每个值 n8n 仅接受一次,用于重放保护。 |
可选声明
| 声明 | 类型 | 说明 |
|---|---|---|
email | 字符串,有效邮箱 | 用户邮箱,用于匹配已有 n8n 用户。n8n 尚不认识的用户首次登录时,即时预配(JIT)需要此字段。除非确认所有用户都已存在,否则始终应发送。 |
given_name | 字符串 | 名,同步到 n8n 用户资料。 |
family_name | 字符串 | 姓,同步到 n8n 用户资料。 |
role | 字符串 | 要分配的 n8n 角色,例如 global:member 或 global:admin。参阅用户预配。 |
nbf | 数字 | 不早于此时生效的时间戳。 |
Iframe SSO 流程
在 iframe 中嵌入 n8n 时使用此流程。n8n 通过会话 Cookie 为用户无缝登录。
sequenceDiagram
participant Browser as User browser
participant Backend as Your backend
participant n8n as n8n instance
Browser->>Backend: 1. Load iframe
Backend->>Backend: 2. Mint JWT (private key)
Backend-->>Browser: 3. Return auto-submitting form (token)
Browser->>n8n: 4. POST /rest/auth/embed (token in body)
n8n->>n8n: 5. Verify JWT
n8n->>n8n: 6. Resolve user
n8n->>n8n: 7. Set session cookie
n8n-->>Browser: 8. Redirect (with session cookie)
Browser->>n8n: 9. User is logged in, loads n8n UI
第 1 步:在后端签发 JWT
后端使用私钥签发短期 JWT。
const fs = require('fs');
const jwt = require('jsonwebtoken');
const { randomUUID } = require('crypto');
const privateKey = fs.readFileSync('private.pem', 'utf8');
const now = Math.floor(Date.now() / 1000);
const token = jwt.sign(
{
sub: 'user-id-in-your-system', // unique user identifier
iss: 'https://your-backend.example.com', // must match trusted key config
aud: 'https://your-n8n.example.com', // must match expectedAudience (if set)
iat: now,
exp: now + 30, // short-lived: 30 seconds
jti: randomUUID(), // unique per request
email: 'user@example.com', // required for first-time users
given_name: 'Jane', // optional
family_name: 'Doe', // optional
role: 'global:member', // optional
},
privateKey,
{ algorithm: 'RS256', header: { kid: 'my-key-1' } }
);
最长有效期为 60 秒
嵌入登录流程的 JWT 有效期(exp - iat)不能超过 60 秒,n8n 会在服务器端强制执行此限制。
第 2 步:将令牌发送到嵌入端点
以表单提交方式,将令牌发送到 POST /rest/auth/embed。将 iframe 的 src 指向后端上的一个页面,该页面返回自动提交的表单:
<form method="POST" action="https://your-n8n.example.com/rest/auth/embed">
<input type="hidden" name="token" value="<jwt>">
<input type="hidden" name="redirectTo" value="/workflow/abc123">
</form>
<script>document.forms[0].submit();</script>
可选的 redirectTo 字段仅接受以 / 开头的相对路径。对于绝对 URL 或其他值,n8n 回退到 /。
还提供 GET /rest/auth/embed?token=<jwt>&redirectTo=/workflow/abc123 形式。优先使用 POST 表单:使用 GET 时,令牌会出现在服务器和代理日志、浏览器历史中,也可能出现在 Referer 标头中。如果必须使用 GET,请确保基础设施从日志中清除查询字符串。
第 3 步:n8n 验证并签发会话
n8n 验证 JWT 签名,解析或预配用户,参阅用户预配,设置安全会话 Cookie(SameSite=None; Secure),并重定向到指定路径。
第三方 Cookie 限制
n8n 与产品位于不同的可注册域名时,会话 Cookie 属于第三方 Cookie。Safari 智能防跟踪和 Chrome 第三方 Cookie 限制等浏览器隐私功能,可能阻止或分区存储此 Cookie,导致 iframe 登录失败。将 n8n 托管在产品站点的子域名上,例如 automation.your-product.example.com,可避免此问题。