Skip to content

docs(spec): bind the direct ID-token grant to a nonce and make it single-use - #20

Draft
antstanley wants to merge 1 commit into
spec/bind-grant-type-at-token-endpointfrom
spec/bind-id-token-grant-replay-protection
Draft

docs(spec): bind the direct ID-token grant to a nonce and make it single-use#20
antstanley wants to merge 1 commit into
spec/bind-grant-type-at-token-endpointfrom
spec/bind-id-token-grant-replay-protection

Conversation

@antstanley

@antstanley antstanley commented Aug 5, 2026

Copy link
Copy Markdown
Owner

Note

Stacked PR 7 of 16 — part of stack #35, which targets main.
Base: #21 (spec/bind-grant-type-at-token-endpoint) · Followed by: #22

This PR's diff shows only its own change. Merge the stack bottom-up; do not merge this before its base.

Why it sits at position 7: Consumes #21's enum and adds the put_single_use/take_single_use pair to SessionRepository; #22 adds further obligations to the same trait.

Scope of this PR

This PR currently carries the change spec. It will also carry the implementation plan and the build work for that spec — plan and code land as later commits on this same branch, so the spec, its plan, and its implementation review and merge as one unit.

Review order within the PR: spec first, then plan, then implementation.


Change spec proposing replay protection for the direct ID-token grant.

Problem. Both validators — crates/adapters/src/oidc/mod.rs and crates/providers/src/apple.rs — check signature, issuer, audience and expiry, and enforce no nonce, no azp, no at_hash, and no one-time-use. A repo-wide check confirms no nonce, azp, at_hash or jti handling exists anywhere in crates/. Possession of a victim's provider ID token issued for the configured client_id is sufficient to mint first-party access and refresh tokens, and the grant cannot be turned off by configuration.

Proposed delta. Four controls, enforced once in a new crates/core/src/service/assertion.rs called from AppService::exchange rather than in either validator — the same omission occurring twice in two implementations is itself the argument for a shared mechanism:

  • a nonce this service mints at a new POST /nonce, stored as a SHA-256 digest with a grants.nonce_ttl (default 10m) lifetime, burned atomically on use;
  • an azp check, required when aud is multi-valued and enforced whenever present;
  • an at_hash check when a provider access token accompanies the assertion;
  • one-time-use keyed on jti (else a digest of the compact JWT), TTL bounded by a new grants.max_assertion_lifetime ceiling of 1h so the marker always outlives the assertion.

The adapters change only to report signing_alg on IdentityClaims and expose client_id(). The replay store is added as put_single_use/take_single_use on SessionRepository, so all five persistence adapters implement it, and both operations treat an expired record as absent — correctness does not depend on the unscheduled reaper.

A new [grants] id_token switch defaults to false, argued on three grounds: it is the only grant whose credential is a transferable bearer assertion with no back-channel redemption, discovery has never advertised it, and the mandatory nonce is already a breaking client-contract change. The counter-argument — hard upgrade failure for current users — is stated rather than softened.

Drafted with the spec-creator plugin from a deep security scan of the repository at 53cbdec9. Spec only — no source changes.

@antstanley
antstanley force-pushed the spec/bind-id-token-grant-replay-protection branch 2 times, most recently from 83ad7b9 to efdec78 Compare August 5, 2026 08:22
@antstanley
antstanley changed the base branch from main to spec/bind-grant-type-at-token-endpoint August 5, 2026 08:22
@antstanley
antstanley marked this pull request as draft August 5, 2026 08:25
@antstanley
antstanley force-pushed the spec/bind-id-token-grant-replay-protection branch from efdec78 to 9d8ec41 Compare August 5, 2026 09:00
@antstanley
antstanley force-pushed the spec/bind-id-token-grant-replay-protection branch from 9d8ec41 to 1a58222 Compare August 5, 2026 11:29
…gle-use

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@antstanley
antstanley force-pushed the spec/bind-id-token-grant-replay-protection branch from 1a58222 to d786188 Compare August 5, 2026 12:01
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