Skip to content

fix: make OAuth2 code redemption single-use under concurrency - #28744

Draft
BobbyHo wants to merge 1 commit into
plat480-2-stale-scope-recheckfrom
plat480-3-single-use-code
Draft

fix: make OAuth2 code redemption single-use under concurrency#28744
BobbyHo wants to merge 1 commit into
plat480-2-stale-scope-recheckfrom
plat480-3-single-use-code

Conversation

@BobbyHo

@BobbyHo BobbyHo commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

TL;DR

Third of the PLAT-480 stack, stacked on #28740. An authorization code could be redeemed twice under concurrency; this makes the redeeming delete the arbiter of single use.

PR What it does
#28237 Mints access tokens with the negotiated scope and reports it per RFC 6749 §5.1.
#28740 Re-checks the code's scope against the app's registered scopes at redemption.
this Makes code redemption single-use under concurrency. Carries no scope logic.

RFC 6749 §10.5 requires an authorization code to be single-use. The redeeming delete was a blind :exec with no affected-rows check, so two concurrent redemptions of one code both minted a token.

  • The delete now returns the row it removed, so the request that removed nothing sees sql.ErrNoRows and is refused with invalid_grant, the same response an unknown code already gets.
  • Each redemption runs in its own transaction, so the loser blocks on the winner's row lock and then finds the code gone.
  • The test races two redemptions rather than running them in sequence. A sequential pair passes whether or not the delete arbitrates single use.

Depends on #28740

Stack: #28237, #28740, this PR.

Two concurrent redemptions of one authorization code both minted a
token. The redeeming delete was a blind :exec with no affected-rows
check, so the request that lost the race deleted nothing and carried on
as if it had won.

DeleteOAuth2ProviderAppCodeByIDReturningRow returns the row it removed,
so a delete that removed nothing surfaces sql.ErrNoRows. The token
exchange maps that to the invalid_grant it already returns for an
unknown code, which makes the delete the arbiter of single use
(RFC 6749 §10.5).
@BobbyHo BobbyHo changed the title fix(coderd): make OAuth2 code redemption single-use under concurrency fix: make OAuth2 code redemption single-use under concurrency Aug 28, 2026
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