OAuth через SSH и удалённые хосты
Некоторые провайдеры Hermes — Spotify (каталожный плагин spotify) и удалённые MCP-серверы (Linear, Sentry, Atlassian, Asana, Figma, …) — используют OAuth-поток с loopback-редиректом. Сервер авторизации перенаправляет ваш браузер на http://127.0.0.1:<port>/callback, чтобы небольшой HTTP-листенер, запущенный Hermes, мог перехватить код авторизации.
Это отлично работает, когда Hermes и ваш браузер находятся на одной машине. Но стоит им оказаться на разных — и всё ломается: браузер на ноутбуке пытается достучаться до 127.0.0.1 на самом ноутбуке, а листенер слушает 127.0.0.1 на удалённом сервере.
Чинится это однострочным SSH local-forward. Для MCP-серверов в интерактивном терминале зачастую можно обойтись без туннеля — просто вставив redirect URL обратно.
OAuth xAI Grok (xai-oauth) использует device code flow, а не loopback-колбэк — достаточно открыть напечатанный URL для подтверждения в любом браузере, а Hermes сам дождётся одобрения через опрос. SSH-туннель не нужен. См. OAuth xAI Grok.
Коротко
# На локальной машине (ноутбуке), в отдельном терминале:
ssh -N -L 43827:127.0.0.1:43827 user@remote-host
# В уже открытой SSH-сессии на удалённой машине:
hermes spotify login --no-browser
# → Hermes напечатает URL авторизации. Откройте его в браузере на ноутбуке.
# → Браузер перенаправит на 127.0.0.1:43827/callback, туннель прокинет
# запрос на удалённый листенер, и логин завершится.
Hermes печатает точный порт, к которому привязался, в строке Waiting for callback on ... — берите значение оттуда. У Spotify порт по умолчанию — 43827.
Каким провайдерам это нужно
| Провайдер | Loopback-порт | Нужен туннель? |
|---|---|---|
| Spotify | 43827 (по умолчанию) | Да, если Hermes на удалённой машине |
MCP-серверы (auth: oauth) | подбирается автоматически для каждого сервера | Да, если Hermes на удалённой машине (либо можно вставить redirect URL) |
xai-oauth (Grok SuperGrok) | нет | Нет — device code flow |
anthropic (Claude Pro/Max) | нет | Нет — flow с вставкой кода |
openai-codex (ChatGPT Plus/Pro) | нет (по умолчанию device code); 1455 с --browser / auth.codex_login_flow: browser | Только для включаемого вручную browser PKCE flow, если Hermes на удалённой машине |
minimax, nous-portal | нет | Нет — device code flow |
openrouter (hermes auth add openrouter --type oauth) | назначает ОС, только локально | Нет — по SSH Hermes переключается на headless-режим OpenRouter и просит вставить код, показанный в браузере |
Если вашего провайдера нет в таблице — туннель вам не нужен.
MCP-серверы
Desktop Skills → MCP: нативное приложение принимает обратный вызов у вас на
компьютере и передаёт его выбранному соединению и профилю, поэтому здесь не нужны ни
SSH-туннель для колбэка, ни dashboard.public_url. Токены остаются на профиле того
бэкенда, которому принадлежат. Уход с вкладки MCP или смена её области отменяют
незавершённый вход. Если десктоп просит обновить бэкенд — обновите его и повторите:
на удалённый HTTP-колбэк он не откатывается. Терминальные сценарии ниже не изменились.
Удалённые MCP-серверы (Linear, Sentry, Atlassian, Asana, Figma и т. д.) используют тот же loopback-редирект. Hermes автоматически подбирает свободный порт для каждого сервера и печатает URL авторизации в момент запуска OAuth-потока — либо при старте (когда в mcp_servers: появляется новый сервер), либо при запуске hermes mcp login <server>.
С удалённого хоста завершить авторизацию можно двумя способами:
Вариант 1 — вставить redirect URL обратно (без настройки, работает где угодно). В интерактивном терминале Hermes одновременно с запуском локального листенера предложит вставить redirect URL. После подтверждения в браузере редирект на http://127.0.0.1:<port>/callback покажет ошибку соединения — это ожидаемо. Скопируйте полный URL из адресной строки браузера и вставьте его в приглашении 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.
Голая строка запроса вида ?code=...&state=... тоже подходит. Этот способ работает для любого MCP-сервера с auth: oauth и не требует никаких изменений в настройках SSH.
Вариант 2 — проброс порта по SSH (как со Spotify). Hermes печатает точный порт, к которому привязался, в подсказке для SSH-сессии. Откройте отдельный терминал на ноутбуке:
ssh -N -L <port>:127.0.0.1:<port> user@remote-host
Затем откройте URL авторизации в браузере как обычно — редирект пройдёт через туннель, и листенер его подхватит. Используйте этот способ, когда поток должен завершиться без вашего присутствия (например, при скриптовой повторной аутентификации, где вставить код интерактивно нельзя).
Подводный камень — гонка с 30-секундной перезагрузкой конфига. Если вы редактируете ~/.hermes/config.yaml, добавляя OAuth MCP-сервер прямо из работающей сессии Hermes, CLI автоматически перезагружает MCP-подключения с таймаутом 30 секунд. Этого времени не хватает на завершение интерактивного OAuth-потока, и перезагрузка просто сдастся. Вместо этого запустите hermes mcp login <server> из свежего терминала — там такого ограничения нет, и Hermes подождёт полные 5 минут, пока вы вставите код обратно.
Почему листенер нельзя просто забиндить на 0.0.0.0
Spotify и большинство MCP OAuth-серверов проверяют параметр redirect_uri по белому списку. Оба требуют loopback-форму (http://127.0.0.1:<точный-порт>/callback). Если забиндить листенер на 0.0.0.0 или другой порт, сервер авторизации отклонит запрос как несовпадение redirect_uri. SSH-туннель сохраняет loopback-URI неизменным на всём пути.
Пошагово: одиночный SSH-переход
1. Запустите туннель с локальной машины
# Spotify (порт 43827)
ssh -N -L 43827:127.0.0.1:43827 user@remote-host
-N означает «не открывать удалённую оболочку, просто держать туннель открытым». Держите этот терминал запущенным на всё время логина.
2. В отдельной SSH-сессии выполните команду авторизации
ssh user@remote-host
hermes spotify login --no-browser
Hermes определяет, что запущен в SSH-сессии, пропускает автооткрытие браузера и печатает URL авторизации вместе со строкой Waiting for callback on http://127.0.0.1:<port>/callback.
3. Откройте URL в локальном браузере
Скопируйте URL авторизации из удалённого терминала и вставьте его в браузер на ноутбуке. Подтвердите экран согласия. Сервер авторизации перенаправит на http://127.0.0.1:<port>/callback. Браузер попадёт в туннель, запрос будет переслан на удалённый листенер, и Hermes напечатает Login successful!.
Туннель можно снести (Ctrl+C в первом терминале), как только увидите строку об успехе.
Пошагово: через jump box
Если вы попадаете к Hermes через bastion / jump host, используйте встроенный в SSH -J (ProxyJump):
ssh -N -L 43827:127.0.0.1:43827 -J jump-user@jump-host user@final-host
Это выстраивает цепочку SSH-соединения через jump host, не размещая loopback-порт на самом jump box. Локальный 127.0.0.1:43827 на вашем ноутбуке туннелируется напрямую к 127.0.0.1:43827 на конечном удалённом хосте.
Для старых версий OpenSSH, не поддерживающих -J, есть развёрнутая форма:
ssh -N \
-o "ProxyCommand=ssh -W %h:%p jump-user@jump-host" \
-L 43827:127.0.0.1:43827 \
user@final-host
Mosh, tmux, ssh ControlMaster
Туннель — это свойство базового SSH-соединения. Если вы запускаете Hermes внутри tmux поверх mosh-сессии, роуминг mosh не переносит проброс -L. Откройте отдельную обычную SSH-сессию только для туннеля -L — именно это соединение должно оставаться живым на время OAuth-потока. Ваша интерактивная mosh/tmux-сессия при этом продолжает работать с Hermes как обычно.
Если вы используете ssh -o ControlMaster=auto, проброс портов на мультиплексированном соединении живёт столько же, сколько мастер-соединение. Если туннель не поднимается — перезапустите мастер:
ssh -O exit user@remote-host
ssh -N -L 43827:127.0.0.1:43827 user@remote-host
Диагностика проблем
bind [127.0.0.1]:43827: Address already in use
Что-то на вашем ноутбуке уже занимает этот порт. Либо предыдущий туннель не закрылся корректно, либо на нём же слушает локальный Hermes. Найдите и завершите виновника:
# macOS / Linux
lsof -iTCP:43827 -sTCP:LISTEN
kill <PID>
Затем повторите команду ssh -L.
Авторизация не дождалась локального колбэка (тайм-аут)
Редирект так и не дошёл до удалённого листенера. Проверьте, что туннель ещё жив (ssh -N не выводит ничего в консоль, поэтому смотрите на терминал, из которого его запускали), убедитесь, что использовали порт из самой свежей строки Waiting for callback on ... (Hermes может автоматически переключиться на другой порт, если предпочтительный занят), при необходимости перезапустите туннель и повторите команду авторизации.
Токены попадают не в тот ~/.hermes
Токены записываются от имени того Linux-пользователя, который выполнил hermes auth add .... Если ваш шлюз / systemd-сервис работает от другого пользователя (например, root или отдельного пользователя hermes), авторизуйтесь именно от этого пользователя, чтобы токены попали в его ~/.hermes/auth.json. Подойдёт sudo -u hermes -i или аналог.
См. также
- OAuth xAI Grok — device code; SSH-туннель не нужен
- Spotify (раздел «Running over SSH»)
- Нативный MCP-клиент (раздел OAuth)
- SSH
-J/ ProxyJump (man page)