🐺

OAuth 2.0 Attestation-Based Client Authenticationのドラフトを読んでみた

に公開
2026/03/06

このドラフトが解きたい課題

OAuth 2.0 のドラフト「OAuth 2.0 Attestation-Based Client Authentication」をざっと読んでみました。

このドラフトがやりたいのは、Public client (おそらく Native App で SPA は考慮してないように読めた)でも Authorization Server に対して「この Client Instance は信頼できそうだ」と強く示せるようにすることの様です。

まず背景として、SD-JWT で触れられるような Issuer-Holder-Verifier モデルでは、backend 経由の client authentication に寄せすぎると、backend がクライアントと Authorization Server の対応関係を把握しやすくなり、プライバシー上の懸念が出てきます。そこでこのドラフトは、backend が裏側のチャネルで毎回認証して付き合うのではなく、クライアントがフロントチャネルで提示できる形の client authentication を用意して、そうした懸念を減らしたい、という立ち位置に見えます。

Traditional OAuth security concepts perform client authentication through a backend channel. In ecosystems such as the Issuer-Holder-Verifier model used in [SD-JWT], this model raises privacy concerns, as it would enable the backend to recognize which Holder (i.e. client) interacts with which Issuer (i.e. Authorization Server) and potentially furthermore see the credentials being issued. This specification establishes a mechanism for a backend-attested client authentication through a frontend channel to address these issues.

OAuth 2 の展開の状況が変化していると記載もあり。Public client が安全かつ確実に認証できる能力の重要性が増していることも踏まえています。プラットフォームの仕組みを活用して Client Instance を検証することで、従来の OAuth クライアント認証では難しかった安全な認証が可能になるとのことです。

Additionally, this approach acknowledges the evolving landscape of OAuth 2 deployments, where the ability for public clients to authenticate securely and reliably has become increasingly important. Leveraging platform mechanisms to validate a client instance, e.g. for mobile native apps, enables secure authentication that would otherwise be difficult with traditional OAuth client authentication methods. Transforming these platform-specific mechanisms into a common format as described in this specification abstracts this complexity to minimize the efforts for the Authorization Server.

どういう仕組みで Public client を認証するのか

ポイントは、Client Attestation と PoP(Proof of Possession)の2点。ざっくり言うと「Attester が Client Instance を検証して Client Attestation JWT を発行する」と「Client Instance がその鍵を持っていることを PoP で示す」を組み合わせます。

  • Client Instance: 実際に動いているクライアント(端末上のアプリ)
  • Client Attester: Client Instance を検証し、「このインスタンスは本物っぽい」と判断して Client Attestation JWT を発行する主体(多くの場合はクライアント側 backend を想定)
  • Authorization Server: 最終的にその証明を検証して、Client Instance を“認証済み”として扱う側

フローを理解する

  1. Client Instance は鍵(Client Instance Key)を生成し、必要に応じて追加の attestation(仕様のスコープ外)も用意して、Client Attester に対して自身の authenticity を示す。

  2. Client Instance はそれらのデータを Client Attester に送信し、Client Attestation JWT の発行を依頼する。(1で生成した Key に紐づく公開鍵を送信してると推測してます)

  3. Client Attester は Client Instance Key と(あれば)追加データを検証する。(ドラフトでは具体的な検証内容はスコープ外と記載あり。)続いて、クライアントが生成した Client Instance Key に暗号学的に binding された、署名付きの Client Attestation JWT を生成する。これにより attestation はこの特定の Client Instance に紐づく。

  4. Client Attester は Client Instance に対し、Client Attestation JWT を返す。

  5. Client Instance は Client Instance Key を用いて Proof of Possession(PoP)を生成する(Client Attestation PoP JWT を作る)。

  6. Client Instance は Client Attestation JWT と Client Attestation PoP JWT の両方を Authorization Server に送る(例: token request 内)。Authorization Server は Client Attestation を検証し、その結果として Client Instance を認証する。

Client Attestation JWT は「Attester がこの Client Instance を認めた」こと、Client Attestation PoP JWT は「そのとき binding された鍵を、いま本当にこのクライアントが保持している」ことを示します。結果として、Public client でも「インスタンス単位での認証」をやりやすくしています。

Client Attestation JWT について

このドラフトは、Client Attestation JWT の発行判断のプロセスまで細かく規定しているわけではありません。たとえば、Client Attester が何を根拠に Client Instance を「OK」と判断するのか、Client Instance が Client Attester にどう認証するのか、といった部分はスコープ外みたいです。

一方で、Client Attestation JWT 自体のフォーマットは比較的はっきり定義されてました。JWT であることを前提に、typ / iss / sub / exp / cnf といった必須 claims。cnf に jwk 形式で公開鍵(Client Instance Key)を載せること。そして署名(または MAC)で保護されることが求められます。ここで cnf に公開鍵を入れることで、後続の Client Attestation PoP JWT の署名検証に使えるようになり、「この Client Instance が持つ鍵に対して attestation が binding されている」ことを表現できます。

Client Attestation PoP JWT について

Client Attestation PoP JWT は、Client Instance が「Client Instance Key を本当に保持している」ことを示すための JWT です。Client Attestation JWT が cnf claim で公開鍵を提示し、PoP 側は対応する秘密鍵で署名する、という組み合わせになっているため、Authorization Server は cnf に入っている公開鍵で PoP の署名を検証できます。

PoP 側の claims には、たとえば次のような要素が出てきます。

  • iss: client_id(PoP を作っている OAuth Client を示す)
  • aud: Authorization Server の issuer identifier URL(この PoP の宛先)
  • jti: PoP の一意な識別子(リプレイ検出の材料)
  • iat: 発行時刻(古すぎる PoP を弾く余地)
  • challenge: サーバーが払い出した challenge を含めるための任意要素

これにより「この Client Instance は、Attester が認めたインスタンスであり、かつそのインスタンスが鍵を保持している」という2段構えの主張が成立します。

Client Attestation HTTP Headers

Authorization Server に Client Attestation を提示する方法として、HTTP headers を使う形式が定義されています。具体的には、HTTP request に次の2つのヘッダーを載せます。

  • OAuth-Client-Attestation: Client Attestation JWT
  • OAuth-Client-Attestation-PoP: Client Attestation PoP JWT

リプレイ対策

Client Attestation PoP JWT には jti が必須で、Authorization Server 側で観測済み jti を保持してリプレイ検出する道が用意されています。加えて challenge(nonce と同じみたい)をサーバーが払い出して PoP に含めることで、より強いリプレイ耐性を狙える設計になっています。

Refresh Token Binding

この方式で Refresh Token を発行する場合、Refresh Token を「client」ではなく Client Instance とその公開鍵に binding すべき、という話も出てきました。

気になったこと

Client Instance の概念が GNAP と似ている?

私は GNAP の仕様はまだ読んでないです。ただ、以下の podcast を以前に聴いたときに「GNAP には client instance のような概念がある」と知りました。
https://changelog.com/podcast/456
このドラフトでも Client Instance という言葉がかなり前面に出てきていて、これを機に GNAP も読んでみようかと思っています。GNAPを読めば、両者に関連があるのかわかるかもしれません。(むしろ、GNAP を読むためにもこのドラフトを理解してからにしようと思い、内容をまとめてみました!)


全体としては、「Public client でも、鍵に binding した Attestation + PoP でインスタンス認証をやる」という主張で、読み物としても面白かったです。
OAuth の世界では長年 Public Client のクライアント認証をどうするのかは課題でした。例えば FAPI ではセキュリティ上の理由で対象外となっていたり。Public client がよりセキュアな基準にも準拠できる未来があるかもしれません。

https://openid.net/specs/fapi-2_0-security-profile-ID2.html
We are not currently aware of any mechanisms that would allow public clients to be secured to the same degree and hence their use is not within the scope of this specification.

Discussion