Токен доступа с привязкой к отправителю связан с ключом или сертификатом, которым владеет клиент. Тот, кто скопировал такой токен, не сможет им воспользоваться без этого ключа или сертификата. Versola поддерживает два механизма: DPoP (RFC 9449) и токены, привязанные к сертификату mTLS (RFC 8705).
Чем опасны bearer-токены
Bearer-токен работает у любого, кто его предъявил. Если он утёк из лога, прокси, расширения браузера или отчёта о падении, владелец утечки может вызывать ваш API до истечения срока токена. Привязка к ключу меняет доказательство: вместо «у меня есть строка» нужно «у меня есть строка и закрытый ключ».
Тенанты с профилем безопасности fapi2 требуют от каждого клиента один из двух механизмов.
DPoP
В DPoP клиент владеет парой ключей и подписывает для каждого запроса короткий JWT-proof.
Что отправляет клиент:
| Запрос | Заголовки |
|---|---|
Запрос токена в auth | DPoP: <proof>. Access-токен в ответе привязан к ключу proof как cnf.jkt |
Вызов API через edge | Authorization: DPoP <access_token> и DPoP: <proof>. В proof есть htm, htu и ath (хеш access-токена) |
Как это проверяется:
- Алгоритмы. По умолчанию
ES256иPS256.RS256поддерживается, но в набор по умолчанию не входит. Ключи RSA — не короче 2048 бит. htu.authстроит ожидаемый URL из настроенного issuer, аedge— изedge-url. ЗаголовокHostне используется. На listener с mTLS-алиасамиhtuдолжен совпадать с адресом алиаса.- Повтор.
jtiproof запоминается, повторное использование отклоняется. - Nonce. Если nonce обязателен, proof без свежего nonce получает ответ
use_dpop_nonceс заголовкомDPoP-Nonce. Клиент повторяет запрос, добавив nonce в proof.authтребует nonce на уровне тенанта (requireDpopNonce, по умолчаниюfalse; Challenges & Security, DPoP).edgeтребует его, когда у записи в Edges включено Require DPoP nonce. - Где.
authпроверяет proof на/tokenи/userinfo.edgeпроверяет его на проксируемых вызовах.
Ключи конфигурации:
| Где | Ключ | По умолчанию | Смысл |
|---|---|---|---|
auth | dpop.iat-leeway | 60s | Допустимое расстояние между iat proof и текущим временем |
auth | dpop.nonce-ttl | 300s | Сколько времени выданный nonce принимается |
edge | dpop.iat-leeway | задаётся в конфигурации edge | То же на прокси |
edge | dpop.nonce-ttl | задаётся в конфигурации edge | То же на прокси |
edge | dpop.nonce-salt | задаётся в конфигурации edge | Секрет для генерации nonce |
Поля клиента (Central API, Clients в консоли):
| Поле | Смысл |
|---|---|
dpopBoundAccessTokens | Клиент всегда использует DPoP. Запрос токена без proof отклоняется с invalid_dpop_proof |
dpopSigningAlgs | Сужает алгоритмы proof для клиента. Пустой список ничего не сужает. Можно только убрать алгоритмы, которые сервер объявляет в dpop_signing_alg_values_supported |
dpopMinRsaKeySize | Минимальная длина модуля RSA в битах. Значения меньше 2048 отклоняются при регистрации |
Токены, привязанные к сертификату mTLS
В mTLS клиент аутентифицируется сертификатом X.509, а access-токен содержит SHA-256-отпечаток сертификата как cnf.x5t#S256. Introspection возвращает тот же claim.
- Listener.
authдублирует/token,/introspect,/revoke,/parи/userinfoна отдельном TLS-listener на портуMPORT(по умолчанию8083), который требует клиентский сертификат. Listener запускается, только если в конфигурацииauthесть блокmutual-tlsсcertificate,private-key,trusted-certificatesиexternal-url. Уtrusted-certificatesнет значения по умолчанию: без него не доверялся бы ни один сертификат. - Discovery. Когда listener включён, discovery-документ получает
mtls_endpoint_aliasesс адресом изexternal-url. Для запросов с сертификатом клиент должен обращаться к этим алиасам. - mTLS на прокси. Если mTLS завершает обратный прокси,
authчитает сертификат из заголовка. Настройка выполняется на уровне тенанта в Challenges & Security, Mutual TLS:mtlsCertificateHeaderиmtlsCertificateEncoding(urlEncodedPemилиbase64Der). Оба поля задаются или очищаются вместе. Клиент с аутентификацией по сертификату на тенанте без этой настройки отклоняется.
Поля клиента:
| Поле | Смысл |
|---|---|
authMethod | tls_client_auth (субъект сертификата сверяется с зарегистрированным значением) или self_signed_tls_client_auth (ключ сертификата сверяется с jwks клиента). Не меняется после создания |
mtlsAuth | Для tls_client_auth: subjectType (subject_dn, san_dns, san_uri, san_ip, san_email) и subjectValue |
certificateBoundAccessTokens | Привязывает токены к сертификату для клиента, который аутентифицируется иначе. Клиент с аутентификацией по сертификату всегда получает привязанные токены |
Исключение: у нативного клиента за edge сертификат принадлежит edge и общий для всех установок, поэтому его токены привязаны только к ключу DPoP устройства, а certificateBoundAccessTokens должно быть false.
Как выбрать
| Ситуация | Что использовать |
|---|---|
| Мобильное или десктопное приложение | DPoP. Ключ хранится в хранилище ключей устройства |
| Сервис вызывает ваш API с контролируемого вами бэкенда | tls_client_auth с токенами, привязанными к сертификату, или private_key_jwt с DPoP |
Браузерное приложение за edge | На стороне клиента настраивать нечего. Токены остаются внутри edge, браузер работает с сессией в cookie EDGE_SESSION |
| Нельзя запустить listener с клиентскими сертификатами или прокси | DPoP |
Как с ними работает edge
edge решает по claim cnf токена, а не по схеме, которую выбрал вызывающий.
| Токен | Как предъявлен | Результат |
|---|---|---|
cnf.jkt | Authorization: DPoP с корректным proof | Принят |
cnf.jkt | Authorization: Bearer | Отклонён (dpop_downgrade). Это действует даже без блока dpop в edge |
cnf.x5t#S256 | cookie EDGE_SESSION | Принят. Сертификат принадлежит самому edge |
cnf.x5t#S256 | заголовок Authorization | Отклонён. edge не может проверить сертификат вызывающего |
cnf.jkt и cnf.x5t#S256 | proof DPoP | Отклонён. Корректного proof недостаточно, если вторую половину привязки (сертификат) показать нельзя |
без cnf | Authorization: DPoP | Отклонён. Proof ничего не говорит о непривязанном токене |
Если убрать блок dpop из конфигурации edge, проверка DPoP отключается, но токены, привязанные к ключу, по Bearer всё равно отклоняются. Нативному приложению, работающему через edge, блок dpop обязателен.