В этом руководстве регистрируется мобильный или десктопный клиент в тенанте fapi2 и разбираются вызовы, которые приложение делает к нативным эндпоинтам edge. В конце у вас есть access token с привязкой DPoP, вы вызвали с ним API, обновили и отозвали токен. Нужен edge с блоками native и dpop и auth с mutual-TLS-слушателем. Основные понятия описаны в разделе Нативные приложения через edge.
В примере используются https://edge.example.com для edge, https://id.example.com для auth, acme-mobile как ID клиента и https://app.example.com/callback как redirect URI.
Предварительные условия
- Тенант с профилем безопасности
fapi2, привязанный к вашему edge. Тенант по умолчанию —fapi2. edge, настроенный как в разделе Конфигурация edge: блокnative, блокdpop,EDGE_NATIVE_BLOB_KEY.authс блокомmutual-tls, вtrusted-certificatesкоторого есть УЦ, которымcentralподписывает клиентские сертификаты, иcentralс бэкендомclient-certificates(Клиентские сертификаты edge). Цели CLI настраивают всё это сами.- Домен под вашим контролем для App Link (Android) или Universal Link (iOS) с размещённым файлом ассоциации, чтобы
https://app.example.com/callbackоткрывал ваше приложение. - Ресурс за
edgeи роль, которая его разрешает, как в Как защитить API с помощью edge, если в конце хотите вызвать API.
1. Создайте клиента
- Откройте Clients и запустите мастер создания.
- На шаге 1 выберите Mobile or desktop app. Панель плана перечисляет, что мастер фиксирует для этого типа в вашем тенанте: PKCE
S256, mTLS от имениedge, access token с привязкой DPoP, pushed authorization requests, только перенаправление на App Link, access token на 1 час, ротация refresh token. - В Basics введите ID клиента
acme-mobile, имя и redirect URIhttps://app.example.com/callback. Пользовательская схема вродеcom.example.app://в тенантеfapi2отклоняется. - Выдайте клиенту scope, которые нужны приложению, в мастере или позже на странице редактирования клиента:
openid, а такжеoffline_access, если приложению нужен refresh token. - Проверьте и создайте клиента.
Тип учётных данных — edge-provisioned: вводить и хранить ничего не нужно. В терминах Central API мастер регистрирует applicationType: native, authMethod: tls_client_auth, dpopBoundAccessTokens: true, requirePushedAuthorizationRequests: true, certificateBoundAccessTokens: false и enrollEdgeClientCertificate: true. Если вы регистрируете через API, передайте эти поля: enrollEdgeClientCertificate и issueEdgeClientCertificate обязательны и по умолчанию не подставляются.
Чтобы сузить алгоритмы DPoP-proof для этого клиента, откройте клиента и задайте алгоритмы DPoP. Пустое значение принимает всё, что объявляет сервер (ES256 и PS256 в метаданных по умолчанию).
Если приложение будет вызывать API, добавьте acme-mobile в Audience ресурса и убедитесь, что у пользователей есть роль с нужным разрешением.
2. Дайте edge подключить сертификат
Делать ничего не нужно. В течение configuration-cache-refresh-interval (генерируется 5 минут) edge загрузит клиента, сгенерирует пару ключей и попросит central подписать для неё сертификат (шаги enrollment).
Чтобы проверить, что edge обслуживает клиента, вызовите start без proof:
curl -i -X POST https://edge.example.com/native/start/acme-mobile
HTTP/1.1 400 Bad Request
{"error":"invalid_dpop_proof","error_description":"request must carry a DPoP proof from the device key"}
400 значит, что у edge есть сертификат и он считает клиента нативным. 404 значит, что сертификата пока нет или клиент не отвечает требованиям. Подождите один интервал и посмотрите в логе edge edge_certificate_unavailable.
3. Подготовьте ключ устройства
На устройстве один раз сгенерируйте в хранилище ключей пару ES256 и храните её всё время жизни установки. Для каждого вызова вы строите proof — JWT, подписанный закрытым ключом:
| Часть | Содержимое |
|---|---|
| Header | typ: dpop+jwt, alg: ES256, jwk: открытый ключ |
| Payload | htm: POST, htu: см. таблицу ниже, iat: текущее время, jti: новое случайное значение, nonce, если edge или auth его запросили |
Proof строит любая библиотека с поддержкой DPoP (RFC 9449). htu у вызовов разный:
| Вызов | htu |
|---|---|
| start | https://edge.example.com/native/start/acme-mobile |
| complete, token | token_endpoint из ответа start |
| revoke | revocation_endpoint из ответа start |
4. Пройдите вызовы
Значения DPoP ниже — заглушки для proof из шага 3.
Start. Отправьте запрос авторизации через edge.
curl -X POST https://edge.example.com/native/start/acme-mobile \
-H "DPoP: <proof for start>" \
-d "scope=openid offline_access"
{"client_id":"acme-mobile","request_uri":"urn:ietf:params:oauth:request_uri:...","expires_in":600,
"authorization_endpoint":"https://id.example.com/authorize","state":"...","blob":"...",
"token_endpoint":"https://id.example.com:8083/token","revocation_endpoint":"https://id.example.com:8083/revoke"}
Если ответ 401 с DPoP-Nonce, постройте proof заново с этим nonce и повторите запрос.
Браузер. Откройте системный браузер на authorization_endpoint, передав client_id и request_uri:
https://id.example.com/authorize?client_id=acme-mobile&request_uri=urn%3Aietf%3Aparams%3Aoauth%3Arequest_uri%3A...
Пользователь входит. auth перенаправляет на https://app.example.com/callback?code=...&state=...&iss=..., и открывается ваше приложение.
Complete. В течение expires_in секунд отправьте три значения и blob, с proof для token_endpoint.
curl -X POST https://edge.example.com/native/complete/acme-mobile \
-H "DPoP: <proof for token_endpoint>" \
-d "code=<code>" -d "state=<state>" -d "iss=<iss>" -d "blob=<blob>"
{"access_token":"...","token_type":"DPoP","expires_in":3600,"refresh_token":"...","scope":"openid offline_access"}
Вызов API. Proof для вызова API адресован URL edge и содержит htm метода запроса и хеш access token (ath).
curl https://edge.example.com/resources/orders-api/orders \
-H "Authorization: DPoP <access token>" \
-H "DPoP: <proof for this request>"
Refresh.
curl -X POST https://edge.example.com/native/token/acme-mobile \
-H "DPoP: <proof for token_endpoint>" \
-H "Idempotency-Key: <unique per rotation>" \
-d "refresh_token=<refresh token>"
Revoke при выходе.
curl -X POST https://edge.example.com/native/revoke/acme-mobile \
-H "DPoP: <proof for revocation_endpoint>" \
-d "token=<refresh token>" -d "token_type_hint=refresh_token"
Повторное обновление по тому же refresh token теперь возвращает invalid_grant.
5. Если что-то не работает
| Ответ | Вероятная причина |
|---|---|
404 на любой вызов /native/* | Нет блока native. Клиент не native или у него ещё нет сертификата edge. Тенант клиента не привязан к этому edge. Неверный ID клиента |
400 invalid_dpop_proof | Заголовка DPoP нет или их два. Неверный htu (берите URL из ответа start, а не свои). iat вне допустимого окна. Proof от start использован повторно, поэтому на каждый вызов стройте новый. Алгоритм ключа не принимается |
401 с DPoP-Nonce | Нужен nonce. Повторите один раз с proof, содержащим его |
400 invalid_grant на complete | Blob истёк (прошло expires_in). state или iss не совпадают с тем, что вернул редирект. Proof подписан другим ключом, чем тот, с которого вызывали start. Если причина в iss, проверьте, что native.auth-issuer равен jwt.issuer у auth |
400 invalid_request | redirect_uri не зарегистрирован, параметр отсутствует или повторён либо тело не в form-кодировке |
invalid_client от auth | auth не доверяет УЦ, подписавшему сертификат edge, или subject сертификата не соответствует mtlsAuth. См. Клиентские сертификаты edge |
400 из /par про redirect URI или PKCE | Регистрация клиента и запрос противоречат друг другу. Проверьте redirect URI и то, что тенант fapi2 |
Авторизация в auth падает сразу после открытия браузера | request_uri истёк или уже использован. Вызовите start заново |
401 от API с invalid_dpop_proof | В proof для API нет ath, неверный htu (должен быть URL edge для этого вызова) или токен отправлен как Bearer. Токен, привязанный к ключу, как Bearer не принимается никогда |
Что дальше
- Нативные приложения через edge: модель, которая стоит за этими вызовами.
- Клиентские сертификаты edge: обновление, файлы и режимы отказа.
- Токены с привязкой к отправителю: DPoP и nonce.