Как собрать поток нативного мобильного приложения — Versola Docs
VersolaVersola/docs
До 1.00.7.1versola.kzGitHub

Как собрать поток нативного мобильного приложения

Зарегистрируйте нативного клиента в консоли администратора, дайте edge подключить его сертификат, пройдите вызовы native start, complete, token и revoke и узнайте, что проверять при сбое.

В этом руководстве регистрируется мобильный или десктопный клиент в тенанте 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. Создайте клиента

  1. Откройте Clients и запустите мастер создания.
  2. На шаге 1 выберите Mobile or desktop app. Панель плана перечисляет, что мастер фиксирует для этого типа в вашем тенанте: PKCE S256, mTLS от имени edge, access token с привязкой DPoP, pushed authorization requests, только перенаправление на App Link, access token на 1 час, ротация refresh token.
  3. В Basics введите ID клиента acme-mobile, имя и redirect URI https://app.example.com/callback. Пользовательская схема вроде com.example.app:// в тенанте fapi2 отклоняется.
  4. Выдайте клиенту scope, которые нужны приложению, в мастере или позже на странице редактирования клиента: openid, а также offline_access, если приложению нужен refresh token.
  5. Проверьте и создайте клиента.

Тип учётных данных — 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, подписанный закрытым ключом:

ЧастьСодержимое
Headertyp: dpop+jwt, alg: ES256, jwk: открытый ключ
Payloadhtm: POST, htu: см. таблицу ниже, iat: текущее время, jti: новое случайное значение, nonce, если edge или auth его запросили

Proof строит любая библиотека с поддержкой DPoP (RFC 9449). htu у вызовов разный:

Вызовhtu
starthttps://edge.example.com/native/start/acme-mobile
complete, tokentoken_endpoint из ответа start
revokerevocation_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 на completeBlob истёк (прошло expires_in). state или iss не совпадают с тем, что вернул редирект. Proof подписан другим ключом, чем тот, с которого вызывали start. Если причина в iss, проверьте, что native.auth-issuer равен jwt.issuer у auth
400 invalid_requestredirect_uri не зарегистрирован, параметр отсутствует или повторён либо тело не в form-кодировке
invalid_client от authauth не доверяет УЦ, подписавшему сертификат 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 не принимается никогда

Что дальше