OpenID Connect · OAuth 2.0
Ces mécanismes renforcent la sécurité du flux de base. Chacun répond à une faille précise : on commence par le problème, puis la solution.
Le fil rouge
Le flux de base envoie des informations dans le navigateur, où tout le monde peut y toucher. Les fonctions avancées fixent ça à chaque étape : PAR et JAR protègent la demande, private_key_jwt protège l’identité de l’application, JARM protège la réponse, DPoP protège le jeton une fois émis, et CIBA traite le cas où il n’y a pas de navigateur. Viennent s’y ajouter resource (à quelle API), RAR (quelle opération précise), acr (quel niveau d’authentification) et l’enregistrement dynamique des clients.
private_key_jwt · PAR · JAR · JARM · DPoP · CIBA · Resource indicators · RAR · acr et step-up · Enregistrement dynamique de clients · Pairwise et userinfo signé
RFC 7523 / OIDC Core 9
Prouver qui vous êtes avec une signature plutôt qu’avec un mot de passe partagé.
Un client confidentiel s’authentifie normalement avec un « client_secret », un mot de passe que le client et le serveur connaissent tous les deux. S’il fuit d’un côté, l’autre n’a aucun moyen de le savoir, et le secret circule dans des fichiers de configuration.
Le client garde une clé privée qui ne quitte jamais chez lui. À chaque appel, il fabrique une petite « attestation » signée (un JWT) qui dit « je suis tel client, je m’adresse à ce serveur, et ceci n’est valable que quelques minutes ». Le serveur ne possède que la clé publique : il vérifie la signature, c’est tout.
En image
Au lieu d’un mot de passe que le gardien connaît, vous signez chaque entrée avec votre stylo personnel ; le gardien a seulement un spécimen de votre signature.
Banques, administrations, tout environnement qui interdit les secrets partagés. Obligatoire dans plusieurs profils de sécurité (FAPI).
Attestation rejouée (d’où l’identifiant « jti » à usage unique), audience incorrecte, durée trop longue, mauvaise clé.
Créez un client de type « Confidentiel par clé (private_key_jwt) » dans votre fournisseur de test, collez-y votre certificat ou votre JWKS public, puis appelez /token avec client_assertion. ID-Kit vous dit précisément pourquoi une attestation est refusée.
RFC 9126
Déposer sa demande de connexion directement chez le serveur, sans la faire passer par le navigateur.
Normalement, la demande (qui je suis, ce que je veux, où revenir) est écrite dans l’adresse de la page et traverse le navigateur : l’utilisateur ou un programme malveillant peut la lire ou la modifier, et les adresses très longues posent problème.
L’application envoie d’abord toute sa demande au serveur, de serveur à serveur, en s’authentifiant. Le serveur répond par un simple « ticket » (request_uri) valable une minute. L’application envoie alors le navigateur vers le serveur avec seulement ce ticket. Le ticket ne sert qu’une fois.
En image
Au lieu de dicter votre commande à voix haute dans la salle, vous la remettez en cuisine et vous ne montrez à la salle qu’un numéro de ticket.
Dès qu’on veut empêcher qu’un paramètre soit falsifié (montant, scopes, redirect_uri) : banque, santé, paiement. Exigé par FAPI.
Ticket expiré (60 s), déjà utilisé, ou présenté par un autre client que celui qui l’a déposé.
Fournisseur de test : appelez /par. Côté application de test, choisissez le flux « code_par ». Le réglage « Exiger PAR » du fournisseur refuse les demandes classiques.
RFC 9101
Signer la demande de connexion pour que personne ne puisse la modifier en route.
Même avec les paramètres dans l’adresse, rien ne prouve qu’ils viennent bien de l’application et qu’ils n’ont pas été changés.
Les paramètres sont rassemblés dans un JWT signé avec la clé du client (paramètre « request »). Le serveur vérifie la signature, l’émetteur, l’audience et la date. Les paramètres du JWT remplacent ceux de l’adresse.
En image
Une lettre sous enveloppe scellée à la cire : si le sceau est brisé, le serveur le voit.
Avec PAR dans les profils de sécurité élevés ; seul, quand on n’a pas de canal serveur à serveur.
Clé publique du client non enregistrée, « aud » qui n’est pas l’émetteur du serveur, JWT expiré, client_id différent.
Enregistrez la clé publique du client puis envoyez votre JWT dans « request » ou dans /par. Les mauvaises signatures et audiences sont expliquées.
FAPI JARM
Le serveur signe aussi sa réponse : l’application sait qu’elle vient bien de lui et n’a pas été modifiée.
Quand le serveur renvoie l’utilisateur vers l’application avec « code=… » dans l’adresse, n’importe qui dans le navigateur peut y toucher. Cela ouvre la voie à des attaques où un autre serveur se fait passer pour le bon (« mix-up »).
Au lieu de plusieurs paramètres en clair, le serveur renvoie un seul paramètre « response » : un JWT signé qui contient code, state, l’émetteur (iss) et l’audience (aud). L’application vérifie la signature, puis lit le code à l’intérieur. Quatre variantes : jwt, query.jwt, fragment.jwt, form_post.jwt (où le JWT est placé).
En image
La réponse arrive dans une enveloppe plombée plutôt que sur une carte postale.
Profil FAPI 2.0 et secteurs réglementés. Utile aussi pour détecter les attaques de mélange entre serveurs.
Oublier de vérifier la signature, ou lire des paramètres en clair à côté du JWT. query.jwt est interdit quand des jetons reviennent par le navigateur.
Ajoutez response_mode=jwt (ou une variante) à votre demande d’autorisation sur le fournisseur de test : même un refus ou une erreur est signé.
RFC 9449
Un jeton volé ne sert à rien sans la clé de l’application qui l’a demandé.
Un jeton « Bearer » (porteur) fonctionne comme de l’argent liquide : celui qui le possède peut l’utiliser, même s’il l’a volé dans un journal, une extension de navigateur ou une API compromise.
L’application fabrique une paire de clés. À chaque appel, elle joint une « preuve » courte signée avec sa clé privée, qui dit « voici la méthode et l’adresse que j’appelle, à cet instant ». Le serveur lie le jeton à l’empreinte de la clé (cnf.jkt). L’API vérifie que la preuve a été signée par la même clé et qu’elle porte l’empreinte du jeton (ath). Un voleur qui a le jeton mais pas la clé privée est bloqué.
En image
Une carte bancaire avec code PIN, plutôt que des billets : la carte seule ne suffit pas.
Applications mobiles et pages web (SPA) où les jetons sont exposés, API sensibles, profils FAPI 2.0.
Preuve rejouée (jti à usage unique), mauvaise méthode ou adresse (htm, htu), horloge décalée (iat ±2 min), jeton présenté avec « Bearer » au lieu de « DPoP », ath manquant sur l’API.
Envoyez un en-tête DPoP à /token du fournisseur de test : le jeton devient token_type DPoP avec cnf.jkt. Sur /userinfo, il faut « Authorization: DPoP » plus une preuve avec ath, sinon un message explique ce qui manque.
OpenID CIBA Core
Une application demande au serveur d’authentifier quelqu’un sur son téléphone, sans navigateur ni redirection.
Dans un guichet, un centre d’appels, un terminal de paiement ou un assistant vocal, la personne n’est pas devant un navigateur qui peut se rediriger.
L’application dit au serveur « authentifie Alice » (login_hint), avec éventuellement un message à afficher (« Autoriser le paiement de 25 € ? ») et reçoit un auth_req_id. Le serveur sollicite Alice sur son téléphone. Pendant ce temps, l’application interroge /token (« poll ») : « authorization_pending » tant qu’Alice n’a pas répondu, puis les jetons si elle accepte, ou « access_denied » si elle refuse.
En image
Le vendeur appelle votre banque pour dire « mon client est là », et c’est votre banque qui vous envoie une notification pour confirmer.
Validation de paiement ou d’opération sensible, centres d’appels, bornes, téléphonie. Le message de liaison (binding_message) apparaît sur les deux écrans pour vérifier qu’on valide bien la bonne opération.
Interroger trop vite (slow_down), laisser expirer la demande, oublier que le login_hint doit désigner un utilisateur connu du serveur.
Activez le flux CIBA sur un client du fournisseur de test, appelez /bc-authorize. La réponse contient un lien « idkit_approval_url » : c’est la page du « téléphone », où vous approuvez ou refusez, puis vous récupérez les jetons avec /token. ID-Kit ne gère que le mode poll.
RFC 8707
Dire à quelle API on veut parler, pour que le jeton ne marche que là.
Un jeton d’accès valable pour « n’importe quelle API » est dangereux : si l’API de facturation est compromise, elle peut rejouer le jeton contre l’API des salaires.
L’application ajoute « resource=https://api.exemple.fr/factures » à sa demande (autorisation ou jeton). Le fournisseur place cette adresse dans l’audience (aud) du jeton. L’API vérifie que aud est bien elle, et refuse sinon.
En image
Un billet de train nominatif pour une ligne précise, plutôt qu’un ticket valable partout.
Dès qu’il y a plusieurs API derrière un même fournisseur, et pour les jetons sensibles.
API qui ne vérifie pas aud ; resource avec un fragment (#) ou qui n’est pas une adresse absolue (invalid_target).
Ajoutez resource à /authorize ou /token du fournisseur de test : l’audience du jeton JWT et de l’introspection change. La ressource est mémorisée dans le code et conservée au rafraîchissement.
RFC 9396
Demander un droit précis (« payer 25 € à X ») plutôt qu’un large scope (« payer »).
Un scope comme « payments » est trop vague : l’utilisateur ne sait pas ce qu’il approuve, et le jeton permet bien plus que l’opération prévue.
L’application envoie authorization_details : une liste JSON d’objets avec un « type » (par exemple payment_initiation) et les détails de l’opération (montant, bénéficiaire). Le fournisseur les affiche, les recopie dans le jeton et dans la réponse, et l’API les lit.
En image
Un chèque rempli (montant, destinataire) plutôt qu’un chèque en blanc.
Paiement, santé, délégations fines. Obligatoire dans certains profils bancaires.
JSON invalide ou sans « type » (invalid_authorization_details) ; API qui ignore le contenu du jeton.
Envoyez authorization_details sur /authorize ou /token : ID-Kit le valide et le retrouve dans le jeton, la réponse /token et l’introspection.
OIDC Core, RFC 9470
Savoir avec quelle force la personne s’est authentifiée, et en exiger davantage si besoin.
Se connecter avec un mot de passe ne suffit pas pour virer 10 000 €. L’application doit pouvoir dire « je veux une authentification forte ».
L’application demande acr_values (par exemple le niveau « mfa »). Le fournisseur renvoie dans l’ID token la valeur acr obtenue et la liste amr des méthodes utilisées (mot de passe, code à usage unique…). Une API peut refuser un jeton de niveau trop faible et demander un « step-up » : on repart faire une authentification plus forte.
En image
À la banque, on vous laisse consulter votre compte avec votre carte, mais pour un virement important on vous demande une pièce d’identité en plus.
Opérations sensibles, conformité (authentification forte).
Considérer acr_values comme une obligation alors que c’est une demande (« voluntary ») ; ne pas vérifier acr reçu.
Ajoutez acr_values=urn:idkit:acr:mfa : l’ID token contient acr et amr [pwd, otp], et l’introspection renvoie acr et auth_time. Sans demande, le niveau est « pwd ».
RFC 7591
Une application s’enregistre toute seule chez le fournisseur, sans passer par un administrateur.
Créer les clients à la main ne passe pas à l’échelle : outils, extensions, serveurs MCP, applications mobiles installées par des milliers de personnes.
L’application envoie un POST JSON à /register (nom, adresses de retour, flux voulus, méthode d’authentification). Le fournisseur crée le client et renvoie client_id et, si besoin, client_secret.
En image
Un guichet automatique d’inscription au lieu d’un rendez-vous avec l’accueil.
Écosystèmes ouverts, outils qui se branchent sur des fournisseurs inconnus.
Redirections non sûres (http hors localhost), grants incohérents avec la méthode d’authentification, absence de limite.
Activez « Autoriser l’enregistrement dynamique » dans les réglages du fournisseur de test, puis POST sur /register. La limite est de 25 clients.
OIDC Core 8.1 et 5.3.2
Un identifiant différent par application, et des informations de profil signées.
Si toutes les applications voient le même identifiant (sub) pour une personne, elles peuvent comparer leurs bases et la suivre d’un service à l’autre.
En mode pairwise, le fournisseur calcule un sub différent pour chaque domaine d’application : le même utilisateur apparaît sous deux identifiants différents chez deux applications. Séparément, /userinfo peut répondre par un JWT signé au lieu d’un simple JSON, pour que l’application puisse prouver l’origine des informations.
En image
Un pseudonyme différent par site au lieu d’un numéro de sécurité sociale partout.
Respect de la vie privée (administrations, santé, éducation) ; environnements où les informations de profil doivent être signées.
Associer des comptes entre applications avec le sub (impossible en pairwise) ; oublier que sub dans userinfo doit être identique à celui de l’ID token.
Deux réglages du fournisseur de test : « Identifiant propre à chaque application (pairwise) » et « userinfo en JWT signé ».
Voir aussi : les diagrammes de séquence · quel flow choisir ? · la frise