Хватит играть в open bar: почему MCP без OAuth — это катастрофа
Скажу прямо: MCP-сервер без аутентификации — это как оставить ключи от Ferrari в зажигании на парковке у ТЦ. Рано или поздно найдётся «умник». Цифры не врут: по данным свежего сканирования 2181 MCP-эндпоинта, 37% из них хоть как-то проверяют подлинность запроса. Но из этих 37% подавляющее большинство использует статические API-ключи, которые не различают пользователей. Это не аутентификация — это проформа.
Проблема вдвойне опаснее, когда ваш MCP-сервер работает с чувствительными данными: HR-системой, CRM, финансовыми отчётами. Без привязки к конкретному пользователю любой агент, подключившийся к серверу, получает доступ ко всему. Один неверный промпт — и миллионы клиентов утекли. Звучит как паранойя? Погуглите prompt injection MCP — увидите реальные кейсы.
К счастью, 2026 год принёс чёткий стандарт: OAuth 2.0 — обязательный слой для remote MCP. Никаких самописных bearer-токенов. Никакой магии. Только проверенный протокол с PKCE, refresh-токенами и скоупами.
Ключевой факт: Спецификация MCP версии 2026-04 (апрельский релиз) включает поддержку OAuth 2.0 как нативный транспорт аутентификации. Серверы обязаны реализовывать как минимум authorization_code с PKCE или device_code. Всё остальное — legacy, который будет deprecated в 2027.
Какой OAuth flow выбрать? Три кита, и каждый тонет в своей луже
Универсального решения нет. Всё упирается в окружение, где живёт ваш AI-агент. Вот три сценария с мясом:
| Flow | Когда использовать | Тип клиента | PKCE обязателен? |
|---|---|---|---|
| Authorization Code с PKCE | Веб-агенты (Claude Desktop, Continue web), десктопные приложения | Публичный (public client) | Да |
| Device Code | CLI, headless-агенты (Claude Code, Cline терминал) | Публичный | Нет, но рекомендуется |
| Client Credentials | Machine-to-machine (сервис-сервис без пользователя) | Конфиденциальный | Не применимо |
Лично я бешусь, когда вижу Device Code в вебе или попытку протащить Client Credentials для пользовательского агента. Это технически неправильно и ломает всю модель безопасности. Не делайте так, если не хотите получить дыру в аудите.
Поднимаем сервер с OAuth за 15 минут (да, реально)
Предположим, у вас уже есть MCP-сервер на Python (например, на fastapi-mcp). В 2026 году большая часть SDK уже включает готовые middleware для OAuth. Я покажу минимальный, но рабочий пример с использованием mcp-sdk-auth (версия 2.1+).
1 Добавьте OAuth endpoints
Сервер должен предоставлять /.well-known/oauth-authorization-server (метаданные), /authorize, /token, /jwks. В SDK это делается одной строчкой:
from mcp_sdk_auth import AuthMiddleware
from your_server import app
# Включаем OAuth с поддержкой authorization_code и device_code
auth = AuthMiddleware(
issuer="https://mcp.myserver.com",
public_clients=True, # разрешаем PKCE
device_code=True, # включаем device flow
jwt_private_key=open("private.pem").read()
)
app.mount("/auth", auth.router)
app.add_middleware(auth.oauth_check)
Важный нюанс: ключ private.pem должен генерироваться один раз и храниться в Vault или секрет-менеджере. Если ключ утечёт, злоумышленник сможет подделывать токены. Используйте HashiCorp Vault или AWS Secrets Manager для управления.
2 Конфигурируйте scopes
Скоупы — это разрешения, которые запрашивает клиент. Они маппятся на конкретные инструменты. Например:
{
"scopes": {
"crm:read": ["get_contacts", "get_deals"],
"crm:write": ["create_contact", "update_deal"],
"admin": ["delete_contact"]
}
}
Клиент при запросе токена передаёт список нужных скоупов. Сервер проверяет, что они входят в разрешённые для данного клиента. Так вы получаете гранулярный контроль: агент Пети может только читать, а агент Васи — ещё и писать.
3 Тесты с curl (а не надейтесь на удачу)
Проверим Device Code flow — такой чаще всего используют CLI-агенты:
# Шаг 1: инициируем флоу
curl -s -X POST "https://mcp.myserver.com/auth/device_authorization" \
-H "Content-Type: application/json" \
-d '{"client_id": "my-cli-agent", "scope": "crm:read"}'
# Ответ: device_code, user_code, verification_uri, interval
# Шаг 2: пользователь открывает verification_uri и вводит user_code
# Шаг 3 (через interval секунд): опрашиваем token endpoint
curl -s -X POST "https://mcp.myserver.com/auth/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=urn:ietf:params:oauth:grant-type:device_code" \
-d "device_code=..." \
-d "client_id=my-cli-agent"
# Получаем access_token + refresh_token
Типичная ошибка: не ставить interval в теле ответа или игнорировать его на клиенте. Клиенты, которые долбят token endpoint каждые 100ms, получают HTTP 429. Респект за настойчивость, но делайте как сказано в спецификации.
Клиентская сторона: настраиваем Claude Desktop, Cline и Continue
В 2026 году почти все популярные MCP-клиенты умеют работать с OAuth из коробки. Нужно только передать правильные параметры.
Пример конфига для Claude Desktop (файл ~/.config/Claude/claude_desktop_config.json):
{
"mcpServers": {
"my-crm": {
"url": "https://mcp.myserver.com",
"auth": {
"type": "oauth",
"oauth": {
"authorizationEndpoint": "https://mcp.myserver.com/auth/authorize",
"tokenEndpoint": "https://mcp.myserver.com/auth/token",
"clientId": "claude-desktop",
"scopes": ["crm:read", "crm:write"],
"pkce": true
}
}
}
}
}
Для Continue.dev конфиг в YAML выглядит похоже, только без pkce — он включён по умолчанию для всех браузерных клиентов.
Если используете CLI-агент (Claude Code), он сам обнаружит Device Code flow по метаданным /.well-known/oauth-authorization-server. Вам достаточно указать client_id.
Топ-5 граблей, на которые я наступал (и вы наступите)
- Невалидные JWK — сервер отдаёт публичный ключ в неверном формате. Клиенты падают с ошибкой «invalid signature». Проверяйте
/.well-known/jwks.jsonна валидность через гайд по настройке токенов. - Отсутствие refresh-токена — access_token живёт 5 минут, а клиент не запрашивает refresh. Через 5 минут агент перестаёт работать. Решение: включите
refresh_tokenв ответе token endpoint и убедитесь, что клиент умеет его использовать. - Жёсткая привязка к скоупам — если клиент запросил скоуп
admin, а сервер его не поддерживает, он вернёт ошибкуinvalid_scope. Пропишите fallback: если скоуп не распознан, игнорируем его. - Пропущенный PKCE — без PKCE ваш Authorization Code flow уязвим для перехвата кода. В 2026 году это считается моветоном. MCP SDK включает PKCE автоматически, но если вы пишете свой сервер с нуля — не забудьте про
code_challengeиcode_verifier. - CORS — веб-агенты (Claude Desktop) делают redirect к вашему серверу. Если сервер не отдаёт корректные CORS-заголовки, браузер заблокирует редирект. Решение:
Access-Control-Allow-Origin: *(или конкретный origin агента) иAccess-Control-Allow-Credentials: true.
FAQ: то, о чём обычно молчат
Можно ли использовать API-ключи вместо OAuth?
Формально — да, если ваш MCP-сервер работает только в локальной сети и вы контролируете все клиенты. Но для публичных remote-серверов спецификация MCP 2026 запрещает простые bearer-токены без OAuth. Если хотите совместимость со всеми современными агентами — внедряйте OAuth.
Как тестировать OAuth локально?
Используйте localhost с самоподписанным сертификатом и укажите в конфиге агента "allowInsecure": true (для Device Code). Или поднимите ngrok — тогда сработает полный Authorization Code flow через браузер.
Что если мой MCP-сервер не поддерживает OAuth?
Есть два пути: либо обновить SDK до версии, где есть OAuth (скорее всего, автор уже добавил), либо поставить перед сервером OAuth Proxy. Второй вариант разобран в статье про Auth Proxy с Keycloak.
Нужно ли логировать все OAuth-события?
Обязательно. Каждый /authorize и /token должен попадать в SIEM. Инциденты с перебором client_id или подозрительным количеством failed attempts — сразу в алерт. OAuth — не панацея, а часть defence in depth.
Ладно, хватит страшилок. Главное, что я хочу донести: OAuth для MCP в 2026 — не «опциональная фича», а базовый слой безопасности. Без него ваш сервер — решето. С ним — вы спите спокойно, а аудиторы не долбят в дверь.
Один неочевидный совет, который сэкономит вам часы: мониторьте Token Exchange endpoint. Внезапный всплеск 401 ошибок на /auth/token может сигнализировать о попытке атаки перебором — но чаще это просто клиент с просроченным refresh-токеном. Настройте алерт на variance +30% от baseline. И не забудьте дать клиентам внятное сообщение об ошибке, а не generic 401 Unauthorized.