FAPI 2.0 入門
FAPI(Financial-grade API)は、OpenID Foundation が策定する OAuth 2.0 ベースの高セキュリティプロファイル群です。FAPI 2.0 Security Profile は、認証済み利用者の識別子を返す配備で OpenID Connect を使いますが、すべてのユースケースで OIDC を要求するわけではありません。
もともとは Open Banking 向けに作られました。英国、豪州、ブラジルなどの Open Banking / Open Finance では、FAPI または地域向けプロファイルが銀行とフィンテック間のセキュリティ標準に使われています。最近は医療、政府デジタル ID など、「RFC に従っています」だけでは監査が通らない領域にも採用が広がっています。
Open Banking とは何か
「Open Banking」は、利用者が銀行のパスワードを渡さずに、サードパーティアプリへ 口座データの参照や支払開始の権限を委譲 できるようにする規制枠組みです。代表例は英国の PSD2 由来 Open Banking(2018)、ブラジルの Open Finance(2020)、豪州の Consumer Data Right(2020)です。これらの制度では、FAPI や関連プロファイルが異なる版・組み合わせで採用されています。規制対象でなければ FAPI を準拠目的で使う必要はありません。ただし、CVE 級の脆弱性を避けたい場面では、同じプロファイルを既定の出発点に据える価値があります。
このページで触れる仕様
- FAPI 2.0 Security Profile — Final(旧 Baseline 仕様)
- FAPI 2.0 Message Signing — Final
- FAPI 2.0 Attacker Model — Final
- RFC 9126 — Pushed Authorization Requests (PAR)
- RFC 9101 — JWT-Secured Authorization Request (JAR)
- JARM — JWT-Secured Authorization Response Mode
- RFC 7636 — PKCE
- RFC 9449 — DPoP
- RFC 8705 — Mutual-TLS Client Authentication
- RFC 7518 — JOSE algorithms
なぜ「OIDC + ベストプラクティス」ではなくプロファイルなのか
OAuth 2.0 と関連仕様では、多くの選択肢が実装や配備に委ねられています:
- 認可要求を平文のクエリ文字列で送れる
- 高セキュリティの配備には広すぎる署名アルゴリズムを公開できる
- クライアント認証に
client_secret_basic(長寿命の共有秘密)を使える
これらの選択肢を不適切に組み合わせると、深刻な実装脆弱性につながることがあります。
プロファイル は、配備でセキュリティ上重要な選択肢を固定する契約です。FAPI 2.0 はその必須要件を固定し、基盤仕様が任意として残す選択肢は明示的に残します。これにより適合確認をチェックリストとして扱えます。
FAPI 1.0 と FAPI 2.0
FAPI 1.0("Read-Only" / "Read-Write" の 2 区分)への参照を見かけることもあります。FAPI 2.0 はその後継で、形式的に分析された攻撃者モデルと、汎用の Security Profile を備えています。Message Signing は非否認性の方式を追加する別の FAPI 2.0 プロファイルです:
| FAPI 1.0(依然現役) | FAPI 2.0 | |
|---|---|---|
| 区分 | Read-Only / Read-Write | Security Profile / Message Signing |
| authorize 要求の経路 | 任意の request JWT | PAR 必須 |
| 送信者制約 | mTLS または holder-of-key | DPoP または mTLS 必須 |
response_type | code id_token(hybrid)も許可 | code のみ |
| アルゴリズムリスト | クライアントメタデータで交渉 | 固定の許可リスト |
FAPI 1.0 の Read-Only と Read-Write の違い
FAPI 1.0 には区分が 2 つありました。Read-Only はデータ取得だけを行う使い方向け(残高参照アプリなど)、Read-Write は状態変更を伴う使い方向け(決済を起票するアプリなど)で、要求署名・応答署名・holder-of-key などの追加要件がありました。FAPI 2.0 は汎用のセキュリティ基盤として Security Profile を置き、非否認性が必要な配備向けに Message Signing を別に定義しています。Message Signing は Security Profile を置き換えるものではなく、拡張するものです。
go-oidc-provider は FAPI 2.0 を対象にしており、FAPI 1.0 は対象外です。
Security Profile と Message Signing
FAPI 2.0 には、全体の基盤となる Security Profile と、独立した Message Signing プロファイルがあります。Go ライブラリでは前者を表す識別子に、旧称に由来する profile.FAPI2Baseline を使います。
FAPI は別のプロトコルではありません。OAuth 2.0 と関連仕様を高セキュリティの配備で使う方法を固定するプロファイルです。図で基盤仕様の横ではなく上に積んでいるのはそのためです。
Security Profile — 必須要件の最低ライン
FAPI 2.0 Security Profile は、confidential client、DPoP または mTLS による送信者制約付きアクセストークン、PAR、S256 の PKCE、response_type=code、認可レスポンスの iss パラメータ、TLS 保護されたエンドポイントを要求します。トークンエンドポイントのクライアント認証には mTLS または private_key_jwt を使います。JWT アルゴリズムは PS256、ES256、EdDSA で、JAR や ES256 限定を要求する仕様ではありません。
Message Signing — 後から否認できない証拠
Message Signing は Security Profile を継承し、PAR に送る JAR 署名付きプッシュ認可リクエストオブジェクト、JARM による署名付き認可レスポンス、RFC 9701 による署名付き introspection レスポンスという 3 つの方式を定義します。これらは個別に適合性テストできます。DPoP nonce や、すべてのリソース API リクエスト / レスポンスへの署名を要求するものではありません。本ライブラリの profile.FAPI2MessageSigning は PAR、JAR、JARM を自動有効化します。feature.Introspect も有効にして /introspect をマウントした場合は、そのエンドポイントの成功レスポンスを署名付きに強制します。DPoP が有効な場合、ライブラリの validator は WithDPoPNonceSource も要求します。これはライブラリの制約であり、Message Signing の要件ではありません。プロファイル自体が introspection エンドポイントをマウントするわけではありません。
地域の Open Banking / Open Insurance の制度が Message Signing を求める場合もあります。FAPI 仕様とは別に、対象制度の最新のセキュリティ標準を確認してください。
message signing は何を得る仕組みか
FAPI 2.0 の「Message Signing」は、選択した OAuth メッセージ(PAR に送る JAR 署名付きプッシュ認可リクエストオブジェクト、JARM による認可レスポンス、署名付き introspection レスポンス)に署名して非否認性を補う仕組みです。PAR 利用時のブラウザのフロントチャネル要求は request_uri と client_id だけを運び、非否認性のために別途署名する認可リクエストではありません。受信者は公開鍵で署名を検証します。Security Profile がすべての API 通信を署名する仕様という意味ではなく、Message Signing もすべてのリソース API リクエスト / レスポンスを JWS で包む仕様ではありません。
JWS とは何か
JWS は "JSON Web Signature"(RFC 7515)です。ペイロードに任意のバイト列を持てる署名付き表現です。署名付き JWT は JSON の claim をペイロードにした JWS で、JWT には暗号化された JWE もあります。FAPI が「署名付き認可リクエスト」「署名付き認可レスポンス」と言うときは、対応するプロトコルオブジェクトを署名付き JWT / JWS で運ぶことを指します。
各略号が実際に何をしているか
PAR — Pushed Authorization Request (RFC 9126)
古典的な OAuth では、RP が /auth?... の URL を組み立て、すべてのパラメータをクエリ文字列に乗せてブラウザにリダイレクトさせます。間に挟まる主体(ブラウザ拡張、中継プロキシ、Referer 漏洩、サーバログなど)はパラメータを見られます。
PAR では、RP がまずパラメータを OP の /par エンドポイントへ サーバ間で直接 POST します(クライアント認証付き)。OP は短寿命の request_uri(例: urn:ietf:params:oauth:request_uri:abc...)を返します。ブラウザは /auth?request_uri=urn:...&client_id=... にリダイレクトされ、OP は内部で元のパラメータを引き当てます。
結果: 機微なパラメータがブラウザを通らない。
request_uri とは何か
authorize パラメータをサーバ側にいったん預けた「預かり票」への参照です。PAR は URN 形式の request_uri(OP が約 60 秒間だけ保持する 1 回限りの行を指す)を返します。ブラウザはその URN と client_id だけを /auth に渡し、OP が内部で預かり済みのパラメータと結びつけます。request_uri という仕組み自体は PAR より先に存在しており、RFC 9101 では HTTPS URL 上の request JWT を取りに行く用途にも使われます。PAR はそれを「OP 自身が保持する」「1 回交換したら消える」用途に特化させたものです。
JAR — JWT-Secured Authorization Request (RFC 9101)
PAR は「パラメータが OP に機密で届く」ことを保証します。JAR は 「正規の RP から来た」ことを OP が暗号学的に検証できる ようにします — パラメータは RP の秘密鍵で署名された JWT として渡され、OP は登録済みの公開鍵で署名を検証します。
PAR + JAR の組み合わせで、authorize 要求の サーバ間配送 と 発信元の暗号学的な確認 の両方が手に入ります。
JARM — JWT-Secured Authorization Response Mode
JARM は認可 レスポンス(code、state、iss、エラーなど)を OP が署名付き JWT として返す方式です。RP は処理前に署名と claim を検証します。FAPI 2.0 Message Signing では、署名付き認可レスポンス方式を実装する場合に JARM を使います。本ライブラリの profile.FAPI2MessageSigning は JARM を必須にします。
DPoP / mTLS — 送信者制約
発行されたアクセストークンを、正規クライアントが保有する鍵に結びつけます。トークンが漏れても鍵がなければ使えません。仕組みは 送信者制約 を参照。
FAPI の JWT アルゴリズム — なぜ RS256 ではないのか
FAPI 2.0 Security Profile は JWT の処理に PS256、ES256、EdDSA(Ed25519)を要求し、none を禁じています。そのため RS256 はプロファイルの許可リストに含まれません。go-oidc-provider は OP 発行 JWT を OP ごとに 1 つのアルゴリズム(既定は ES256、op.WithSigningAlgorithm(op.AlgRS256) で RS256)で署名し、クライアントが署名するオブジェクトでは PS256、ES256、EdDSA を受け付けます。RS256 はプロファイルの集合の外なので、op.New は profile.FAPI2Baseline、profile.FAPI2MessageSigning、profile.FAPICIBA との組み合わせを拒否します。これらのプロファイルでは OP は ES256 で署名します。発行を ES256 に限ることは、Final Security Profile の要件ではありません。
アルゴリズム許可リスト — なぜ「交渉」ではなく「固定」なのか
FAPI 2.0 Security Profile は JWT のアルゴリズムを PS256、ES256、EdDSA に固定し、none を禁じています。Discovery 文書で受け付けるアルゴリズムを広告できても、サーバ側は許可リスト外を受理してはいけません。本ライブラリも各 JOSE 処理で独自の許可リストを適用します。OP ごとに発行アルゴリズムを 1 つ(ES256 か RS256)にするのはライブラリの実装方針です。
private_key_jwt — FAPI クライアント認証
Confidential クライアントは token endpoint に対して自身の身元を証明する必要があります。FAPI 2.0 Security Profile が許可する方式は mTLS と private_key_jwt です。mTLS には RFC 8705 §2.1.1 の tls_client_auth と §2.2 の self_signed_tls_client_auth があります。
| 方式 | 出典 | 提示するもの | OP 側の検証 |
|---|---|---|---|
private_key_jwt | OIDC Core §9 | 登録済みの秘密鍵で署名した短い JWT を client_assertion として /token 要求に乗せる | 登録済み JWKS で署名検証 |
tls_client_auth | RFC 8705 §2.1.1 | TLS ハンドシェイクで提示する X.509 証明書 | 構成済みのトラストアンカーに対してチェーン検証し、登録済みの subject DN / SAN と突き合わせ |
self_signed_tls_client_auth | RFC 8705 §2.2 | 自己署名の X.509 証明書 | CA チェーンは辿らず、証明書の公開鍵を登録済み JWKS と突き合わせ |
private_key_jwt:JWT にはiss、sub(自分のclient_id)、aud(Final FAPI プロファイルでは認可サーバの issuer 識別子を文字列で指定)、exp、jtiを入れる。
いずれも client_secret_basic の落とし穴(長寿命の共有秘密が一度漏れると、交換するまで侵害され続ける)を回避します。
実装メモ: 本ライブラリの FAPI プロファイルは、private_key_jwt を広告して振り分けます。feature.MTLS が有効なら tls_client_auth と self_signed_tls_client_auth も同様です。シード型は op.PrivateKeyJWTClient、op.TLSClientAuthClient、op.SelfSignedTLSClientAuthClient で、FAPI 2.0 Baseline に一覧があります。
Final Security Profile は aud を issuer 識別子の文字列に限定します。profile.FAPI2Baseline と profile.FAPI2MessageSigning では、verifier はそれだけを受理し、配列やエンドポイント URL は invalid_client で拒否します。それ以外のプロファイルは、RFC 7523 が許すとおり、issuer と token endpoint URL のどちらも受理します。
FAPI 2.0 と RFC の対応表
FAPI は プロファイル であり、OAuth や OIDC を作り直しているわけではありません。既存仕様の使い方を高セキュリティ向けに絞り込みます。下表では、Final Security Profile、個別に適合性テストできる Message Signing の方式、ライブラリ固有の設定を分けています。
| Spec / RFC | FAPI 2.0 Security Profile | FAPI 2.0 Message Signing | FAPI-CIBA |
|---|---|---|---|
| OAuth 2.0 (RFC 6749) | 必須 | 継承 | プロファイル依存 |
| OIDC Core 1.0 | 認証済み利用者の識別子を返す場合に必要 | 継承 | プロファイル依存 |
PKCE — S256 のみ(RFC 7636) | 認可コードで必須 | 継承 | 対象外(CIBA はフロントチャネルなし) |
| PAR (RFC 9126) | 必須 | 継承 | 対象外(CIBA は /bc-authorize に POST) |
| JAR (RFC 9101) — 署名付き request object | 任意 | 署名付き認可リクエスト方式 | プロファイル依存 |
| JARM — 署名付き認可レスポンス | 任意 | 署名付き認可レスポンス方式 | 対象外(CIBA はフロントチャネルなし) |
| 署名付き introspection レスポンス(RFC 9701) | 任意 | 署名付き introspection レスポンス方式 | プロファイル依存 |
| DPoP (RFC 9449) または mTLS (RFC 8705) | いずれか必須 | 継承 | プロファイル依存 |
| Resource Indicators (RFC 8707) | 任意 | 継承 | プロファイル依存 |
認可レスポンスの iss パラメータ(RFC 9207) | 必須 | 継承 | 対象外(CIBA はフロントチャネルなし) |
| OAuth 2.0 Security BCP (RFC 9700) | プロファイルに従う | 継承 | プロファイル依存 |
| Token endpoint クライアント認証 | mTLS または private_key_jwt | 継承 | プロファイル依存 |
client_secret_basic / _post / _jwt | 不可 | 継承 | プロファイル依存 |
| JWT 署名 alg | PS256、ES256、EdDSA。none は禁止 | 継承 | プロファイル依存 |
PAR 内の redirect_uri | 必須 | 継承 | 対象外(リダイレクトなし) |
| リフレッシュトークンのローテーション | 例外的な状況を除き禁止 | 継承 | プロファイル依存 |
| 送信者制約付きアクセストークン | 必須 | 継承 | プロファイル依存 |
op.WithProfile に渡す値 | profile.FAPI2Baseline | profile.FAPI2MessageSigning | profile.FAPICIBA |
いくつかの行について補足します。
なぜ DPoP か mTLS のどちらかで十分なのか。 どちらも、発行されたアクセストークンを正規クライアントが保有する鍵に結びつける仕組みです。トークンが漏れても鍵がなければ使えません。選択は運用上の判断であり、既存 PKI / プロキシ構成に合う方式を選びます。仕組みの詳細は 送信者制約 を参照してください。Final Security Profile はいずれか 1 つを要求します。本ライブラリでは、どちらも明示しない場合に DPoP を既定として選びます。
なぜ JAR と JARM は別の方式なのか。 Message Signing は、署名付き認可リクエスト、署名付き認可レスポンス、署名付き introspection レスポンスを別々の方式として定義します。配備に必要な方式だけを選べます。profile.FAPI2MessageSigning はライブラリ設定として PAR、JAR、JARM を自動有効化します。別途 feature.Introspect を有効にして /introspect をマウントした場合は、成功レスポンスを署名付き RFC 9701 形式に強制します。DPoP が有効な場合、ライブラリのプロファイルは DPoP nonce source も要求しますが、Final Message Signing 仕様の要件ではありません。
本ライブラリと Final Security Profile の違い。 profile.FAPI2Baseline はライブラリ設定として PAR と JAR を自動有効化し、OP 発行 JWT を ES256 に限定し、redirect_uri の完全一致を強制します。これらは Final Security Profile の追加要件ではありません。2 つの FAPI 2.0 プロファイルは、さらに 2 点で最終版の記述に従います。クライアント assertion は issuer 識別子を文字列の aud に持つ必要があり、リフレッシュトークンはローテーションしません。op.New が通ったことは適合の主張にはならないため、適合を主張する前に、TLS の設定を含めて実行時の設定を確認してください。
続きはこちら
- RFC 対応一覧 — FAPI に限定しない、ライブラリ全体の RFC 別実装状況。
- ユースケース: FAPI 2.0 Baseline —
private_key_jwtクライアントと DPoP を組み合わせた構成例。 - 送信者制約(DPoP / mTLS) — バインディングの仕組みを詳しく。
- CIBA — FAPI-CIBA が制約しているバックチャネル処理。
go-oidc-provider での 1 行有効化
import (
"github.com/libraz/go-oidc-provider/op"
"github.com/libraz/go-oidc-provider/op/profile"
)
op.New(
/* 必須オプション */
op.WithProfile(profile.FAPI2Baseline),
)WithProfile は次をまとめて行います:
ライブラリ設定として
feature.PAR/feature.JARを自動有効化します。JAR は Final Security Profile では任意です。token_endpoint_auth_methods_supportedをライブラリの FAPI 許可リストに絞り込みます。残るのはprivate_key_jwtと、feature.MTLSが有効な場合のtls_client_auth、self_signed_tls_client_authです。送信者制約 feature として、明示された
feature.MTLSを尊重し、未指定ならfeature.DPoPを既定で有効にします。op.Newでop.WithSigningAlgorithm(op.AlgRS256)を拒否し、OP 発行 ID トークンをES256に保ちます。RS256は Final Security Profile の集合(PS256、ES256、EdDSA)の外にあるためです。ライブラリはそれ以外のアルゴリズムでは発行しないので、このプロファイルでは常にES256で署名します。redirect_uriの完全一致をライブラリの方針として強制します。最終版が求めるとおり、リフレッシュトークンをローテーションしません。リフレッシュ応答は新しいアクセストークンを返し、
refresh_tokenは含みません。そのため、このプロファイルはop.WithRefreshGracePeriodと両立せず、op.Newが拒否します。
プロファイルと矛盾するオプションを後から重ねると、op.New は OP を生成する前に検出した矛盾を理由に起動を拒否します。主な制約を、対象ごとにまとめます。
| 設定 | FAPI 2.0 / FAPI-CIBA プロファイル下での扱い |
|---|---|
| token endpoint のクライアント認証 | private_key_jwt。feature.MTLS が有効なら tls_client_auth と self_signed_tls_client_auth も使える。動的登録でそれ以外の token_endpoint_auth_method を指定すると invalid_client_metadata で拒否される |
| クライアントが署名する入力(assertion、request object)のアルゴリズム | PS256、ES256、EdDSA。RS256 は実行時、discovery、登録のいずれでも拒否される |
クライアント assertion の aud | 2 つの FAPI 2.0 プロファイルでは issuer 識別子を単一の文字列で指定したものだけ。profile.FAPICIBA は引き続き issuer とエンドポイント URL を受理する |
| リフレッシュトークン | 2 つの FAPI 2.0 プロファイルではローテーションせず、op.WithRefreshGracePeriod は拒否される。profile.FAPICIBA はローテーションを続ける |
DPoP の htu | リクエストから組み立て直した URL ではなく、OP が広告するエンドポイント URL と比較する |
op.WithAccessTokenTTL | 10 分以内 |
op.WithPARTTL | 600 秒未満 |
/par と /bc-authorize の request object、および FAPI プロファイル下の /auth | 署名なしの query / form パラメータとマージされず、置き換える。認可パラメータはすべて署名済みオブジェクトの中に入れる必要がある |
| アクセストークンの失効戦略 | RevocationStrategyNone は拒否される |
op.WithFirstPartyClients、op.WithOpenIDScopeOptional | FAPI 2.0 プロファイル下では拒否される |
| DPoP nonce source | FAPI2MessageSigning と FAPICIBA で DPoP を使う場合は op.WithDPoPNonceSource が必須。本ライブラリのプリセットであり、最終版仕様の MUST ではない |
profile.FAPICIBA | grant.CIBA を有効にしておく必要がある(自動では有効化されない)。CIBA の既定・最大有効期間は 10 分以内 |
| レスポンス | すべてのレスポンスに x-fapi-interaction-id が付く。リクエストに値があればそれを、なければ生成した値を返す |
表の client assertion aud とリフレッシュトークンの 2 行の挙動は、profile.RequiresIssuerAssertionAudience と profile.ForbidsRefreshTokenRotation として公開されています。どちらも 2 つの FAPI 2.0 プロファイルでは true、profile.FAPICIBA では false です。startup.profile 監査レコードの refresh_rotation フィールドは、プロファイルが 1 つのリフレッシュトークンを存続期間中保つ場合に false になります。
ライブラリの Message Signing プロファイルを選ぶ場合は、profile.FAPI2Baseline を profile.FAPI2MessageSigning に差し替えます。DPoP を使う場合は WithDPoPNonceSource も構成してください。組み込み側で何が変わるかは FAPI 2.0 Message Signing にまとめています。適合性テストは両プランを検査します(OFCS 適合状況)。
続きはこちら
- 送信者制約(DPoP / mTLS) — バインディングの仕組みを詳しく。
- ユースケース: FAPI 2.0 Baseline —
private_key_jwtクライアントと DPoP を組み合わせた構成例。 - 認可コード + PKCE — FAPI 2.0 が前提にしている処理。