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
Draft
Conversation
antstanley
force-pushed
the
spec/bind-id-token-grant-replay-protection
branch
2 times, most recently
from
August 5, 2026 08:22
83ad7b9 to
efdec78
Compare
antstanley
changed the base branch from
main
to
spec/bind-grant-type-at-token-endpoint
August 5, 2026 08:22
antstanley
marked this pull request as draft
August 5, 2026 08:25
This was referenced Aug 5, 2026
antstanley
force-pushed
the
spec/bind-id-token-grant-replay-protection
branch
from
August 5, 2026 09:00
efdec78 to
9d8ec41
Compare
antstanley
force-pushed
the
spec/bind-id-token-grant-replay-protection
branch
from
August 5, 2026 11:29
9d8ec41 to
1a58222
Compare
…gle-use Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
antstanley
force-pushed
the
spec/bind-id-token-grant-replay-protection
branch
from
August 5, 2026 12:01
1a58222 to
d786188
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Note
Stacked PR 7 of 16 — part of stack #35, which targets
main.Base: #21 (
spec/bind-grant-type-at-token-endpoint) · Followed by: #22This 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_usepair toSessionRepository; #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.rsandcrates/providers/src/apple.rs— check signature, issuer, audience and expiry, and enforce nononce, noazp, noat_hash, and no one-time-use. A repo-wide check confirms nononce,azp,at_hashorjtihandling exists anywhere incrates/. Possession of a victim's provider ID token issued for the configuredclient_idis 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.rscalled fromAppService::exchangerather than in either validator — the same omission occurring twice in two implementations is itself the argument for a shared mechanism:POST /nonce, stored as a SHA-256 digest with agrants.nonce_ttl(default 10m) lifetime, burned atomically on use;azpcheck, required whenaudis multi-valued and enforced whenever present;at_hashcheck when a provider access token accompanies the assertion;jti(else a digest of the compact JWT), TTL bounded by a newgrants.max_assertion_lifetimeceiling of 1h so the marker always outlives the assertion.The adapters change only to report
signing_algonIdentityClaimsand exposeclient_id(). The replay store is added asput_single_use/take_single_useonSessionRepository, 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_tokenswitch defaults tofalse, 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-creatorplugin from a deep security scan of the repository at53cbdec9. Spec only — no source changes.