Public Authentication API#

この API は、Hosted Login を使わずにログイン画面を構築するための Public Authentication API である。OIDC 認可コードフローの transaction、Email OTP、Passkey、Social login、認証後の自ユーザー管理を提供する。

Base URL#

同じ Public Authentication API を、Environment の Interaction Domain または Canonical Issuer で呼び出せる。

形式 Base URL 用途
Interaction Domain https://<interaction-domain>/v1 Hosted Login と同じ Interaction Domain 上で認証する。Passkey の RP ID と Origin は Interaction Domain に結び付く。通常の Headless UI ではこちらを使う。
Canonical Issuer alias https://id.zeroword.smartcrab.ai/e/<environmentId>/v1 Canonical Issuer だけを知る Client や Native Client から使う。Environment は path の <environmentId> で指定し、host は Canonical Issuer でなければならない。

Default Interaction Domain は env-<environmentId の env_ 以降>.login.zeroword.smartcrab.ai である。Environment で有効な Custom Domain が設定されている場合は、その Interaction Domain を使う。Canonical Issuer、Interaction Domain、RP ID の関係は概念とテナントモデルを参照する。

例では次の値を使う。

AUTH_ENVIRONMENT_ID="<environmentId>"
AUTH_API="https://<interaction-domain>/v1"
AUTH_ISSUER_ENV="https://id.zeroword.smartcrab.ai/e/$AUTH_ENVIRONMENT_ID"
AUTH_CLIENT_ID="<clientId>"
AUTH_REDIRECT_URI="https://app.example.com/auth/callback"

Canonical Issuer alias を使う場合は、AUTH_API="$AUTH_ISSUER_ENV/v1" とする。各 Endpoint の path は、上記どちらかの Base URL に続けて指定する。

統合方法の選択#

通常の OIDC ログインで Hosted Login へ遷移する場合は、アプリから GET /oauth2/authorize を呼び出す。Authorization Server が認可 request から内部 transaction を作成し、Interaction Domain へ handoff する。この経路に対して POST /v1/auth/transactions を重ねて呼ばない。

独自ログイン UI では、OIDC authorize redirect を開始せず、アプリが保持する client_id、redirect_uri、PKCE、state、scope を POST /v1/auth/transactions へ渡す。これは独立した認証 transaction を作るが、Client 登録、許可済み redirect URI、PKCE を省略できる standalone login ではない。

/v1/config は公開 Environment 設定を返し、/v1/me 以下の Endpoint は OIDC access token で認証する。Token Endpoint、scope、TTL、Client 認証の詳細はOIDC / OAuth 2.0を参照する。

公開設定#

GET /v1/config?client_id=<clientId> は現在の Host から Environment を解決し、Client の公開設定を返す。client_id は必須である。レスポンスには issuer、rp_id、available_methods、theme、sdk_compatibility_version、legal が含まれる。available_methods には passkey、email_otp および有効な Social provider が入る。Provider secret や内部構成は返さない。

curl "$AUTH_API/config?client_id=$AUTH_CLIENT_ID"

Transaction lifecycle#

作成#

POST /v1/auth/transactions は 201 Created を返す。client_id、redirect_uri、code_challenge、code_challenge_method: "S256"、state、scope は必須。nonce、resource、response_mode は任意である。PKCE の plain は使えない。redirect_uri は Client に登録された値と完全一致させる。

curl -i -X POST "$AUTH_API/auth/transactions" \
  -H 'content-type: application/json' \
  -d '{
    "client_id": "<clientId>",
    "redirect_uri": "https://app.example.com/auth/callback",
    "code_challenge": "<base64url-sha256-of-code-verifier>",
    "code_challenge_method": "S256",
    "state": "<random-state>",
    "nonce": "<random-nonce>",
    "scope": "openid profile email offline_access",
    "resource": ["https://api.example.com"],
    "response_mode": "redirect"
  }'

response_mode は redirect または native_return である。resource は最大 20 個の URI を指定できる。作成レスポンスは transaction_id、expires_at、available_methods を返す。transaction の TTL は 10 分である。

状態取得、cancel、complete#

Method Path 動作
GET /auth/transactions/{transactionId} 現在の状態、期限、利用可能な認証方式、必要な場合は method と pending_link を返す。
POST /auth/transactions/{transactionId}/cancel body なし。failed にして再開不可にする。
POST /auth/transactions/{transactionId}/complete body なし。authenticated の transaction を完了し、1 回限りの authorization code を返す。

状態は created → interaction_started → challenge_issued → authenticated → code_issued → consumed と進む。Social account linking が必要な場合は link_required になる。キャンセル後は failed、TTL 超過後は expired である。各 verify Endpoint は成功時に authenticated へ進めるが、access token / refresh token は返さない。complete のレスポンスに含まれる state は作成時の値と照合し、redirect_uri は Client 登録値と再照合する。authorization code の TTL は 60 秒である。

curl -i "$AUTH_API/auth/transactions/<transactionId>"
curl -i -X POST "$AUTH_API/auth/transactions/<transactionId>/complete"

complete で得た authorization_code を、元の PKCE verifier と redirect URI で Token Endpoint へ交換する。

curl -X POST "$AUTH_ISSUER_ENV/oauth2/token" \
  -H 'content-type: application/x-www-form-urlencoded' \
  --data-urlencode 'grant_type=authorization_code' \
  --data-urlencode 'code=<authorization_code>' \
  --data-urlencode "redirect_uri=$AUTH_REDIRECT_URI" \
  --data-urlencode 'code_verifier=<original-code-verifier>' \
  --data-urlencode "client_id=$AUTH_CLIENT_ID"

SPA / React Native Client では Client secret を送らず、PKCE を使う。Token Endpoint は OIDC access token と、grant に応じて refresh token・ID token を返す。/v1/me には ID token ではなく access token を使う。

Email OTP#

OTP は 6 桁、TTL は 5 分である。Environment・email・purpose ごとに同時に有効な code は 1 つであり、保存時は server pepper 付き HMAC を用いる。誤った code の verify は最大 5 回で、5 回目に challenge を失効する。Resend は最短 30 秒間隔であり、再送時には generation が増えて以前の code が無効になる。

Method Path Request 成功時
POST /auth/transactions/{transactionId}/email/start {"email":"user@example.com"} 202、challenge_id と expires_at
POST /auth/transactions/{transactionId}/email/resend {"challenge_id":"<challengeId>"} 202、新しい challenge 情報
POST /auth/transactions/{transactionId}/email/verify {"challenge_id":"<challengeId>","code":"123456"} 200、transaction_id と status: "authenticated"

メールアドレスが既存かどうかをレスポンスから判別できない。既存・未登録アドレスの start は同じ HTTP status と response shape を返し、UI には「コードを送信できる場合は送信した」と表示する。suppression 対象への送信も外部レスポンスを変えない。resend_too_soon などの 429 では Retry-After を確認し、challenge 状態を取り直してから再試行する。Verify 後は別途 transaction を complete する。

Passkey / WebAuthn#

WebAuthn の binary 値は Base64URL で JSON に直列化する。ブラウザでは options の Base64URL field を ArrayBuffer へ戻し、navigator.credentials.create() または navigator.credentials.get() へ渡す。返された PublicKeyCredential は Base64URL へ変換して verify Endpoint へ送る。Browser の credential origin は、Environment の Interaction Domain(active な Custom Domain が RP として有効ならその Domain)または ceremony で選択した RP ID の HTTPS origin のいずれかに一致させる。

登録#

認証済みで fresh な transaction(直前の OTP / Social 再認証を含む)または fresh session が必要である。

Method Path Request Response
POST /auth/transactions/{transactionId}/passkeys/registration/options body なし。Native で RP を明示する場合は optional な rp_id を指定できる。 WebAuthn creation options
POST /auth/transactions/{transactionId}/passkeys/registration/verify {"credential":{"id":"<base64url>","rawId":"<base64url>","type":"public-key","response":{"clientDataJSON":"<base64url>","attestationObject":"<base64url>"}},"name":"<optional-label>"} 登録した passkey summary と transaction status

Creation options の JSON は rp: {id, name}、user: {id, name, displayName}、challenge、pubKeyCredParams、timeout、excludeCredentials、authenticatorSelection、attestation を含む。user.id は opaque な user handle であり、email を入れない。residentKey: "required"、userVerification: "required"、attestation: "none" は固定値である。Platform authenticator と security key の双方を許可する。

Verify payload の credential.response には clientDataJSON と attestationObject が必須で、transports、publicKey、publicKeyAlgorithm などは Authenticator が返した場合に含められる。Server は challenge、origin、RP ID、UP、UV、signature を検証し、challenge は成功・失敗を問わず再利用できない。

/v1/me/passkeys/registration/verify でも、clientDataJSON.origin は上記の WebAuthn origin と一致しなければならない。Client の allowed_origins は CORS 用であり、別の app origin を WebAuthn verifier の許可 origin には追加しない。Android Native の origin は登録済み mobile app の署名 fingerprint から導出する。

認証#

Method Path Request Response
POST /auth/transactions/{transactionId}/passkeys/authentication/options application/json body。email は optional で credential を絞れる。rp(active / legacy)または rp_id も指定可能で、両方は同時に指定できない。指定なしなら {} を送る。 WebAuthn request options
POST /auth/transactions/{transactionId}/passkeys/authentication/verify {"credential":{"id":"<base64url>","rawId":"<base64url>","type":"public-key","response":{"clientDataJSON":"<base64url>","authenticatorData":"<base64url>","signature":"<base64url>"}}} transaction_id と status: "authenticated"

Discoverable credential によるユーザー名なしの認証が標準である。Options response は challenge、rpId、timeout、allowCredentials、userVerification: "required" を含む。任意の email で絞った場合も、該当 credential の有無をレスポンスから判別できないようにする。Verify 後は complete を呼び、code を Token Endpoint へ交換する。sign counter の変化だけで同期 passkey を直ちに block しない。

/v1/me/passkeys 以下の追加登録は Bearer access token で認証する。List は credential ID、公開鍵、sign count を返さない。最後の認証手段を削除しようとすると last_factor_deletion_requires_confirmation で拒否される。

Social login#

利用可能な provider segment は google、apple、github、microsoft、generic_oidc である。Environment で active な connection だけを開始できる。POST /auth/transactions/{transactionId}/social/{provider}/start は body なしで、{"authorization_url":"https://..."} を返す。System browser へ遷移し、embedded WebView は使わない。

Provider callback は /v1/auth/social/{provider}/callback である。Managed connection は Canonical Issuer の /e/{environmentId}/v1/auth/social/{provider}/callback へ、BYO connection は Environment の Interaction Domain へ戻る。Callback に transaction ID path parameter はなく、state に含まれる署名済み transaction ID から transaction を復元する。Provider は GET query、または POST の form / JSON body で state と code(拒否時は error / error_description)を返す。認証成功後、通常の redirect mode では Interaction Domain へ戻るため、Client は transaction 状態を取得して complete する。Native return mode では登録 redirect URI に tx、元の state、social=completed が付いて戻る。

/v1/me/identities/{provider}/link は既存ユーザーの identity linking を開始する。fresh な Bearer session と {"redirect_uri":"<redirect-uri>"} が必要で、レスポンスは同じ authorization_url shape である。redirect_uri は呼び出し元 Client に登録済みの redirect URI と byte-for-byte 完全一致し、fragment を含めない。不一致は 400 (invalid_redirect_uri) を返す。OAuth callback 後の app return 先として link state に保存され、callback はこの URI へ結果 query を付けて redirect する。Provider callback は transaction ID ではなく署名済み link state を使う。Email 一致だけで自動 link / account merge はしない。GET /v1/me/identities で一覧を取得し、DELETE /v1/me/identities/{identityId} で unlink する。

/v1/me self-service#

すべての Endpoint で OIDC access token を Authorization: Bearer <access_token> に設定する。Hosted Login の session cookie や ID token は認証に使わない。Bearer token を明示的な HTTP header で送るため、CSRF token は要求されない。Email 変更、全 session 失効など security version を更新する操作後は、再認証して access token を取得し直す。

Method Path Request / response
GET /me プロフィール。email、verification 状態、name、avatar_url、locale、timezone、status、作成・最終 login 時刻などを返す。
PATCH /me 更新可能 field は name、avatar_url、locale、timezone。email は更新不可。成功時は更新後プロフィールを返す。
POST /me/email/change/start {"new_email":"new@example.com"}。現在の email へ OTP を送り、202 で challenge を返す。
POST /me/email/change/verify-old {"challenge_id":"<old-challenge>","code":"123456"}。旧 email を確認し、新 email 宛の challenge を 202 で返す。
POST /me/email/change/verify-new {"challenge_id":"<new-challenge>","code":"123456"}。成功時は {"email":"new@example.com","email_verified":true} を返し、email を切り替える。
GET /me/identities linked Social identities の identity_id、provider、link 時刻、最終利用時刻を返す。
POST /me/identities/{provider}/link fresh session が必要。{"redirect_uri":"<redirect-uri>"} は provider callback 後の app return URL である。authorization_url を得て system browser を開く。
DELETE /me/identities/{identityId} 自ユーザーの identity を unlink し、{"identity_id":"<identityId>","unlinked":true} を返す。
GET /me/passkeys 自ユーザーの passkey summary を返す。credential ID と公開鍵は含まない。
POST /me/passkeys/registration/options Bearer access token で追加登録 options を取得する。body なし。Native で RP を明示する場合は optional な {"rp_id":"<hostname>"} を送る。
POST /me/passkeys/registration/verify {"credential":{...WebAuthn registration credential...},"name":"<optional-label>"}。成功時は登録 passkey summary を返す。
DELETE /me/passkeys/{passkeyId} 自ユーザーの passkey を削除し、{"passkey_id":"<passkeyId>","deleted":true} を返す。最後の認証手段は削除できない。
GET /me/sessions session_id、client_id、device_name、作成時刻、最終活動時刻、現在の session かを返す。
DELETE /me/sessions/{sessionId} 指定 session を失効し、{"session_id":"<sessionId>","revoked":true} を返す。現在の session を失効すると、その後の API call には新しい認証が必要になる。
DELETE /me/sessions すべての session を失効し、{"revoked":true} を返す。
DELETE /me body なし。fresh session が必要。論理削除状態 pending_deletion へ移し、user_id と status を返す。

fresh session は auth_time が直近 5 分以内であることを指す。Email 変更は旧・新 email 両方の所有確認が必須で、旧 email へ変更通知を送る。変更成功後は security version が更新され、以前の access token が無効になるため再認証する。Email 変更用 challenge の期限は 5 分である。Login OTP の resend route は Email 変更にはない。

CORS と Allowed Origins#

Browser UI から cross-origin で呼ぶ場合、Client の Allowed Origins へ UI の Origin を正確に登録する。Wildcard origin は使わない。JSON request や Authorization header は preflight を発生させるため、OPTIONS を通せる Origin であることも確認する。未登録 Origin は 403 origin_not_allowed となる。Native Client や same-origin server-to-server request ではブラウザ CORS preflight は発生しない。

Errors#

Public Authentication API の error body は RFC 9457 Problem Details(application/problem+json)で、すべての response に X-Request-Id が付く。概形は次のとおりである。

{
  "type": "https://docs.example-auth.com/errors/resend-too-soon",
  "title": "Too Many Requests",
  "status": 429,
  "code": "resend_too_soon",
  "request_id": "req_<request-id>"
}

Problem Details の code は拡張される可能性のある文字列であり、固定の enum ではない。未知の code を許容し、必要な分岐だけを行う。完全な schema は specs/public-api.openapi.yaml を参照する。よく使う code の例を示す。

HTTP code 例
400 invalid_request, invalid_request_body JSON / input shape が不正
400 invalid_code, webauthn_verification_failed, state_mismatch OTP、WebAuthn、Social state の検証失敗
400 passkey_not_found, identity_not_found /v1/me の対象 credential / identity が見つからない
400 last_factor_deletion_requires_confirmation 最後の認証手段を削除しようとした
401 unauthorized, invalid_token Bearer token がない、期限切れ、失効済み、または security version が古い
403 origin_not_allowed, new_user_creation_blocked Allowed Origin 拒否、または Environment の新規ユーザー作成 gate
404 auth_transaction_not_found, session_not_found, connection_not_found transaction、session、provider connection が見つからない
409 transaction_not_authenticated, email_already_registered 認証前の complete、または既存 email との競合
410 auth_transaction_expired transaction 期限切れ
429 rate_limited, resend_too_soon Rate limit。OTP resend のときは Retry-After を確認する
503 user_unavailable user / session state または security status の参照に失敗した一時障害。再認証せず retry する。
OAuth Token Endpoint のエラーは OAuth error response であり、この Problem Details 表とは別である。再試行、Rate limit、Problem Details の共通方針はErrors、rate limit、retry、冪等性を参照する。