Aller au contenu principal

Connecteur MCP distant : trois pièges OAuth 2.1 qui font échouer l'appairage

Un serveur MCP distant conforme à la spec peut rester impossible à connecter depuis un client web. Retour d'expérience sur trois pannes empilées — 401 trop précoce, découverte OAuth servie en HTML par le catch-all SPA, et CSP form-action qui bloque silencieusement la redirection de consentement — avec la méthode de diagnostic de chacune.

Rendre un serveur MCP accessible depuis un client web n'est pas le même exercice que le rendre accessible depuis un client de bureau. Le second accepte une clé d'API passée en en-tête ; le premier exige un flux OAuth 2.1 complet, et vous découvrez alors que trois couches de votre infrastructure — le gestionnaire de requêtes, le routage de l'hébergeur et l'en-tête de sécurité du navigateur — peuvent chacune casser l'appairage, deux d'entre elles sans produire le moindre message d'erreur exploitable. Voici les trois pannes rencontrées en mettant le connecteur Everlange en service, et comment reconnaître chacune.

Pourquoi un connecteur MCP distant a besoin d'OAuth

Le Model Context Protocol permet à un assistant d'appeler des outils hébergés ailleurs. En local, l'authentification est triviale : le client lit un fichier de configuration, y trouve une clé, et la place dans un en-tête. En distant, le client est une application web tierce, et cette application ne peut pas détenir votre clé : elle doit obtenir une autorisation déléguée. C'est le rôle d'OAuth 2.1, avec PKCE et enregistrement dynamique de client.

La spécification est claire et les bibliothèques existent. Le problème n'est pas là. Le problème, c'est que le flux OAuth traverse des composants que vous n'avez pas écrits en pensant à lui : le routage par défaut de votre hébergeur, la politique de sécurité de contenu de votre application, et le comportement de votre gestionnaire de requêtes face à un appel non authentifié. Chacun a un réglage parfaitement raisonnable qui, appliqué à ce flux, l'interrompt.

Piège 1 : renvoyer 401 sur toutes les requêtes, y compris initialize

Symptôme : le client affiche un message d'échec d'inscription au service d'authentification, du type « Couldn't register with sign-in service ». L'appairage ne démarre jamais.

Le réflexe de sécurité normal consiste à protéger l'endpoint entier : pas de jeton valide, pas de réponse. Appliqué à un serveur MCP, ce réflexe est faux, parce que le protocole distingue deux familles de messages qui n'ont pas le même statut.

Les messages de découverte — initialize, ping, notifications, tools/list — décrivent ce que le serveur sait faire. Ils n'exposent aucune donnée client. Les messages d'exécution — tools/call — déclenchent un travail réel sur des données réelles. Seuls les seconds justifient une authentification.

Renvoyer 401 sur initialize a un effet de bord précis : le client interprète ce refus comme le signal qu'un serveur d'autorisation existe, et lance immédiatement l'enregistrement dynamique de client, avant même de savoir de quoi le serveur est capable. Il échoue, et le message d'erreur affiché parle d'inscription, pas d'autorisation — ce qui envoie le diagnostic dans une mauvaise direction.

La règle qui fonctionne : découverte ouverte, exécution protégée. Le gestionnaire lit le corps de la requête JSON-RPC avant d'appliquer la garde, détermine la méthode appelée, et n'exige un jeton que pour tools/call. Effet secondaire bienvenu : les annuaires de serveurs MCP peuvent énumérer vos outils sans clé, et votre serveur apparaît comme fonctionnel dans leurs listings au lieu d'être marqué injoignable.

Piège 2 : le catch-all de votre application à page unique sert du HTML sur les chemins de découverte

Symptôme : le client lance bien le flux OAuth, puis redirige l'utilisateur vers la page de connexion de votre application au lieu de la page de consentement. Rien dans les journaux n'indique une erreur.

Un client MCP web sonde deux adresses avant de commencer : la ressource protégée, décrite par la RFC 9728, et le serveur d'autorisation, décrit par la RFC 8414. Ces adresses vivent sous un chemin commençant par un point, dans le répertoire de métadonnées bien connu du domaine.

Or une application à page unique se déploie presque toujours avec une règle de réécriture attrape-tout : toute adresse qui ne correspond à rien renvoie le document HTML principal, avec un statut 200, pour que le routage côté client prenne le relais. Cette règle attrape aussi les chemins de découverte OAuth.

Le client reçoit donc 200 et du HTML là où il attendait 200 et du JSON. Beaucoup d'implémentations en déduisent qu'un serveur d'autorisation existe et poursuivent le flux, qui part alors dans le décor. Un 404 franc aurait produit le comportement correct : le client aurait su qu'il n'y a rien à cette adresse.

Le point qui coûte du temps : sur plusieurs hébergeurs, les règles de réécriture et de redirection déclarées dans le fichier de configuration ne s'appliquent pas aux chemins commençant par un point. Déclarer une règle explicite pour ce répertoire est sans effet ; exclure ce répertoire du motif attrape-tout est sans effet également. Les deux ont été testées, aucune n'a fonctionné.

La seule couche qui intercepte ces requêtes est le middleware exécuté en périphérie, avant le routage. C'est là qu'il faut soit servir les vraies métadonnées JSON, soit renvoyer un 404 JSON explicite si vous ne fournissez pas de serveur d'autorisation.

Piège 3 : la politique de sécurité de contenu bloque la redirection de consentement

Symptôme : l'utilisateur arrive sur la page de consentement, clique sur le bouton d'autorisation, et l'onglet reste sur place. Aucune erreur affichée, aucune requête vers l'endpoint de jeton dans les journaux serveur.

C'est la panne la plus coûteuse des trois, parce qu'elle est entièrement muette côté serveur. Les journaux montrent que la page de consentement a bien renvoyé une redirection 302 vers l'adresse de rappel du client. Ils ne montrent aucune requête suivante. Le serveur a fait son travail ; c'est le navigateur qui a refusé de suivre.

La cause est une directive de politique de sécurité de contenu généralement posée globalement, et à juste titre : la directive form-action limitée à la même origine. Elle empêche un contenu injecté de faire poster vos formulaires vers un domaine tiers. Excellente protection.

Sauf que cette directive couvre aussi les redirections qui résultent de la soumission d'un formulaire. Votre page de consentement poste vers votre propre endpoint — donc conforme —, mais cet endpoint répond par une redirection vers le domaine du client. Le navigateur applique la directive à la destination finale, la trouve hors origine, et bloque la navigation. Les violations de cette politique ne sont pas remontées à l'utilisateur : la page ne bouge pas, c'est tout.

Le correctif consiste à exclure la seule page de consentement de la politique globale et à lui appliquer une directive form-action qui autorise les destinations externes en HTTPS. Le reste de l'application conserve sa protection stricte.

Comment reconnaître cette panne

Deux signes la distinguent de toutes les autres : l'onglet reste sur la page après le clic, et les journaux serveur contiennent une redirection 302 sans requête consécutive vers l'endpoint de jeton. Si vous observez ces deux faits ensemble, cherchez une directive de sécurité de contenu avant de chercher un bogue dans votre implémentation OAuth — celle-ci est probablement correcte.

Une méthode de diagnostic plus rapide que la lecture du code

Ces trois pannes ont en commun de produire des symptômes qui pointent ailleurs que vers leur cause. La méthode qui les a démêlées ne consiste pas à relire l'implémentation, mais à localiser la dernière étape qui a réussi et à identifier la première qui manque.

Ce raisonnement — comparer ce qui a marché à ce qui manque, plutôt que supposer une cause — s'applique bien au-delà d'OAuth. Il a servi, dans le même produit, à établir qu'un jeton d'API rejeté par un fournisseur n'avait pas changé de valeur : un appel réussi postérieur au dernier déploiement prouvait que la variable d'environnement envoyée était la bonne, donc que la cause était chez le fournisseur et non dans la configuration.

Ce qui reste vrai une fois le connecteur en service

Une fois ces trois points réglés, l'appairage devient banal : le client découvre les outils, ouvre la page de consentement, l'utilisateur colle sa clé, le jeton circule, les outils répondent. Deux détails d'exploitation méritent d'être connus.

D'abord, un client web met en cache l'état OAuth de sa première tentative. Après avoir corrigé ces pannes, retirer puis rajouter le connecteur est nécessaire — sans quoi vous continuerez à observer l'ancien échec sur un serveur devenu correct.

Ensuite, la liste d'outils d'une session cliente est figée à son démarrage. Ajouter un outil côté serveur ne le fait pas apparaître dans une conversation déjà ouverte. Pour vérifier qu'un nouvel outil répond, appelez-le directement en JSON-RPC avec votre clé, plutôt que de conclure d'une absence dans l'interface.

Le connecteur MCP d'Everlange expose aujourd'hui l'audit AEO, la génération de correctifs, leur application sur les sites connectés, le suivi de citations et le pilotage Google Ads. Le détail des outils et la procédure de connexion sont sur la page connecteur MCP pour agences. Si vous voulez d'abord voir ce que l'audit mesure, l'audit gratuit donne le score et le détail par critère sans connexion préalable.

Questions fréquentes

Faut-il obligatoirement OAuth pour un serveur MCP distant ?

Non, cela dépend du client. Un client de bureau accepte une clé passée en en-tête HTTP, ce qui suffit et se met en place en quelques minutes. Les clients web, eux, exigent un flux OAuth 2.1 avec PKCE. Si vous visez les deux, gardez les deux chemins d'authentification : l'en-tête pour les clients de bureau, OAuth pour le web. Ils cohabitent sans conflit dès lors que la présence d'un jeton, quelle que soit sa provenance, court-circuite le renvoi du 401.

Pourquoi renvoyer 404 sur les métadonnées OAuth plutôt que rien du tout ?

Parce que « rien du tout » n'existe pas : votre hébergeur répondra quelque chose, et sur une application à page unique ce sera votre document HTML avec un statut 200. Un 404 explicite est la seule façon de dire au client qu'il n'y a pas de serveur d'autorisation à cette adresse.

Peut-on tester ce flux sans passer par le client final ?

Oui, et c'est recommandé. Le flux complet — page d'autorisation, échange du code, appel authentifié — se déroule avec un navigateur ordinaire et un client HTTP en ligne de commande. Vous vérifiez ainsi la conformité PKCE, la vérification de l'état et l'intégrité de la redirection sans dépendre du comportement d'un client tiers, dont les messages d'erreur sont souvent trop génériques pour diagnostiquer quoi que ce soit.

Ces pièges concernent-ils uniquement le protocole MCP ?

Le premier est spécifique à MCP, puisqu'il tient à la distinction entre messages de découverte et messages d'exécution. Les deux autres concernent tout flux OAuth hébergé derrière une application à page unique : le routage attrape-tout qui intercepte les chemins de découverte, et la directive form-action qui bloque une redirection de consentement, se produisent à l'identique sur n'importe quelle intégration tierce.