Ещё год назад MCP-сервер можно было запустить с Bearer sk-... и считать задачу решённой. С ревизии 2025-11-25 это перестало быть опцией: любой MCP-сервер с публичным URL обязан реализовать OAuth 2.1 с PKCE, а спецификация 2026-07-28 сделала стек ещё строже — discovery, audience, обмен токенами. Разберём, как это устроено и где команды чаще всего ошибаются.
Почему API key больше не достаточно
Спецификация MCP прямо формулирует правило: STDIO на localhost — можно с ключом из окружения, HTTP в интернете — только OAuth 2.1 с PKCE S256. Метод plain запрещён, S256 обязателен даже для confidential-клиентов. Причина — перехват кода авторизации без привязки запроса к обмену.
Это не «рекомендация», а условие соответствия. Сервер без OAuth 2.1 + PKCE — non-compliant, и сканеры безопасности уже это подсвечивают. Модель «вставил API key и забыл» не даёт ни персонификации, ни scope, ни отзыва.
MCP — resource server, а не IdP: три роли
Главная ментальная модель, которая ломается:
- MCP-клиент (рантайм агента) — получает токен;
- authorization server (ваш IdP: Keycloak, Auth0, Cognito) — аутентифицирует пользователя и выпускает токен;
- MCP-сервер — только валидирует токен на каждый запрос и решает, что разрешить.
MCP-сервер не хранит пароли, не показывает логин-страницу и не выпускает токены сам. Его единственная работа в безопасности — проверить токен и отказать всему, что не для него. Если сервер сам «выдаёт» токены без внешнего IdP — это уже не MCP-авторизация.
Discovery: Protected Resource Metadata (RFC 9728)
Как клиент узнаёт, куда идти за авторизацией? MCP-сервер публикует документ Protected Resource Metadata по пути /.well-known/oauth-protected-resource. Клиент читает его до любой аутентификации и узнаёт три вещи: какому authorization server доверяет сервер, какие scope он понимает и свой канонический идентификатор ресурса.
Сервер обязан отдавать PRM, клиент обязан его использовать для discovery — это не опционально. На практике это даёт zero-touch: агент динамически узнаёт требования без хардкода. В ответе 401 сервер указывает WWW-Authenticate: Bearer resource_metadata="..." — и клиент идёт по этой ссылке.
Дальше клиент делает discovery самого authorization server через RFC 8414 или OpenID Connect Discovery и получает authorization_endpoint, token_endpoint, registration_endpoint.
Audience и Resource Indicators (RFC 8707) — против confused deputy
Валидный, неистёкший, правильно подписанный токен — ещё не повод пускать. Сервер обязан проверить, что токен выдан именно ему — поле aud должно содержать его канонический идентификатор.
Клиент при запросе токена передаёт resource (индикатор ресурса) и на этапе авторизации, и на этапе обмена кода. Authorization server вшивает это в audience токена. Сервер отклоняет любой токен с чужим aud, даже если подпись верна. Без этой проверки токен, утёкший с одного сервиса, становится мастер-ключом к другому — классический confused deputy.
С ревизии 2026-07-28 Resource Indicators обязательны для клиентов, а не «рекомендованы».
Token passthrough — главный антипаттерн
Самый опасный антипаттерн — принять токен от клиента и переслать его дальше в downstream-сервис как есть. Спецификация запрещает это прямо.
Почему это дыра:
- downstream считает, что токен валидировал MCP-сервер;
- в логах теряется атрибуция — чей это был вызов;
- аудитор не может восстановить цепочку.
Правильные варианты:
- Прямая валидация — MCP-сервер валидирует токен напрямую у authorization server (или по JWKS) и не форвардит его;
- Token Exchange (RFC 8693) — сервер обменивает входящий токен на downstream-токен с нужным audience и scope. Обмен логируется у IdP и даёт узкий токен для конкретного сервиса.
Правило простое: «никогда не форвардь токены через MCP-сервер в бэкенды, валидируй напрямую, для downstream — обмен».
Что проверить перед продом
Чек-лист, который ловит 90% ошибок:
- PKCE
S256обязателен,plainотклоняется; - PRM отдаётся и содержит
authorization_servers,scopes_supported,resource; - на
401возвращаетсяWWW-AuthenticateсBearer+resource_metadataи по возможностиscope; - клиент передаёт
resourceна обоих этапах и проверяетissв ответе (RFC 9207); - сервер проверяет
aud== своему идентификатору на каждый запрос; - Dynamic Client Registration — только если нужен fallback; приоритет — Client ID Metadata Documents (URL как идентичность клиента, с 2026 — предпочтительно);
refresh_tokenхранится конфиденциально, scope при refresh пробрасывается для провайдеров вроде Azure AD;- на
403 insufficient_scopeсервер возвращаетWWW-Authenticateс нужнымscope, клиент делает step-up, а не просит всё подряд.
Если любой из этих пунктов не реализован — сервер формально не соответствует спеке и практически уязвим.
Где Codenik: управляемый слой для MCP-авторизации
MCP-авторизация — не то, что хочется писать руками для каждого сервера. Codenik закрывает её как управляемый слой: публикует PRM, держит discovery, валидирует audience, применяет секреты через Token Exchange вместо passthrough и пишет аудит каждого обмена.
Команда получает least-privilege scope из WWW-Authenticate и scopes_supported без ручного хардкода, а агент — zero-touch открытие: узнал требования динамически, прошёл PKCE, получил узкий токен для конкретного ресурса. Подробнее про секреты без попадания в контекст — в статье «MCP без локальных секретов».
Короткий вывод
Авторизация MCP в 2026 — это OAuth 2.1 + PKCE S256 + Protected Resource Metadata + Resource Indicators + Token Exchange для downstream. MCP-сервер — resource server, который валидирует audience на каждый запрос и никогда не форвардит чужие токены.
Сделайте discovery, audience и обмен токенами с первого дня — и вы закроете и соответствие спеке, и самый частый вектор атаки MCP.