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
Share

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/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
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
Privileges Required
None
User Interaction
None
Scope
X

Lifecycle Timeline

1
Analysis Generated
Jul 07, 2026 - 20:51 vuln.today

Blast Radius

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

Ecosystem-wide dependent count for version 1.6.11.

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

AnalysisAI

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.

Technical ContextAI

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.

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.

CVE-2012-0217 HIGH POC
7.2 Jun 12

The x86-64 kernel system-call functionality in Xen 4.1.2 and earlier, as used in Citrix XenServer 6.0.2 and earlier and

CVE-2026-33309 CRITICAL POC
9.9 Mar 19

An authenticated path traversal vulnerability in Langflow's file upload functionality allows attackers to write arbitrar

CVE-2026-48753 CRITICAL POC
9.9 Jun 26

Arbitrary host file write in Incus before 7.1.0 lets a holder of S3 bucket credentials escape the storage volume via a p

CVE-2019-7304 CRITICAL POC
9.8 Apr 23

Canonical snapd before version 2.37.1 incorrectly performed socket owner validation, allowing an attacker to run arbitra

CVE-2026-33186 CRITICAL POC
9.1 Mar 18

An authorization bypass vulnerability in gRPC-Go allows attackers to circumvent path-based access control by sending HTT

CVE-2026-53727 HIGH POC
8.9 Jul 09

Server-side request forgery in the Ruby css_parser gem (< 3.0.0) lets a remote unauthenticated attacker force the parsin

CVE-2026-50180 HIGH POC
8.7 Jul 02

Arbitrary file read in Langroid's SQLChatAgent (<= 0.63.0) lets an attacker who can influence the LLM-generated SQL exfi

CVE-2026-49852 HIGH POC
8.7 Jul 02

Authentication bypass in the joserfc Python JOSE/JWT library (PyPI, versions <= 1.6.7) lets unauthenticated attackers fo

CVE-2026-55245 HIGH POC
8.7 Aug 28

SSRF in the Bifrost AI gateway's multimodal URL-fetch path permits any client that can submit a Bedrock or Vertex provid

CVE-2026-52776 HIGH POC
8.6 Aug 12

URLSecurityValidator SSRF bypass in compliance-trestle <= 4.0.3 allows a network-positioned attacker to reach cloud meta

CVE-2026-71309 HIGH POC
8.6 Aug 05

Path traversal in rclone's `serve restic` REST API allows any attacker with network access to the endpoint to read, crea

CVE-2026-48595 HIGH POC
8.2 Jun 02

Credential leakage in elixir-tesla (Tesla HTTP client for Elixir) versions 1.4.0 through 1.18.2 allows Authorization and

Share

CVE-2026-53512 vulnerability details – vuln.today

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