OFCS 再現レシピ
このページは、OpenID Foundation Conformance Suite(OFCS)を自分の環境でライブラリに対して実行するための runbook です。OFCS 適合状況が「最新の実行で何が観測されたか」を記録するのに対し、本ページは「どう再現するか」と「各プランがどの op.With... オプションを使うか」を扱います。
適合状況ページは、2026-11-04T19:51:48Z に core のコミット 9e8d5153 で取得した run を要約しています。対象は OFCS release-v5.3.4 です。JSON snapshot はローカルの gitignore 対象 artifact で、イメージの tag も digest も記録していません。再現に使う pin は、conformance/docker-compose.yml の既定である release-v5.3.4 です。
PASS / REVIEW / SKIPPED / WARNING の件数は OFCS 適合状況 にあります。自分の OP に同じプランを通すために必要な op.With... オプションは、このまま読み進めてください。
個人開発、認証取得は無し
本サイトの数値は再現可能なスナップショットであり、本ページはその再現手順を扱います。これは有償の OpenID Foundation 認証の代替ではありません。ローカルでスイートを実行して自分の組み込みを検証する用途には使えますが、ローカルの PASS 数を認証として引用しないでください。
前提
- Docker + Compose v2(ハーネスは
docker composeを呼び出します。docker-composeではありません) - OpenSSL(
scripts/conformance.sh certsの証明書生成に使用) - Go 1.26 以降。
go.modは toolchain をgo1.27.3に固定しています。 - seed-plans / drive スクリプト用の
curlとpython3
OFCS イメージは、conformance/docker-compose.yml の既定 tag release-v5.3.4 で OpenID Foundation の registry から pull します。OFCS の checkout や mvn install は不要です。
ワンコマンドでのクイックスタート
libraz/go-oidc-provider の clean clone から:
make conformance-upこのターゲットは 4 ステップを順に実行します。
scripts/conformance.sh certs— OP listener 用の自己署名 RSA-2048 証明書(localhost+host.docker.internalをカバー)、FAPI 2.0 mtls / mtls2 plan slot が使うクライアント証明書ペア、OFCS コンテナがマウントして OP を信頼するための JKS truststore を生成します。scripts/conformance.sh ofcs-up—docker compose up -dで OFCS コンテナを起動し、https://localhost:8443の応答を待ちます。scripts/conformance.sh op-up—cmd/op-demoをビルドしてhttps://127.0.0.1:9443で起動し、スイートが使う全プランの redirect URI を seed します。scripts/conformance.sh seed-plans—conformance/plans/の各プランテンプレートを OFCS REST API に POST し、生成されたプラン ID を出力します。
OFCS state を保持する mongo volume も含めてすべて停止する場合は:
make conformance-downプラン、op-demo フラグ、有効化される実装
OFCS テストプランによって検証するライブラリ機能が異なります。下表は各プランに対し、対応する cmd/op-demo の起動方法と、そのプロファイルが有効化する op.With... オプションを読める該当ソースファイルを対応づけたものです。
| OFCS プラン | op-demo 起動 | 該当ソース |
|---|---|---|
oidcc-basic-certification-test-plan | make conformance-op-up(basic プロファイル。profile.OIDCCore を宣言) | cmd/op-demo/main.go — commonOptions |
oidcc-config-certification-test-plan | make conformance-op-up(basic プロファイル。profile.OIDCCore を宣言) | cmd/op-demo/main.go — commonOptions |
oidcc-dynamic-certification-test-plan | make conformance-op-up(conformance.sh では DCR が既定で有効) | cmd/op-demo/main.go — op.WithDynamicRegistration 分岐 |
oidcc-formpost-basic-certification-test-plan | make conformance-op-up(basic プロファイル。profile.OIDCCore を宣言) | cmd/op-demo/main.go |
oidcc-rp-initiated-logout-certification-test-plan | make conformance-op-up(conformance.sh では DCR が既定で有効) | cmd/op-demo/main.go |
oidcc-backchannel-rp-initiated-logout-certification-test-plan | make conformance-op-up(conformance.sh では DCR が既定で有効) | cmd/op-demo/main.go |
fapi2-security-profile-final-test-plan | OP_PROFILE=fapi2-baseline make conformance-op-up | cmd/op-demo/profile_fapi2.go — fapi2BaselineOptions |
fapi2-message-signing-final-test-plan | OP_PROFILE=fapi2-message-signing make conformance-op-up | cmd/op-demo/profile_fapi2.go — fapi2MessageSigningOptions |
fapi-ciba-id1-test-plan | OP_PROFILE=fapi-ciba make conformance-op-up | cmd/op-demo/profile_fapi_ciba.go — fapiCIBAOptions |
scripts/conformance.sh op-up は、OP_ENABLE_DCR=0 を指定しない限り -enable-dcr を付けて op-demo を起動します。ストアは、OP_STORE=composite を指定しない限り in-memory です。composite では、永続側の substore に MySQL、揮発側に Redis を使います(OP_MYSQL_DSN と OP_REDIS_DSN で指定)。デプロイと同じ構成のストアで baseline を取れますが、前回の run で残ったレコードは replay や再利用の module の観測結果を変えるため、取得のたびに両方のエンジンをリセットしてください。
FAPI 2.0 の 2 行は、Security Profile と Message Signing それぞれの Final プランです。結果は固定版スイートがこれらの仕様をどこまで検査するかを示すもので、回帰実行は認証ではありません。
cmd/op-demo/ 配下の各プロファイルファイルは冒頭にコメントブロックを持ち、そのプロファイルが有効化するオプション、各オプションが満たす仕様セクション、その実装が対象としている OFCS module を列挙しています。自分の OP にパターンを取り込む前に、実行するプランに対応するファイルを通読してください。
自分の OP に組み込むもの
cmd/op-demo/ 配下のプロファイル別ファイルが正典の実装リファレンスです。以下のセクションは内容を要約したもので、自分の組み込みと照らし合わせる際の参考にしてください。
OIDC Core 1.0(basic / config / formpost / RP-initiated logout)
vanilla プロファイルは op.WithProfile(profile.OIDCCore) を宣言します。OIDC Basic プランは PKCE なしで code フローを実行するため、OAuth 2.1 の既定のままでは拒否されるからです。基本オプションは cmd/op-demo/main.go の commonOptions にまとまっており、すべてのプランに共通します。
op.WithIssuer(...)— discovery と ID token が広告する issuer URL。op.WithStore(...)— ストレージ adapter(demo はop/storeadapter/inmem、本番は sql / redis adapter を使う想定)。op.WithKeyset(...)— 署名 keyset(demo は鍵 1 本)。op.WithSigningAlgorithm(...)— 発行に使うアルゴリズム 1 つ。demo をRS256で起動しない限りES256。op.WithCookieKeys(...)— cookie 暗号化用の AEAD 鍵。op.WithMountPrefix(...)— OP handler を mount する URL prefix。op.WithStaticClients(...)— seed クライアント(public + confidential trio)。op.WithLoginFlow(...)— login orchestration。demo はop.PrimaryPasswordを使用。op.WithFeature(feature.JAR)— 非 FAPI プロファイル向けにオプトインで JAR を有効化します。discovery が(noneを含まない)request object の署名 alg を広告するため、OFCS の request-object module は pass か skip のどちらかにきれいに分かれます。op.WithClaimsSupported(...)とop.WithACRValuesSupported(op.AAL1.ACRURI())— discovery が広告する claim 一覧と、demo の AAL1 ログインで既定のポリシーが満たす唯一のacr。op.WithAllowPrivateNetwork(op.OutboundJWKS)とop.WithAllowPrivateNetwork(op.OutboundJAR)— スイートは relying party 側のjwks_uriとrequest_uriの endpoint を loopback に置きます。本番の OP は、relying party が実際に private network 上にいる場合を除き、これを有効にしません。
dynamic 系プラン(oidcc-dynamic、*-rp-initiated-logout-*)では、conformance ハーネスが DCR を既定で有効化します。op.WithDynamicRegistration(op.DynamicRegistration{...}) を追加して /register を mount し、起動時に Initial Access Token を発行します。DCR なしの discovery surface を意図的に測りたい場合だけ OP_ENABLE_DCR=0 を設定します。demo は Open: true で登録を受け付けます。OFCS のプランテンプレートに Initial Access Token を渡す枠がないためで、あわせて IAT も stdout に出力します。本番では Open を false のままにし、IAT は Provider.IssueInitialAccessToken で発行して log に出さないでください。詳しくは動的クライアント登録を参照してください。
FAPI 2.0 Baseline
op-demo 起動時に OP_PROFILE=fapi2-baseline を設定します。fapi2BaselineOptions がまとめている profile 固有の追加項目は次のとおりです。
op.WithProfile(profile.FAPI2Baseline)— PAR(RFC 9126)、JAR(RFC 9101)、FAPI alg 制限を自動有効化します。op.WithFeature(feature.DPoP)— 送信者制約付きトークン。FAPI 2.0 は DPoP または mTLS を求めます。デモが DPoP を選ぶのは、OFCS テンプレートの fapi2-* プランでsender_constrain=dpopが指定されているためです。
demo は OFCS テストクライアントの公開鍵を保持する op.PrivateKeyJWTClient を 2 件(demo-fapi、demo-fapi-2)seed します。対応する秘密鍵は conformance/plans/fapi2-*.json に置かれており、OFCS が private_key_jwt assertion の署名に使用します。
各オプションの詳細は FAPI 2.0 入門、DPoP、FAPI 2.0 Baseline を参照してください。
FAPI 2.0 Message Signing
OP_PROFILE=fapi2-message-signing を設定します。Baseline がやることに加えて:
op.WithProfile(profile.FAPI2MessageSigning)— PAR / JAR / alg 制限の上に JARM(署名 authorization response)を自動有効化します。op.WithFeature(feature.DPoP)— Baseline と同じ送信者制約。op.WithDPoPNonceSource(...)—profile.FAPI2MessageSigningまたはprofile.FAPICIBAで DPoP を有効にする場合、本ライブラリはサーバが発行する DPoP nonce の source を要求します。source がなければop.Newが構成を拒否します(op/options_validate.go)。これはライブラリの構成要件であり、FAPI 2.0 Message Signing Final の要件ではありません。同仕様には §5.3.4 がなく、§5.6 は ID トークンの署名を扱います。mTLS を明示的に選んで DPoP を使わない場合、DPoP nonce source は不要です。demo は in-memory rotator を使用しますが、マルチインスタンスの OP では共有ストアで source を裏付けてください。
FAPI-CIBA
OP_PROFILE=fapi-ciba を設定します。CIBA は fapi2-* プランと比較して 3 点で大きく異なります。
- OFCS の
fapi-ciba-id1プランは FAPI 1.0 由来の cert-bound アクセストークン要件をハードコードしており、sender-constraint variant を提供しません。そのため demo はfeature.DPoPではなくop.WithFeature(feature.MTLS)を組み込みます。どちらでもプロファイルの「DPoP または mTLS」の要件を満たします。mTLS を選ぶため、DPoP nonce source は不要です。 - CIBA クライアントは認可エンドポイントを訪れません。seed は空の
redirect_uri集合と絞り込まれた grant 一覧(CIBA URN +refresh_token)を持ちます。プロバイダはop.WithGrants(AuthorizationCode, CIBA, RefreshToken)とop.WithCIBAGrant(op.CIBAGrant{PollInterval: time.Second, MaxPollViolations: 50, ...})で構築し、ポーリング間隔を 1 秒、違反上限を 50 に上げて、プランの高頻度なポーリングがストライク上限に掛からないようにしています。 - OFCS プランは認証デバイスが実在しない状態で
auth_req_idポーリングループを実行します。demo は in-memoryCIBARequestStoreをcibaAutoApproveStoreでラップし、設定可能な遅延後に out-of-band のApprove()をスケジュールしてデバイスコールバックをシミュレーションします。本番組み込みでは、ユーザの実際の認証デバイスのコールバックからApproveを呼び出す必要があります。OP プロセス内部から呼び出してはなりません。
実装の全体(auto-approve サブストアとヒントリゾルバを含む)は cmd/op-demo/profile_fapi_ciba.go に置かれています。
実行方法
スタックを起動してプランを seed したら、3 通りの方法で module を実行できます。
ブラウザで進める単一フロー
OFCS UI から特定の module を反復確認するときに有用です。
scripts/conformance.sh drive 'https://host.docker.internal:9443/oidc/auth?...'OFCS のテスト詳細パネルが出力する Browser URL をコピーします。
複数 module を一括実行
seed-plans が出力する plan ID を渡します。
scripts/conformance.sh batch <plan-id> oidcc-server oidcc-userinfo-get ...batch サブコマンドは module 名から適切な driver hint を選択します(例: *user-rejects-authentication* 系では OFCS_DRIVE_REJECT=1 を立てて demo に同意キャンセルを行わせます)。
全プランの全 module
make conformance-baseline LABEL=local-checkseed されたすべてのプランの全 module を、プランごとに適切なプロファイルで op-demo を再起動しながら実行し、conformance/baselines/<UTC-date>-local-check.json に決定的な JSON snapshot を書き出します。リファクタが既存 baseline に対して regression を起こしていないか検証する用途で使います。
結果の読み方
OFCS は主に 4 種の終端 result value を返し、ハーネスは advisory の WARNING result も保持します。
- PASSED — module を実行し、仕様が要求する挙動を観測。
- REVIEW — module は実行されましたが、視覚的 / out-of-band の挙動(同意 UI の文言、エラーページのスクリーンショット)を人間が確認する必要があります。failure ではありません。ヘッドレスハーネスは
REVIEWをそのまま記録します。OpenID Foundation の認証を取る場合は、OFCS の UI で一件ずつ人が解消します。 - SKIPPED — module は、OP が discovery やクライアントメタデータで宣言していない機能に依存します。例えば、クライアントメタデータが署名 alg に
PS256を宣言していると、RS256の負例テストは対象外になりスキップされます。failure ではありません。 - WARNING — module は failure ではない advisory の result に到達した状態です。status ページでは FAILED と分けて集計します。
- FAILED — module がスイートの期待結果に到達しなかった状態です。
make conformance-release-verifyがレビュー済みかつ期限内の exclusion と照合できない限り、release を止めます(リリースゲートを参照)。現在の内訳は OFCS 適合状況 にあります。 - 未判定 — module が終端状態に到達しなかった状態です(runner の idle 上限後も
WAITINGかCONFIGUREDのまま)。証跡付きのunreachable_verdictsエントリだけがこれを受け入れます。
現在 REVIEW と SKIPPED になっている module と、その理由は OFCS 適合状況 に一覧があります。
リリースゲート
baseline-diff は変化を知らせるだけで、リリースの承認ではありません。承認には、厳格なゲートの make conformance-release-verify を使います。
make conformance-release-verify \
BASELINE_REFERENCE=conformance/baselines/<approved-reference>.json \
BASELINE_CANDIDATE=conformance/baselines/<candidate>.jsoncandidate は reference と完全に同じプラン・module のカタログを持ち、status と result が空であってはなりません。すべての module は FINISHED / PASSED でなければならず、conformance/release-exclusions.json に記録された module だけが例外になります。このマニフェストは git で追跡され、HEAD から変更されていない必要があります。3 つのセクションが、それぞれ別の種類の結果を受け持ちます。
| 結果 | マニフェストのセクション | エントリの照合方法 | エントリに必要なもの |
|---|---|---|---|
| FAILED(interrupted を含む) | exclusions | plan、module、status、result の完全一致 | 設計上失敗する理由 |
| REVIEW または SKIPPED | accepted_outcomes | result と module 名パターン(oidcc-rp-initiated-logout-* など)によるクラス単位のルール。intermittent は、スイートが PASSED で終えることもある module を表す | スイートが判定できない理由 |
未判定(WAITING、CONFIGURED) | unreachable_verdicts | plan、module、status の完全一致 | 単体実行で試したことの evidence |
どのエントリにも担当者と、その日を含まない ISO 形式の期限日が必要です。エントリの欠落、重複、陳腐化、期限切れはそれ自体が blocker になるため、例外が承認したレビューより長く残ることはありません。pin を上げて module 名が変わるといった OFCS のカタログ変更も、新しい承認済み reference を取り直すまで同じゲートで失敗します。
ベースラインのキャプチャと差分取得
ハーネスは「現状を凍結し、変更がベースラインに対して回帰を起こさないか検証する」サイクルを中心に組まれています。
make conformance-up
make conformance-baseline LABEL=pre-change
# … 変更を加える …
make conformance-baseline LABEL=post-change
make conformance-baseline-diff \
BASELINE_OLD=conformance/baselines/<utc>-pre-change.json \
BASELINE_NEW=conformance/baselines/<utc>-post-change.jsonbaseline-diff は、2 つのスナップショット間で PASSED を失った module が 1 つでもあれば非 0 で終了します。差分は module を次に分類します。
- regressions —
PASSEDから非PASSEDへの遷移(ゲート対象)。 - fixes — 非
PASSEDからPASSEDへの遷移。 - non-pass churn — どちらも非
PASSEDだが状態が異なる(例: REVIEW → SKIPPED)。 - catalog drift — module が片方のスナップショットにしか存在しない(OFCS pin を上げたときに典型的に発生)。
snapshot ファイルは gitignore 対象です。環境固有の瞬間を記録するため、マシン / OFCS リリース間でのちらつきが大きいからです。標準のスナップショットを 1 つ保持したい場合は、conformance/baselines/ の下からコピーアウトして自分で commit するパスに置いてください。
module 一覧は実行時に OFCS から問い合わせるため、OFCS リリース間の catalog drift は自動的に捕捉されます。サブセットだけを baseline 化したい場合は BASELINE_FILTER に正規表現を渡します。
BASELINE_FILTER='^oidcc-' make conformance-baseline LABEL=oidcc-onlyOFCS pin の更新
conformance/docker-compose.yml の IMAGE_TAG の既定値が標準の pin です。1 回の実行だけ先に進めるには次のようにします。
IMAGE_TAG=release-vX.Y.Z make conformance-upOFCS のテストロジックはリリース間で変わります(module の改名・統合、条件の厳格化)。pin を上げたあとは各プランの baseline を取り直し、前回との差分を確認してください。新しい SKIPPED / REVIEW の行は、たいてい catalog の改名であって regression ではありません。regression と判断する前に、改名された module の意図を確認してください。新しい既定を恒久化するには、compose ファイルを編集してコミットします。
自分の OP への取り込み
cmd/op-demo/ のプロファイル別ファイルは意図的に小さく、コメントを充実させてあります。組み込み側にそのまま構造を持ち込めるようになっています。
- FAPI 2.0 入門 —
op.WithProfile(profile.FAPI2Baseline)が実際に何を変えるか。 - DPoP — DPoP がアクセストークン発行パスにどう組み込まれるか。
- mTLS —
feature.MTLSがクライアント証明書を抽出してcnf.x5t#S256を生成する仕組み。 - FAPI 2.0 Baseline — Baseline 対応 deployment 向けの組み込みウォークスルー。
本番組み込みでは cmd/op-demo の暫定要素(in-memory store、ephemeral 署名鍵、auto-approve CIBA wrapper)を vault-backed 鍵、durable storage adapter、実際のデバイスコールバックパスに置き換えます。op.With... の形は同じままです。
明示しておく制限事項
- プランスイートのバージョン。 既定の pin は
release-v5.3.4です。保持している snapshot のラベルはv1.5.0で、イメージの tag も digest も記録されていません。 - ヘッドレス実行。 実行スクリプトは OFCS の REST API をリバースエンジニアリングしたもので、OFCS 側にドキュメントはありません。
- 本物の RP 証明書なし。 mTLS プラン枠は
conformance/certs/の生成済み自己署名証明書を使っており、プランをインスタンス化できる程度に整えてあるだけです。本物の CA チェーン検証はしていません。 - OP インスタンス 1 個。 クロスインスタンスの挙動(共有ストアを介した 2 つの OP 間での token introspection 等)は OFCS ではなく
test/scenarios/配下の Spec Scenario Suite で検証しています。