Vulnerabilities in pkg:npm/@better-auth/oauth-provider

PACKAGE
Advisory Identifier: GHSA-7w99-5wm4-3g79
Advisory Summary

CVE-2026-53518 is a HIGH (CVSS 8.1) @better-auth/oauth-provider's oauth authorization-code grant allows vulnerability (CWE-362: Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition')) affecting @better auth/oauth provider libraries in the npm ecosystem. The issue involves @better-auth/oauth-provider's oauth authorization-code grant allows concurrent redemption when two token requests race the find-then-delete primitive. Remediation is available by upgrading affected packages to version 1.6.11.

4 Known Advisory Record(s)
Package Vulnerability Intelligence

pkg:npm/@better-auth/oauth-provider

4 Known Advisory Record(s)

Vulnerability advisories affecting package pkg:npm/@better-auth/oauth-provider. Select an advisory below to view technical details or fixes.

Advisory ID CVE / NVD Severity Summary
GHSA-7w99-5wm4-3g79 CVE-2026-53518 HIGH @better-auth/oauth-provider's OAuth authorization-code grant allows concurrent redemption when two token requests race the find-then-delete primitive
GHSA-392p-2q2v-4372 CVE-2026-53517 HIGH Better Auth: OAuth refresh-token rotation forks the token family on concurrent redemption
GHSA-p2fr-6hmx-4528 - MODERATE @better-auth/oauth-provider may provide access tokens for unauthorized audiences via unbound resource indicators
GHSA-xr8f-h2gw-9xh6 CVE-2026-41427 HIGH OAuth 2.1 Provider: Unprivileged users can register OAuth clients

CVE-2026-53518 at a Glance

CVE Identifier CVE-2026-53518
GHSA Identifier GHSA-7w99-5wm4-3g79
Severity HIGH
CVSS v3.1 Base Score 8.1
CWE Classification CWE-362: Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition')
Affected Product @better auth/oauth provider
Package Ecosystem npm
Published Date
Last Updated

Security Impact

CVSS: 8.1

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N).

Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required None (PR:N)
User Interaction Required (UI:R)
Scope Unchanged (S:U)
Confidentiality High (C:H)
Integrity High (I:H)
Availability None (A:N)

How to Fix CVE-2026-53518

To remediate this vulnerability, upgrade all affected @better auth/oauth provider dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

@better-auth/oauth-provider fixed: 1.6.11
npm install @better-auth/oauth-provider@1.6.11
better-auth fixed: 1.6.11
npm install better-auth@1.6.11
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

npm ls @better-auth/oauth-provider

How to Check if You Are Affected

To determine whether your Node.js application is affected by CVE-2026-53518, inspect your project dependencies using npm:

npm ls @better-auth/oauth-provider

Analyze the terminal output to locate any version of @better auth/oauth provider in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 1.6.11), your application is affected.

Note: The npm ls @better-auth/oauth-provider command inspects dependencies declared in your package.json and lockfile; it does not perform automated runtime dynamic scanning.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-362: Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition'), where the product contains a concurrent code sequence that requires temporary, exclusive access to a shared resource, but a timing window exists in which the shared resource can be modified by another code sequence operating concurrently. In @better auth/oauth provider, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

### 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/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. 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/oauth-provider@1.6.11` and `better-auth@1.6.11` 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 - [CWE-362: Concurrent Execution using Shared Resource with Improper Synchronization (Race Condition)](https://cwe.mitre.org/data/definitions/362.html) - [CWE-367: Time-of-check Time-of-use (TOCTOU) Race Condition](https://cwe.mitre.org/data/definitions/367.html) - [CWE-294: Authentication Bypass by Capture-replay](https://cwe.mitre.org/data/definitions/294.html) - [RFC 6749 §4.1.2: Authorization Response](https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2) - [OAuth 2.1 §4.1: Authorization Code Grant](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1#section-4.1)

CVE-2026-53518 Affected @better auth/oauth provider Versions

2 component(s)
Fixed Versions:
1.6.11
Product / Package Ecosystem Affected Versions Fixed Version
@better-auth/oauth-provider
npm
≥ 1.6.0
< 1.6.11
1.6.11
better-auth
npm
< 1.6.11
1.6.11

References

Frequently Asked Questions

What is CVE-2026-53518?
CVE-2026-53518 is a security vulnerability in @better auth/oauth provider involving @better-auth/oauth-provider's OAuth authorization-code grant allows concurrent redemption when two token requests race the find-then-delete primitive under CWE-362: Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition').
Which @better auth/oauth provider versions are affected?
Affected versions include releases prior to 1.6.11 across affected packages: @better-auth/oauth-provider, better-auth.
What version fixes CVE-2026-53518?
Upgrading to 1.6.11 (or the latest patched release for your respective branch) resolves the vulnerability.
Is @better-auth/oauth-provider affected?
Yes, @better-auth/oauth-provider is affected in releases prior to 1.6.11. Upgrade your dependency to 1.6.11 or later.
Is better-auth affected?
Yes, better-auth is affected in releases prior to 1.6.11. Upgrade your dependency to 1.6.11 or later.
What is the CVSS score of CVE-2026-53518?
CVE-2026-53518 has a CVSS v3.1 base score of 8.1 (HIGH) with vector string CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N.
What CWE is associated with CVE-2026-53518?
CVE-2026-53518 is classified under CWE-362: Concurrent Execution using Shared Resource with Improper Synchronization ('Race Condition') (The product contains a concurrent code sequence that requires temporary, exclusive access to a shared resource, but a timing window exists in which the shared resource can be modified by another code sequence operating concurrently.).
How can I check npm dependencies for CVE-2026-53518?
Run the dependency tree command: 'npm ls @better-auth/oauth-provider'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-7w99-5wm4-3g79",
  "modified" : "2026-07-20T19:24:51Z",
  "published" : "2026-07-07T20:56:35Z",
  "aliases" : [ "CVE-2026-53518" ],
  "summary" : "@better-auth/oauth-provider's OAuth authorization-code grant allows concurrent redemption when two token requests race the find-then-delete primitive",
  "details" : "### Am I affected?\n\nUsers are affected if all of the following are true:\n\n- 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`.\n- 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).\n- 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.\n\nFix:\n\n1. Upgrade to `@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.\n2. If developers cannot upgrade, see workarounds below.\n\n### Summary\n\nThe 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.\n\n### Details\n\nThe 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.\n\nThe 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.\n\n### Patches\n\nFixed in `@better-auth/oauth-provider@1.6.11` and `better-auth@1.6.11` 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`.\n\nError-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.\n\n### Workarounds\n\nNone of these close the bug fully without a code patch. Upgrading is the only good path.\n\n- **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).\n- **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.\n- **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.\n\n### Impact\n\n- **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.\n- **Detection bypass**: standard OAuth single-use enforcement does not fire for the second redemption when both requests interleave through the read step.\n- **Legacy-plugin reach**: `oidc-provider` and `mcp` plugins share the primitive on the same surface, so deployments using them inherit the same impact.\n\n### Credit\n\nReported by @chdanielmueller.\n\n### Resources\n\n- [CWE-362: Concurrent Execution using Shared Resource with Improper Synchronization (Race Condition)](https://cwe.mitre.org/data/definitions/362.html)\n- [CWE-367: Time-of-check Time-of-use (TOCTOU) Race Condition](https://cwe.mitre.org/data/definitions/367.html)\n- [CWE-294: Authentication Bypass by Capture-replay](https://cwe.mitre.org/data/definitions/294.html)\n- [RFC 6749 §4.1.2: Authorization Response](https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2)\n- [OAuth 2.1 §4.1: Authorization Code Grant](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1#section-4.1)",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:N"
  }, {
    "type" : "CVSS_V4",
    "score" : "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"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "npm",
      "name" : "@better-auth/oauth-provider"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "1.6.0"
      }, {
        "fixed" : "1.6.11"
      } ]
    } ]
  }, {
    "package" : {
      "ecosystem" : "npm",
      "name" : "better-auth"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "1.6.11"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/better-auth/better-auth/security/advisories/GHSA-7w99-5wm4-3g79"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-53518"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/better-auth/better-auth/commit/b4bc65a007784b2eb0efb459e5fa6fd8055d3ec9"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/better-auth/better-auth"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/better-auth/better-auth/releases/tag/v1.6.11"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-294", "CWE-362", "CWE-367" ],
    "severity" : "HIGH",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-07-07T20:56:35Z",
    "nvd_published_at" : "2026-07-15T18:16:48Z"
  }
}