Skip to content

Test our OIDC login against the official OpenID conformance suite#21

Merged
koomen merged 2 commits into
mainfrom
oidc-conformance
Jul 21, 2026
Merged

Test our OIDC login against the official OpenID conformance suite#21
koomen merged 2 commits into
mainfrom
oidc-conformance

Conversation

@koomen

@koomen koomen commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

We use Google sign-in via OIDC, but until now nothing independently verified that our client rejects bad ID tokens. This PR adds a test setup that runs the official OpenID Foundation conformance suite against our server and makes sure we fail the way we're supposed to when a token is tampered with.

What's in here

  • New test runner (e2e/conformance/, run with bun run conformance): spins up the OpenID conformance suite in Docker (pinned to release-v5.2.0), points it at a fresh instance of our server, and drives each test module through a real browser. Reports pass/fail per module.
  • Config override for testing (SCRATCHWORK_LOCAL_OAUTH_ISSUER): the conformance suite acts as the identity provider, so we need to tell the server to expect its issuer URL instead of Google's. This only works alongside the existing loopback-only local endpoint overrides — it can't loosen anything in production.
  • Token endpoint auth fix: we were sending the client secret in the POST body; the spec (RFC 6749 §2.3.1) says to prefer HTTP Basic auth, and the conformance suite fails you for it. Switched to Basic auth, and updated the fake OAuth provider in our e2e tests to enforce it too.
  • Unit tests for both changes.

What we test (and what we skip)

The suite's full certification plan has 14 modules. Seven of them check that a client rejects tampered ID tokens — wrong issuer, wrong audience, bad signature, alg=none, stale timestamps, wrong nonce, ambiguous signing key. Those are the security-critical ones, and all seven pass.

The other seven check full login completion, which requires features we deliberately don't have (a userinfo call — we treat the ID token as the only credential). Those are skipped with a log line explaining why. Details and the decision are written up in notes/oidc-conformance-spike.md.

Testing

Ran the suite for real: 7/7 required modules pass in about 2.5 minutes including boot. bun run ci covers the unit tests.

🤖 Generated with Claude Code

koomen and others added 2 commits July 20, 2026 00:26
Implements the conformance item spiked in notes/oidc-conformance-spike.md:

- SCRATCHWORK_LOCAL_OAUTH_ISSUER: expected-issuer override riding the
  loopback-gated local endpoint overrides, replacing (never widening) the
  Google issuer check — the conformance suite issues under its own URL.
- Token-endpoint client auth moves to client_secret_basic (RFC 6749 §2.3.1),
  which the suite's basic RP plan enforces at FAILURE level; the hermetic e2e
  provider now requires the Basic header and rejects body-borne secrets.
- e2e/conformance/: pinned suite compose (release-v5.2.0, devmode) and a
  runner (`bun run conformance`) that drives the full
  oidcc-client-basic-certification-test-plan through a loopback proxy, one
  fresh backend + browser per module, and reports per-module verdicts.

Verified against the real suite: 8/14 modules green — every ID-token
tampering/rejection lane passes (invalid iss/aud/sub/iat/nonce, bad
signature, kid-absent-multiple, alg=none refused). The six that require
completing a login stall on a known product gap (no userinfo support; email
required in the ID token, which the suite's OP never provides; single-key
kid-less JWKS rejected) — recorded with the decision needed in the spike
note. The lane joins bun run ci once that's resolved and the plan is green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Decision (Pete, 2026-07-20, recorded in notes/oidc-conformance-spike.md):
keep the RP as is — no userinfo call, the ID token stays the only credential
consumed — and require exactly the seven modules that judge the RP by
rejecting a tampered ID token. The plan's login-completion modules are
N/A by design and skipped with a log line, narrowing "conformance coverage"
from certification-green to security-modules-green deliberately.

The scoped lane runs green: 7/7 PASSED in ~2m20s wall including suite boot.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@koomen koomen changed the title Land the OIDC RP conformance lane Test our OIDC login against the official OpenID conformance suite Jul 21, 2026
@koomen
koomen merged commit 0a4cb41 into main Jul 21, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant