# @better-auth/oauth-provider's OAuth authorization-code grant allows concurrent redemption when two token requests race the find-then-delete primitive

Better Auth's OAuth token endpoint redeemed single-use authorization codes via non-atomic find-then-delete, so concurrent requests each minted independent token sets.

- Payout: Research
- Severity: high
- Program: Better Auth
- Found by: chdanielmueller
- Disclosed: 2026-07-07
- CVE: CVE-2026-53518
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-7w99-5wm4-3g79
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-7w99-5wm4-3g79
- Tags: area:web domain:api domain:web impact:ato kind:research platform:ghsa sev:high tech:nodejs tech:oauth technique:race-window technique:source-review vuln:auth-bypass vuln:race-condition vuln:signature-replay

**Target:** OAuth2 token endpoint (`/api/auth/oauth2/token`, legacy `/oauth2/token`, `/mcp/token`)
**Root cause:** Authorization code redemption used a non-atomic find-then-delete on the verification row; the delete primitive returned Promise<void>, discarding the affected-row count, so no caller could detect a concurrent claim.
**Steps:**
1. Send two concurrent `POST /api/auth/oauth2/token` requests with `grant_type=authorization_code` and the same `code` value.
2. Both requests pass the read step before either delete completes.
3. Both proceed through PKCE verification and `createUserTokens`.
4. Each request receives a fresh access token, refresh token, and id token for the same authorization.
**How found:** Reporter recognized the find-then-delete pattern as a TOCTOU primitive and noted the delete helper discarded `adapter.deleteMany`'s row count, making concurrent claims undetectable; the same primitive appears at 20 other call sites.
**Exploitation:** Attacker who obtains a single authorization code (e.g. via interception or a leaked redirect) fires parallel token requests; each interleaving past the read step yields its own valid token set, defeating single-use enforcement and extending the window for replay.
**Insight:** When auditing OAuth/token issuance, check whether single-use consumption is an atomic claim (delete-and-return-value) or a separate find+delete; a delete helper returning void instead of a row count is a strong signal the codebase cannot detect concurrent redemption anywhere it is used.

