Comment terminer un OAuth sur un serveur distant : tunnels SSH, callbacks collés et codes d'appareil


Vous installez Hermes sur une VM cloud, ou sur une machine de dev derrière un bastion, et vous voulez maintenant Spotify dans la boucle — ou un serveur MCP distant comme Linear, Sentry ou Atlassian. Vous lancez la commande, Hermes affiche une URL d’autorisation, vous l’ouvrez dans le navigateur de votre portable et vous cliquez sur Allow. Le navigateur atterrit alors sur une adresse 127.0.0.1 et n’affiche que « This site can’t be reached ». De retour sur le serveur, Hermes attend toujours tranquillement un callback qui n’arrivera jamais.

Ce n’est pas une erreur de configuration. C’est un décalage physique dans l’endroit où se déroule la poignée de main OAuth : le 127.0.0.1 de l’URI de redirection désigne « la machine sur laquelle tourne votre navigateur », alors que l’écouteur est attaché sur l’hôte distant. Voici les trois routes qui fonctionnent, plus les providers qui n’ont jamais eu besoin d’un tunnel.

Pourquoi ça casse sur un hôte distant

Une minute de théorie, parce qu’elle rend le correctif évident. Hermes utilise deux types de connexion OAuth :

  • Redirection loopback. Hermes démarre un minuscule écouteur HTTP sur 127.0.0.1 sur la machine locale. Quand vous approuvez, le serveur d’autorisation redirige votre navigateur vers http://127.0.0.1:port/callback, et ce petit écouteur récupère le code. Spotify et la plupart des serveurs MCP distants protégés par OAuth fonctionnent ainsi.
  • Device code / code à coller. Aucune URI de callback. Le serveur d’autorisation vous donne un code court et une URL de vérification ; vous approuvez dans n’importe quel navigateur, et Hermes interroge jusqu’à ce que le token apparaisse.

Les deux se passent bien quand Hermes et votre navigateur partagent une machine. Le premier casse dans le cas contraire : le navigateur de votre portable frappe le 127.0.0.1 du portable, tandis que l’écouteur est assis sur le 127.0.0.1 du serveur. Des machines différentes, une adresse qui se ressemble.

Alors pourquoi ne pas simplement attacher l’écouteur à 0.0.0.0, ou utiliser le nom d’hôte public du serveur ? Parce que les serveurs d’autorisation valident l’URI de redirection contre une liste blanche fixe. Spotify et la plupart des serveurs d’autorisation MCP exigent la forme loopback http://127.0.0.1:port/callback ; attachez ailleurs, ou choisissez un autre port, et la requête est rejetée comme redirect_uri mismatch. Le correctif n’est pas de déplacer l’écouteur — il est de transporter ce port loopback jusqu’à votre portable, ce que fait exactement un forward local SSH.

Route une : un forward local SSH (avec Spotify comme exemple)

Ouvrez un second terminal sur votre portable et consacrez-le au tunnel :

# Terminal local : redirige portable:43827 vers remote-host:43827
ssh -N -L 43827:127.0.0.1:43827 user@remote-host

-N signifie « n’ouvre pas de shell distant, garde simplement ce forward ouvert ». Laissez ce terminal tourner jusqu’à la fin de la connexion.

Puis, dans une session SSH séparée, lancez la commande d’authentification :

ssh user@remote-host
hermes auth spotify --no-browser

--no-browser saute l’étape d’ouverture automatique (il n’y a pas de navigateur sur le serveur). Hermes affiche une URL d’autorisation et, surtout, le port qu’il a réellement attaché :

Waiting for callback on http://127.0.0.1:43827/callback

Collez l’URL d’autorisation dans un navigateur sur votre portable et approuvez. Le serveur d’autorisation redirige vers 127.0.0.1:43827/callback — cette fois cette adresse est votre portable, la requête frappe votre tunnel, est transmise à l’écouteur distant, et le terminal annonce le succès. Vous pouvez ensuite démonter le tunnel.

D’où vient le port ? De la ligne Waiting for callback on .... Spotify utilise 43827 par défaut, mais si le port est occupé Hermes peut l’incrémenter automatiquement — lisez toujours le numéro dans la sortie au lieu de recopier un article de blog.

À travers un jump box, utilisez le ProxyJump intégré de SSH pour que le port loopback n’atterrisse jamais sur le bastion :

ssh -N -L 43827:127.0.0.1:43827 -J jump-user@jump-host user@final-host

Sur les OpenSSH plus anciens sans -J, la forme longue est :

ssh -N \
    -o "ProxyCommand=ssh -W %h:%p jump-user@jump-host" \
    -L 43827:127.0.0.1:43827 \
    user@final-host

Trois pièges à connaître d’emblée. L’itinérance Mosh ne transporte pas les forwards -L, donc si vous lancez Hermes dans tmux via mosh, ouvrez une session SSH simple séparée uniquement pour le tunnel — cette connexion doit rester vivante pendant tout le flux tandis que votre session interactive continue de fonctionner. Si vous utilisez ssh -o ControlMaster=auto, les forwards partagent la durée de vie de la connexion maître ; si le tunnel refuse de monter, tuez le maître avec ssh -O exit user@remote-host et réessayez.

Route deux : recoller l’URL de redirection (serveurs MCP)

Les serveurs MCP distants utilisent le même flux loopback, mais sur un terminal interactif Hermes propose un raccourci sans tunnel : recoller l’URL de callback à l’invite.

Après avoir cliqué sur Allow dans le navigateur, la page redirige vers une adresse 127.0.0.1 et échoue à charger — c’est normal. Copiez l’URL complète depuis la barre d’adresse du navigateur et collez-la dans l’invite Hermes :

  MCP OAuth: authorization required.
  Open this URL in your browser:

    https://mcp.linear.app/authorize?response_type=code&...

  Or paste the redirect URL here (or the ?code=...&state=... portion) and press Enter:
> https://mcp.linear.app/callback?code=abc123&state=xyz
  Got authorization code from paste — completing flow.

Coller uniquement la chaîne de requête ?code=...&state=... fonctionne aussi. Cette route ne demande aucune configuration SSH et s’applique à tout serveur MCP en auth: oauth.

Vous n’avez besoin du tunnel de la route une que lorsque le flux doit se terminer sans personne — une ré-authentification scriptée où personne ne peut coller de façon interactive.

Une différence côté desktop : dans le panneau Skills → MCP de l’application desktop, l’application native reçoit le callback sur votre ordinateur et le relaie vers la connexion et le profile sélectionnés, donc aucun tunnel SSH et aucun dashboard.public_url n’entrent en jeu, et les tokens restent avec le profile du backend propriétaire. Quitter l’onglet MCP ou changer sa portée annule une connexion en attente.

Route trois : device code et code à coller (aucun tunnel)

Si le provider utilise un flux device-code, rien dans votre réseau n’a besoin de changer — vous approuvez une fois dans un navigateur et Hermes interroge. Référence rapide pour « ai-je besoin d’un tunnel » :

Provider Port loopback Tunnel nécessaire
Spotify 43827 par défaut Oui, quand Hermes est distant
Serveurs MCP en mode d’auth oauth choisi automatiquement par serveur Oui, ou recoller l’URL de redirection
xai-oauth (Grok SuperGrok) s.o. Non, device code
anthropic (Claude Pro/Max) s.o. Non, coller le code
openai-codex (ChatGPT Plus/Pro) s.o. Non, device code
minimax, nous-portal s.o. Non, device code
openrouter local uniquement Non : en SSH Hermes bascule sur un flux headless et vous demande de coller le code affiché dans le navigateur

OpenRouter est le plus récent de la liste. hermes auth add openrouter --type oauth exécute le flux PKCE navigateur d’OpenRouter et, en cas de succès, forge une clé API dans le pool de credentials (sa source est enregistrée comme manual:openrouter_pkce) — plus de clé à copier-coller depuis une page web. En local, il utilise un écouteur loopback bind-first sur un port attribué par l’OS, ce qui contourne la course probe-then-rebind en nommant un port qu’il possède déjà. Pour les sessions distantes, il détecte l’environnement SSH et bascule sur le mode headless documenté d’OpenRouter : pas de callback_url, le code s’affiche à l’écran et vous le collez. Omettez --type et il se comporte toujours comme une connexion par clé API, donc le workflow plus ancien hermes auth add openrouter --api-key ... reste inchangé.

Quatre échecs que vous rencontrerez vraiment

  1. Modifier la config dans une session active puis tenter de se connecter : 30 secondes et il abandonne. Quand vous ajoutez un serveur MCP OAuth à config.yaml depuis une session Hermes en cours, la CLI recharge automatiquement les connexions MCP avec un délai de 30 secondes. C’est très loin de suffire pour un flux OAuth interactif. Lancez plutôt hermes mcp login <server> dans un terminal neuf — il n’a pas cette limite et attend les cinq minutes complètes pour votre collage.
  2. Les tokens atterrissent dans le mauvais ~/.hermes. Les credentials sont écrits sous l’utilisateur qui a lancé la commande. Si votre gateway ou service systemd tourne sous un autre compte (disons root, ou un utilisateur hermes dédié), authentifiez-vous en tant que ce compte, sinon les tokens restent dans votre répertoire personnel et le service ne les voit pas. sudo -u hermes -i ou équivalent.
  3. bind [127.0.0.1]:43827: Address already in use. Quelque chose sur votre portable occupe déjà le port — un tunnel obsolète, ou un écouteur Hermes local. Trouvez-le et tuez-le avec lsof -iTCP:43827 -sTCP:LISTEN, puis retentez le forward.
  4. Authorization timed out waiting for the local callback. La redirection n’a jamais atteint l’écouteur distant. Confirmez que le terminal du tunnel est toujours vivant (une session ssh -N n’affiche rien, regardez donc le terminal lui-même), assurez-vous que le port correspond à la dernière ligne Waiting for callback on ..., redémarrez le tunnel et relancez la commande d’auth.

Une note de sécurité qui mérite d’être répétée : les tokens OAuth sont des credentials à longue durée de vie, donc l’endroit où ils atterrissent et qui peut les lire compte. Pour la machinerie environnante, voir les pools de credentials et l’autofill du navigateur et le chiffrement des secrets adossé au trousseau. Si le serveur auquel vous vous connectez est tiers, lisez le guide de sécurité du catalogue MCP et le tour d’horizon du catalogue MCP distant officiel avant de le brancher sur un gateway.

La version courte

Posez une seule question : ce provider utilise-t-il un callback, ou un code ?

  • Basé sur callback (Spotify, serveurs MCP OAuth) : sur un terminal interactif, recollez l’URL de redirection — le plus simple. Pour l’automatisation, transportez le port loopback avec ssh -N -L, en prenant le port dans la sortie de Hermes elle-même, et surveillez mosh, tmux et ControlMaster.
  • Basé sur code (xai-oauth, openai-codex, anthropic, minimax, nous-portal, et OpenRouter en SSH) : ne changez rien. Ouvrez le lien, saisissez le code, appuyez sur Entrée.

Une suite facile à oublier : après la connexion, redémarrez tout gateway ou service de longue durée pour qu’il prenne en compte le credential dans son propre processus. Et si vous montez la machine de zéro, le guide d’installation couvre tout le parcours.