Users are affected if all of the following are true:
@better-auth/oauth-provider at a version >= 1.6.0, < 1.6.11, or uses the embedded plugin in better-auth >= 1.4.8-beta.7, < 1.6.0, or enables the legacy oidc-provider or mcp plugins from better-auth/plugins./api/auth/oauth2/token (or the legacy plugins' /oauth2/token and /mcp/token) as a token endpoint to OAuth/OIDC clients, including internal MCP clients (Claude Desktop, custom MCP tool callers, AI agents).code, a database trigger that rejects duplicate token issuance for the same authorization code, or a custom adapter override that performs an atomic compare-and-delete.Fix:
@better-auth/oauth-provider@1.6.11 or later. If developers use the legacy plugin paths from better-auth/plugins, upgrade better-auth to 1.6.11 or later.The OAuth provider's POST /oauth2/token endpoint, on the authorization_code grant, redeems a single-use authorization code through a non-atomic find-then-delete sequence. Two concurrent requests with the same code value both pass the read step before either delete completes, then both proceed to PKCE verification and createUserTokens. Each surviving request mints a fresh access token, refresh token, and id token. RFC 6749 §4.1.2 requires authorization codes to be single-use; this primitive does not enforce that under concurrency.
The same architectural primitive (find a single-use verification row, then delete it, then trust the row to authorize) is used in 20 other call sites across the codebase. The deletion primitive returns Promise<void>, discarding the row count surfaced by adapter.deleteMany, so no call site can detect "another caller already claimed this...
1.6.111.6.11Exploitability
AV:NAC:LAT:PPR:NUI:PVulnerable System
VC:HVI:HVA:NSubsequent System
SC:NSI:NSA:N7.6/CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N