Token Exchange
기존 토큰을 서비스별 위임 토큰으로 교환하는 OAuth 2.0 확장
Token Exchange(RFC 8693)는 기존 토큰을 제시하고 다른 대상(audience)을 위한 토큰을 발급받는 OAuth 2.0 확장이다. API Gateway가 사용자 토큰을 각 백엔드 서비스 전용 토큰으로 교환해 권한 최소화와 서비스 간 격리를 구현한다.
개념
OAuth 2.0 Token Exchange(RFC 8693)는 이미 갖고 있는 토큰을 제시하고, 다른 대상(audience)을 위한 새 토큰을 발급받는 방식이다. 사용자가 게이트웨이에서 받은 토큰을 그대로 모든 백엔드에 전달하는 대신, 각 서비스에 맞는 전용 토큰으로 교환해서 전달한다.
사용자 → 게이트웨이 토큰 (전체 scope)
↓ Token Exchange
HR 서비스 전용 토큰 (audience: hr, 제한된 scope)
↓
HR 서비스로 전달
왜 필요한가
게이트웨이 토큰을 모든 서비스에 그대로 전달하면 두 가지 문제가 생긴다.
권한 과잉
사용자의 게이트웨이 토큰에는 전체 권한이 담겨 있다. HR 서비스가 이 토큰을 받으면 HR 서비스 권한 외의 클레임도 볼 수 있고, 토큰이 유출됐을 때 피해 범위가 커진다.
서비스 간 신뢰 경계 불명확
HR 서비스가 게이트웨이 토큰을 그냥 받아들이면, HR 서비스는 "게이트웨이를 신뢰한다"는 암묵적 가정에 의존하게 된다. 명시적 audience 검증이 없으면 다른 서비스의 토큰으로 HR 서비스를 호출하는 것도 허용될 수 있다.
Token Exchange를 쓰면 각 토큰의 aud 클레임이 특정 서비스로 고정된다. HR 서비스는 aud: hr인 토큰만 받아들이면 되고, 다른 서비스의 토큰으로는 접근이 불가능하다.
동작 방식
Keycloak token 엔드포인트에 특수한 grant_type으로 요청한다.
POST /realms/{realm}/protocol/openid-connect/token
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
client_id=api-gateway
client_secret=<secret>
subject_token=<게이트웨이 access_token>
subject_token_type=urn:ietf:params:oauth:token-type:access_token
audience=hr
Keycloak이 subject_token을 검증하고, audience로 지정된 서비스에 맞는 새 토큰을 발급한다. 새 토큰의 aud 클레임은 hr로 설정되고, 해당 서비스에서만 유효하다.
API Gateway에서의 활용
경로 기반 audience 매핑
요청 경로의 prefix로 어떤 서비스인지 판별한다.
/api/hr/* → audience: hr
/api/dmp/* → audience: dmp-common
/api/logistics/* → audience: logistics
세션 캐시
매 요청마다 Keycloak에 Token Exchange를 요청하면 지연이 생긴다. 발급받은 토큰을 세션의 exchanged_tokens 필드에 캐시하고, 만료 30초 전까지 재사용한다.
{
"exchanged_tokens": {
"hr": {
"token": "eyJhbGci...",
"expires_at": 1713607200.0
}
}
}
백엔드 서비스의 검증 의무
게이트웨이가 Token Exchange를 해서 보내더라도, 백엔드 서비스는 토큰을 직접 검증해야 한다. 서명, iss, aud, exp를 검증하지 않으면 Token Exchange를 쓰는 의미가 없다. "게이트웨이가 보냈으니 믿는다"는 암묵적 신뢰는 Token Exchange 패턴의 목적에 반한다.
헤더 신뢰 방식과 비교
Token Exchange를 쓰지 않고 게이트웨이가 헤더로 사용자 정보를 전달하는 방식이 있다.
헤더 방식:
게이트웨이 → X-User-Sub: alice, X-User-Roles: ["admin"] → 백엔드
Token Exchange 방식:
게이트웨이 → Authorization: Bearer <signed JWT> → 백엔드
| 헤더 방식 | Token Exchange | |
|---|---|---|
| 검증 | 없음 (게이트웨이 신뢰) | JWT 서명 검증 |
| 위변조 | 헤더 조작 가능 | 불가능 (private key 필요) |
| 네트워크 격리 의존 | 강하게 의존 | 의존하지 않아도 됨 |
| 구현 복잡도 | 낮음 | 높음 |
헤더 방식이 완전히 틀린 건 아니다. mTLS로 서비스 간 통신을 암호화하고, 네트워크 레벨에서 게이트웨이 외 접근을 차단하면 허용 가능하다. 그 보장이 없는 환경에서는 Token Exchange를 써야 한다.
더 보기
- API-Gateway — Token Exchange를 운용하는 게이트웨이 구조
- 세션-보안 — exchanged_tokens 캐시가 저장되는 Redis 세션 구조
- OAuth-OIDC-SAML — Token Exchange의 기반이 되는 OAuth 2.0 개요
- RBAC — 서비스별 토큰에 담기는 권한 모델