Cómo completar OAuth en un servidor remoto: túneles SSH, callbacks pegados y códigos de dispositivo

Instalas Hermes en una VM de la nube, o en una máquina de desarrollo detrás de un bastion host, y ahora quieres Spotify en el circuito, o un servidor MCP remoto como Linear, Sentry o Atlassian. Ejecutas el comando, Hermes imprime una URL de autorización, la abres en el navegador de tu portátil y pulsas Permitir. Entonces el navegador aterriza en una dirección 127.0.0.1 y no muestra más que “This site can’t be reached”. De vuelta en el servidor, Hermes sigue esperando en silencio un callback que nunca va a llegar.
Esto no es un error de configuración. Es un desajuste físico de dónde ocurre el handshake de OAuth: el 127.0.0.1 de la redirect URI significa “la máquina donde está tu navegador”, mientras que el listener está enlazado en el host remoto. Abajo tienes las tres rutas que funcionan, más los providers que nunca necesitaron un túnel.
Por qué se rompe en un host remoto
Un minuto de teoría, porque hace obvia la solución. Hermes usa dos tipos de login OAuth:
- Redirect a loopback. Hermes levanta un pequeño listener HTTP en
127.0.0.1de la máquina local. Cuando apruebas, el servidor de autorización redirige tu navegador ahttp://127.0.0.1:port/callbacky ese pequeño listener captura el código. Spotify y la mayoría de servidores MCP remotos protegidos con OAuth funcionan así. - Device code / paste-the-code. Sin callback URI de ningún tipo. El servidor de autenticación te da un código corto y una URL de verificación; apruebas desde cualquier navegador y Hermes hace polling hasta que aparece el token.
Ambos van finos cuando Hermes y tu navegador comparten máquina. El primero se rompe en caso contrario: el navegador de tu portátil pega contra el 127.0.0.1 del portátil, mientras que el listener vive en el 127.0.0.1 del servidor. Máquinas distintas, dirección de aspecto idéntico.
Entonces, ¿por qué no basta con enlazar el listener a 0.0.0.0 o poner el hostname público del servidor? Porque los servidores de autorización validan la redirect URI contra una allowlist fija. Spotify y la mayoría de servidores de autorización MCP exigen la forma loopback http://127.0.0.1:port/callback; si enlazas en cualquier otro sitio o eliges otro puerto, la petición se rechaza como redirect_uri mismatch. La solución no es mover el listener, sino llevar ese puerto de loopback a tu portátil, que es exactamente lo que hace un local forward por SSH.
Ruta uno: un local forward por SSH (con Spotify como ejemplo)
Abre un segundo terminal en tu portátil y dedícalo al túnel:
# Terminal local: reenvía portátil:43827 a remote-host:43827
ssh -N -L 43827:127.0.0.1:43827 user@remote-host
-N significa “no abras una shell remota, solo mantén este forward abierto”. Deja ese terminal corriendo hasta que termine el login.
Luego, en una sesión SSH aparte, ejecuta el comando de autenticación:
ssh user@remote-host
hermes auth spotify --no-browser
--no-browser se salta el paso de apertura automática (en el servidor no hay navegador). Hermes imprime una URL de autorización y, lo importante, el puerto que ha enlazado realmente:
Waiting for callback on http://127.0.0.1:43827/callback
Pega la URL de autorización en un navegador de tu portátil y aprueba. El servidor de autorización redirige a 127.0.0.1:43827/callback; esta vez esa dirección es tu portátil, entra por tu túnel, la petición se reenvía al listener remoto y el terminal reporta éxito. Después puedes cerrar el túnel.
¿De dónde sale el puerto? De esa línea Waiting for callback on .... Spotify usa 43827 por defecto, pero si el puerto está ocupado Hermes puede subirlo automáticamente: lee siempre el número de la salida en vez de copiarlo de un blog.
A través de un jump box, usa el ProxyJump integrado de SSH para que el puerto de loopback nunca aterrice en el bastión:
ssh -N -L 43827:127.0.0.1:43827 -J jump-user@jump-host user@final-host
En OpenSSH antiguos sin -J, la forma larga es:
ssh -N \
-o "ProxyCommand=ssh -W %h:%p jump-user@jump-host" \
-L 43827:127.0.0.1:43827 \
user@final-host
Tres trampas que conviene conocer de antemano. El roaming de Mosh no transporta forwards -L, así que si ejecutas Hermes dentro de tmux sobre mosh, abre una sesión SSH simple aparte solo para el túnel: esa conexión debe seguir viva durante todo el flujo mientras tu sesión interactiva sigue trabajando. Si usas ssh -o ControlMaster=auto, los forwards comparten la vida de la conexión maestra; si el túnel no levanta, mata la maestra con ssh -O exit user@remote-host y vuelve a intentarlo.
Ruta dos: pegar de vuelta la redirect URL (servidores MCP)
Los servidores MCP remotos usan el mismo flujo de loopback, pero en un terminal interactivo Hermes ofrece un atajo que no necesita túnel: pegar la callback URL de vuelta en el prompt.
Después de pulsar Permitir en el navegador, la página redirige a una dirección 127.0.0.1 y no carga: eso es lo esperado. Copia la URL completa de la barra de direcciones y pégala en el prompt de 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.
Pegar solo la query string ?code=...&state=... también funciona. Esta ruta no necesita ninguna configuración SSH y sirve para cualquier servidor MCP con auth: oauth.
Solo necesitas el túnel de la ruta uno cuando el flujo debe completarse sin supervisión — una re-autenticación por script donde nadie puede pegar de forma interactiva.
Una diferencia del lado del escritorio: en el panel Skills → MCP de la app de escritorio, la app nativa recibe el callback en tu ordenador y lo retransmite a la conexión y el profile seleccionados, así que no hay túnel SSH ni dashboard.public_url de por medio, y los tokens se quedan con el profile backend propietario. Salir de la pestaña MCP o cambiar su alcance cancela un inicio de sesión pendiente.
Ruta tres: device code y paste-the-code (sin túnel)
Si el provider usa un flujo de device code, nada de tu red tiene que cambiar: apruebas una vez en un navegador y Hermes hace polling. Referencia rápida para “¿necesito túnel?”:
| Provider | Puerto loopback | ¿Necesita túnel? |
|---|---|---|
| Spotify | 43827 por defecto | Sí, cuando Hermes es remoto |
| Servidores MCP con el modo de auth oauth | se elige automáticamente por servidor | Sí, o pega de vuelta la redirect URL |
| xai-oauth (Grok SuperGrok) | n/a | No, device code |
| anthropic (Claude Pro/Max) | n/a | No, pega el código |
| openai-codex (ChatGPT Plus/Pro) | n/a | No, device code |
| minimax, nous-portal | n/a | No, device code |
| openrouter | solo local | No: por SSH Hermes cambia a un flujo headless y te pide pegar el código que aparece en el navegador |
OpenRouter es el más reciente de todos. hermes auth add openrouter --type oauth ejecuta el flujo PKCE de navegador de OpenRouter y, si tiene éxito, acuña una API key en el credential pool (su origen queda registrado como manual:openrouter_pkce), sin copiar y pegar una clave desde una página web. En local usa un listener de loopback que enlaza primero (bind-first) en un puerto asignado por el SO, lo que esquiva la carrera de probe-then-rebind al nombrar un puerto que ya posee. Para sesiones remotas detecta el entorno SSH y cambia al modo headless documentado por OpenRouter: sin callback_url, el código se muestra en pantalla y tú lo pegas. Si omites --type, sigue comportándose como un login por API key, así que el flujo anterior hermes auth add openrouter --api-key ... no cambia.
Cuatro fallos con los que te toparás de verdad
- Editar la config en una sesión viva y luego intentar iniciar sesión: 30 segundos y se rinde. Cuando añades un servidor MCP OAuth a
config.yamldesde dentro de una sesión de Hermes en ejecución, el CLI recarga automáticamente las conexiones MCP con un timeout de 30 segundos. Eso no alcanza ni de lejos para un flujo OAuth interactivo. Ejecutahermes mcp login <server>en un terminal nuevo: no tiene ese límite y espera los cinco minutos completos para tu pegado. - Los tokens acaban en el
~/.hermesequivocado. Las credenciales se escriben bajo el usuario que ejecutó el comando. Si tu gateway o servicio systemd corre con otra cuenta (por ejemplorooto un usuariohermesdedicado), autentícate como esa cuenta; si no, los tokens se quedan en tu home y el servicio no los ve.sudo -u hermes -io equivalente. bind [127.0.0.1]:43827: Address already in use. Algo en tu portátil ya ocupa el puerto: un túnel muerto o un listener local de Hermes. Localízalo y mátalo conlsof -iTCP:43827 -sTCP:LISTEN, y reintenta el forward.- Authorization timed out waiting for the local callback. El redirect nunca llegó al listener remoto. Confirma que el terminal del túnel sigue vivo (una sesión
ssh -Nno imprime nada, así que mira el terminal en sí), asegúrate de que el puerto coincide con la última líneaWaiting for callback on ..., reinicia el túnel y vuelve a ejecutar el comando de auth.
Una nota de seguridad que merece repetirse: los tokens OAuth son credenciales de larga vida, así que dónde aterrizan y quién puede leerlos importa. Para la maquinaria que los rodea, mira credential pools y autofill del navegador y cifrado de secretos respaldado por keychain. Si el servidor al que te conectas es de terceros, lee la guía de seguridad del catálogo MCP y el repaso del catálogo oficial de MCP remotos antes de conectarlo a un gateway.
La versión corta
Hazte una sola pregunta: ¿este provider usa un callback o un código?
- Con callback (Spotify, servidores MCP OAuth): en un terminal interactivo, pega de vuelta la redirect URL, es lo más simple. Para automatización, reenvía el puerto de loopback con
ssh -N -L, tomando el puerto de la propia salida de Hermes, y ojo con mosh, tmux y ControlMaster. - Con código (xai-oauth, openai-codex, anthropic, minimax, nous-portal y OpenRouter por SSH): no cambies nada. Abre el enlace, introduce el código, pulsa Enter.
Un paso que se olvida con facilidad: después de iniciar sesión, reinicia cualquier gateway o servicio de larga duración para que recoja la credencial en su propio proceso. Y si estás montando la máquina desde cero, la guía de instalación cubre todo el camino.