Обновлено 1 августа 2026 года
Аутентификация AI-агента подтверждает, кто отправил запрос и каким приватным ключом владеет отправитель. Авторизация определяет, что этому агенту разрешено делать от имени пользователя, с конкретным ресурсом, инструментом и параметрами операции. Валидная подпись или OAuth-токен сами по себе не дают неограниченного доступа.
Коротко
- Аутентификация агента отвечает на вопрос: «Кто отправил запрос и каким приватным ключом он владеет?»
- OAuth-авторизация показывает, какие полномочия пользователь, приложение или другой субъект передал клиенту.
- Авторизация действия определяет, можно ли выполнить конкретную операцию с указанными параметрами.
- Валидная подпись доказывает identity отправителя, но не безопасность принятого им решения.
- Обычный OAuth bearer-токен может использовать любой процесс, получивший его значение, поэтому он не всегда подтверждает identity агента.
- Перед выполнением MCP-инструмента серверу обычно нужно проверить подпись, токен, scopes, целевой ресурс, параметры операции, replay status и политику доступа.
- Итоговое решение должно выглядеть как
allowed, а не просто какverified.
Оглавление
- Главное различие между аутентификацией и авторизацией
- Термины за 30 секунд
- Что доказывает аутентификация AI-агента
- Что означает авторизация AI-агента
- Где в этой модели находится OAuth
- Identity агента и пользователя
- Почему scopes недостаточно
- Step-up authorization для опасных действий
- Три практических сценария
- Матрица принятия решения
- Аутентификация и авторизация в MCP
401и403- Почему решение нельзя оставлять модели
- Рекомендуемая архитектура
- Пример с AgentBouncer
- Распространённые ошибки
- FAQ
AI-агент отправляет правильно подписанный запрос:
POST /mcp
tools/call → delete_production_databaseСервер проверяет подпись и подтверждает: запрос действительно поступил от известного агента.
Можно ли выполнять операцию?
Нет — по крайней мере, не только на основании успешной аутентификации.
Подпись доказывает, кто отправил запрос. Она не сообщает:
- разрешил ли пользователь удаление базы данных;
- имеет ли агент необходимые полномочия;
- допускает ли политика вызов этого инструмента;
- соответствует ли операция текущей задаче;
- не попал ли агент под prompt injection;
- не был ли запрос перехвачен и отправлен повторно.
Поэтому аутентификация и авторизация AI-агентов — связанные, но независимые уровни безопасности.
Упрощённо разница выглядит так:
Authentication:
«Кто отправил запрос?»
User authorization:
«Какие полномочия были делегированы клиенту?»
Action policy:
«Можно ли выполнить именно это действие прямо сейчас?»Для автономных агентов одной проверки identity почти никогда не достаточно.
Главное различие между аутентификацией и авторизацией
Аутентификация обычно выполняется раньше авторизации.
Сначала сервер устанавливает, кто обращается к ресурсу. Затем определяет, какие действия разрешены установленному субъекту.
| Проверка | Главный вопрос | Пример |
|---|---|---|
| Аутентификация агента | Кто отправил запрос? | Подпись создана зарегистрированным ключом |
| Пользовательская авторизация | Какие права были делегированы? | Токен содержит scope calendar.events.create |
| Авторизация действия | Разрешена ли конкретная операция? | Можно создать событие, но нельзя удалить календарь |
| Проверка целостности | Не изменился ли запрос? | Content-Digest соответствует body |
| Replay protection | Не использовали ли запрос раньше? | Fingerprint отсутствует в replay cache |
| Оценка риска | Нужны ли дополнительные ограничения? | Крупный платёж требует подтверждения |
Это не альтернативные способы защиты. Каждый уровень закрывает отдельную проблему.
Удобно представлять итоговую проверку как три независимых результата:
{
"verified": true,
"userAuthorized": true,
"allowed": false
}Здесь:
verified— агент прошёл криптографическую проверку;userAuthorized— предъявлена действительная пользовательская авторизация;allowed— политика запретила конкретное действие.
Именно allowed должен определять, будет ли вызван инструмент.
Термины за 30 секунд
| Термин | Значение |
|---|---|
| Authentication | Проверка identity субъекта или владения credential |
| Authorization | Определение разрешённых субъекту действий |
| Access token | Credential, представляющий выданные клиенту полномочия |
| Scope | Класс разрешений, например files.read или payments.create |
| Policy | Правило, разрешающее или запрещающее конкретную операцию |
Дополнительные понятия — bearer-токены, proof of possession, replay protection и HTTP Message Signatures — подробно разобраны в предыдущей статье: «Аутентификация AI-агентов: identity, authorization и контроль доступа».
Что доказывает аутентификация AI-агента
Практическая аутентификация AI-агента может включать три шага:
- Сервер получает подписанный запрос.
- Проверяет подпись с помощью публичного ключа.
- Связывает ключ с зарегистрированным агентом, провайдером или развёртыванием.
Результат может выглядеть так:
{
"verified": true,
"agent": "https://agent.example.com",
"provider": "Example AI",
"keyId": "agent-key-2026-08",
"trustTier": 2
}Сервер знает, что запрос подписан владельцем соответствующего приватного ключа.
Но поле:
{
"verified": true
}не означает:
{
"allowed": true
}RFC 9421 позволяет получателю убедиться, что подписанные компоненты HTTP-сообщения не изменились по пути и подписаны владельцем соответствующего ключа. Готовую модель доверия, бизнес-политику и правила авторизации спецификация не задаёт.
Что аутентификация не доказывает
Успешная проверка identity не подтверждает, что:
- пользователь разрешил запрашиваемое действие;
- агенту доступен указанный MCP-инструмент;
- сумма платежа укладывается в лимит;
- получатель входит в allowlist;
- решение агента не вызвано вредоносной инструкцией;
- запрос не является повтором ранее принятого сообщения;
- ключ не принадлежит скомпрометированному развёртыванию;
- операция соответствует первоначальной цели пользователя.
Аутентификация предоставляет данные для последующей авторизации, но не заменяет её.
Для запросов с body также важна проверка целостности через Content-Digest. Подробнее о подписываемых компонентах и replay protection — в руководстве по аутентификации AI-агентов.
Что означает авторизация AI-агента
Авторизация определяет, какие операции субъект может выполнять после проверки обязательных credentials.
Решение может зависеть от комбинации:
agent identity
+ user identity
+ OAuth scopes
+ resource
+ action
+ tool
+ parameters
+ risk
+ current contextНапример, один и тот же агент может получить разные решения:
calendar.events.read
→ ALLOW
calendar.events.create
→ ALLOW
calendar.calendars.delete
→ DENY
payments.send, amount=20 USD
→ ALLOW
payments.send, amount=5000 USD
→ REQUIRE_USER_CONFIRMATIONЭто и есть action-level authorization: сервер оценивает не только субъект и общий класс разрешений, но и конкретное действие.
Конвенция именования в примерах
Чтобы не смешивать scopes, внутренние действия и названия инструментов, далее используется единый стиль:
OAuth scope → точки:
payments.create
Policy action → двоеточие:
payments:create
MCP tool → snake_case:
payments_sendЭти значения связаны по смыслу, но не обязаны совпадать буквально.
Где в этой модели находится OAuth
OAuth предназначен для предоставления ограниченного доступа к защищённому ресурсу.
В пользовательском сценарии access token может представлять полномочия, которые владелец ресурса передал клиенту:
{
"iss": "https://auth.example.com",
"sub": "user-1842",
"aud": "https://mcp.example.com",
"scope": "calendar.read calendar.events.create",
"exp": 1785603600
}Сервер должен проверить как минимум:
- доверенного издателя
iss; - целевой ресурс или audience;
- срок действия токена;
- необходимые scopes;
- другие обязательные claims и ограничения;
- криптографическую подпись JWT либо результат introspection — в зависимости от формата токена и используемого профиля.
OAuth определяет модель делегированного доступа, но access token не обязательно представляет человека. Он может быть выпущен для workload, приложения или client_credentials flow.
Интерпретировать sub как пользователя можно только тогда, когда это гарантируют профиль токена и конфигурация authorization server.
OAuth 2.0 описывает access token как credential, представляющий выданную авторизацию, а scope — как способ ограничить предоставляемый клиенту доступ (RFC 6749, разделы 1.4 и 3.3).
Почему OAuth-токен не всегда является identity агента
Рассмотрим запрос:
Authorization: Bearer USER_ACCESS_TOKENОбычный bearer-токен можно использовать без дополнительного доказательства владения ключом.
Если значение токена получат:
- вредоносный процесс;
- другой агент;
- скомпрометированный MCP-сервер;
- сотрудник с доступом к журналам;
- сторонняя интеграция,
сервер может увидеть корректный токен, но не узнать, какой программный субъект на самом деле его предъявил.
Получается:
OAuth access token
→ сообщает о предоставленном доступе
Подпись запроса
→ подтверждает владение ключом отправителяЭти данные дополняют друг друга.
Для уменьшения риска кражи токенов применяются sender-constrained механизмы, например DPoP или mTLS-bound access tokens. DPoP связывает токен с публичным ключом и требует доказательства владения соответствующим приватным ключом (RFC 9449).
При этом proof of possession не отвечает на главный бизнес-вопрос: разрешено ли выполнить конкретную операцию.
Identity агента и identity пользователя — не одно и то же
Типичный запрос автономного агента может содержать две независимые identity:
Agent identity:
подтверждается HTTP Message Signature
User authorization:
подтверждается OAuth access tokenНапример:
POST /mcp HTTP/1.1
Host: mcp.example.com
Content-Type: application/json
Authorization: Bearer USER_ACCESS_TOKEN
Content-Digest: sha-256=:BODY_DIGEST:
Signature-Agent: "https://agent.example.com"
Signature-Input: sig1=("@method" "@authority" "@path" "content-digest" "signature-agent");
created=1785600000;
expires=1785600060;
keyid="agent-key-2026-08";
tag="web-bot-auth"
Signature: sig1=:BASE64_SIGNATURE:Поле tag="web-bot-auth" связывает подпись с ожидаемым назначением запроса. Сервер может отклонить криптографически корректную подпись, если endpoint ожидает другой intent tag.
После проверки сервер получает два контекста:
{
"agent": {
"identity": "https://agent.example.com",
"verified": true
},
"user": {
"subject": "user-1842",
"scopes": [
"documents.read",
"documents.summarize"
]
}
}Но после этого всё равно требуется третье решение:
{
"action": "documents:export",
"tool": "documents_export",
"destination": "https://external.example.com/upload",
"allowed": false,
"reason": "external_destination_not_allowed"
}Пользователь разрешил читать документы и создавать краткие пересказы. Это не означает, что агенту разрешено экспортировать исходные файлы на внешний сервер.

Почему scopes недостаточно
Scopes задают границы выданного доступа, но обычно остаются шире отдельной операции.
Scope:
payments.createможет разрешать создание платежей. Но он не отвечает на вопросы:
- какую сумму можно отправить;
- в какой валюте;
- каким получателям;
- сколько операций разрешено за день;
- можно ли создавать международные переводы;
- нужно ли подтверждение пользователя;
- допустим ли вызов от нового агента;
- соответствует ли операция задаче пользователя.
Для этого требуется policy engine.
Концептуальная политика может выглядеть так:
IF
agent.trustTier <= 2
AND user.scope includes "payments.create"
AND action == "payments:create"
AND tool == "payments_send"
AND amount <= 100
AND recipient in user.allowlist
AND replay == false
THEN
ALLOW
ELSE
REQUIRE_CONFIRMATION or DENYЭто псевдокод, а не синтаксис конкретного продукта. На практике такую проверку реализуют в Cedar, Open Policy Agent, собственном policy engine или в политиках проекта AgentBouncer.
Scope — входное условие авторизации, а не её окончательный результат.
Step-up authorization для опасных действий
Не каждому инструменту сразу нужны максимальные OAuth scopes.
Например, при первом подключении клиент может запросить только:
documents.readЕсли позднее пользователь просит экспортировать документ, сервер отвечает:
HTTP/1.1 403 Forbidden
WWW-Authenticate: Bearer error="insufficient_scope",
scope="documents.export",
resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource"
Cache-Control: no-storeКлиент запускает повторную авторизацию и запрашивает расширенный набор полномочий.
В актуальной спецификации MCP клиент должен вычислять объединение ранее запрошенных и новых scopes:
previous scopes:
documents.read
challenged scopes:
documents.export
new authorization request:
documents.read documents.exportЭто предотвращает ситуацию, когда step-up выдаёт новый токен для экспорта, но случайно теряет ранее предоставленное право чтения.
Серверу, в свою очередь, рекомендуется возвращать сразу все scopes, необходимые для текущей операции, а не запрашивать их по одному в нескольких циклах. Клиент должен ограничивать количество повторных попыток, чтобы не попасть в бесконечный authorization loop. Эти правила закреплены в step-up flow MCP 2026-07-28.
Важно: новый scope всё ещё не гарантирует выполнение операции.
Например:
documents.export
+ destination in corporate allowlist
→ ALLOW
documents.export
+ destination controlled by unknown third party
→ DENYStep-up authorization расширяет делегированные полномочия, но не отменяет action-level policy.
Три сценария, показывающих разницу
Сценарий 1. Известный агент без OAuth-токена
Агент вызывает инструмент:
calendar_create_eventПодпись корректна, identity известна. Но политика требует пользовательской авторизации со scope:
calendar.events.createРезультат:
{
"verified": true,
"userAuthorized": false,
"allowed": false,
"reason": "oauth_token_required"
}Практический ответ:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer
resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource",
scope="calendar.events.create"
Cache-Control: no-storeАгент аутентифицирован, но обязательный пользовательский credential отсутствует.
Сценарий 2. Корректная подпись и недостаточный scope
Агент предъявляет токен со scope:
calendar.readНо запрашивает:
calendar.events.createРезультат:
{
"verified": true,
"userAuthorized": true,
"allowed": false,
"reason": "insufficient_scope"
}Ответ:
HTTP/1.1 403 Forbidden
WWW-Authenticate: Bearer
error="insufficient_scope",
scope="calendar.events.create",
resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource"
Cache-Control: no-storeCredentials действительны, но не дают требуемого разрешения.
Сценарий 3. Все credentials корректны, но действие опасно
Агент имеет:
- валидную подпись;
- известную identity;
- OAuth-токен;
- scope
payments.create.
Он запрашивает:
{
"tool": "payments_send",
"amount": 5000,
"currency": "USD",
"recipient": "new-recipient"
}Политика допускает автоматические платежи только до 100 долларов, а для новых получателей требуется ручное подтверждение.
Результат:
{
"verified": true,
"userAuthorized": true,
"allowed": false,
"reason": "user_confirmation_required"
}Текущий запрос не выполняется. Сервер может вернуть 403 с прикладным кодом ошибки и создать отдельный confirmation flow:
HTTP/1.1 403 Forbidden
Content-Type: application/json
Cache-Control: no-store{
"error": "user_confirmation_required",
"confirmationId": "confirm-7f3c",
"expiresIn": 300
}Подтверждение должно быть связано с точными параметрами операции:
tool
+ amount
+ currency
+ recipient
+ expirationНельзя принимать абстрактное подтверждение «разрешить следующий платёж», потому что агент сможет изменить параметры между подтверждением и выполнением.
Матрица принятия решения
| Подпись агента | OAuth | Scopes и policy | Результат | HTTP |
|---|---|---|---|---|
| Некорректна или отсутствует | Любой | — | invalid_agent_credentials | 401 |
| Корректна | Не требуется | Policy разрешает | Выполнить | 200 |
| Корректна | Отсутствует | OAuth обязателен | oauth_required | 401 |
| Корректна | Некорректен или просрочен | — | invalid_token | 401 |
| Корректна | Корректен | Scopes недостаточны | insufficient_scope | 403 |
| Корректна | Корректен | Policy запрещает | agent_access_denied | 403 |
| Корректна | Корректен | Нужно подтверждение | user_confirmation_required | 403 |
| Корректна | Корректен | Всё разрешено | Выполнить | 200 |
| Replay обнаружен | Любой | — | replay_detected | 401 |
| Любая | Любой | Превышен rate limit | rate_limit_exceeded | 429 |
| Не удалось выполнить обязательную проверку | Любой | Решение неизвестно | verification_unavailable | 503 |
Для временных ошибок полезно добавлять:
Retry-After: 30Подробный словарь ошибок, включая 429 и 503, приведён в статье «Аутентификация AI-агентов: identity, authorization и контроль доступа».
Главное правило интеграции:
if (!verification.allowed) {
deny();
}Проверять только verified недостаточно.
В AgentBouncer эти поля разделены намеренно: verified отражает результат криптографической проверки, а allowed — окончательное решение политики проекта. В режиме мониторинга возможна даже комбинация verified: false и allowed: true, поэтому ориентироваться нужно только на итоговый результат. (agentbouncer.io)
Аутентификация и авторизация в MCP
MCP Authorization регулирует доступ клиентов к удалённым MCP-серверам через OAuth.
В актуальной редакции MCP от 28 июля 2026 года:
- authorization остаётся необязательной возможностью протокола;
- для HTTP-транспорта используется OAuth-модель;
- MCP-сервер выступает как OAuth resource server;
- клиент обнаруживает authorization servers через Protected Resource Metadata;
- клиент должен передавать параметр
resource; - сервер должен принимать только токены, предназначенные для его ресурса;
- для authorization code flow требуется PKCE;
- stdio-серверы не должны применять HTTP OAuth flow и обычно получают credentials из окружения;
- клиент должен корректно обрабатывать step-up authorization;
- OAuth credentials должны быть привязаны к issuer выдавшего их authorization server;
- Dynamic Client Registration сохраняется для совместимости, но помечена deprecated в пользу Client ID Metadata Documents.
Официальная страница MCP Authorization для редакции 2026-07-28 закрепляет проверку audience, Protected Resource Metadata, 401 для отсутствующих или недействительных токенов и 403 для недостаточных scopes.
Что изменилось в MCP 2026-07-28
Редакция 2026-07-28 стала крупнейшим обновлением протокола с момента его запуска. В неё вошли:
- stateless-ядро без обязательного
initializehandshake; - удаление protocol-level sessions и
Mcp-Session-Id; server/discoverдля обнаружения версий, capabilities и identity сервера;- Multi Round-Trip Requests для получения дополнительных данных;
- обязательные заголовки
Mcp-MethodиMcp-Nameдля HTTP-маршрутизации; - кешируемые результаты списков через
ttlMsиcacheScope; - формальный extensions framework;
- усиление OAuth- и OpenID Connect-совместимости;
- формальный жизненный цикл Active → Deprecated → Removed.
Полный список опубликован в changelog MCP 2026-07-28
Переход на новую редакцию не означает, что все реализации 2025-11-25 мгновенно перестают работать. Клиенты и серверы могут сохранять обратную совместимость и согласовывать поддерживаемую версию. Для возможностей, официально получивших статус Deprecated, политика MCP устанавливает не менее двенадцати месяцев до возможного удаления.
Проверка iss и защита от mix-up attacks
В MCP 2026-07-28 усилена проверка issuer в OAuth flow.
Перед перенаправлением пользователя клиент сохраняет ожидаемый issuer из проверенного metadata-документа authorization server. При получении authorization response клиент проверяет параметр iss по правилам RFC 9207.
Если authorization server сообщает:
{
"authorization_response_iss_parameter_supported": true
}но не возвращает iss, клиент должен отклонить response.
Если iss присутствует, его нужно сравнить с сохранённым issuer точным строковым сравнением — без автоматического изменения регистра, удаления завершающего слеша или нормализации URL.
Эта проверка снижает риск OAuth authorization server mix-up attacks, особенно в MCP-клиентах, которые подключаются ко множеству независимых серверов.
Credentials должны быть привязаны к issuer
Client ID и связанные credentials действуют в контексте authorization server, который их выдал.
Если MCP-ресурс переезжает с одного authorization server на другой, клиент не должен автоматически повторно использовать прежние credentials. Он должен:
- обнаружить новый authorization server;
- проверить его metadata;
- найти credentials, привязанные именно к этому issuer;
- при необходимости зарегистрироваться заново.
Это предотвращает случайную отправку client secret, refresh token или других credentials неправильному серверу.
Client ID Metadata Documents вместо DCR
MCP 2026-07-28 рекомендует OAuth Client ID Metadata Documents как основной механизм получения client ID.
Dynamic Client Registration по RFC 7591 по-прежнему может поддерживаться, но в спецификации MCP она отмечена как deprecated и сохранена для обратной совместимости.
Перед началом authorization flow клиент может получить client ID одним из трёх способов:
- Client ID Metadata Document;
- предварительная регистрация;
- Dynamic Client Registration.
DCR нельзя удалять из работающих интеграций без анализа совместимости, однако новые реализации стоит проектировать с поддержкой Client ID Metadata Documents.
Protected Resource Metadata
Protected Resource Metadata стандартизировано RFC 9728. Metadata может сообщать:
- authorization servers;
- поддерживаемые scopes;
- способы передачи bearer-токенов;
- поддержку DPoP;
- другие свойства защищённого ресурса.
Пример:
{
"resource": "https://mcp.example.com",
"authorization_servers": [
"https://auth.example.com"
],
"scopes_supported": [
"documents.read",
"documents.write"
],
"bearer_methods_supported": [
"header"
]
}Чего MCP OAuth не решает автоматически
OAuth-модель MCP не создаёт универсальную криптографическую identity самого AI-агента.
Она может сообщить серверу:
Клиент предъявил токен,
действительный для этого MCP resource,
с указанным набором scopes.Но для ответа на отдельный вопрос:
Какой агент, провайдер или workload
отправил этот HTTP-запрос?может потребоваться дополнительная проверка:
- HTTP Message Signatures;
- mTLS;
- DPoP-bound access token;
- workload identity;
- зарегистрированный signing key.
Это особенно важно, если один пользовательский токен потенциально могут использовать несколько программных участников.
Статус спецификаций на 1 августа 2026 года: актуальная редакция MCP —
2026-07-28. OAuth 2.1 всё ещё является Internet-Draft, а не опубликованным RFC. Текущая редакция IETF —draft-ietf-oauth-v2-1-15от 2 марта 2026 года со сроком действия до 3 сентября 2026 года.
401 и 403: краткая шпаргалка
Названия HTTP-статусов могут сбивать с толку: 401 Unauthorized используется, когда запросу не хватает допустимых authentication credentials.
Возвращайте 401, если:
- отсутствует обязательная подпись;
- подпись не прошла проверку;
- signing key неизвестен;
- OAuth-токен отсутствует;
- токен просрочен, отозван или некорректен;
- токен предназначен для другого ресурса;
- DPoP proof не прошёл проверку;
- обнаружен replay подписанного запроса.
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer
resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource"
Cache-Control: no-storeВозвращайте 403, если:
- credentials уже приняты;
- не хватает scopes;
- инструмент недоступен этому агенту;
- пользователь не имеет доступа к объекту;
- превышен policy-лимит операции;
- получатель запрещён;
- действие требует отдельного подтверждения.
HTTP/1.1 403 Forbidden
WWW-Authenticate: Bearer
error="insufficient_scope",
scope="payments.create",
resource_metadata="https://mcp.example.com/.well-known/oauth-protected-resource"
Cache-Control: no-storeRFC 6750 предусматривает 401 для недействительного bearer-токена и 403 с insufficient_scope, когда предоставленных полномочий недостаточно. Общая семантика статусов определена в RFC 9110.
Расширенный разбор 401, 403, 429 и 503 приведён в первой статье серии.
Почему решение нельзя оставлять самой модели
Можно добавить в system prompt правило:
Никогда не отправляй платежи больше 100 долларов.Но это инструкция для модели, а не надёжная граница безопасности.
Prompt injection может попытаться убедить агента, что:
- операция уже подтверждена;
- лимит временно отменён;
- внешний получатель является внутренним;
- инструмент нужно вызвать только для проверки;
- системная инструкция больше не действует.
Если модель после этого отправит запрос, его криптографическая подпись останется валидной.
Поэтому правила, ограничивающие реальные последствия, должны выполняться:
- в MCP-сервере;
- в API gateway;
- в downstream API;
- в отдельном policy engine;
- непосредственно перед необратимой операцией.
OWASP рекомендует применять принцип минимальных полномочий, отдельно авторизовывать чувствительные инструменты, проверять параметры tool calls и не полагаться только на инструкции внутри prompt. Для необратимых и дорогостоящих действий также рекомендуется human-in-the-loop.
Модель может предложить действие:
{
"tool": "payments_send",
"amount": 5000
}Но доверенный код должен независимо решить:
{
"allowed": false,
"reason": "transaction_limit_exceeded"
}Рекомендуемая архитектура
AI-агент
│
│ Подписанный запрос
│ OAuth-токен пользователя
▼
MCP endpoint или API gateway
│
├── 1. Проверка Content-Digest
├── 2. Проверка HTTP Message Signature
├── 3. Поиск identity агента
├── 4. Проверка timestamps и intent tag
├── 5. Replay protection
├── 6. Проверка OAuth issuer и audience
├── 7. Проверка scopes
├── 8. Определение action и tool
├── 9. Проверка параметров и лимитов
└── 10. Итоговая policy evaluation
│
├── DENY
├── REQUIRE_CONFIRMATION
└── ALLOW
▼
MCP toolВсе проверки должны выполняться до вызова инструмента.
Для запросов с body проверять Content-Digest следует по исходным байтам до JSON.parse(). RFC 9530 определяет Content-Digest как digest фактического содержимого HTTP-сообщения.
Порядок важен:
получить raw request
→ проверить digest
→ проверить подпись
→ проверить replay
→ проверить OAuth
→ применить policy
→ разобрать аргументы
→ выполнить toolЕсли приложение сначала изменило или пересериализовало JSON, а затем начало проверять digest, оно уже работает не с теми байтами, которые подписывал отправитель.
Практический пример с AgentBouncer
import {
createAgentBouncer,
getRequiredOAuthScopes,
isOAuthRequired,
isOAuthScopeDenied,
} from "@agentbouncer/sdk";
const publicOrigin =
process.env.AGENTBOUNCER_PUBLIC_ORIGIN!;
const resourceMetadataUrl =
`${publicOrigin}/.well-known/oauth-protected-resource`;
const agentBouncer = createAgentBouncer({
apiKey:
process.env.AGENTBOUNCER_API_KEY!,
publicOrigin,
validateContentDigest: true,
});
export async function POST(
request: Request,
): Promise<Response> {
const verification =
await agentBouncer.verify({
request,
expectedTag: "web-bot-auth",
action: "payments:create",
tool: "payments_send",
forwardAuthorization: true,
});
if (!verification.allowed) {
const headers = new Headers({
"Cache-Control": "no-store",
});
if (isOAuthRequired(verification)) {
const requiredScopes =
getRequiredOAuthScopes(verification);
headers.set(
"WWW-Authenticate",
`Bearer resource_metadata="${resourceMetadataUrl}", scope="${requiredScopes.join(" ")}"`,
);
return Response.json(
{
error: "oauth_required",
requiredScopes,
},
{
status: 401,
headers,
},
);
}
if (isOAuthScopeDenied(verification)) {
const requiredScopes =
getRequiredOAuthScopes(verification);
headers.set(
"WWW-Authenticate",
`Bearer error="insufficient_scope", scope="${requiredScopes.join(" ")}", resource_metadata="${resourceMetadataUrl}"`,
);
return Response.json(
{
error: "insufficient_scope",
requiredScopes,
},
{
status: 403,
headers,
},
);
}
return Response.json(
{
error: verification.verified
? "agent_access_denied"
: "invalid_agent_credentials",
reason: verification.reason,
},
{
status:
verification.verified
? 403
: 401,
headers,
},
);
}
const payload = await request.json();
return await executePaymentTool(payload);
}В этом примере:
verifiedпоказывает, прошла ли криптографическая проверка identity;- OAuth helpers определяют, нужен ли токен и хватает ли scopes;
allowedсодержит окончательное решение;expectedTagпроверяет назначение подписи;- body разбирается только после всех проверок;
- выполнение инструмента начинается только при
allowed: true.
Актуальная документация AgentBouncer подтверждает, что verify() проверяет Content-Digest до чтения body, по умолчанию пересылает bearer-токен для OAuth-проверки и предоставляет helpers isOAuthRequired(), isOAuthScopeDenied() и getRequiredOAuthScopes(). (agentbouncer.io)
Распространённые ошибки
1. Считать валидную подпись разрешением
Подпись подтверждает владение ключом. Она не выдаёт право на все действия.
Правильно:
if (!verification.allowed) {
deny();
}Неправильно:
if (verification.verified) {
executeTool();
}2. Считать OAuth-токен identity агента
Bearer-токен сообщает о предоставленном доступе, но не всегда доказывает, какой процесс его предъявил.
Если важно установить identity программного участника, используйте отдельную подпись, workload identity или sender-constrained token.
3. Проверять только scopes
Scope может быть необходимым условием, однако параметры операции всё равно должны пройти policy-проверку.
payments.createне означает:
любая сумма
+ любой получатель
+ любая страна
+ неограниченное количество операций4. Использовать один общий scope
mcp.full_accessТакой scope упрощает интеграцию, но мешает разделять:
- чтение;
- изменение;
- удаление;
- экспорт;
- административные операции;
- финансовые действия.
Предпочтительнее:
documents.read
documents.write
documents.export
documents.delete5. Не проверять audience
Токен для другого API нельзя автоматически принимать на MCP-сервере.
MCP требует передавать resource и принимать только токены, выпущенные для конкретного защищённого ресурса. Сервер также не должен принимать или транзитно передавать токены, предназначенные другим сервисам.
6. Передавать пользовательский токен дальше по цепочке
MCP-сервер не должен просто пересылать входящий access token в downstream API.
Для следующего ресурса нужен отдельный токен с подходящим audience. Один из стандартных способов получить его — OAuth Token Exchange по RFC 8693.
Пример:
Incoming token:
aud = https://mcp.example.com
Downstream API:
https://payments.example.comПередавать первый токен напрямую в Payments API нельзя. MCP-сервер может обменять его на более узкий токен:
aud = https://payments.example.com
scope = payments.executeRFC 8693 позволяет resource server обменять входящий токен на новый credential, предназначенный для конкретного downstream-сервиса.
7. Применять политику после вызова инструмента
Проверка, выполненная после удаления данных или отправки платежа, уже не защищает систему.
Авторизация должна происходить непосредственно перед side effect.
8. Разрешать модели самостоятельно отменять ограничения
Модель может предлагать решение, но итоговую авторизацию должен выполнять доверенный код.
Нельзя принимать такие аргументы модели:
{
"limitOverride": true,
"userAlreadyConfirmed": true
}без независимой проверки сервером.
9. Не различать пользователя и агента в журналах
Audit log должен сохранять оба контекста:
{
"agent": "https://agent.example.com",
"agentKeyId": "agent-key-2026-08",
"userSubject": "user-1842",
"action": "payments:create",
"tool": "payments_send",
"decision": "DENY",
"reason": "transaction_limit_exceeded",
"policyVersion": "payments-v12"
}Для чувствительных операций также полезно сохранять:
- risk level;
- confirmation ID;
- хеш параметров подтверждённой операции;
- issuer токена;
- scopes;
- replay status;
- версию политики.
10. Использовать verified вместо allowed
Это превращает успешную криптографическую проверку в неограниченное разрешение.
Кроме того, в monitor mode неподтверждённый запрос может временно получить allowed: true, чтобы команда могла изучить реальный трафик перед включением блокировок.
Поэтому:
verified
→ результат проверки подписи
allowed
→ итоговое решение для выполнения операции11. Бесконечно повторять step-up authorization
Если сервер продолжает отвечать insufficient_scope, клиент не должен запускать бесконечные OAuth-циклы.
Нужно:
- объединять старые и новые scopes;
- ограничивать число повторов;
- связывать попытки с ресурсом и операцией;
- после нескольких неудач считать отказ постоянным.
12. Повторно использовать credentials другого issuer
Client ID, client secret и refresh tokens должны быть привязаны к authorization server, который их выдал.
При смене issuer клиент должен выполнить discovery заново и при необходимости перерегистрироваться.
Часто задаваемые вопросы
В чём разница между аутентификацией и авторизацией AI-агента?
Аутентификация устанавливает identity агента или подтверждает владение credential. Авторизация определяет, какие действия разрешены этому агенту с учётом пользователя, scopes, ресурса, инструмента, параметров и политики доступа.
Является ли OAuth аутентификацией агента?
Не обязательно. OAuth прежде всего представляет выданные полномочия. Для проверки identity вызывающего агента может потребоваться отдельная HTTP Message Signature, сертификат, workload identity или proof-of-possession механизм.
Достаточно ли HTTP Message Signature?
Нет. Подпись защищает выбранные компоненты сообщения и подтверждает владение signing key, но не решает, разрешена ли бизнес-операция.
Чем scope отличается от policy?
Scope описывает класс разрешений, например files.write. Policy проверяет конкретную операцию: файл, владельца, размер, место назначения, уровень риска, параметры запроса и текущий контекст.
Может ли аутентифицированный агент получить 403?
Да. Установленная identity не означает, что субъекту разрешено любое действие. Например, агент может быть известен серверу, но не иметь доступа к конкретному MCP-инструменту.
Когда нужен 401, а когда 403?
401 обычно возвращается при отсутствующих или недействительных credentials. 403 — когда credentials приняты, но scopes или policy-разрешения недостаточно.
Нужно ли проверять identity пользователя и агента отдельно?
Да. Пользователь и агент — разные участники. Пользователь делегирует полномочия, а агент технически формирует и отправляет запрос.
Что должен проверять MCP-сервер перед вызовом инструмента?
Как минимум:
- подпись агента;
- целостность body;
- срок действия подписи;
- intent tag;
- replay status;
- issuer и audience токена;
- OAuth scopes;
- вызываемый инструмент;
- параметры операции;
- квоты и лимиты;
- итоговую политику доступа.
Заключение
Аутентификация и авторизация AI-агентов решают разные задачи.
Аутентификация подтверждает:
«Этот запрос подписан владельцем известного ключа».OAuth сообщает:
«Клиенту предоставлены определённые полномочия».Политика доступа принимает окончательное решение:
«Этому агенту с этими полномочиями разрешено выполнить
данное действие с указанными параметрами прямо сейчас».Без первого уровня сервер не знает, кто отправил запрос. Без второго — какие полномочия были делегированы клиенту. Без третьего — допустима ли конкретная операция.
AgentBouncer объединяет проверку HTTP Message Signatures, identity агента, пользовательскую OAuth-авторизацию, replay protection и политики проекта в одно итоговое решение allowed.
Защитите API или MCP-сервер с AgentBouncer — начните с monitor mode, изучите реальный агентский трафик и только затем включайте блокирующие политики.
Предыдущая статья: «Аутентификация AI-агентов: identity, authorization и контроль доступа».
Следующая статья серии: «MCP Authentication and Authorization: полное руководство» — скоро.
Источники
- AgentBouncer — документация
- RFC 9421 — HTTP Message Signatures
- RFC 9530 — Digest Fields
- RFC 6749 — The OAuth 2.0 Authorization Framework
- RFC 6750 — OAuth 2.0 Bearer Token Usage
- RFC 9449 — OAuth 2.0 Demonstrating Proof of Possession
- RFC 9728 — OAuth 2.0 Protected Resource Metadata
- RFC 8707 — Resource Indicators for OAuth 2.0
- RFC 8693 — OAuth 2.0 Token Exchange
- RFC 9207 — OAuth 2.0 Authorization Server Issuer Identification
- RFC 9110 — HTTP Semantics
- OAuth 2.1 Internet-Draft — история редакций
- MCP Authorization — спецификация от 28 июля 2026 года
- MCP 2026-07-28 — Key Changes
- OWASP AI Agent Security Cheat Sheet
- OWASP MCP Security Cheat Sheet
- OWASP LLM Prompt Injection Prevention Cheat Sheet
