Skip to main content

better-auth CVE-2026-53512

| EUVDEUVD-2026-44735 CRITICAL
Improper Authentication (CWE-287)
2026-07-07 https://github.com/better-auth/better-auth GHSA-pw9m-5jxm-xr6h
9.1
CVSS 4.0 · Vendor: https://github.com/better-auth/better-auth
View

Attack ChainAIDerived

Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain

  1. Access
    Obtain leaked refresh_token via log/XSS/DB/CORS
  2. Delivery
    Recover public client_id
  3. Exploit
    POST refresh_token grant to token endpoint
  4. Execution
    Endpoint skips client_secret verification
  5. Persist
    Mint access token plus rotated refresh_token
  6. Impact
    Access resources at victim's authorized scope

Vulnerability Assessment

Weakness
CWE
OWASP Top 10:2025
A07:2025 - Authentication Failures
ASVS 5.0
V6 - Authentication Authentication mechanism validation
MITRE ATT&CK

2 techniques across 2 tactics: Defense Evasion, Initial Access

The technique list with ATT&CK links and the kill-chain view is part of the Basic plan and up.

Exploitation Conditions

The target application must run better-auth below 1.6.11 with the oidcProvider() or mcp() plugin enabled AND have at least one confidential OAuth client registered (oauthApplication type "web", "native", or "user-agent-based", or a trustedClients entry not marked type "public"). The attacker must first obtain two items: a valid refresh_token for that client (via database read, log capture, browser-side XSS, or CORS-amplified script against the mcp endpoint) and the client's public client_id. What LIMITS exploitation: public clients using PKCE are entirely unaffected (no client_secret exists to skip); the attacker cannot exploit the flaw without an already-leaked refresh_token, so the vulnerability depends on a separate token-disclosure vector rather than being directly reachable against a default install; and applications using only @better-auth/oauth-provider (without mcp) are immune. The mcp /mcp/token endpoint's unconditional Access-Control-Allow-Origin: * materially lowers the bar in browser contexts by enabling cross-origin script to reach the endpoint.

Impact

The supplied CVSS 3.1 vector (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N, base 9.1) treats this as trivially exploitable over the network with no privileges, but that overstates real-world ease: the attack is only reachable once the adversary already possesses a valid refresh_token, obtained out-of-band via database read, log capture, browser XSS, or the mcp CORS wildcard. That precondition is not attacker-controllable by default, which in my independent assessment justifies AC:H and lowers effective severity to the mid-7 range. No EPSS score, no CISA KEV listing, and no POC were provided, so there is no evidence of active exploitation - treat this as a real but conditional priority, not a wormable internet-wide threat. It becomes genuinely high-priority where the additional token-leak vector exists - notably mcp deployments whose wildcard CORS actively enables browser-side theft, or environments with app-layer XSS or exposed logs. Confidentiality and integrity impact are high (full impersonation at the victim's scope); availability is unaffected (A:N).

Exploit ScenarioDerived

An attacker captures a confidential client's refresh_token from an application access log (or, for an mcp deployment, exfiltrates it via injected browser script abetted by the endpoint's wildcard CORS) along with the non-secret client_id. They POST a refresh_token grant to /api/auth/oauth2/token or /api/auth/mcp/token without supplying the client_secret; the endpoint mints a new access token and a rotated refresh token. Repeating on each rotation keeps the chain alive indefinitely, granting the attacker resource access at the original user's authorized scope until the token row is revoked. No POC was published at time of analysis.

Act Now Prioritize within 24-48h. Why: CRITICAL severity.
Patch available 0.3% exploitation probability (EPSS)

Get an e-mail when this CVE is exploited, patched or re-scored. Free account.

Alert me

AnalysisAIv1 – Jul 07, 2026

Confidential-client impersonation in better-auth below 1.6.11 lets an attacker who holds any leaked refresh_token and the public client_id mint fresh access tokens and rotated refresh tokens indefinitely. The deprecated oidcProvider() and mcp() plugins expose an OAuth 2.0 token endpoint whose refresh_token grant authenticates solely on the bound refresh-token row plus a matching client_id, never verifying the registered client_secret - a regression, since the same plugins' authorization_code grant does enforce the secret. No public exploit identified at time of analysis; the flaw was reported by @subhanUmer and fixed by the maintainers, with no CISA KEV listing or EPSS score supplied in the input.

RemediationAI

Vendor-released patch: better-auth@1.6.11 - upgrade to 1.6.11 or later, which requires client_secret on the refresh_token grant for confidential clients using the same constant-time comparison the authorization_code grant already used, routes the mcp authorization_code grant through verifyStoredClientSecret (honoring storeClientSecret), fixes the RFC 6749 §2.3.1 Authorization: Basic parser, and removes the wildcard CORS header from /mcp/token (per https://github.com/better-auth/better-auth/releases/tag/v1.6.11 and advisory GHSA-pw9m-5jxm-xr6h). Where feasible, migrate from the deprecated oidcProvider() to @better-auth/oauth-provider, which enforces client authentication on both grants by default. If you cannot upgrade, effective workarounds each carry trade-offs: force every client to type "public" plus mandatory PKCE (removes the client_secret the bug fails to verify, but drops confidential-client semantics); restrict /api/auth/oauth2/token and /api/auth/mcp/token at the load balancer to known client IPs (works for server-to-server flows, not end-user-device clients); for mcp specifically, strip the wildcard Access-Control-Allow-Origin at an upstream proxy and replace it with a tight allowlist; and on any suspicion of leak, invalidate all refresh tokens for the affected client via db.deleteMany({ model: "oauthAccessToken", where: [{ field: "clientId", value: <id> }] }). None of the workarounds fully close the flaw without the code patch.

Affected VersionsENISA

better-auth < 1.6.11

Affected Packages

npm better-auth fix: 1.6.11

Source: OSV (Open Source Vulnerabilities)

Affects your stack

Affects 1 package in npm

Checking this CVE against your own stacks and servers is part of the Basic plan and up.

Technical ContextAI · show details

better-auth is a TypeScript/JavaScript authentication framework distributed on npm (pkg:npm/better-auth). The affected surface is its OAuth 2.0 / OIDC authorization-server functionality provided by two legacy plugins: oidcProvider() (better-auth/plugins/oidc-provider) and mcp() (better-auth/plugins/mcp), each of which stands up a token endpoint (/api/auth/oauth2/token and /api/auth/mcp/token respectively). The root cause maps to CWE-287 (Improper Authentication), and more precisely CWE-306 (Missing Authentication for Critical Function): RFC 6749 §6 and OAuth 2.1 §4.3 require confidential clients to authenticate to the token endpoint on every grant including refresh, but these plugins omit the client_secret check on the refresh path while enforcing it (via verifyStoredClientSecret in oidc-provider and raw equality in mcp) on the authorization_code path. Token rotation issues a new refresh_token per call and resets refreshTokenExpiresAt (default 7 days), so a single leak yields an indefinitely renewable session. Two adjacent mcp defects ship in the same patch: the mcp authorization_code grant used raw === comparison and ignored storeClientSecret ("encrypted"|"hashed"), and the /mcp/token endpoint returned Access-Control-Allow-Origin: * unconditionally, which lets browser-side script (XSS/CORS) reach the endpoint and amplify the bypass. The newer @better-auth/oauth-provider package routes both grants through validateClientCredentials and is not affected.

Know when Canonical gets its next vulnerability

  • Watch products and get an email when something new is published
  • Check this CVE against your own stack and servers
  • Exploit and PoC references, threat intelligence from 22 sources

No credit card · 7-day full trial

Severity by source

Vendor (https://github.com/better-auth/better-auth) PRIMARY 9.1 CRITICAL
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N
vuln.today AI 7.4 HIGH

AV:N and PR:N because the token endpoint needs no client credentials, but AC:H because exploitation depends on separately obtaining a leaked refresh_token, a precondition outside attacker control; C/I high, A:N.

3.1 AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N
4.0 AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:N

Primary rating from Vendor (https://github.com/better-auth/better-auth).

CVSS VectorVendor: https://github.com/better-auth/better-auth

Attack Vector
Network
Attack Complexity
Low
Attack Requirements
Present
Privileges Required
None
User Interaction
None
Vulnerable system: Confidentiality
High
Vulnerable system: Integrity
High
Vulnerable system: Availability
None
Subsequent system: Confidentiality
None
Subsequent system: Integrity
None
Subsequent system: Availability
None

DescriptionCVE.org

Am I affected?

Users are affected if all of the following are true:

  • Their application uses better-auth and has enabled at least one of: oidcProvider() (imported from better-auth/plugins/oidc-provider), or mcp() (imported from better-auth/plugins/mcp).
  • Their application has at least one confidential OAuth client registered (any client with type: "web" | "native" | "user-agent-based" in the oauthApplication table, or any trustedClients entry without type: "public"). Public clients with PKCE are not affected.
  • Their application uses better-auth at a version below the patched release.

If an application only uses @better-auth/oauth-provider (the canonical replacement for oidc-provider) and the mcp plugin is not enabled, it is not affected.

Fix:

  1. Upgrade to better-auth@1.6.11 or later.
  2. Migrate from the deprecated oidcProvider() to @better-auth/oauth-provider when feasible. The new package enforces client authentication on both grants by default.
  3. If developers cannot upgrade their applications, see workarounds below.

Summary

The legacy oidcProvider and mcp plugins each expose an OAuth 2.0 token endpoint whose refresh_token grant authenticates the request entirely on possession of the bound refreshToken row and a matching client_id. Neither plugin verifies the registered confidential client's client_secret on the refresh path. An attacker who obtains any valid refresh_token (via database read, log capture, browser-side XSS, or CORS-amplified script in the mcp case) and the public client_id can mint fresh access tokens and rotated refresh tokens until the chain is revoked.

Details

RFC 6749 §6 and OAuth 2.1 §4.3 require confidential clients to authenticate to the token endpoint on every grant, including refresh. The same plugins' authorization_code grant correctly enforces client_secret (the oidc-provider via verifyStoredClientSecret, the mcp plugin via raw equality), which proves the omission on the refresh path is a regression rather than a design choice.

Token rotation issues a new refresh_token with each call, so a single leaked refresh-token grants indefinite access until the row is revoked or its refreshTokenExpiresAt (default 7 days) passes; rotation refreshes that window each call.

Two adjacent issues on the mcp surface ship in the same patch. The mcp authorization_code grant uses raw === for client-secret comparison and ignores the storeClientSecret: "encrypted" | "hashed" configuration; the fix routes both grants through verifyStoredClientSecret. The mcp /mcp/token endpoint sets Access-Control-Allow-Origin: * unconditionally, which amplifies the refresh bypass in browser contexts; the fix narrows the CORS allowlist.

The newer @better-auth/oauth-provider package routes both grants through validateClientCredentials and is not affected.

Patches

Fixed in better-auth@1.6.11. The legacy oidcProvider and mcp token endpoints now require client_secret on the refresh_token grant for confidential clients, using the same constant-time comparison the authorization_code grant already used. Public clients are unaffected (they have no secret to enforce, and PKCE substitutes on the auth-code grant).

The Authorization: Basic parser is fixed to follow RFC 6749 §2.3.1: the credential is split on the first colon and each half is percent-decoded. Client IDs and secrets that contain reserved characters now authenticate correctly. The /mcp/token endpoint's CORS configuration is narrowed in the same change (the wildcard Access-Control-Allow-Origin: * header is removed), matching the standalone @better-auth/oauth-provider package.

The deprecated oidc-provider plugin remains deprecated. The recommended migration path is @better-auth/oauth-provider.

Workarounds

None of these close the bug fully without a code patch.

  • Migrate to @better-auth/oauth-provider if your deployment can adopt the new plugin. It enforces client_secret on both grants.
  • Force all clients to public + PKCE: set every client's type: "public" and require PKCE. The bug is unreachable when there is no client_secret to verify.
  • Network-layer ingress restriction: limit /api/auth/oauth2/token and /api/auth/mcp/token to known client IPs at the load balancer. Practical for server-to-server flows, not for end-user-device clients.
  • Out-of-band refresh-token rotation: on any suspicion of leak, run db.deleteMany({ model: "oauthAccessToken", where: [{ field: "clientId", value: <id> }] }) to invalidate all refresh tokens for the affected client.
  • For the mcp endpoint specifically: drop the wildcard CORS at an upstream proxy and replace with a tight allowlist.

Impact

  • Indefinite confidential-client impersonation: an attacker holding any valid refresh_token and the public client_id can mint access tokens and rotated refresh tokens indefinitely, until the row is revoked. Rotation refreshes the expiration window each call.
  • Resource access at the user's authorized scope: every minted access token carries the original user's authorization scope, so the attacker reads or writes whatever the resource server grants for that scope.

Credit

Reported by @subhanUmer.

Resources

Blast Radius

ecosystem impact
† from your stack dependencies † transitive graph · vuln.today resolves 4-path depth
  • 34 npm packages depend on better-auth (21 direct, 13 indirect)

Ecosystem-wide dependent count for version 1.6.11.

Executive ViewAI

Why It Matters

CVE-2026-53512 in better-auth is a CRITICAL authentication vulnerability that allows attackers with exposed refresh credentials to gain persistent unauthorized access to user accounts across all applications using affected versions. Organizations running better-auth below version 1.6.11 with OAuth plugins face significant risk of credential compromise and user impersonation, potentially leading to data breach and compliance violations if refresh tokens are exposed. Immediate patching to version 1.6.11 or disabling the deprecated plugins is required to prevent identity-based attacks.

Business Impact: Severe
Compensating Controls

Apply vendor patch: upgrade to better-auth version 1.6.11 or later. If immediate upgrade is not possible, disable oidcProvider() and mcp() plugins; implement refresh token rotation with 24-hour maximum expiration; enable rate limiting on token refresh endpoints; and implement additional client secret validation at the application layer.

Threat Intelligence Overview

EPSS Score
0.3%
Top 78% exploitable
Prevalence
Medium
popular open-source npm auth library, but not ubiquitous like OpenSSL/nginx
Threat Intel
1
1 source
Social Media
1
Sightings
Data Quality
7 sources
Confidence: High
EUVD NVD CVE.org EPSS Vulnrichment GHSA CIRCL GitHub VulDB

How this decision was reached

The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.

SignalValue
Severity CRITICAL
CISA KEV No
Public POC No
EPSS 0.3%
Patch available Yes
Active threat No
Deciding rule
CRITICAL severity
What every action level means
Emergency Immediate action required. Criteria: CRITICAL + in CISA KEV (actively exploited) without patch, OR CRITICAL + EPSS > 30% without patch, OR CRITICAL + active threat signals without patch.
Act Now Prioritize within 24-48h. Criteria: in KEV with patch available, OR CRITICAL + public POC, OR HIGH + EPSS > 10%, OR CRITICAL + active threat, OR any CRITICAL severity.
This Week Schedule remediation this week. Criteria: HIGH severity (CVSS 7.0-8.9). Patch within normal change window.
This Month Plan within 30 days. Criteria: MEDIUM severity (CVSS 4.0-6.9), lower exploitation probability. Include in regular patch cycle.
Monitor Track and reassess. Criteria: LOW severity (CVSS 0.1-3.9), minimal exploitation risk. No immediate action needed.

SSVC AssessmentENISA

SSVC Decision
Out-of-Cycle
Exploitation
None
System Exposure· derived
Open
Automatable
Yes
Human Impact
Very High
Technical Impact
Total

Priority Score

46
Low Medium High Critical
KEV: 0
EPSS: 0
CVSS: +46
PoC: 0
Weaponized: 0
Threat intel: 0
Gov advisory: 0

Attack Technique

Cross-Site Scripting occurs when an application accepts untrusted data and sends it to a web browser without proper validation or encoding.

Authentication bypass attacks exploit flaws in the verification mechanisms that control access to systems and applications.

CVE-2011-3544 CRITICAL POC
9.8 Oct 19

Oracle Java SE JDK/JRE 7 and 6 Update 27 and earlier allows remote code execution with complete system compromise throug

CVE-2019-11043 CRITICAL POC
9.8 Oct 28

In PHP versions 7.1.x below 7.1.33, 7.2.x below 7.2.24 and 7.3.x below 7.3.11 in certain configurations of FPM setup it

CVE-2014-6271 CRITICAL POC
9.8 Sep 24

GNU Bash through 4.3 processes trailing strings after function definitions in the values of environment variables, which

CVE-2013-0422 CRITICAL POC
9.8 Jan 10

Multiple vulnerabilities in Oracle Java 7 before Update 11 allow remote attackers to execute arbitrary code by (1) using

CVE-2016-3714 HIGH POC
8.4 May 05

The (1) EPHEMERAL, (2) HTTPS, (3) MVG, (4) MSL, (5) TEXT, (6) SHOW, (7) WIN, and (8) PLT coders in ImageMagick before 6.

CVE-2017-12617 HIGH POC
8.1 Oct 04

When running Apache Tomcat versions 9.0.0.M1 to 9.0.0, 8.5.0 to 8.5.22, 8.0.0.RC1 to 8.0.46 and 7.0.0 to 7.0.81 with HTT

CVE-2016-8735 CRITICAL POC
9.8 Apr 06

Remote code execution is possible with Apache Tomcat before 6.0.48, 7.x before 7.0.73, 8.x before 8.0.39, 8.5.x before 8

CVE-2014-7169 CRITICAL POC
9.8 Sep 25

GNU Bash through 4.3 bash43-025 processes trailing strings after certain malformed function definitions in the values of

CVE-2014-0160 HIGH POC
7.5 Apr 07

The (1) TLS and (2) DTLS implementations in OpenSSL 1.0.1 before 1.0.1g do not properly handle Heartbeat Extension packe

CVE-2016-5195 HIGH POC
7.0 Nov 10

Race condition in mm/gup.c in the Linux kernel 2.x through 4.x before 4.8.3 allows local users to gain privileges by lev

CVE-2013-2423 LOW POC
3.7 Apr 17

Unspecified vulnerability in the Java Runtime Environment (JRE) component in Oracle Java SE 7 Update 17 and earlier, and

CVE-2023-4911 HIGH POC
7.8 Oct 03

Local privilege escalation in the GNU C Library (glibc) dynamic loader ld.so allows unprivileged local users on affected

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy