Skip to content

Reproducing the OFCS run ​

This page is the operator's runbook. It complements OFCS conformance status, which records what the suite saw on the latest run; this page covers how to reproduce it on your own machine and which library options each OFCS plan exercises.

The status page summarizes the retained release-equivalent run captured immediately before the release tag at core commit 8ee7ca3, captured at 2026-09-28T13:43:49Z. The run is treated as release-equivalent to OFCS release-v5.3.1 because it was measured immediately before the release tag. Its JSON snapshot is a local, gitignored release artifact and does not record the OFCS image tag or digest. The current conformance/docker-compose.yml default is release-v5.3.1.

If you are looking for the latest PASS/REVIEW/SKIPPED/WARNING counts, start at OFCS conformance status. If you want to understand which op.With... options to wire into your own OP so the same OFCS plans pass against it, you are in the right place.

Personal project, not certified

The numbers on this site are reproducible snapshots — this page tells you how to reproduce them. They are not a substitute for paid OpenID Foundation certification. Run the suite locally to verify your own embedder configuration; do not cite local PASS counts as certification.

Reproducing a run, end to end
Six steps reproduce a conformance run: generate the certificates, start the conformance suite, start the demo OP on the profile under test, seed the plans, run them, and diff the result against the recorded baseline.1Generate certsmake certs2Start OFCSdocker compose3Start op-demoon the profile under test4Seed the plansseed-plans5Runall nine, unattended6Diffagainst the baseline
Step 6 is what makes the run worth keeping. A raw pass count drifts silently; a diff against a recorded baseline names the module that changed.

Prerequisites ​

  • Docker with Compose v2 (the harness invokes docker compose, not docker-compose).
  • OpenSSL for cert generation (scripts/conformance.sh certs).
  • Go 1.26+; go.mod specifies the go1.27.1 toolchain.
  • curl and python3 for the seed-plans / drive scripts.

The OFCS image is pulled from the OpenID Foundation registry at the current default tag release-v5.3.1 in conformance/docker-compose.yml. The retained run is treated as release-equivalent to that tag because it was measured immediately before the release tag, although its snapshot does not record the image tag or digest. You do not need a separate OFCS checkout or mvn install step.

One-command quick start ​

From a clean clone of libraz/go-oidc-provider:

sh
make conformance-up

That target chains four steps:

  1. scripts/conformance.sh certs — self-signed RSA-2048 cert for the OP listener (covering localhost + host.docker.internal), a pair of client certs the FAPI 2.0 mtls / mtls2 plan slots use, and a JKS truststore the OFCS container mounts so the suite trusts the OP without manual cert work.
  2. scripts/conformance.sh ofcs-up — docker compose up -d brings up the OFCS containers and waits for https://localhost:8443 to answer.
  3. scripts/conformance.sh op-up — builds and launches cmd/op-demo on https://127.0.0.1:9443, seeded with redirect URIs for every plan alias the suite will use.
  4. scripts/conformance.sh seed-plans — POSTs each plan template from conformance/plans/ to the OFCS REST API and prints the resulting plan IDs.

Tear everything down (including the mongo volume that holds OFCS state) with:

sh
make conformance-down

Plans, op-demo flags, and the wiring they activate ​

OFCS test plans differ in which library features they exercise. The table below maps each plan to the cmd/op-demo invocation that activates the matching wiring, and to the source file you can read end-to-end to see exactly what op.With... options that profile turns on.

OFCS planop-demo invocationReference wiring
oidcc-basic-certification-test-planmake conformance-op-up (no profile)cmd/op-demo/main.go — commonOptions
oidcc-config-certification-test-planmake conformance-op-up (no profile)cmd/op-demo/main.go — commonOptions
oidcc-dynamic-certification-test-planmake conformance-op-up (DCR enabled by default)cmd/op-demo/main.go — op.WithDynamicRegistration branch
oidcc-formpost-basic-certification-test-planmake conformance-op-up (no profile)cmd/op-demo/main.go
oidcc-rp-initiated-logout-certification-test-planmake conformance-op-up (DCR enabled by default)cmd/op-demo/main.go
oidcc-backchannel-rp-initiated-logout-certification-test-planmake conformance-op-up (DCR enabled by default)cmd/op-demo/main.go
fapi2-security-profile-id2-test-planOP_PROFILE=fapi2-baseline make conformance-op-upcmd/op-demo/profile_fapi2.go — fapi2BaselineOptions
fapi2-message-signing-id1-test-planOP_PROFILE=fapi2-message-signing make conformance-op-upcmd/op-demo/profile_fapi2.go — fapi2MessageSigningOptions
fapi-ciba-id1-test-planOP_PROFILE=fapi-ciba make conformance-op-upcmd/op-demo/profile_fapi_ciba.go — fapiCIBAOptions

The pinned OFCS release calls the Message Signing plan fapi2-message-signing-id1-test-plan; the id1 suffix is an OFCS plan identifier, not a version label for the FAPI 2.0 Message Signing Final specification. Results from that plan describe only the pinned suite coverage and do not establish conformance or certification against every requirement in the current Final specification.

Every per-profile file in cmd/op-demo/ opens with a comment block enumerating the options that profile activates, the spec sections each option satisfies, and the OFCS modules the wiring is in service of. Read the file for the plan you intend to run before you copy patterns into your own OP.

What to wire into your own OP ​

The per-profile files in cmd/op-demo/ are the canonical wiring reference. The following sections summarise them so you can cross-check against your own embedder.

OIDC Core 1.0 (basic, config, formpost, RP-initiated logout) ​

The vanilla profile needs no op.WithProfile call. The base options live in commonOptions in cmd/op-demo/main.go and apply to every plan:

  • op.WithIssuer(...) — the issuer URL discovery and ID tokens advertise.
  • op.WithStore(...) — the storage adapter (op/storeadapter/inmem for the demo; production uses sql / redis adapters).
  • op.WithKeyset(...) — the signing keyset.
  • op.WithCookieKeys(...) — the AEAD key for cookie encryption.
  • op.WithMountPrefix(...) — the URL prefix the OP handler is mounted under.
  • op.WithStaticClients(...) — the seed clients (public + confidential trio).
  • op.WithLoginFlow(...) — the login orchestration; demo uses op.PrimaryPassword.
  • op.WithFeature(feature.JAR) — opt-in JAR for non-FAPI profiles so the OFCS request-object modules either pass or skip cleanly.
  • op.WithClaimsSupported(...) — the claim list discovery advertises.

The conformance harness enables DCR by default. For the dynamic plans (oidcc-dynamic, *-rp-initiated-logout-*), op.WithDynamicRegistration(op.RegistrationOption{...}) is added so /register is mounted and an Initial Access Token is minted at startup. Set OP_ENABLE_DCR=0 only when you intentionally want to measure a non-DCR discovery surface. The demo prints the IAT to stdout; in production hand it to a secret manager and never log it.

FAPI 2.0 Baseline ​

Set OP_PROFILE=fapi2-baseline when starting op-demo. The profile-specific additions, captured in fapi2BaselineOptions, are:

  • op.WithProfile(profile.FAPI2Baseline) — auto-enables PAR (RFC 9126), JAR (RFC 9101), and the FAPI alg lockdown.
  • op.WithFeature(feature.DPoP) — sender-constrained access tokens. The final FAPI 2.0 Security Profile §5.3.2.1(4)–(5) requires DPoP or mTLS; the demo picks DPoP because the OFCS templates ship sender_constrain=dpop for the fapi2-* plans.

The demo also seeds two op.PrivateKeyJWTClient records (demo-fapi, demo-fapi-2) holding the public halves of the OFCS test client JWKSes. The matching private halves live in conformance/plans/fapi2-*.json so OFCS can sign private_key_jwt assertions.

For deeper context on each option, see Concepts → FAPI, Concepts → DPoP, and Use case: FAPI 2.0 Baseline.

FAPI 2.0 Message Signing ​

Set OP_PROFILE=fapi2-message-signing. Everything Baseline does plus:

  • op.WithProfile(profile.FAPI2MessageSigning) — auto-enables JARM (signed authorization response) on top of PAR / JAR / alg lockdown.
  • op.WithFeature(feature.DPoP) — same sender constraint as Baseline.
  • op.WithDPoPNonceSource(...) — when profile.FAPI2MessageSigning is active with DPoP, the library requires a source for server-supplied DPoP nonces, enforced by op/options_validate.go. This is a library configuration requirement, not a mandate in the FAPI 2.0 Message Signing Final specification: that document has no §5.3.4, and §5.6 covers ID-token signing. If mTLS is explicitly selected instead of DPoP, no DPoP nonce source is needed. The demo uses the in-memory rotator; a multi-instance OP must back the source with a shared store.

FAPI-CIBA ​

Set OP_PROFILE=fapi-ciba. CIBA differs from the fapi2-* plans in three significant ways:

  • The OFCS fapi-ciba-id1 plan inherits FAPI 1.0's hardcoded cert-bound access-token requirement and exposes no sender-constraint variant. The demo therefore wires op.WithFeature(feature.MTLS) instead of feature.DPoP. Both satisfy WithProfile's "DPoP OR MTLS" disjunctive constraint.
  • CIBA clients never visit the authorization endpoint. Their seeds carry an empty redirect_uri set and a narrowed grant list (CIBA URN + refresh_token).
  • The OFCS plan drives the auth_req_id poll loop without a real authentication device. The demo wraps the in-memory CIBARequestStore with cibaAutoApproveStore, which schedules an out-of-band Approve() after a configurable delay so the OP can simulate a device callback. Production embedders MUST trigger Approve from the user's actual authentication-device callback, never from inside the OP process.

The full wiring (including the auto-approve substore and the hint resolver) lives in cmd/op-demo/profile_fapi_ciba.go.

Driving the run ​

With the stack up and the plans seeded, you can drive modules in three ways.

Single browser-driven flow ​

Useful when iterating on one module from the OFCS UI:

sh
scripts/conformance.sh drive 'https://host.docker.internal:9443/oidc/auth?...'

Copy the Browser URL OFCS prints in the test detail panel.

Multiple modules at once ​

Pass a plan ID printed by seed-plans:

sh
scripts/conformance.sh batch <plan-id> oidcc-server oidcc-userinfo-get ...

The batch sub-command picks the right driver hint per module name (e.g. it sets OFCS_DRIVE_REJECT=1 for *user-rejects-authentication* modules so the demo cancels consent).

Every module in every plan ​

sh
make conformance-baseline LABEL=local-check

That runs every module in every seeded plan and writes a deterministic JSON snapshot to conformance/baselines/<UTC-date>-local-check.json. Use this when verifying a refactor does not regress against a previously-captured baseline.

Reading the result ​

OFCS mainly reports four terminal result values, and the harness also preserves advisory WARNING results.

  • PASSED — the module ran and observed the spec-required behaviour.
  • REVIEW — the module ran but a human reviewer must verify visual or out-of-band behaviour the harness cannot capture (consent UI strings, error page screenshots). Not a failure. The headless harness records REVIEW as-is; paid certification would require sitting in front of the OFCS UI to clear it.
  • SKIPPED — the module depends on a feature this OP does not advertise in discovery or per-client metadata. For example, a per-client RS256 negative test skips when the client metadata declares PS256 as its signing alg, putting RS256 out of scope for that probe. Not a failure.
  • WARNING — the module reached a non-failed advisory result. The current status page tracks these separately from FAILED.
  • FAILED — the module did not reach the suite's expected result. It blocks the release unless make conformance-release-verify matches it to a reviewed, unexpired exclusion; see OFCS conformance status for the current breakdown.

The detailed breakdown of which modules currently sit in REVIEW or SKIPPED, and why, is on OFCS conformance status.

Capturing and diffing a baseline ​

The harness is built around the cycle "freeze the current state, then verify a change does not regress against it":

sh
make conformance-up
make conformance-baseline LABEL=pre-change

# … make changes …

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.json

baseline-diff exits non-zero on any module that lost PASSED between the two snapshots. The diff classifies modules as:

  • regressions — were PASSED, now anything else (the gate).
  • fixes — were not PASSED, now PASSED.
  • non-pass churn — both states are non-PASSED but differ (e.g. REVIEW → SKIPPED).
  • catalog drift — module appears in only one snapshot (typical when bumping the OFCS pin).

Snapshot files are gitignored — they record an environment-specific moment in time and would churn between machines and OFCS releases. If you want to keep a single canonical snapshot, copy it out from under conformance/baselines/ to a path you commit elsewhere.

The module list is queried from OFCS at runtime, so a catalog drift between OFCS releases is captured automatically. Set BASELINE_FILTER to a regex when you only want to baseline a subset:

sh
BASELINE_FILTER='^oidcc-' make conformance-baseline LABEL=oidcc-only

Bumping the pinned OFCS release ​

conformance/docker-compose.yml carries the canonical pin via IMAGE_TAG. To move forward:

sh
IMAGE_TAG=release-vX.Y.Z make conformance-up

OFCS test logic does drift between releases — module renames, condition strictness changes, new variants. After any bump, recapture every plan's baseline and diff it against the previous one. A new SKIPPED or REVIEW row is usually a catalog rename and not a regression; verify the renamed module's intent before treating it as one.

Re-using the wiring in your own OP ​

The per-profile files in cmd/op-demo/ are deliberately small and well-commented so you can lift their structure directly into your own embedder:

A production embedder will replace the cmd/op-demo ephemera (in-memory store, ephemeral signing key, the auto-approve CIBA wrapper) with vault-backed keys, a durable storage adapter, and a real device-callback path — but the op.With... shape stays identical.

Caveats worth naming ​

  • Plan suite version. The current default pin is release-v5.3.1. The retained run is treated as release-equivalent to that pin because it was measured immediately before the release tag; its snapshot does not record the actual image tag or digest.
  • Headless drive. The drive script reverse-engineers the OFCS REST surface; OFCS does not document it. The retained snapshot does not record the actual image tag or digest.
  • No real RP cert. The mTLS plan slots use generated self-signed certs at conformance/certs/ so the plan can be instantiated. No real CA chain is exercised.
  • Single OP instance. Cross-instance behaviour (e.g. token introspection across two OPs sharing a store) is exercised by the in-process Spec Scenario Suite under test/scenarios/, not OFCS.