MCP OAuth 2.1 Авторизация
Постройте OAuth 2.1 сервер авторизации для MCP клиентов с PKCE, ротацией refresh токенов и управлением scope доступом.
AACWorkflow предоставляет свой API через MCP (Model Context Protocol) с OAuth 2.1 авторизацией. Внешние MCP клиенты — включая Claude, ChatGPT коннекторы и пользовательские инструменты — аутентифицируются через OAuth сервер чтобы получить scopт-скопированные, обновляемые токены доступа привязанные к паре (пользователь, workspace).
Обзор
OAuth сервер реализует Authorization Code grant с PKCE (Proof Key for Code Exchange), ротацией refresh токенов и динамической регистрацией клиентов. Это обеспечивает:
- Контроль пользователя — пользователи явно предоставляют доступ к workspace через экран согласия
- Scope-базированный доступ — клиенты объявляют какие ресурсы им нужны (issues, комментарии, агенты и т.д.)
- Жизненный цикл токена — короткоживущие токены доступа (~15 мин) и обновляемые долгоживущие токены
- Обнаружение переиспользования — повтор refresh токена отзывает всю семью токенов, обнаруживая утечки токенов
OAuth поток
1. Регистрация клиента
Клиенты регистрируют себя один раз через:
POST /oauth/register
Content-Type: application/json
{
"client_name": "My MCP Tool",
"redirect_uris": ["http://localhost:5000/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"token_endpoint_auth_method": "none"
}Возвращает client_id и client_secret (или нет secret для public клиентов).
2. Authorization code поток
Клиент инициирует логин:
GET /oauth/authorize?
client_id=<id>&
redirect_uri=http://localhost:5000/callback&
response_type=code&
scope=issues:read+issues:write+comments:read&
code_challenge=<sha256_hash>&
code_challenge_method=S256Пользователь видит экран согласия со списком запрашиваемых scope и workspace для доступа. При одобрении они получают auth код (действителен 10 минут):
HTTP 302
Location: http://localhost:5000/callback?code=<auth_code>&state=<state>3. Обмен кода на токен
POST /oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&
code=<auth_code>&
client_id=<id>&
code_verifier=<original_plain_challenge>&
redirect_uri=http://localhost:5000/callbackВозвращает:
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
"token_type": "Bearer",
"expires_in": 900,
"refresh_token": "<refresh_token>",
"scope": "issues:read issues:write comments:read"
}Access токен — это подписанный JWT содержащий:
sub— ID пользователяwsid— ID workspace'аscope— space-delimited scopesexp— expiration (900 секунд)
4. Refresh токен ротация
Когда access токен expiration, используйте refresh токен:
POST /oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&
refresh_token=<refresh_token>&
client_id=<id>Возвращает новый access токен и новый refresh токен. Старый refresh токен автоматически инвалидируется. Если старый refresh токен повторно использован, сервер отзывает всю семью токенов, сигнализируя возможную утечку.
Scopes
Scope токены определяют какие ресурсы клиент может получить доступ:
| Scope | Доступ |
|---|---|
issues:read | Читать issues, статус, timeline |
issues:write | Создавать, обновлять issues; менять статус |
comments:read | Читать комментарии issue и потоки |
comments:write | Создавать и редактировать комментарии |
agents:read | Список агентов, просмотр деталей |
agents:write | Создавать агентов, назначать на issues |
runtimes:read | Запрос статуса runtime и логов |
autopilot:run | Активировать autopilots |
dashboard:read | Запрос дашбордов использования и качества |
Клиенты запрашивают подмножество scopes во время авторизации. Токен выдаётся только с запрашиваемыми scopes. Если refresh сужает scopes, новый токен несёт меньше scopes чем оригинал.
Endpoints
| Method | Path | Цель |
|---|---|---|
GET | /.well-known/oauth-authorization-server | RFC 8414 метаданные discovery |
GET | /oauth/authorize | UI согласия; выдаёт auth codes |
POST | /oauth/token | Code-to-token и refresh потоки |
POST | /oauth/register | Динамическая регистрация клиента (RFC 7591) |
POST | /oauth/revoke | RFC 7009 отзыв токена |
Discovery
Клиенты могут обнаружить метаданные сервера авторизации:
GET /.well-known/oauth-authorization-serverВозвращает:
{
"issuer": "https://aacworkflow.example.com",
"authorization_endpoint": "https://aacworkflow.example.com/oauth/authorize",
"token_endpoint": "https://aacworkflow.example.com/oauth/token",
"registration_endpoint": "https://aacworkflow.example.com/oauth/register",
"revocation_endpoint": "https://aacworkflow.example.com/oauth/revoke",
"scopes_supported": [
"issues:read", "issues:write", "comments:read", "comments:write",
"agents:read", "agents:write", "runtimes:read", "autopilot:run", "dashboard:read"
],
"response_types_supported": ["code"],
"grant_types_supported": ["authorization_code", "refresh_token"],
"token_endpoint_auth_methods_supported": ["none"],
"code_challenge_methods_supported": ["S256"]
}Соображения безопасности
PKCE обязателен — code_challenge_method=S256 требуется; plain не принимается.
- Точное совпадение redirect URI — redirect URIs проверяются точно против зарегистрированных значений (без prefix match)
- Auth коды одноразовые — auth код может быть обменян только один раз; вторая попытка не удаёт
- Refresh-токен ротация — каждый refresh инвалидирует предыдущий refresh токен чтобы обнаружить и ответить на утечки
- Short-lived access токены — access токены expiration через 15 минут; клиенты должны refresh для обновления
- Безопасное хранилище токена — токены захешированы в rest (такой же KDF как personal access токены); plaintext токены никогда не сохраняются
Отзыв доступа
Отзовите токен (access или refresh) в любое время:
POST /oauth/revoke
Content-Type: application/x-www-form-urlencoded
token=<access_or_refresh_token>&
client_id=<id>Отзыв refresh токена каскадирует: все access токены выданные из этого refresh также инвалидируются.
Интеграция с MCP
Внешние MCP серверы используют OAuth токены чтобы вызывать инструменты AACWorkflow. Токены передаются в Authorization header:
GET /api/issues
Authorization: Bearer <access_token>Сервер валидирует подпись токена, проверяет expiration и применяет scopes. Если инструмент требует issues:write но токен имеет только issues:read, вызов отрицается с 403 Forbidden.
Лог аудита
Каждое OAuth событие (логин, выдача токена, refresh, отзыв, обнаружение переиспользования) записывается в лог аудита для соответствия и расследования.