• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hyperscale-stack / security / 38017422515
97%

Build:
DEFAULT BRANCH: master
Ran 10 Oct 2026 02:34AM UTC
Jobs 1
Files 95
Run time 1min
Badge
Embed ▾
README BADGES
x

If you need to use a raster PNG badge, change the '.svg' to '.png' in the link

Markdown

Textile

RDoc

HTML

Rst

10 Oct 2026 02:33AM UTC coverage: 94.611% (+0.2%) from 94.444%
38017422515

push

github

web-flow
fix(oauth2): revoke tokens issued from a replayed authorization code

RFC 6749 §4.1.2 says the tokens issued from a replayed authorization
code SHOULD be revoked, and RFC 9700 §4.2.4 relies on it against stolen
codes. Every store deleted a code as it consumed it, so a replay looked
like an unknown code, and issuance picked a random token family after
the consumption: nothing tied a code to its tokens, and the grant left
the first exchange's tokens alive.

The authorization_code grant now derives the token family from the code
hash (a domain-separated SHA-256, 22 characters) before consuming the
code. Stores keep a consumed code, flagged and without its raw value, at
least until it expires, and answer a replay with an
oauth2.CodeReplayError carrying the record; it matches
ErrCodeAlreadyUsed, and an unknown code still gets the bare sentinel.
The SQL store flags the row write-first in one READ COMMITTED
transaction (new consumed column); the Redis store flags the code's
JSON in one script with SET ... KEEPTTL and a per-call nonce, so a
go-redis resend after a lost reply still wins; the memory store keeps a
consumed set and no longer keeps the raw code.

A replay revokes the code's family only when it would itself have been
accepted: the code has not expired, and its client presents it with the
same redirect_uri and PKCE verifier and may still use the grant (OAuth
2.1 §4.1.3, §7.5.3). The revocation runs on a context detached from the
request; a failure is reported through grant.Config.OnError, and the
next proven replay retries it. Every replay answers the same bare
ErrCodeAlreadyUsed, so the response tells nothing about which check
failed. A replay landing before the first exchange persists its tokens
makes that exchange fail with invalid_grant; one landing after revokes
them.

This changes the AuthorizationCodeStore contract, adds a consumed
column to oauth2_auth_codes (existing tables need an ALTER TABLE) and a
flag to the Redis code payload; every in... (continued)

230 of 236 new or added lines in 11 files covered. (97.46%)

9 existing lines in 1 file now uncovered.

4828 of 5103 relevant lines covered (94.61%)

75.68 hits per line

Uncovered Changes

Lines Coverage ∆ File
2
94.12
-5.88% oauth2/grant/legacy_password.go
2
91.55
-0.24% oauth2/store/redis/store.go
2
95.99
2.11% oauth2/store/sql/store.go

Coverage Regressions

Lines Coverage ∆ File
9
96.22
-2.33% oauth2/storetest/family.go
Jobs
ID Job ID Ran Files Coverage
1 38017422515.1 10 Oct 2026 02:34AM UTC 95
94.61
GitHub Action Run
Source Files on build 38017422515
  • Tree
  • List 95
  • Changed 13
  • Source Changed 0
  • Coverage Changed 13
Coverage ∆ File Lines Relevant Covered Missed Hits/Line
  • Back to Repo
  • d8ccbb56 on github
  • Prev Build on master (#38015736430)
  • Next Build on master (#38048191226)
  • Delete
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc