Аутентификация
OAuth 2.0, привязка токена к клиенту по DPoP или mTLS, серверный nonce, scopes.
OAuth 2.0
API LegalStack использует OAuth 2.0 с требованиями OAuth 2.1: PKCE обязателен, токены короткоживущие, каждый access token привязан к клиенту. Точка выдачи токенов — POST https://api.legalstack.ru/oauth/token, метаданные — https://api.legalstack.ru/.well-known/oauth-authorization-server.
Поддерживаемые сценарии получения токена:
- Client Credentials — для серверных интеграций от имени команды.
- Authorization Code с PKCE — для приложений, действующих от имени пользователя.
Все операции OAuth (авторизация, обмен кода, обновление, отзыв и проверка токена) — в разделе «OAuth» справочника API.
Привязка токена к клиенту
Платформа не выдаёт токенов, которыми может воспользоваться любой, кто их перехватил. Каждый access token привязан к клиенту одним из двух способов, и ресурсные сервисы отклоняют токен без привязки:
- DPoP (RFC 9449) — токен привязан к ключу, который клиент сгенерировал сам и хранит у себя. Это основной способ для интеграций, SDK и MCP-клиентов. В справочнике API схема безопасности называется
<Fact k="dpopSecurityScheme" />. - mTLS (RFC 8705) — токен привязан к клиентскому сертификату, которым установлено TLS-соединение. Так работают сервисы самой платформы и клиенты, которым платформа выдала сертификат. Схема безопасности —
<Fact k="mtlsSecurityScheme" />.
Как работает DPoP
- Клиент один раз создаёт пару ключей. Поддерживаемые алгоритмы:
ES256, ES384, ES512, EdDSA, PS256, PS384, PS512, RS256. Приватный ключ не покидает клиента. - На каждый HTTP-запрос клиент подписывает этим ключом короткий JWT-доказательство и передаёт его в заголовке
DPoP. В доказательстве:htm(метод),htu(URL без query и fragment),iat, уникальныйjti,nonceсервера, а для запросов с токеном ещёath— SHA-256 от access token в base64url. В заголовке JWT:typ: dpop+jwt,algи публичный ключ вjwk. - Токен передаётся со схемой
DPoP, неBearer:
GET /api/v1/tasks HTTP/1.1
Authorization: DPoP <access_token>
DPoP: <доказательство для GET https://api.legalstack.ru/api/v1/tasks>- Сервер проверяет подпись, совпадение ключа с привязкой в токене (
cnf.jkt), метод, адрес, свежесть,ath, nonce и однократностьjti. Доказательство действительно300секунд с моментаiat, повторное использованиеjtiотклоняется.
Серверный nonce
Чтобы доказательства нельзя было заготовить впрок, сервер требует включать в них свой nonce и присылает актуальное значение в заголовке DPoP-Nonce каждого ответа. Значение действует не меньше 120 секунд с момента выдачи, поэтому для следующего запроса берите nonce из последнего ответа.
Если клиент только запускается или простаивал дольше этого срока, получите nonce заранее: GET на путь /.well-known/dpop-nonce того же хоста API отвечает 204 без тела и с заголовком DPoP-Nonce. Токен и доказательство для этого запроса не нужны. Параллельные запросы после простоя дождитесь одного такого запроса, тогда все они уйдут с действующим nonce.
Запрос без nonce или с устаревшим значением не выполняется:
- точка выдачи токенов отвечает
400сerror: use_dpop_nonce; - ресурсные сервисы отвечают
401с кодомUSE_DPOP_NONCEи заголовкомWWW-Authenticate: DPoP error="use_dpop_nonce".
В обоих случаях в ответе есть DPoP-Nonce: повторите тот же запрос с новым доказательством, включив это значение в nonce.
Получение токена
На POST /oauth/token клиент передаёт заголовок DPoP с доказательством для этого запроса. В ответе token_type равен DPoP, а токен содержит отпечаток ключа в cnf.jkt. Запрос за токеном без доказательства и без клиентского сертификата отклоняется с error: invalid_dpop_proof.
HTTP/1.1 200 OK
DPoP-Nonce: <nonce для следующего доказательства>
Content-Type: application/json
{ "access_token": "…", "token_type": "DPoP", "expires_in": 900, "scope": "tasks:read" }Токены короткоживущие; обновляйте их по refresh_token или повторным client-credentials запросом тем же ключом DPoP. Ответ 401 с кодом TOKEN_EXPIRED — сигнал получить новый токен. Refresh token выдаётся только клиентам с DPoP и клиентам с сертификатом, при каждом обновлении он ротируется.
Ресурсы
Каждый токен выдаётся для одного ресурса платформы, и ресурс указывается параметром resource (RFC 8707) при авторизации и при запросе токена:
| Ресурс | Для чего | Scope |
|---|---|---|
https://api.legalstack.ru | REST API, ресурс по умолчанию | все scope каталога, кроме mcp:tools:execute |
https://mcp.legalstack.ru/mcp | MCP-сервер, все разделы | mcp:tools:execute |
https://mcp.legalstack.ru/mcp/<раздел> | MCP-сервер, один раздел | mcp:tools:execute |
Запрос токена для любого другого адреса отклоняется с error: invalid_target. В настройках OAuth-приложения можно сузить список ресурсов, для которых приложение получает токены: пустой список разрешает все ресурсы платформы.
Персональные ключи
Персональный ключ создаётся в «Настройки → API → API-ключи» и начинается с lk_. Это долгоживущий секрет пользователя, и в запросах к API он не передаётся: ресурсные сервисы отвечают 401 с кодом INVALID_TOKEN на любой запрос с ключом в заголовке Authorization. Ключ обменивается на короткоживущий токен доступа по RFC 8693, а токен привязывается к ключу DPoP так же, как в разделе выше.
POST /oauth/token HTTP/1.1
Host: api.legalstack.ru
Content-Type: application/x-www-form-urlencoded
DPoP: <доказательство для POST https://api.legalstack.ru/oauth/token>
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
&subject_token=lk_…
&subject_token_type=urn:legalstack:params:oauth:token-type:personal-token
&resource=https://api.legalstack.ru
&scope=tasks:read{
"access_token": "…",
"issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
"token_type": "DPoP",
"expires_in": 900,
"scope": "tasks:read"
}- Без DPoP ключ не обменивается. Запрос без доказательства получает
error: invalid_dpop_proof, первый запрос с доказательством без nonce получаетuse_dpop_nonceи значение в заголовкеDPoP-Nonce. - Scope токена не шире ключа и ресурса. Без параметра
scopeтокен получает все scope ключа, доступные для ресурса. Ключ без ограничений получает все scope ресурса. Запрос scope, которого нет у ключа или у ресурса, отклоняется сerror: invalid_scope. - Токен не переживает ключ. Срок токена не больше срока ключа, а отзыв ключа сразу делает недействительными все выданные по нему токены.
- Ключ не управляет учётными данными. Создавать и отзывать ключи и OAuth-приложения можно только из пользовательской сессии в веб-приложении.
Scopes
Доступ контролируется scopes вида <домен>:<действие> — например tasks:read, registry:write, webhooks:write. Требуемые scopes каждой операции указаны в справочнике API, полный список — в каталоге scope. Запрашивайте минимально необходимый набор: запрос с недостающим scope получит 403 с кодом OAUTH_INSUFFICIENT_SCOPE.
Ключи подписи
Публичные ключи для проверки JWT публикуются в JWKS: https://api.legalstack.ru/.well-known/jwks.json. Ключи ротируются — резолвьте их по kid из заголовка токена, а не кэшируйте единственный ключ навсегда.