Token Exchange (RFC 8693)
Dans les architectures microservices, un seul token émis au login n’est souvent pas adapté à tous les services de la chaîne d’appels. Les problèmes courants :
- Le token a trop de privilèges pour un service spécifique (violation du principe du moindre privilège)
- Le token a le mauvais audience pour le service cible
- Un admin doit usurper l’identité d’un utilisateur pour les opérations de support
- Un service intermédiaire doit déléguer l’identité de l’utilisateur original à un service downstream
Token Exchange (RFC 8693) fournit un mécanisme standard pour échanger un token existant contre un nouveau token avec une portée, un audience ou un sujet différent.
La Requête d’Échange
POST /api/auth/token
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer ORIGINAL_ACCESS_TOKEN
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token=ORIGINAL_ACCESS_TOKEN
&subject_token_type=urn:ietf:params:oauth:token-type:access_token
&requested_token_type=urn:ietf:params:oauth:token-type:access_token
&audience=payment-service
&scope=payments:read payments:createParamètres :
| Paramètre | Requis | Description |
|---|---|---|
subject_token | Oui | Token représentant l’identité à échanger |
subject_token_type | Oui | Type du subject_token (access_token, id_token, refresh_token) |
requested_token_type | Non | Type du token désiré en retour |
audience | Non | Service(s) destinataire(s) du nouveau token |
scope | Non | Scopes pour le nouveau token (doit être sous-ensemble du sujet) |
actor_token | Non | Token de l’acteur qui effectue la délégation |
actor_token_type | Non | Type de l’actor_token |
Impersonation vs Délégation
Token Exchange supporte deux modes fondamentaux avec des significations et cas d’usage différents.
Impersonation
L’acteur agit en tant que le sujet. Le token résultant ne peut pas être distingué de celui que le sujet aurait obtenu directement.
Cas d’usage : Support technique qui doit tester un problème spécifique à l’utilisateur.
// Token résultant
{
"sub": "user_alice", // Alice est le sujet
"act": {
"sub": "user_admin" // Admin est l'acteur (trace d'audit)
}
}L’impersonation requiert la permission impersonate:users sur le compte de l’acteur.
Délégation
L’acteur agit en faveur de le sujet, mais son identité est clairement distincte. Le sujet original reste visible dans le token.
Cas d’usage : API Gateway qui délègue la requête d’Alice au service Order, qui la délègue ensuite au service Payment.
// Token résultant
{
"sub": "user_alice", // Alice est toujours le sujet
"act": {
"sub": "service_api_gateway" // Acteur direct
}
}La délégation requiert la permission delegate:tokens sur le compte du service acteur.
Chaînes de Délégation
Pour les architectures microservices profondes, le claim act peut être imbriqué pour tracer toute la chaîne de délégation :
{
"sub": "user_alice",
"act": {
"sub": "service_order",
"act": {
"sub": "service_payment",
"act": {
"sub": "service_notification"
}
}
}
}La profondeur maximale de chaîne par défaut est 5 niveaux. Cela prévient les chaînes de délégation infinies et aide à détecter les cycles accidentels.
La profondeur de chaîne maximale est configurable par application dans Administration → Applications → [Application] → Token Exchange.
Propriétés de Sécurité
Réduction de Portée Uniquement
Les tokens échangés ne peuvent jamais avoir plus de scopes que le token sujet original. Tenter d’étendre les scopes résulte en un 400 Bad Request.
Piste d’Audit
Tous les échanges de tokens sont enregistrés dans les audit logs avec :
- Identité de l’acteur
- Identité du sujet
- Type d’opération (impersonation/délégation)
- Scopes demandés vs accordés
- Audience du token résultant
Durée de Vie Réduite
Les tokens échangés ont par défaut une durée de vie de 50% du temps restant du token sujet. Cela limite la fenêtre d’exposition si un token intermédiaire est compromis.
Configuration dans la Console
Dans Administration → Applications → [Application] → Token Exchange :
| Paramètre | Description |
|---|---|
| Activer Token Exchange | Activer le grant type pour cette application |
| Permettre l’impersonation | Permettre aux acteurs d’usurper l’identité d’autres utilisateurs |
| Permettre la délégation | Permettre aux services de déléguer les identités utilisateur |
| Profondeur max de chaîne | Niveaux d’imbrication act maximum (défaut : 5) |
| Durée de vie token échangé | Pourcentage du temps restant du sujet (défaut : 50%) |
Exemple TypeScript — Microservice
// order-service délègue à payment-service pour Alice
async function createPaymentOnBehalfOfUser(
userToken: string,
paymentDetails: PaymentDetails
): Promise<string> {
// Échanger le token utilisateur contre un token payment-service scoped
const exchangeRes = await fetch('https://auth.yourdomain.com/api/auth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: `Bearer ${SERVICE_TOKEN}`, // token du service order
},
body: new URLSearchParams({
grant_type: 'urn:ietf:params:oauth:grant-type:token-exchange',
subject_token: userToken,
subject_token_type: 'urn:ietf:params:oauth:token-type:access_token',
actor_token: SERVICE_TOKEN,
actor_token_type: 'urn:ietf:params:oauth:token-type:access_token',
audience: 'payment-service',
scope: 'payments:create',
// NB : scope réduit — pas accès à tout le token utilisateur original
}),
})
const { access_token } = await exchangeRes.json()
// Utiliser le token échangé pour appeler payment-service
const paymentRes = await fetch('https://payments.internal/api/payments', {
method: 'POST',
headers: { Authorization: `Bearer ${access_token}` },
body: JSON.stringify(paymentDetails),
})
return paymentRes.json()
}Concepts Associés
- Tokens — Structure des access tokens, claims et durées de vie
- OAuth 2.0 & OIDC — Le cadre d’autorisation sous-jacent
- Fine-Grained Authorization — Combiner Token Exchange et FGA pour le contrôle d’accès M2M
- DPoP — Lier les tokens échangés à des clés cryptographiques