Your RSA-2048 keys break in 2030. Find every one of them before attackers do.
📦
📦 npm
Not in CISA KEV
HIGH severity

GHSA-7w99-5wm4-3g79

HIGHFix: better-auth/better-auth@b4bc65a

GHSA-7w99-5wm4-3g79 is a high-severity (CVSS 8.1) CWE-362 vulnerability in @better-auth/oauth-provider. O3 Security confirms whether GHSA-7w99-5wm4-3g79 is actually reachable in your code before you act, and blocks exploitation at runtime until you patch.

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

Also known asCVE-2026-53518
Published
Jul 7, 2026
Updated
Jul 20, 2026
Affected
2 pkgs
Patched
2 / 2
Exploits
None indexed
Exploitation data as of Aug 29, 2026 · OSV.dev, NVD, FIRST.org (EPSS)

Exploitation Status

No confirmed exploitation observed yet

  • A successful exploit gives an attacker total control of the affected component, not partial access.
  • CISA’s own triage has not observed active exploitation or public proof-of-concept code for this CVE as of its last assessment.

Exploitation and automatability from CISA’s SSVC triage for GHSA-7w99-5wm4-3g79.

EPSS Exploitation Probability

via FIRST.org ↗
0.4%probability of exploitation in next 30 days
Lower Risk+0.18%
Lower risk than most CVEs34th percentile — riskier than 34% of all scored CVEsHighest risk
0.00%0.30%0.61%0.91%0.2%0.4%Aug 26Aug 26

EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.

How urgent is this, really

GHSA-7w99-5wm4-3g79 plotted by exploitation likelihood (EPSS) against impact (CVSS). The shaded corner — EPSS 50%+ and CVSS 7.0+ — is where this CVE doesn't sit, though severity or exploitability alone can still warrant action.

Where this sits among everything scored

Of 366,526 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Real counts from FIRST.org, not a sample — log-scaled since the landscape is heavily right-skewed.

Real-World Exposure

2 pkgs affected

How broadly this vulnerability is actually deployed: weekly install volume shows current usage, and reverse-dependency count shows how many other packages break if it stays unpatched.

21other npm packages depend on this — each one inherits the vulnerability until it's patched upstream
@better-auth/oauth-providernpm
918Kdownloads / week
better-authnpm
7.4Mdownloads / week

Description

Am I affected?

Users are affected if all of the following are true:

  • Their project depends on @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.
  • Their application exposes /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).
  • Their application has not implemented an external mitigation: a load-balancer-level idempotency cache keyed by 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:

  1. Upgrade to @better-auth/[email protected] or later. If developers use the legacy plugin paths from better-auth/plugins, upgrade better-auth to 1.6.11 or later.
  2. If developers cannot upgrade, see workarounds below.

Summary

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.

Details

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 row". The fix lands at the primitive layer rather than at any individual call site.

The fix introduces a claimVerificationByIdentifier primitive at the internal-adapter layer that performs an atomic claim-and-return, replaces the find-then-delete pair at this call site, and migrates the highest-impact variant sites in the same release.

Patches

Fixed in @better-auth/[email protected] and [email protected] for the legacy oidc-provider and mcp plugin paths. All three token-exchange call sites now consume the verification row through internalAdapter.consumeVerificationValue, an atomic claim primitive that deletes the row and returns its prior value in one operation. The first request to arrive takes the row and mints tokens; concurrent racers observe an empty result and return invalid_grant.

Error-code consistency is also tightened on the @better-auth/oauth-provider token endpoint: the malformed-verification-value branches previously returned a project-specific invalid_verification code, which is not part of RFC 6749 §5.2's response error set. Both branches now return invalid_grant so spec-compliant clients can branch on the standard code without a special case.

Workarounds

None of these close the bug fully without a code patch. Upgrading is the only good path.

  • Network-layer: deploy an authorization-server-aware reverse proxy (Envoy, NGINX with Lua, custom Cloudflare Worker) that holds an in-flight registry keyed by the code parameter and serializes concurrent requests for the same code. Fragile under multi-instance deployments unless the registry is shared (Redis-backed).
  • Database-layer: add a SQL or Mongo uniqueness constraint that prevents two oauthAccessToken rows from being created with the same upstream code reference. Adapter-specific and not always feasible since the schema does not currently store the source code.
  • Application-layer: wrap deleteVerificationByIdentifier with a custom hook that uses adapter.deleteMany and surfaces the count, then injects an invalid_grant rejection when the count is zero. Requires forking the internal adapter.

Impact

  • Multiple independent token sets from a single authorization: forked access tokens, refresh tokens, and id tokens issued from the same code, all valid for the original user's authorization scope.
  • Detection bypass: standard OAuth single-use enforcement does not fire for the second redemption when both requests interleave through the read step.
  • Legacy-plugin reach: oidc-provider and mcp plugins share the primitive on the same surface, so deployments using them inherit the same impact.

Credit

Reported by @chdanielmueller.

Resources

Affected Packages

2 total 2 fixed
EcosystemPackageVulnerable rangeFix
📦npm@better-auth/oauth-provider1.6.0&&< 1.6.111.6.11
📦npmbetter-authall versions1.6.11

Detection & mitigation playbook

Open-source dependency
  1. Detect

    Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for @better-auth/oauth-provider. O3's reachability analysis confirms whether the vulnerable code path is actually invoked in your application, so you act on real exposure instead of every transitive match.

  2. Fix

    Update @better-auth/oauth-provider to 1.6.11 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-7w99-5wm4-3g79 is resolved across your whole dependency graph.

  3. Workarounds

    If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.

  4. How O3 protects you

    O3 pinpoints whether GHSA-7w99-5wm4-3g79 is reachable in your code and exactly where to fix it, then blocks exploitation in production at runtime until the patched version is deployed.

Tailored to GHSA-7w99-5wm4-3g79. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.

Frequently Asked Questions

### Am I affected? Users are affected if all of the following are true: - Their project depends on `@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`. - Their application exposes `/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). - Their application has not implemented a
O3 Security · Impact-Aware SCA

Is GHSA-7w99-5wm4-3g79 in your dependencies?

O3 detects GHSA-7w99-5wm4-3g79 across npm dependencies and uses function-level reachability to confirm whether the vulnerable code path is actually reachable — not just present. No false positives.

GHSA-7w99-5wm4-3g79: @better-auth/oauth… | O3 Security