TutorielGuides

Déconnexion OpenID Connect par canal dérobé

Sur cette page

Avec la connexion unique, un utilisateur ouvre plusieurs applications avec une seule authentification TOSIAM. Mais chaque application garde aussi sa propre session : quand il se déconnecte de l’une, les autres le croient toujours connecté.

La déconnexion par canal dérobé (OpenID Connect Back-Channel Logout) règle ce problème. Quand la session TOSIAM se termine, TOSIAM envoie directement, de serveur à serveur, un jeton de déconnexion (logout token) signé à chaque application où l’utilisateur s’était connecté. Chacune vérifie le jeton et ferme la session locale de cet utilisateur.

texte
Navigateur ── se déconnecter ──▶ RPA ──▶ TOSIAM /connect/endSession
                                           │ fin de la session TOSIAM
                                           ├── POST logout_token ──▶ RPA /backchannel_logout
                                           └── POST logout_token ──▶ RPB /backchannel_logout ─▶ session RPB fermée

Le code est dans le dépôt tosiam-samples, dossier oidc_backchannel_logout : une application Node.js (rp), lancée deux fois sous les noms RPA (port 9001) et RPB (port 9005), et une petite API protégée (protected, port 9002).

Étape 1 : le fournisseur#

Dans le royaume ref, le service OAuth2 Provider (voir Fournisseur OAuth2) a deux réglages pour cette fonction :

Réglages du fournisseur OAuth2 : Prendre en charge la déconnexion par canal dérobé activé, Transmettre une revendication sid dans le jeton de déconnexion désactivé Réglages du fournisseur OAuth2 : Prendre en charge la déconnexion par canal dérobé activé, Transmettre une revendication sid dans le jeton de déconnexion désactivé
Déconnexion par canal dérobé activée (1), revendication sid désactivée (2).
  • Prendre en charge la déconnexion par canal dérobé (backChannelLogoutSupported) : à activer. Le document de découverte annonce alors "backchannel_logout_supported": true.
  • Transmettre une revendication sid dans le jeton de déconnexion (backChannelLogoutSessionSupported) : laissez-le désactivé, voir l’encadré ci-dessous.

Activez aussi Autoriser les clients à passer outre le consentement, comme dans le tutoriel Angular, pour ne pas demander de consentement.

Étape 2 : les clients#

Créez deux clients rpa et rpb, de type Confidentiel : ces applications tournent sur un serveur et gardent un secret. Pour rpa :

Faites défiler le tableau
RéglageValeur
URI de redirectionhttp://localhost:9001/callback
Scope(s)openid, profile
Méthode d’authentification au point d’accès des jetonsclient_secret_basic
Paramètre Code verifier requis (PKCE)activé
Consentement impliciteactivé
URI de redirection après déconnexion (onglet OpenID Connect)http://localhost:9001/loggedout
Algorithme de signature du jeton d’identitéRS256
URI de déconnexion par canal dérobé (onglet OpenID Connect)http://localhost:9001/backchannel_logout
Identifiant de session requis à la déconnexion par canal dérobédésactivé

rpb a les mêmes réglages avec le port 9005.

Fiche du client rpa, onglet OpenID Connect : URI de déconnexion par canal dérobé http://localhost:9001/backchannel_logout, identifiant de session requis désactivé Fiche du client rpa, onglet OpenID Connect : URI de déconnexion par canal dérobé http://localhost:9001/backchannel_logout, identifiant de session requis désactivé
URI de déconnexion par canal dérobé (1) et identifiant de session non requis (2).

L’algorithme RS256 compte double : TOSIAM signe le jeton de déconnexion avec l’algorithme du jeton d’identité du client. Avec la valeur par défaut HS256, le jeton serait signé avec le secret du client au lieu de la clé publiée par le fournisseur.

Étape 3 : recevoir le jeton de déconnexion#

Chaque application expose POST /backchannel_logout. TOSIAM y envoie un formulaire application/x-www-form-urlencoded avec un seul champ, logout_token, un JWT signé.

L’application doit vérifier ce jeton avant d’agir. La spécification impose de contrôler la signature, l’émetteur, l’audience, la présence de l’événement de déconnexion et l’absence de nonce. L’exemple utilise la bibliothèque jose et les clés publiques du fournisseur (jwks_uri du document de découverte) :

javascript
const { payload } = await jwtVerify(logoutToken, jwks, {
  issuer: ISSUER,                 // http://localhost:8080/tosiam/oauth2/ref
  audience: CLIENT_ID,            // rpa ou rpb
  algorithms: ['RS256'],
  maxTokenAge: '2 minutes',
  requiredClaims: ['iat', 'exp', 'jti'],
});
const event = payload.events?.[BACKCHANNEL_EVENT];  // http://schemas.openid.net/event/backchannel-logout
if (!(event === '{}' || (event && typeof event === 'object'))) throw new Error('événement absent');
if ('nonce' in payload) throw new Error('nonce interdit dans un jeton de déconnexion');
if (!payload.sub && !payload.sid) throw new Error('ni sub ni sid');
if (seenJti.has(payload.jti)) throw new Error('jti déjà reçu (rejeu)');

Le jeton reçu est accepté une seule fois (jti mémorisé jusqu’à son expiration). La valeur de l’événement est normalement l’objet vide {} ; TOSIAM envoie aujourd’hui la chaîne "{}", que l’exemple accepte aussi.

Il faut ensuite retrouver les sessions de l’utilisateur. L’application tient, à la connexion, un index sub → sessions ; à la réception du jeton, elle ferme toutes les sessions de ce sub et répond 200 :

javascript
const ids = [...(sessionsBySub.get(claims.sub) ?? [])];
for (const id of ids) {
  await destroySession(id);   // session express-session de l'utilisateur
}
res.set('Cache-Control', 'no-store');
res.sendStatus(200);

Un jeton invalide reçoit 400 : TOSIAM le journalise, sans réessayer.

Étape 4 : lancer et tester#

Dans trois terminaux :

bash
cd tosiam-samples/oidc_backchannel_logout/rp && npm install && npm run rpa   # http://localhost:9001
cd tosiam-samples/oidc_backchannel_logout/rp && npm run rpb                  # http://localhost:9005
cd tosiam-samples/oidc_backchannel_logout/protected && npm install && npm start

Ouvrez http://localhost:9001, cliquez sur Se connecter et authentifiez-vous dans TOSIAM. Appeler l’API vérifie que le jeton d’accès est accepté.

Application RPA : connecté en tant que dduck, boutons Appeler l'API et Se déconnecter, réponse de l'API protégée Application RPA : connecté en tant que dduck, boutons Appeler l'API et Se déconnecter, réponse de l'API protégée
RPA après la connexion, avec la réponse de l’API protégée.

Ouvrez ensuite http://localhost:9005 et cliquez sur Se connecter : RPB obtient sa session sans redemander le mot de passe, grâce à la session TOSIAM.

Application RPB : connecté en tant que dduck sans nouvelle saisie du mot de passe Application RPB : connecté en tant que dduck sans nouvelle saisie du mot de passe
RPB, connecté par la connexion unique.

Revenez sur RPA et cliquez sur Se déconnecter. RPA ferme sa session, puis redirige le navigateur vers le point de fin de session de TOSIAM (/connect/endSession, avec id_token_hint). TOSIAM ferme sa propre session et envoie un jeton de déconnexion à RPA et à RPB. Rafraîchissez RPB : l’utilisateur n’y est plus connecté, et la page indique l’avis reçu.

Application RPB après la déconnexion depuis RPA : non connecté, avis de déconnexion reçu pour le sub dduck, une session fermée Application RPB après la déconnexion depuis RPA : non connecté, avis de déconnexion reçu pour le sub dduck, une session fermée
RPB a reçu le jeton de déconnexion et fermé la session de l’utilisateur.

Ce que TOSIAM envoie#

Contenu d’un jeton de déconnexion reçu par RPB (signé RS256, en-tête avec le kid de la clé du fournisseur) :

Faites défiler le tableau
RevendicationValeur
isshttp://localhost:8080/tosiam/oauth2/ref
audidentifiant du client (rpb)
subidentifiant de l’utilisateur (dduck)
iat, expémission et expiration (durée de vie des jetons d’identité du client)
jtiidentifiant unique du jeton
events{"http://schemas.openid.net/event/backchannel-logout": "{}"}

TOSIAM n’avertit que les applications où l’utilisateur s’est connecté pendant cette session : il les enregistre dans la session au moment de chaque autorisation. Les envois se font l’un après l’autre, pendant la requête de déconnexion ; une réponse 200 ou 204 est attendue. Les échecs sont journalisés (journal amSession) et chaque envoi produit l’événement d’audit AM-BACK-CHANNEL-LOGOUT.

Quand TOSIAM envoie-t-il les jetons ?#

Faites défiler le tableau
Fin de la session TOSIAMJetons envoyés
Déconnexion demandée par une application (/connect/endSession)Oui
Déconnexion depuis les pages de TOSIAM (XUI)Oui
Suppression de la session par un administrateur (console, page Sessions)Non : dans la version actuelle, la suppression échoue pour ces sessions
Expiration de la session (inactivité, durée maximale)Non
Sessions sans état (stateless)Non pris en charge

Comme l’expiration n’est pas signalée, donnez aux sessions des applications une durée de vie au plus égale à celle des sessions TOSIAM.

En cas de problème#

Faites défiler le tableau
SymptômeCause probable
RPB reste connecté après la déconnexion de RPAURI de déconnexion par canal dérobé absente du client rpb, ou fournisseur sans Prendre en charge la déconnexion par canal dérobé ; RPB s’était connecté avant l’activation
L’application refuse le jeton (400, algorithme ou signature)Client en HS256 : passez l’algorithme de signature du jeton d’identité en RS256
L’application répond 400 « jti déjà reçu »Même jeton reçu deux fois (rejeu) : comportement attendu
Rien n’est reçu alors que TOSIAM est configuréL’application n’est pas joignable depuis le serveur TOSIAM (pare-feu, localhost d’une autre machine) ; voir le journal amSession
Deux applications sur localhost se déconnectent l’une l’autreElles partagent le même nom de cookie de session : les cookies ne distinguent pas les ports

Pour aller plus loin#

Mis à jour le