Attestation-Based Client Authentication
前提知識
- クライアント認証 -
idp-serverがサポートする他の認証方式 - Attestation-Based Client Authentication(仕様編) - draft-10 の解説
- 同(実践編) - モバイル側の鍵管理とデバイス証明の取得
概要
attest_jwt_client_auth は、シークレットを配布せずにネイティブアプリを認証するクライアント認証方式です(draft-ietf-oauth-attestation-based-client-auth-11)。
モバイルアプリは配布物であり、埋め込んだシークレットは取り出せます。そのため従来は Public Client(none)として扱うしかありませんでした。この方式は、アプリのインスタンスごとに端末内で生成した鍵(Client Instance Key)で認証します。鍵は端末のセキュアハードウェアから出ないため、アプリを複製しても他の端末では使えません。
認証は2つの JWT をヘッダで送ることで行います。
| ヘッダ | JWT | 署名する鍵 | 主張する内容 |
|---|---|---|---|
OAuth-Client-Attestation | Client Attestation JWT | Client Attester の鍵、または登録済み Client Instance Key | この client_id のインスタンスがこの公開鍵を持っている |
OAuth-Client-Attestation-PoP | Client Attestation PoP JWT | Client Instance Key | その鍵をいま保持している |
前者が「鍵の持ち主は正当なアプリである」、後者が「その鍵で今このリクエストを出している」を担当します。片方だけでは成立しません。
シーケンス
Attestation JWT に誰が署名するかで、流れが 2 つに分かれます(どちらを選ぶかは信頼モデル)。
| 形 | client_attestation_trust_source | Attestation JWT の署名者 | 認可サーバーへのインスタンス登録 |
|---|---|---|---|
| 自己署名 | registered_instance_key | アプリ自身 | 必要 |
| Attester をバックエンドに持つ | attester_jwks / x5c | アプリ提供者のバックエンド(Client Attester) | 不要 |
自己署名(registered_instance_key)
インストールから最初のトークン取得まで。
Android Key Attestation では、証明書チェーンは鍵の生成時に得られます(手順 3 と 7〜8 が一度に済みます)。どちらの場合も、鍵は challenge を受け取ってから作ります。証明に challenge を埋め込むためです。
Attester をバックエンドに持つ(attester_jwks / x5c)
アプリ提供者が Client Attester(バックエンド)を運用する形です。端末とアプリが本物かを確かめるのは Attester で、認可サーバーは Attester の署名を信頼します。認可サーバーへのインスタンス登録はありません。
- アプリと Attester のやりとり(証明の送り方、チャレンジの出し方)は ABCA の範囲外で、アプリ提供者が決めます。Attester が発行した使い捨てのチャレンジを証明に含めさせ、過去の証明の使い回しを防ぐのが一般的です。
- Attestation JWT の期限が切れたら、アプリは Attester から取り直します。期限切れのまま送ると
use_fresh_attestationが返ります。 - 特定のインスタンスを止める手段は、Attestation JWT の有効期限だけです。 インスタンスは認可サーバーに登録されていないので、認可サーバーからは止められません。Attester が次の Attestation JWT を出さなければ、期限切れで止まります。
idp-serverはこの形では有効期限に上限を掛けない(自己署名は 24 時間まで)ので、Attester 側で短くしてください。 - 利用者への束縛はありません。Attestation JWT が保証するのは「本物のアプリの、この鍵」までで、誰が使っているかはログインで決まります。
Challenge を必須にしている場合は、リクエスト前に POST /{tenant-id}/v1/client-attestation/challenges で取得した値を PoP JWT の challenge クレームに入れます。
エンドポイント体系
クライアント(アプリ)側
| エンドポイント | 用途 |
|---|---|
POST /{tenant-id}/v1/client-instances/challenges | インスタンス登録用チャレンジの取得。{ client_id } を送り、{ challenge, instance_id } を受け取る |
POST /{tenant-id}/v1/client-instances | Client Instance Key の登録。{ challenge, id_token, client_instance_public_key, platform_evidence } |
POST /{tenant-id}/v1/client-attestation/challenges | PoP 用チャレンジの取得。レスポンスの attestation_challenge を PoP JWT に入れる |
登録系の2つはクライアント認証を要求しません(インストール直後で、まだ認証に使う鍵が無いため)。チャレンジ取得は無認証、登録は ID トークン(誰か)とプラットフォーム証明(どの端末・鍵か)で認証されます。詳しくは Client Instance 登録の認証。
なお2つのヘッダは、リクエストごとにそれぞれ厳密に1個です。同じヘッダを複数送ると拒否されます。
クライアント認証を行うエンドポイント
2つのヘッダは、クライアント認証が発生する経路すべてで受け付けます。
| エンドポイント | |
|---|---|
| トークン | POST /{tenant-id}/v1/tokens |
| Pushed Authorization Request | POST /{tenant-id}/v1/authorizations/push |
| CIBA backchannel authentication | POST /{tenant-id}/v1/backchannel/authentications |
| Introspection | POST /{tenant-id}/v1/tokens/introspection |
| Introspection(拡張) | POST /{tenant-id}/v1/tokens/introspection-extensions |
| Revocation | POST /{tenant-id}/v1/tokens/revocation |
管理API側
| エンドポイント | 用途 |
|---|---|
GET /v1/management/tenants/{tenant-id}/client-instances | テナント内のインスタンスの検索 |
POST /v1/management/tenants/{tenant-id}/client-instances | 登録(client_id はボディで指定) |
GET|DELETE /v1/management/tenants/{tenant-id}/client-instances/{id} | 取得・削除 |
POST /v1/management/tenants/{tenant-id}/client-instances/{id}/revoke | 失効(→ 失効と削除) |
上はシステムレベル(管理テナントのトークン)のパスです。組織の管理者は、同じ操作を組織レベルのパス /v1/management/organizations/{organization-id}/tenants/{tenant-id}/client-instances で使います。
どちらも専用権限 idp:client-instance:create / :read / :revoke / :delete で保護されています。詳細は Client Instance 管理API を参照してください。登録は、アプリからの登録を使わず運用側で鍵を登録する場合に使います。
インスタンスはクライアントの下ではなくテナントの直下に置いています。運用で手元にあるのは利用者・鍵・証明書のシリアルで、どのクライアントのものかは調べるまで分からないためです。id は UUID で、テナント内で 1 件に決まります。
検索条件(すべて任意、組み合わせ可):
| パラメータ | 使う場面 |
|---|---|
client_id | クライアントで絞る(client_id でも alias でも可) |
user_id | 利用者の問い合わせ(その人の端末の一覧) |
status / revocation_reason | 有効なもの、失効したものと理由(active / revoked、operator / superseded |