임베딩 파트너를 위한 토큰 교환
n8n v2.25토큰 교환은 환경 플래그 뒤에 있는 프리뷰 기능입니다. OAuth 2.0 토큰 교환(RFC 8693{:target="_blank" .external-link})을 사용하면 임베딩 파트너가 임베드된 n8n 인스턴스 내에서 사용자를 인증하고 대리로 작업을 수행할 수 있습니다.
- Enterprise 플랜에서 사용 가능합니다.
N8N_ENV_FEAT_TOKEN_EXCHANGE환경 변수를true로 설정하면 활성화됩니다. 셀프 호스팅 인스턴스는 직접 설정할 수 있습니다. Cloud 환경에서는 n8n 지원팀에 요청하세요.- 서명된 JWT를 발급하는 외부 IdP(ID 공급자) 또는 백엔드를 운영하는 임베딩 파트너를 위해 설계되었습니다.
토큰 교환은 환경 플래그 뒤에 있는 프리뷰 기능입니다. 환경 변수, 엔드포인트 경로, JWT 클레임 계약은 기능이 GA(General Availability)에 도달하기 전에 변경될 수 있습니다. n8n 버전을 고정하고 업그레이드 후 연동을 재테스트하세요.
OAuth 2.0 토큰 교환(RFC 8693{:target="_blank" .external-link})을 사용하면 임베딩 파트너가 임베드된 n8n 인스턴스 내에서 사용자를 인증하고 대리로 작업을 수행할 수 있습니다. 토큰 교환은 두 가지 사용 사례를 지원합니다:
- Iframe SSO: 외부 JWT를 n8n 세션 쿠키로 교환하여 iframe에 n8n을 임베드할 때 사용자가 원활하게 로그인할 수 있도록 합니다.
- 위임된 API 접근: 외부 JWT를 n8n 액세스 토큰으로 교환하여 사용자를 대신해 n8n API를 호출합니다. 예를 들어 워크플로를 트리거하거나 자격 증명을 관리할 수 있습니다.
두 흐름은 동일한 방식으로 시작됩니다. 백엔드에서 개인 키로 서명된 단기 JWT를 발급합니다. n8n은 등록된 공개 키를 사용하여 이를 검증하고, 사용자를 확인한 다음 세션 쿠키 또는 액세스 토큰을 반환합니다.
시작하기 전에#
다음이 필요합니다:
- 토큰 교환 기능이 활성화된 Enterprise 라이선스.
- 인스턴스에 프리뷰 기능 플래그 설정:
N8N_ENV_FEAT_TOKEN_EXCHANGE=true. - RSA 또는 EC 키 쌍, 또는 IdP가 이미 게시한 JWKS 엔드포인트. 키 쌍 생성을 참고하세요.
- HTTPS로 제공되는 n8n 인스턴스. 브라우저는 일반 HTTP에서
SameSite=None; Secure세션 쿠키를 거부하므로 iframe SSO 흐름이 자동으로 실패합니다. TLS 종료 프록시 뒤에서 n8n을 실행하는 경우, 프록시가X-Forwarded-Proto헤더를 설정하는지 확인하세요.
키 쌍 생성#
비대칭 키 쌍이 필요합니다. 백엔드에서 개인 키로 JWT에 서명하고, n8n은 공개 키로 이를 검증합니다.
# 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
# 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
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은 audience 검증을 완전히 건너뜁니다. 프로덕션에서는 항상 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은 audience 검증을 완전히 건너뜁니다. 프로덕션에서는 항상 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 |
| 에드워즈 곡선 | EdDSA |
정적 키의 경우, 구성의 알고리즘은 모두 동일한 계열에 속하고 키 유형과 일치해야 합니다. JWKS 키의 경우, n8n은 JWK의 alg, kty, crv 필드에서 알고리즘을 자동으로 추론합니다.
필수 및 선택적 JWT 클레임#
필수 클레임#
n8n이 수락하려면 IdP 토큰에 다음 클레임이 포함되어야 합니다:
| 클레임 | 유형 | 설명 |
|---|---|---|
sub |
문자열 | 주체 식별자. IdP에서의 고유 사용자 ID. |
iss |
문자열(URL) | 발급자. 신뢰할 수 있는 키 구성의 issuer와 일치해야 합니다. |
aud |
문자열 또는 문자열 배열 | Audience. 반드시 존재해야 합니다. n8n은 신뢰할 수 있는 키 소스에 expectedAudience가 구성된 경우에만 값을 검증합니다. |
iat |
숫자 | 발급 시각 타임스탬프(Unix epoch 초). |
exp |
숫자 | 만료 타임스탬프(Unix epoch 초). |
jti |
문자열 | 고유 토큰 ID. n8n은 각 값을 한 번만 허용합니다(재전송 방지). |
선택적 클레임#
| 클레임 | 유형 | 설명 |
|---|---|---|
email |
문자열(유효한 이메일) | 기존 n8n 사용자를 매칭하는 데 사용되는 사용자 이메일. JIT 프로비저닝(n8n이 아직 알지 못하는 사용자의 첫 로그인)에 필수입니다. 모든 사용자가 이미 존재한다고 확신하지 않는 한 항상 전송하세요. |
given_name |
문자열 | n8n 사용자 프로필에 동기화되는 이름. |
family_name |
문자열 | n8n 사용자 프로필에 동기화되는 성. |
role |
문자열 | 할당할 n8n 역할. 예: global:member 또는 global:admin. 사용자 프로비저닝을 참고하세요. |
nbf |
숫자 | Not-before 타임스탬프. |
Iframe SSO 흐름#
iframe에 n8n을 임베드할 때 이 흐름을 사용하세요. n8n은 세션 쿠키로 투명하게 사용자를 로그인시킵니다.
소스 코드 보기
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</code></pre></details></div>
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 서명을 검증하고, 사용자를 확인하거나 프로비저닝하며(사용자 프로비저닝 참고), 보안 세션 쿠키(SameSite=None; Secure)를 설정하고, 지정된 경로로 리디렉션합니다.
서드파티 쿠키 제한#
n8n이 제품과 다른 등록 가능한 도메인에서 실행될 때 세션 쿠키는 서드파티 쿠키가 됩니다. Safari의 지능형 추적 방지 및 Chrome의 서드파티 쿠키 제한과 같은 브라우저 개인 정보 보호 기능이 이를 차단하거나 파티션화하여 iframe 로그인이 중단될 수 있습니다. 이를 방지하려면 제품 사이트의 서브도메인(예: automation.your-product.example.com)에서 n8n을 호스팅하세요.
위임된 API 접근 흐름#
워크플로를 트리거하거나 프로그래밍 방식으로 자격 증명을 관리하는 등 백엔드에서 사용자를 대신해 n8n API를 호출해야 할 때 이 흐름을 사용하세요.
이 흐름은 위임을 위한 선택적 행위자 토큰을 지원합니다. 서비스 계정이나 관리자 같은 행위자가 주체(최종 사용자)를 대신해 행동합니다. 이를 통해 감사 귀속이 가능하여 n8n은 누가 작업을 수행했는지와 누구를 대신해서인지 모두 기록합니다.
행위자 토큰을 제공하면 n8n은 행위자의 ID와 권한으로 API 호출을 인가합니다. n8n은 감사 귀속을 위해서만 주체를 기록하며, 주체는 토큰이 할 수 있는 것을 제한하지 않습니다. 이는 주체가 일반적으로 유효 주체로 남는 RFC 8693과 다릅니다. 연동에 필요한 권한만 부여하는 n8n 역할을 가진 행위자를 사용하세요.
Mermaid 다이어그램 (11줄)소스 코드 보기
sequenceDiagram
participant Backend as Your backend
participant n8n as n8n instance
Backend->>Backend: 1. Mint subject JWT (+ optional actor JWT)
Backend->>n8n: 2. POST /rest/auth/oauth/token (token-exchange grant)
n8n->>n8n: 3. Verify JWTs
n8n->>n8n: 4. Resolve users
n8n->>n8n: 5. Issue n8n access token
n8n-->>Backend: 6. access_token, token_type, expires_in
Backend->>n8n: 7. Call n8n APIs with Bearer token</code></pre></details></div>
1단계: 백엔드에서 JWT 발급#
최종 사용자를 나타내는 주체 토큰을 생성하세요. 임베드 흐름의 60초 제한은 여기에 적용되지 않습니다. 발급된 n8n 액세스 토큰은 주체 토큰의 남은 유효 시간, 행위자 토큰의 남은 유효 시간(있는 경우), N8N_TOKEN_EXCHANGE_MAX_TOKEN_TTL(기본 15분) 중 최솟값 후에 만료됩니다.
const now = Math.floor(Date.now() / 1000);
const subjectToken = jwt.sign(
{
sub: 'end-user-id',
iss: 'https://your-backend.example.com',
aud: 'https://your-n8n.example.com',
iat: now,
exp: now + 900, // bounds the issued token's lifetime
jti: randomUUID(),
email: 'user@example.com',
role: 'global:member',
},
privateKey,
{ algorithm: 'RS256', header: { kid: 'my-key-1' } }
);
위임의 경우, 작업을 수행하는 서비스 또는 관리자를 나타내는 행위자 토큰도 발급하세요. 행위자의 n8n 역할이 발급된 토큰의 권한을 결정하므로 가능한 한 최소 권한으로 유지하세요:
const actorToken = jwt.sign(
{
sub: 'service-account-id',
iss: 'https://your-backend.example.com',
aud: 'https://your-n8n.example.com',
iat: now,
exp: now + 900,
jti: randomUUID(),
email: 'service@example.com',
},
privateKey,
{ algorithm: 'RS256', header: { kid: 'my-key-1' } }
);
2단계: n8n 액세스 토큰으로 교환#
curl -X POST https://your-n8n.example.com/rest/auth/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
-d "subject_token=<subject-jwt>" \
-d "actor_token=<actor-jwt>"
요청 필드(application/x-www-form-urlencoded):
필드
필수 여부
설명
grant_type
예
urn:ietf:params:oauth:grant-type:token-exchange이어야 합니다.
subject_token
예
최종 사용자를 나타내는 JWT.
subject_token_type
아니오
토큰 유형 식별자. n8n은 이 필드를 수락하지만 무시합니다. RFC 8693에서 필수이므로 사양 준수 클라이언트 라이브러리를 사용하는 경우 urn:ietf:params:oauth:token-type:jwt를 전송하세요.
actor_token
아니오
행위자를 나타내는 JWT(위임용).
actor_token_type
아니오
행위자 토큰 유형 식별자. subject_token_type과 같이 수락되지만 무시됩니다.
requested_token_type
아니오
요청된 토큰 유형 식별자. 수락되지만 무시됩니다. n8n은 항상 액세스 토큰을 발급합니다.
scope
아니오
요청된 스코프(최대 1024자). 발급된 토큰과 감사 이벤트에 기록되지만 강제되지는 않습니다.
audience
아니오
의도된 audience(최대 1024자). 수락되지만 무시됩니다.
resource
아니오
타깃 리소스 URI, 공백으로 구분(최대 2048자). 발급된 토큰과 감사 이벤트에 기록되지만 강제되지는 않습니다.
스코프는 강제되지 않습니다
발급된 액세스 토큰은 행위 사용자의 전체 권한을 가집니다. 행위자 토큰을 전송하면 행위자의 권한, 그렇지 않으면 주체의 권한입니다. n8n은 scope와 resource를 감사 목적으로만 기록하며 토큰을 그것들로 제한하지 않습니다. 연동이 할 수 있는 것을 제한하려면 낮은 권한의 행위자와 allowedRoles 설정을 사용하세요.
성공 응답(200 OK):
{
"access_token": "<n8n-jwt>",
"token_type": "Bearer",
"expires_in": 900,
"issued_token_type": "urn:ietf:params:oauth:token-type:access_token"
}
3단계: 액세스 토큰 사용#
이후 n8n API 호출에 토큰을 포함하세요:
curl https://your-n8n.example.com/api/v1/workflows \
-H "Authorization: Bearer <access-token>"
토큰은 expires_in초 후에 만료됩니다. 만료되면 새 토큰을 요청하세요. 각 jti는 일회용이므로 원래 외부 JWT를 재사용하지 마세요.
사용자 프로비저닝#
토큰을 교환하면 n8n은 다음 순서로 외부 ID를 n8n 사용자로 확인합니다:
- 알려진 ID: n8n은 ID 저장소에서
sub 클레임을 조회합니다. 이전 교환에서 이 sub를 n8n 사용자에 이미 연결한 경우, n8n은 해당 사용자를 반환합니다.
- 이메일 폴백:
sub를 알 수 없지만 JWT에 email 클레임이 포함된 경우, n8n은 해당 이메일을 가진 기존 사용자를 검색합니다. 찾으면 n8n은 이후 해당 사용자에 외부 ID를 연결합니다.
- JIT(Just-in-Time) 프로비저닝: 둘 다 일치하지 않으면 n8n은 자동으로 새 사용자를 생성합니다. 이를 위해 JWT에
email 클레임이 포함되어야 합니다. n8n은 비밀번호 로그인이 비활성화된 상태로 새 사용자를 생성하므로 토큰 교환을 통해서만 인증할 수 있습니다.
역할 처리#
role 클레임을 포함하면 IdP가 해당 사용자의 글로벌 역할의 진실 공급원이 됩니다. n8n은 모든 교환 시 이를 적용하여 n8n UI에서 할당된 역할을 덮어씁니다. IdP에서 역할을 관리하지 않는 한 클레임을 생략하세요.
시나리오
동작
신규 사용자, role 클레임 없음
global:member 할당.
신규 사용자, role 클레임 있음
클레임된 역할 할당. 역할이 인식되지 않거나 global:owner이거나 allowedRoles에 없는 경우 교환 실패.
기존 사용자, role 클레임 없음
역할 변경 없음.
기존 사용자, 유효한 role 클레임
다른 경우 역할 업데이트. 역할이 allowedRoles에 없는 경우 교환 실패.
기존 사용자, 인식되지 않거나 global:owner 역할 클레임
서버 측 경고와 함께 클레임 무시. 역할 변경 없이 로그인 진행.
global:owner인 기존 사용자
역할 동기화 완전히 건너뜀. 소유자 역할은 토큰 교환을 통해 변경할 수 없습니다.
프로필 동기화#
n8n은 각 로그인 시 given_name 및 family_name 클레임을 사용자 프로필에 동기화합니다. 저장된 값과 다를 때만 변경 사항을 적용하며, 32자를 초과하는 값은 잘라냅니다.
보안 고려 사항#
단기 토큰#
- 임베드 흐름의 외부 JWT는 최대 60초의 유효 시간을 가져야 합니다(
exp - iat <= 60).
- 토큰 교환 흐름의 경우, 발급된 n8n 토큰 만료는 주체 토큰의 남은 유효 시간, 행위자 토큰의 남은 유효 시간(있는 경우),
N8N_TOKEN_EXCHANGE_MAX_TOKEN_TTL(기본 900초) 중 최솟값입니다.
- 토큰 교환 흐름에서, n8n은 발급된 토큰의 계산된 만료가 5초 미만인 경우 요청을 거부합니다.
재전송 방지#
모든 외부 JWT는 고유한 jti(JWT ID) 클레임을 포함해야 합니다. n8n은 각 jti를 기록하고 이미 사용된 jti가 있는 토큰을 거부합니다. n8n은 만료된 jti 레코드를 자동으로 정리합니다.
비대칭 서명만 허용#
n8n은 비대칭 알고리즘(RSA, EC, EdDSA)만 허용합니다. 설계상 HS256과 같은 HMAC 알고리즘과 none을 거부합니다. 이는 n8n이 서명 비밀에 접근할 필요가 없음을 보장합니다.
Audience 제한#
모든 신뢰할 수 있는 키 소스에 expectedAudience를 설정하세요. 설정하지 않으면 n8n이 audience 검증을 건너뛰어, 구성된 발급자로부터의 유효한 토큰이면 다른 서비스나 다른 n8n 인스턴스를 위해 IdP가 발급한 토큰도 교환할 수 있게 됩니다. n8n 베이스 URL과 같이 인스턴스별 값을 사용하세요.
역할 제약#
신뢰할 수 있는 키 소스의 allowedRoles 필드는 토큰 교환을 통해 할당할 수 있는 역할을 제한합니다. 예를 들어 임베딩 연동이 global:member 사용자만 프로비저닝하도록 제한하여 최소 권한을 강제하는 데 사용하세요. OAuth scope 및 resource 요청 필드는 권한을 제한하지 않습니다. 2단계: n8n 액세스 토큰으로 교환을 참고하세요.
프레이밍 제한#
임베드 세션 쿠키는 SameSite=None을 사용하므로 브라우저는 서드파티 iframe 컨텍스트에서 이를 전송합니다. n8n 인스턴스를 프레임할 수 있는 사이트를 제한하세요. 리버스 프록시가 제품의 오리진만 나열하는 Content-Security-Policy: frame-ancestors 헤더를 전송하도록 구성하세요. 프레이밍 보호를 전체적으로 비활성화하지 마세요.
키 교체 및 손상#
다운타임 없이 키를 교체하려면 N8N_TOKEN_EXCHANGE_TRUSTED_KEYS에 새 kid가 있는 새 항목을 추가하고, 백엔드가 새 키로 서명하도록 전환한 다음, 이전 항목을 제거하세요. 개인 키가 손상된 경우, 해당 항목을 즉시 제거하세요. 이렇게 하면 새로운 교환이 중단되지만, 이미 발급된 n8n 토큰과 세션은 만료될 때까지 유효하게 유지되며, 단기 토큰 유효 시간에 의해 제한됩니다.
감사 귀속#
n8n은 모든 토큰 교환 활동에 대한 감사 이벤트를 내보냅니다:
이벤트
발생 시점
n8n.audit.token-exchange.succeeded
성공적인 토큰 교환.
n8n.audit.token-exchange.failed
실패한 토큰 교환(실패 이유 포함).
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
새 외부 ID에 연결된 기존 사용자.
n8n.audit.token-exchange.role-updated
토큰 교환을 통해 변경된 사용자 역할.
행위자 토큰 흐름을 사용하면 n8n은 주체(누구를 대신해서)와 행위자(누가 작업을 수행했는지) 모두를 기록하여 감사 로그에서 완전한 귀속이 가능합니다.
설정 확인#
일회용 JWT를 발급하고 교환하세요. 1단계 예시를 mint-token.js로 저장하고 끝에 console.log(token);을 추가한 다음 실행하세요:
node mint-token.js
그런 다음 토큰 교환 엔드포인트를 호출하세요:
curl -X POST https://your-n8n.example.com/rest/auth/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
-d "subject_token=<jwt>"
access_token이 포함된 200 응답은 키, 클레임, 구성이 올바르게 작동함을 확인합니다. 임베드 흐름의 경우, 브라우저에서 POST /rest/auth/embed에 새 토큰을 제출하고 n8n UI가 로드되는지 확인하세요.
문제 해결#
증상
가능한 원인
두 엔드포인트 모두 404
프리뷰 플래그 N8N_ENV_FEAT_TOKEN_EXCHANGE가 true가 아니거나 라이선스에 토큰 교환 기능이 포함되어 있지 않습니다. 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
토큰 교환 엔드포인트에서의 토큰 검증 실패: 유효하지 않은 서명, 알 수 없는 kid, 누락된 kid 헤더, 재전송된 jti, 만료되거나 곧 만료될 토큰, audience 불일치, 허용되지 않은 역할. n8n은 설계상 일반 설명을 반환합니다. 구체적인 이유는 n8n 서버 로그를 확인하세요.
400 - invalid_request, 설명 Token claims validation failed
토큰 교환 엔드포인트에서: JWT에 필수 클레임(sub, iss, aud, iat, exp, jti)이 없거나 클레임 유형이 잘못되었습니다.
임베드 엔드포인트에서 401
응답에 구체적인 실패 메시지가 포함됩니다. 예: Token has already been used(재전송된 jti), Token lifetime exceeds maximum allowed(임베드 토큰은 exp - iat <= 60초여야 함), Token header missing kid.
임베드 엔드포인트에서 500
JWT에 필수 클레임이 없거나 클레임 유형이 잘못되었습니다.
iframe에 원시 JSON 오류 표시
임베드 로그인 실패는 오류 리디렉션 없이 JSON 응답을 반환합니다. 위의 행과 메시지를 비교하세요.
임베드 로그인 후 세션 쿠키가 설정되지 않음
인스턴스가 HTTPS로 제공되지 않거나 TLS 종료 프록시가 X-Forwarded-Proto를 전달하지 않습니다. 브라우저는 일반 HTTP에서 SameSite=None; Secure 쿠키를 거부합니다. 서드파티 쿠키 제한도 쿠키를 차단할 수 있습니다. 서드파티 쿠키 제한을 참고하세요.
첫 로그인 시 사용자가 생성되지 않음
JWT에 JIT 프로비저닝에 필수인 email 클레임이 없습니다.
역할이 적용되지 않음
기존 사용자의 경우, n8n은 인식되지 않는 역할 클레임과 global:owner 역할 클레임을 서버 측 경고와 함께 무시합니다. allowedRoles에 없는 유효한 역할은 교환을 거부합니다. 역할 처리를 참고하세요.
관련 리소스#
- OEM 배포 개요: 제품 내에 n8n 인터페이스를 임베드하고 표면화합니다.
- SSO 설정: SAML 또는 OIDC를 통한 조직 전체 싱글 사인온.
- HTTP Request 자격 증명: OAuth2 사용: 일반 OAuth 2.0 자격 증명 설정.
- OAuth 2.0 토큰 교환 (RFC 8693){:target="_blank" .external-link}: 토큰 교환 사양.