ユースケース — FAPI 2.0 Baseline
FAPI 2.0 とは
FAPI("Financial-grade API")は OpenID Foundation が策定する OAuth 2.0 + OIDC のプロファイルです。基盤となる仕様の 厳しいサブセット を選び、攻撃者に長年悪用されてきた任意性を禁じます — 例えば RS256 を弾いて ES256 / PS256 を必須化、PKCE をすべての認可で必須化、送信者制約付きトークン(DPoP または mTLS)を必須化、RP の /authorize 要求を生のクエリ文字列ではなく PAR + JAR で送らせる、などです(本ライブラリは id_token を ES256 のみで署名するため、anti-RS256 条項は構造的に満たされます)。
このバーが要求される理由は、銀行・医療・行政の運用が「フラグを全部覚えていますか?」ではなく「チェックリストに対して監査可能か」を要求するからです。FAPI 2.0 は FAPI 1.0(こちらも依然現役)の後継。FAPI 2.0 Baseline はエントリーレベル、FAPI 2.0 Message Signing が JARM + DPoP nonce + RS 側の proof 署名を追加する上位層です。
本ライブラリは Baseline を プロファイル 1 つの切り替え(op.WithProfile(profile.FAPI2Baseline))として公開し、必須フラグをまとめて立て、プロファイルを暗黙に違反するような構成では op.New 自体が起動を拒否します。
PAR / JAR / JARM / DPoP / mTLS / ES256 など各略号の解説は FAPI 2.0 入門 にあります。本ページは構成例を扱います。
このページで触れる仕様
FAPI 2.0 Baseline が要求するもの
| 要件 | RFC | ライブラリの挙動 |
|---|---|---|
| Pushed Authorization Requests | RFC 9126 | プロファイルが feature.PAR を自動有効化。/par の request_uri が唯一の authorize 入口。 |
| Proof Key for Code Exchange | RFC 7636 | code_challenge_method=S256 必須、plain 拒否。 |
| 送信者制約付きトークン(DPoP または mTLS) | RFC 9449 / RFC 8705 | プロファイルが RequiredAnyOf=[DPoP, MTLS] を立てる。どちらも指定されていなければ、インフラ前提の少ない DPoP を既定として自動選択。 |
| ES256 署名 | RFC 7518 | id_token_signing_alg_values_supported は無条件で ["ES256"]。RS256 / none / HS* はそもそも公告しない。 |
redirect_uri 完全一致 | FAPI 2.0 §5.3 | ワイルドカード無し、バイト一致比較。 |
private_key_jwt または mTLS クライアント認証 | FAPI 2.0 §3.1.3 | token endpoint auth-method リストを FAPI allow-list と交差。 |
アーキテクチャ
コード(examples/03-fapi2 からの抜粋)
import (
"github.com/libraz/go-oidc-provider/op"
"github.com/libraz/go-oidc-provider/op/profile"
"github.com/libraz/go-oidc-provider/op/storeadapter/inmem"
)
const (
demoIssuer = "https://op.example.com"
demoClientID = "fapi2-example-client"
demoRedirectURI = "https://rp.example.com/callback"
)
provider, err := op.New(
op.WithIssuer(demoIssuer),
op.WithStore(inmem.New()),
op.WithKeyset(opKeys.Keyset()),
op.WithCookieKeys(opKeys.CookieKey),
op.WithProfile(profile.FAPI2Baseline), // <--- プロファイル切り替え
op.WithStaticClients(op.PrivateKeyJWTClient{
ID: demoClientID,
JWKS: clientJWKs, // 公開 JWK Set を JSON バイト列で
RedirectURIs: []string{demoRedirectURI},
Scopes: []string{"openid", "profile", "email"},
GrantTypes: []string{"authorization_code", "refresh_token"},
ResponseTypes: []string{"code"},
}),
)PrivateKeyJWTClient は FAPI クライアント用の型付き seed で、token_endpoint_auth_method=private_key_jwt を自動でセットします。組み込み側でこのフィールドを書く必要はありません。同種の typed seed として op.PublicClient と op.ConfidentialClient があり、3 つすべてが op.ClientSeed を実装し、WithStaticClients(seeds ...ClientSeed) に渡せます。
WithProfile 呼び出しは:
feature.PARとfeature.JARを自動有効化。token_endpoint_auth_methods_supportedを FAPI 2.0 §3.1.3 allow-list(private_key_jwt、tls_client_auth、self_signed_tls_client_auth)と交差。id_token_signing_alg_values_supported = ["ES256"]を維持(OP は ES256 でしか id_token を署名・公告しないため、FAPI 2.0 の anti-RS256条項は構造的に満たされる)。redirect_uriの完全一致を強制(どこにもワイルドカード無し)。- DPoP-or-mTLS の送信者制約は、明示された
feature.MTLSがあればそれを尊重し、どちらも選ばれていない場合はfeature.DPoPを追加して満たす。
DPoP の代わりに mTLS
プロファイルの既定の送信者制約方式は、TLS クライアント証明書の配管が不要な DPoP です。mTLS に標準化しているデプロイでは feature.MTLS を明示し、TLS 終端 proxy 用に op.WithMTLSProxy(...) を設定してください。その明示選択があれば DPoP 既定は追加されません。FAPI グレードの TLS ヘルパーは examples/50-fapi-tls-jwks。
サーフェス確認
curl -s http://localhost:8080/.well-known/openid-configuration | jq '{
pushed_authorization_request_endpoint,
request_parameter_supported,
dpop_signing_alg_values_supported,
token_endpoint_auth_methods_supported,
id_token_signing_alg_values_supported
}'期待値:
{
"pushed_authorization_request_endpoint": "http://localhost:8080/oidc/par",
"request_parameter_supported": true,
"dpop_signing_alg_values_supported": ["ES256", "EdDSA", "PS256"],
"token_endpoint_auth_methods_supported": ["private_key_jwt"],
"id_token_signing_alg_values_supported": ["ES256"]
}id_token_signing_alg_values_supported はプロファイルに関係なく ["ES256"] のみで、OP が発行する id_token はすべて ES256 署名です。FAPI 2.0 §6.2.1 の anti-RS256 条項は OP 側の対応 alg に RS256 が一切含まれないことで構造的に満たされます。dpop_signing_alg_values_supported は DPoP proof 受理用で ["ES256", "EdDSA", "PS256"] です。
適合状況
OFCS の fapi2-security-profile-id2-test-plan はこの実装を検査します。最新 baseline で 48 PASSED / 9 REVIEW(手動レビュー)/ 1 SKIPPED(追加のクライアント鍵が必要な RSA 鍵での負例)/ 0 FAILED。
OFCS 全体像と REVIEW / SKIPPED 内訳は OFCS 適合状況 を参照。