OAuth pour MCP expliqué : le pointeur 401, PKCE, l'enregistrement dynamique et les scopes, de bout en bout
L'autorisation MCP, c'est OAuth 2.1 : un 401 pointe vers les métadonnées du serveur, le client s'enregistre, PKCE protège le code.
Sur cette page
- Pourquoi MCP utilise OAuth
- Le flux, étape par étape
- Contre quoi chaque pièce protège
- PKCE : pourquoi le code ne sert à rien à un voleur
- Enregistrement dynamique des clients : pourquoi personne ne crée d'application dans une console
- Scopes : ils restreignent, jamais n'élargissent
- Le même flux sur un vrai serveur
- Flux PKCE ou jeton collé : ce que vous perdez
- FAQ
- Prochaine étape
L'autorisation MCP, c'est OAuth 2.1 avec quelques pièces obligatoires. Une requête non authentifiée vers un serveur MCP distant reçoit un 401 qui pointe vers les métadonnées du serveur ; le client le suit jusqu'au serveur d'autorisation, s'identifie, ouvre le navigateur pour que la personne consente à des scopes, et échange un code protégé par PKCE contre un jeton d'accès de courte durée et un jeton de rafraîchissement à rotation. Le résultat est un identifiant à scopes et révocable, au lieu d'une clé collée dans un fichier de configuration.
Voilà tout le flux en un paragraphe. La suite le déroule étape par étape face à la spécification d'autorisation MCP (révision 2025-11-25), explique contre quoi protègent PKCE, l'enregistrement dynamique des clients et les scopes, et montre le flux tel qu'il se déroule réellement contre un vrai serveur, le nôtre, pour que vous voyiez à quoi ressemble chaque étape côté client.
Pourquoi MCP utilise OAuth
Un serveur MCP local en stdio lit ses identifiants dans l'environnement, et la spécification dit qu'il ne devrait pas utiliser OAuth. Un serveur distant est différent : un seul déploiement sert beaucoup de personnes, chacune avec des permissions différentes, depuis des applications d'IA que l'exploitant du serveur n'a jamais vues. Trois problèmes en découlent.
- Le serveur doit savoir qui demande, à chaque appel.
- La personne doit limiter ce que l'application peut faire, sans tout lui confier.
- Les deux ont besoin d'un moyen de mettre fin à l'accès plus tard, sans changer de mot de passe ni faire tourner des clés à la main.
Une clé d'API collée résout mal le premier problème et pas du tout les deux autres. OAuth a été conçu exactement pour cette forme, une application agissant pour une personne auprès d'un service, et OAuth 2.1 en est la version consolidée et plus stricte. L'entrée du glossaire sur OAuth 2.1 liste ce qu'il a retiré de 2.0 ; pour MCP, l'essentiel est que PKCE est obligatoire, que les URI de redirection doivent correspondre exactement, et que les jetons ne peuvent pas voyager dans la query string.
La spécification d'autorisation MCP s'appuie sur cinq documents : le brouillon OAuth 2.1 (draft-ietf-oauth-v2-1-13), Authorization Server Metadata (RFC 8414), Dynamic Client Registration (RFC 7591), Protected Resource Metadata (RFC 9728) et le brouillon Client ID Metadata Documents. Pas besoin de les lire pour utiliser un serveur MCP, mais chacun répond à l'une des questions ci-dessous.
Le flux, étape par étape
Voici ce qui se passe entre le moment où vous ajoutez un serveur distant et celui où le modèle fait son premier appel d'outil. Le client fait tout ; vous voyez un onglet de navigateur.
- Le client appelle le serveur sans jeton. Le serveur, en tant que serveur de ressources OAuth, répond
401 Unauthorizedavec un en-têteWWW-Authenticate: Bearercontenantresource_metadata, une URL. La spécification impose aux serveurs d'implémenter RFC 9728 et aux clients d'analyser cet en-tête. S'il manque, le client doit essayer les URI well-known. - Le client récupère les métadonnées de la ressource protégée. Le document nomme au moins une entrée
authorization_serverset peut listerscopes_supported. - Le client découvre le serveur d'autorisation. Il récupère ses métadonnées via RFC 8414 (
/.well-known/oauth-authorization-server) ou OpenID Connect Discovery ; les clients doivent prendre en charge les deux. Les métadonnées listent les endpoints d'autorisation, de jeton et d'enregistrement, les méthodes PKCE prises en charge et, le cas échéant,client_id_metadata_document_supported. - Le client vérifie la prise en charge de PKCE. Si
code_challenge_methods_supportedest absent des métadonnées, le client doit refuser de continuer. Pas de rétrogradation silencieuse. - Le client s'identifie. Trois options, dans l'ordre de priorité de la spécification : des identifiants pré-enregistrés s'il en a ; un Client ID Metadata Document, où l'identifiant client est une URL HTTPS vers un fichier JSON hébergé par le client ; ou l'enregistrement dynamique, un
POSTvers l'endpoint d'enregistrement qui renvoie unclient_idsur-le-champ. La spécification dit désormais que serveurs et clients devraient prendre en charge les documents de métadonnées et peuvent prendre en charge l'enregistrement dynamique en repli. - Le client ouvre le navigateur. L'URL d'autorisation porte l'identifiant client, l'URI de redirection, les scopes demandés, une valeur
state, lecode_challengePKCE aveccode_challenge_method=S256, et le paramètreresourcequi nomme le serveur MCP (RFC 8707). La personne se connecte et voit un écran de consentement avec le nom du client et les scopes. - Le serveur d'autorisation redirige avec un code. Le code est de courte durée et à usage unique.
- Le client échange le code contre des jetons. La requête de jeton porte le code, le
code_verifierPKCE d'origine et le même paramètreresource. La réponse contient un jeton d'accès et, en général, un jeton de rafraîchissement. - Chaque requête MCP porte le jeton dans
Authorization: Bearer, jamais dans l'URL, et le serveur vérifie à chaque appel que le jeton est valide et a été émis pour ce serveur précisément. - Quand un appel a besoin de plus de scope, le serveur répond
403avecerror="insufficient_scope"et les scopes requis. Le client rejoue le flux pour l'ensemble élargi ; la spécification appelle cela l'autorisation par paliers (step-up) et demande aux clients de limiter les nouvelles tentatives.
Contre quoi chaque pièce protège
| Mécanisme | Défini dans | L'attaque qu'il empêche |
|---|---|---|
| Métadonnées de la ressource protégée | RFC 9728 | Un client qui devine ou se fait piéger vers le mauvais serveur d'autorisation |
| Métadonnées du serveur d'autorisation | RFC 8414 | Des endpoints codés en dur qui cassent quand le serveur change |
| PKCE (S256) | RFC 7636, OAuth 2.1 | Un code d'autorisation volé échangé par quelqu'un d'autre |
| Correspondance exacte de l'URI de redirection | OAuth 2.1 | Des redirections ouvertes qui envoient le code à un attaquant |
Paramètre state | OAuth 2.1 | La falsification de requête intersite sur le callback |
Paramètre resource | RFC 8707 | Un jeton émis pour un serveur rejoué sur un autre |
| Validation de l'audience côté serveur | RFC 8707, OAuth 2.1 | Le passage de jeton et les attaques de type confused deputy |
| Jetons d'accès de courte durée | OAuth 2.1 | Un jeton fuité qui reste utile longtemps |
| Jetons de rafraîchissement à rotation | OAuth 2.1 | Un jeton de rafraîchissement volé réutilisé en silence |
| Scopes et consentement | OAuth 2.0/2.1 | Une application qui fait plus que ce que la personne a accepté |
Deux d'entre elles méritent un regard plus attentif, parce que ce sont celles sur lesquelles on nous interroge.
PKCE : pourquoi le code ne sert à rien à un voleur
La plupart des clients MCP sont des clients publics : une application de bureau, un éditeur ou une CLI sans aucun secret qu'ils pourraient garder. Dans le flux par code d'autorisation, le code revient par une redirection, et sur un portable ou un téléphone, une redirection peut être interceptée par un autre programme enregistré pour la même URL. Avant PKCE, quiconque détenait le code pouvait l'échanger.
PKCE ajoute un secret à usage unique par connexion. Le client génère un code_verifier aléatoire, le garde, et envoie code_challenge = BASE64URL(SHA256(code_verifier)) avec la requête d'autorisation. À l'échange du code, il présente le verifier d'origine. Le serveur d'autorisation le hache, le compare au challenge stocké, et refuse l'échange s'ils diffèrent. Un voleur qui a le code mais pas le verifier n'obtient rien. L'entrée PKCE couvre la méthode plain et explique pourquoi OAuth 2.1 et MCP demandent d'utiliser S256 chaque fois que possible.
Enregistrement dynamique des clients : pourquoi personne ne crée d'application dans une console
Le OAuth classique supposait qu'un développeur enregistrait chaque application à l'avance. Cela ne passe pas à l'échelle dans un monde où n'importe lequel de milliers de clients d'IA peut se connecter à n'importe lequel de milliers de serveurs MCP sans relation préalable. L'enregistrement dynamique des clients (RFC 7591) permet au client d'envoyer par POST ses propres métadonnées, un nom et des URI de redirection, à l'endpoint d'enregistrement et de recevoir immédiatement un client_id.
La révision 2025-11-25 ajoute une réponse plus propre, les Client ID Metadata Documents : l'identifiant client est lui-même une URL HTTPS pointant vers un document JSON qui décrit le client. Le serveur d'autorisation le récupère, vérifie que le client_id du document correspond exactement à l'URL, et valide les URI de redirection par rapport à lui. La spécification dit que clients et serveurs devraient le prendre en charge et peuvent conserver l'enregistrement dynamique pour la compatibilité. Comme n'importe quel client peut s'enregistrer, un serveur d'autorisation prudent considère que l'enregistrement n'apporte aucune confiance : il affiche clairement le nom du client et l'hôte de redirection sur l'écran de consentement et avertit quand la redirection est un simple port localhost, que la spécification signale comme impossible à protéger entièrement par les seuls documents de métadonnées.
Scopes : ils restreignent, jamais n'élargissent
Les scopes sont les noms sur l'écran de consentement : work.read, crm.write, etc. Une application en demande un ensemble, la personne en approuve une partie ou la totalité, et le jeton porte le résultat. La spécification MCP dit aux clients de demander les scopes du paramètre scope du défi 401 s'il est présent, sinon les scopes_supported du serveur, et de monter d'un palier plus tard plutôt que de tout demander d'un coup.
La règle qui compte le plus est une règle que le standard OAuth laisse à chaque API : un scope décrit ce que l'application peut demander au nom de la personne, et il ne doit jamais dépasser ce que cette personne peut faire elle-même. Un bon serveur vérifie les deux à chaque appel : le jeton détient-il le scope, et la personne derrière est-elle autorisée à faire cela sur cet enregistrement ?
Le même flux sur un vrai serveur
Voici à quoi ressemblent les étapes ci-dessus contre le serveur MCP Atrix à https://atrix.dev/api/mcp, que nous exploitons et utilisons chaque jour, pour que vous puissiez faire correspondre le flux abstrait à un comportement concret. Le guide développeurs donne les instructions client par client.
Dans Claude Code, toute la configuration tient en claude mcp add --transport http atrix https://atrix.dev/api/mcp, puis /mcp et Authenticate. La commande ne porte volontairement aucun identifiant. Ce qui se passe ensuite :
- La première requête reçoit
401avec le pointeur RFC 9728. La découverte suit RFC 8414 et RFC 9728. - Le client s'enregistre dynamiquement ; il n'y a de secret client nulle part, parce que chaque client, nos propres applications comprises, est un client public.
- PKCE est
S256uniquement. Le serveur n'annonce queS256dans ses métadonnées et refuseplain. - Le navigateur s'ouvre ; vous choisissez un espace de travail et approuvez des scopes. Les codes vivent 60 secondes et ne servent qu'une fois.
- Le client reçoit un jeton d'accès préfixé
atx_oat_, valable une heure, et un jeton de rafraîchissement préfixéatx_ort_, valable 90 jours et renouvelé à chaque usage. - Les scopes n'ajoutent jamais à vos permissions. Ils sont résolus à chaque appel par rapport au compte derrière le jeton, donc si l'accès d'un collègue est révoqué, tous les jetons qu'il a créés cessent d'atteindre quoi que ce soit dès la requête suivante. Neuf scopes exigent aussi la permission correspondante sur le compte, comme la valeur des deals ou les dossiers d'emploi, et la paie n'est pas accordable du tout.
- Les limites sont de 120 appels et 30 écritures par minute et par jeton. Chaque appel est audité par outil, résultat et sujet, jamais par contenu.
Pour les jobs CI et les serveurs sans navigateur, il existe à la place un jeton d'accès personnel, préfixé atx_pat_, envoyé en --header "Authorization: Bearer ...". Cela fonctionne, et c'est le moins bon choix partout où un navigateur existe : il vit en clair sur le disque, il dure aussi longtemps que vous l'avez décidé à sa création, et il saute l'écran de consentement. Nous gardons cette option pour les pipelines et recommandons OAuth pour les personnes.
Flux PKCE ou jeton collé : ce que vous perdez
| OAuth 2.1 avec PKCE | Jeton d'accès personnel collé | |
|---|---|---|
| Comment on se connecte | Ajouter l'URL, approuver dans le navigateur | Créer le jeton dans les réglages, le coller dans la configuration |
| Durée de vie | Accès 1 heure, rafraîchissement 90 jours à rotation (Atrix) | De 30 jours à illimité, au choix |
| Stocké où | Magasin sécurisé du client | Fichier de configuration, souvent en clair |
| Borné par | Écran de consentement, palier supplémentaire ensuite | Choisi à la création |
| Révoqué par | Révocation de l'autorisation ou du compte | Suppression du jeton |
| Navigateur requis | Oui | Non |
| Adapté à | Des personnes utilisant une application d'IA | CI, cron, serveurs sans interface |
FAQ
MCP impose-t-il OAuth ?
L'autorisation est optionnelle dans le protocole. Quand un serveur utilise un transport HTTP et prend en charge l'autorisation, la spécification dit qu'il devrait se conformer à la spécification d'autorisation fondée sur OAuth 2.1. Les serveurs stdio ne devraient pas l'utiliser et devraient prendre leurs identifiants dans l'environnement.
Quelle différence entre OAuth 2.0 et OAuth 2.1 pour MCP ?
OAuth 2.1 supprime les grants implicite et par mot de passe, exige PKCE pour le flux par code d'autorisation, exige une correspondance exacte de l'URI de redirection, interdit les jetons bearer dans la query string, et exige la rotation ou la liaison à l'émetteur des jetons de rafraîchissement pour les clients publics. MCP hérite de tout cela.
Pourquoi mon client MCP refuse-t-il de se connecter à un serveur d'autorisation ?
La raison la plus fréquente est l'absence de prise en charge de PKCE : si code_challenge_methods_supported manque dans les métadonnées du serveur d'autorisation, un client MCP doit refuser de continuer. La seconde est un en-tête WWW-Authenticate absent ou mal formé sur le 401.
L'enregistrement dynamique des clients est-il encore obligatoire ?
Non. La révision 2025-11-25 dit que clients et serveurs devraient prendre en charge les Client ID Metadata Documents et peuvent prendre en charge l'enregistrement dynamique en repli. Beaucoup de serveurs, dont Atrix, proposent encore l'enregistrement dynamique parce que les clients plus anciens en dépendent.
Un jeton MCP peut-il faire plus que la personne qui l'a créé ?
Il ne devrait pas. Les scopes restreignent l'accès ; ils ne l'élargissent pas. Un serveur bien conçu résout les permissions de la personne à chaque appel, donc un jeton ne peut jamais atteindre plus que son propriétaire, et perd l'accès au moment où le propriétaire le perd.
Quand utiliser un jeton d'accès personnel plutôt qu'OAuth ?
Seulement là où il n'y a pas de navigateur : pipelines CI, tâches cron, serveurs sans interface. Partout ailleurs, OAuth vous donne un identifiant plus court, à scopes et révocable, sans stocker de secret sur le disque.
Prochaine étape
Le guide développeurs déroule le flux Atrix client par client, avec les commandes exactes pour Claude Code, Claude Desktop, ChatGPT et Cursor. Si un terme ci-dessus était nouveau, les entrées OAuth 2.1 et PKCE sont les explications les plus courtes que nous connaissions.