| name | spec-fapi |
| description | FAPI(Financial-grade API)機能の開発・修正を行う際に使用。FAPI 1.0 Baseline/Advanced, FAPI CIBA, mTLS, PAR, JARM実装時に役立つ。 |
FAPI(Financial-grade API)開発ガイド
ドキュメント
documentation/docs/content_06_developer-guide/04-implementation-guides/oauth-oidc/fapi.md - FAPI実装ガイド
documentation/docs/content_03_concepts/03-authentication-authorization/concept-06-fapi.md - FAPI概念
documentation/docs/content_06_developer-guide/03-application-plane/02-01-authorization-request-verification.md - 認可リクエスト検証フロー詳細(プロファイル決定、検証チェーン、エラーハンドリング)
documentation/requirements/fapi-1.0-gap-analysis.yaml - FAPI 1.0 Gap分析(OIDF適合性テスト結果と修正記録)
documentation/docs/content_05_how-to/phase-2-security/05-fapi-setup.md - FAPIセットアップガイド(プロファイル決定、認可サーバー/クライアント設定、OIDF認定チェックリスト)
documentation/requirements/fapi-1.0-advanced-op-test-mapping.md - FAPI 1.0 Advanced OPテスト63件とRFC/仕様要件のマッピング表
機能概要
FAPIは、金融グレードのセキュリティを実現するOIDC/OAuth 2.0プロファイル。
- FAPI 1.0 Baseline Profile: 基本的なセキュリティ要件
- FAPI 1.0 Advanced Profile: 高度なセキュリティ要件(PAR, JARM, mTLS必須)
- FAPI CIBA Profile: CIBAとFAPIの組み合わせ
- Sender-constrained Access Token: mTLSバインディング
プロファイル適用条件
FAPIプロファイルはリクエストのスコープとテナントのextension設定で自動決定される。
| 優先度 | 条件 | プロファイル |
|---|
| 1 | テナントextension.fapi_advance_scopesに該当するスコープあり | FAPI_ADVANCE |
| 2 | テナントextension.fapi_baseline_scopesに該当するスコープあり | FAPI_BASELINE |
| 3 | openidスコープを含む | OIDC |
| 4 | 上記以外 | OAUTH2 |
CIBAフローではbaseline/advanceどちらのスコープでも FAPI_CIBA になる。
詳細: content_06_developer-guide/03-application-plane/02-01-authorization-request-verification.md セクション2.3〜3(プロファイル決定、検証チェーン、エラーハンドリング)
モジュール構成
libs/
├── idp-server-core-extension-fapi/ # FAPI拡張モジュール
│ └── .../extension/fapi/
│ ├── FapiBaselineVerifier.java # FAPI Baseline検証
│ ├── FapiAdvanceVerifier.java # FAPI Advanced検証
│ ├── FapiProfileValidator.java # FAPIプロファイル検証
│ ├── TlsClientAuthAuthenticator.java # mTLSクライアント認証
│ └── SelfSignedTlsClientAuthAuthenticator.java
│
├── idp-server-core-extension-fapi-ciba/ # FAPI-CIBA拡張
│ └── .../extension/fapi/ciba/
│ └── FapiCibaVerifier.java
│
├── idp-server-core/ # コア(PAR, JARM実装)
│ └── .../oauth/
│ ├── request/
│ │ └── OAuthPushedRequestParameters.java # PAR
│ ├── response/
│ │ └── JarmCreatable.java # JARM
│ ├── io/
│ │ ├── OAuthPushedRequest.java
│ │ └── OAuthPushedRequestResponse.java
│ └── verifier/extension/
│ └── JarmVerifier.java
│
└── idp-server-control-plane/ # 管理API
└── .../management/fapi/
└── FapiConfigManagementApi.java
FAPI Baseline検証
idp-server-core-extension-fapi/ モジュール内:
public class FapiBaselineVerifier {
public void verify(
AuthorizationRequest request,
Client client
) {
if (!request.responseType().isCode()) {
throw new InvalidRequestException(
"fapi_baseline_requires_code_flow"
);
}
if (!request.hasCodeChallenge()) {
throw new InvalidRequestException(
"code_challenge_required"
);
}
if (request.codeChallengeMethod() != CodeChallengeMethod.S256) {
throw new InvalidRequestException(
"code_challenge_method_must_be_s256"
);
}
if (!request.hasState()) {
throw new InvalidRequestException("state_required");
}
if (request.scope().contains("openid") &&
!request.hasNonce()) {
throw new InvalidRequestException("nonce_required");
}
}
}
FAPI Advanced検証
public class FapiAdvanceVerifier {
public void verify(
AuthorizationRequest request,
Client client
) {
fapiBaselineVerifier.verify(request, client);
if (!request.isPushedAuthorizationRequest()) {
throw new InvalidRequestException(
"par_required_for_fapi_advanced"
);
}
if (!request.hasResponseMode() ||
!request.responseMode().isJwt()) {
throw new InvalidRequestException(
"jarm_required_for_fapi_advanced"
);
}
if (!request.hasMtlsCertificate()) {
throw new InvalidRequestException(
"mtls_required_for_fapi_advanced"
);
}
}
}
PAR(Pushed Authorization Requests)
idp-server-core/oauth/request/ および oauth/io/ 内:
PAR実装は以下のクラスで構成:
OAuthPushedRequestParameters - PAR処理
OAuthPushedRequest - リクエスト表現
OAuthPushedRequestResponse - レスポンス表現
PARレスポンスのHTTPステータスコード
RFC 9126 Section 2.2 に従い、PAR成功レスポンスは HTTP 201 Created を返す。
OAuthPushedRequestStatus.CREATED(201) - 成功ステータス
OAuthV1Api のPARエンドポイントは response.statusCode() でステータスを動的に決定
PAR + FAPI Advanced の検証アーキテクチャ
FAPI Advanced では認可リクエストに JWS 署名付き Request Object が必須(Section 5.2.2 clause 1)。
PAR 経由のリクエストでは、以下の2段階で検証が分離される:
1. PARエンドポイント(OAuthRequestHandler.handlePushedRequest)
- クライアントは plain パラメータ または Request Object(JWT)を PAR エンドポイントに POST
OAuthRequestPattern が NORMAL または REQUEST_OBJECT として解析される
OAuthRequestVerifier.verify() で FAPI Advanced の全検証を実行(署名、exp、nbf、aud 含む)
- 検証通過後、
AuthorizationRequest をリポジトリに保存
request_uri(urn:ietf:params:oauth:request_uri:...)を発行
2. 認可エンドポイント(OAuthRequestHandler.handleRequest)
- クライアントは
client_id + request_uri で認可エンドポイントにアクセス
OAuthRequestPattern が PUSHED_REQUEST_URI として解析される
PushedRequestUriPatternContextCreator が保存済みパラメータを復元
- JoseContext は空(保存時に JoseContext 情報は保持されない)
FapiAdvanceVerifier は PAR 経由の場合、以下の検証をスキップ:
- Request Object 署名検証(
isUnsignedRequestObject)
- JWT クレーム検証(exp、nbf、aud)
- 以下の検証は PAR 経由でも実行:
- JARM 設定チェック
- response_type / response_mode 検証
- sender-constrained access token 検証
- クライアント認証方式検証
- public client 拒否
PARエンドポイント 認可エンドポイント
┌─────────────────────┐ ┌─────────────────────┐
│ パラメータ受信 │ │ request_uri 受信 │
│ ↓ │ │ ↓ │
│ パターン解析 │ │ PUSHED_REQUEST_URI │
│ (NORMAL/REQUEST_OBJ) │ │ ↓ │
│ ↓ │ │ 保存済みパラメータ復元 │
│ FAPI Advanced 全検証 │ │ (JoseContext は空) │
│ (署名,exp,nbf,aud) │ │ ↓ │
│ ↓ │ │ FAPI Advanced 検証 │
│ クライアント認証 │ │ (JWT検証はスキップ) │
│ ↓ │ │ ↓ │
│ AuthzRequest 保存 │ │ 認可処理続行 │
│ ↓ │ └─────────────────────┘
│ request_uri 発行 │
└─────────────────────┘
関連ファイル:
FapiAdvanceVerifier.java - PAR判定とスキップロジック
OAuthRequestHandler.java - PAR/認可の2段階ハンドリング
PushedRequestUriPatternContextCreator.java - PAR復元時の空JoseContext生成
OAuthRequestPattern.java - NORMAL / REQUEST_OBJECT / REQUEST_URI / PUSHED_REQUEST_URI の4パターン
OAuthPushedRequestStatus.java - CREATED(201) / BAD_REQUEST(400) / UNAUTHORIZED(401) / SERVER_ERROR(500)
JARM(JWT-secured Authorization Response Mode)
idp-server-core/oauth/response/ 内:
JARM実装は以下のクラスで構成:
JarmCreatable - JARM生成インターフェース
JarmVerifier - JARM検証
mTLS(Mutual TLS)
idp-server-core-extension-fapi/ 内:
mTLS実装は以下のクラスで構成:
TlsClientAuthAuthenticator - tls_client_auth方式
SelfSignedTlsClientAuthAuthenticator - self_signed_tls_client_auth方式
FAPI Advanced 必須設定
FAPI Advanced準拠に必要な認可サーバー設定:
| 設定 | 値 | 説明 |
|---|
tls_client_certificate_bound_access_tokens | true | ATをクライアント証明書にバインド |
require_signed_request_object | true | 署名付きリクエストオブジェクト必須 |
pushed_authorization_request_endpoint | 設定必須 | PAR エンドポイント |
PAR(Pushed Authorization Request)フロー
- クライアントが
POST /v1/authorizations/push にリクエストパラメータを送信
- サーバーが
request_uri(urn:ietf:params:oauth:request_uri:...)を返却
- クライアントが
request_uri を使って認可リクエストを送信
JARM(JWT Secured Authorization Response Mode)
認可レスポンスをJWTで署名して返す。対応するresponse_mode:
| response_mode | 説明 |
|---|
jwt | デフォルトの応答方法をJWTで返す |
query.jwt | クエリパラメータでJWTを返す |
fragment.jwt | フラグメントでJWTを返す |
mTLSエンドポイントエイリアス
Discoveryの mtls_endpoint_aliases でmTLS用のエンドポイントURLを公開。クライアント証明書が必要なエンドポイント(トークン、Introspection等)の別URLを提供する。
クライアント認証方式
FAPI Advancedでは private_key_jwt または tls_client_auth を使用。client_secret ベースの認証は非推奨。mTLS環境ではトークンリクエスト時にクライアント証明書のみで認証し、client_secret は不要。
E2Eテスト
e2e/src/tests/
├── spec/
│ ├── fapi_baseline.test.js # FAPI Baseline仕様
│ ├── fapi_advance.test.js # FAPI Advanced仕様
│ ├── fapi_ciba.test.js # FAPI CIBA仕様
│ ├── rfc9126_par.test.js # PAR (RFC 9126)
│ └── jarm.test.js # JARM仕様
│
├── usecase/financial-grade/
│ ├── financial-grade-01-transfer-flow.test.js
│ └── financial-grade-02-authentication-device-rule.test.js
│
└── security/
└── (FAPI関連セキュリティテスト)
コマンド
./gradlew :libs:idp-server-core-extension-fapi:compileJava
./gradlew :libs:idp-server-core-extension-fapi-ciba:compileJava
cd e2e && npm test -- spec/fapi_baseline.test.js
cd e2e && npm test -- spec/fapi_advance.test.js
cd e2e && npm test -- spec/rfc9126_par.test.js
cd e2e && npm test -- usecase/financial-grade/
OIDF適合性テスト(OIDF Conformance Test Suite)
概要
OpenID Foundation (OIDF) が提供する適合性テストスイートで、FAPI仕様への準拠を自動検証する。
テストスイートURL: https://www.certification.openid.net/
本プロジェクトでは以下の2つのテストプランを実行:
| テストプラン | 内容 | テスト数 |
|---|
FAPI 1.0 Advanced Final (fapi1-advanced-final-test-plan) | 認可フロー、Request Object検証、JARM、mTLS、エラーハンドリング | ~30テスト |
FAPI-CIBA ID1 (fapi-ciba-id1-test-plan) | CIBAフロー、poll/pingモード、Request Object検証、Refresh Token | ~30テスト |
テスト環境の構成
OIDF Conformance Suite (https://www.certification.openid.net/)
↕ HTTPS
ローカル idp-server (api.local.test / mtls.api.local.test)
↕
Financial-grade テナント (tenant_id: c3d4e5f6-a7b8-c9d0-e1f2-a3b4c5d6e7f8)
OIDFテストスイートがローカルサーバーにHTTPSでアクセスするため、ローカル環境が外部からアクセス可能である必要がある(ngrok等のトンネリングまたはDNS設定)。
financial-gradeテナントのセットアップ
docker compose up -d
cd config/examples/financial-grade
./setup.sh
./update.sh
./delete.sh
setup.sh が行うこと:
.envから管理者認証情報を読み込み、アクセストークンを取得
onboarding-request.jsonを使って組織+テナント+クライアント群を一括作成
- 認証ポリシー、認証設定(FIDO2等)を個別APIで設定
- テストユーザーを作成
主要設定ファイル:
| ファイル | 内容 |
|---|
onboarding-request.json | テナント定義 + 全クライアント定義(一括オンボーディング) |
financial-tenant.json | テナント設定のリファレンス(discovery応答に反映される値) |
tls-client-auth-client.json | tls_client_auth方式クライアント |
tls-client-auth-client-2.json | tls_client_auth方式クライアント(2nd client for OIDF) |
private-key-jwt-client.json | private_key_jwt方式クライアント |
financial-client.json | self_signed_tls_client_auth方式クライアント |
authentication-policy/oauth.json | FAPI用認証ポリシー |
certs/ | mTLSクライアント証明書・CA証明書 |
OIDF テスト設定ファイル
OIDFテストスイートにインポートするJSON設定:
config/examples/financial-grade/oidc-test/
├── fapi/
│ └── tls_client_auth.json # FAPI 1.0 Advanced Final (tls_client_auth)
└── fapi-ciba/
├── tls_client_auth_poll.json # FAPI-CIBA (tls_client_auth + poll)
├── private_key_jwt_poll.json # FAPI-CIBA (private_key_jwt + poll)
└── FAPI-CIBA-test-cases.md # テストケース詳細ドキュメント
設定ファイルの構造:
{
"alias": "テスト計画の識別名",
"server": {
"discoveryUrl": "https://api.local.test/{tenant_id}/.well-known/openid-configuration"
},
"client": { "client_id": "...", "scope": "...", "jwks": {...} },
"client2": { "client_id": "...", "scope": "...", "jwks": {...} },
"mtls": { "key": "...", "cert": "..." },
"mtls2": { "key": "...", "cert": "..." },
"resource": {
"resourceUrl": "https://mtls.api.local.test/{tenant_id}/v1/me/identity-verification/applications"
}
}
注意:
discoveryUrlは通常エンドポイント (api.local.test)
resourceUrlはmTLSエンドポイント (mtls.api.local.test) — sender-constrainedトークン検証にクライアント証明書が必要
clientとclient2は2クライアントテスト用(証明書バインドアクセストークンの交差検証等)
mtls/mtls2はそれぞれのクライアントのTLSクライアント証明書
FAPI-CIBAテスト時の追加要件
CIBAテストではユーザーがデバイスで認証を承認する必要がある:
cd config/examples/financial-grade
./ciba-device-auth.sh
このスクリプトはCIBAバックチャネル認証リクエストに対して、ユーザーの認証承認をシミュレートする。
Gap分析ドキュメント
documentation/requirements/fapi-1.0-gap-analysis.yaml にOIDF適合性テスト結果と修正記録を管理:
- 各GAP項目のID、仕様参照、修正状態、修正内容を記録
- テナント設定・クライアント設定の要件充足分析も含む
- 新しいGAPが見つかった場合はこのファイルに追記する
トラブルシューティング
PKCE S256エラー
code_challenge_method=S256のみ許可(plainは不可)
- FAPI Baselineでは必須
PAR request_uri無効
- request_uriの有効期限(90秒)を確認
- 使用済みrequest_uriは再利用不可
mTLS証明書検証失敗
- クライアント証明書が正しく送信されているか確認
TlsClientAuthAuthenticator の設定を確認
JARM署名検証失敗
- Authorization Serverの署名鍵(JWK)を確認
responseパラメータのJWT形式を確認
OIDFテスト discoveryUrl エラー
discoveryUrlがapi.local.test(通常エンドポイント)を指しているか確認
resourceUrlがmtls.api.local.test(mTLSエンドポイント)を指しているか確認
host.docker.internal:8445等の旧URL形式が残っていないか確認
PAR + FAPI Advanced 2段階検証アーキテクチャ
FAPI Advanced では PAR エンドポイントと認可エンドポイントで検証が分かれる:
PAR エンドポイント(OAuthRequestHandler.handlePushedRequest)
- FAPI Advanced 全検証実行(署名、exp、nbf、aud)
request_uri(urn:ietf:params:oauth:request_uri:...)を返却
認可エンドポイント(OAuthRequestHandler.handleRequest)
- PAR 済みリクエスト(
isPushedRequest())は Request Object 署名・JWT クレーム検証をスキップ
- response_type/mode 検証、MTLS、クライアント認証、public client チェックは実行
if (!context.isPushedRequest()) {
throwExceptionIfInvalidSigningAlgorithm(context);
}
TlsClientAuthAuthenticator SAN 検証
mTLS クライアント認証で SAN (Subject Alternative Name) の4種を検証:
dNSName
uniformResourceIdentifier
iPAddress
rfc822Name
SelfSignedTlsClientAuthAuthenticator は自己署名証明書用(開発/テスト環境向け)。
探索起点: libs/idp-server-core-extension-fapi/