Skip to content

GHSA-9h47-pqcx-hjr4 CVE-2026-67336

Better Auth has insecure cryptographic defaults in oidcProvider: alg=none advertised and plain PKCE accepted by default

Critical npm
Severity
9.4 / 10
Critical · CVSS 4.0
Exploit chance (EPSS)
0.24%
Chance of exploitation in the next 30 days. Higher than 14.67% of scored vulnerabilities.
Known exploited
No
Not in CISA's catalog
Weakness
CWE-1188 Initialization of a Resource with an Insecure Default
CWE-327 Use of a Broken or Risky Cryptographic Algorithm
CWE-757 Selection of Less-Secure Algorithm During Negotiation ('Algorithm Downgrade')
Fix
1.6.11
Upgrade better-auth to this version or later

Description

Am I affected?

Users are affected if all of the following are true:

  • Their application uses better-auth at a version below the patched release.
  • Their application enables oidcProvider() from better-auth/plugins/oidc-provider or mcp() from better-auth/plugins/mcp (the mcp plugin delegates to oidcProvider and inherits both defaults).
  • For the algorithm-negotiation impact: relying parties of the application's OIDC server use a JWT verification library that performs algorithm negotiation from the discovery document without pinning to a specific signing algorithm.
  • For the PKCE impact: the authorization URL is exposed to any party other than the user agent and the application's OP.

If the application only uses @better-auth/oauth-provider (the canonical replacement) and have not enabled the legacy plugins, it is not affected. The new package's discovery document excludes none and its authorize schema rejects plain at parse time.

Fix:

  1. Upgrade to better-auth@1.6.11 or later.
  2. Migrate from the deprecated oidcProvider and mcp plugins to @better-auth/oauth-provider when feasible.
  3. If developers cannot upgrade their applications, see workarounds below.

Summary

The legacy oidcProvider and mcp plugins exhibit two related defects in their OIDC discovery and authorize surfaces.

The discovery document advertises "none" in id_token_signing_alg_values_supported (and, for mcp, in resource_signing_alg_values_supported on the OAuth protected-resource metadata). Any relying party that performs algorithm negotiation from this metadata without pinning to a real signing algorithm may accept unsigned tokens.

PKCE plain is enabled by default. The runtime gate in the authorize handler accepts code_challenge_method=plain under this default, and a missing code_challenge_method parameter is silently downgraded to "plain" before the allowlist check. Discovery advertises code_challenge_methods_supported: ["S256"], contradicting the runtime acceptance of plain. RFC 9700 §2.1.1 (OAuth 2.1) explicitly forbids plain.

Details

The metadata builders unconditionally inject "none" into the alg list. The runtime authorize gate is structured so a buggy client that strips the code_challenge_method parameter still enters the plain code path because the handler rewrites the missing value to "plain" before the allowlist check fires.

@better-auth/oauth-provider (the deprecation target for oidcProvider) is not affected by either defect. The metadata builder uses a JWSAlgorithms type union that structurally excludes "none". The authorize schema is code_challenge_method: z.literal("S256").optional(), which rejects plain at parse time.

Patches

Fixed in better-auth@1.6.11. The legacy oidcProvider and mcp plugins now:

  • Drop "none" from id_token_signing_alg_values_supported (both plugins) and from resource_signing_alg_values_supported (mcp). Discovery no longer advertises the unsigned-token option.
  • Default allowPlainCodeChallengeMethod to false. A request that explicitly passes code_challenge_method=plain is rejected with invalid_request unless the integrator opts in.
  • Reject a code_challenge without an accompanying code_challenge_method instead of silently rewriting the missing value to plain. Clients that send code_challenge must also send code_challenge_method=S256.

Discovery and runtime behavior align on S256 only by default.

Integrators who must keep plain PKCE for legacy clients can restore the previous shape with oidcProvider({ allowPlainCodeChallengeMethod: true }) (and likewise for mcp). With the opt-in set, a request that omits code_challenge_method is treated as plain again, preserving backwards compatibility while keeping the secure default for everyone else. Both legacy plugins are deprecated long-term; the recommended migration is @better-auth/oauth-provider, which never advertised none or accepted plain PKCE.

Workarounds

If developers cannot upgrade their applications immediately:

  • Disable plain PKCE explicitly: set oidcProvider({ allowPlainCodeChallengeMethod: false }) (and the equivalent on mcp). Closes the runtime acceptance of plain even though the silent downgrade still rewrites missing methods.
  • Override the metadata to drop "none" from id_token_signing_alg_values_supported. For oidcProvider, pass metadata: { id_token_signing_alg_values_supported: ["RS256"] }. For mcp, set the same on options.oidcConfig.metadata. Verify by curling the .well-known endpoint.
  • Migrate to @better-auth/oauth-provider: the package is the deprecation target and is unaffected by both defects.

Impact

  • Algorithm-negotiation downgrade: relying parties that read the discovery document without pinning may accept unsigned (alg: "none") tokens.
  • Authorization-code interception: PKCE plain does not protect the authorization code if the URL leaks (Referer headers, browser history, screen capture, proxy logs). PKCE S256 is what protects against that exposure; with plain the protection is absent.
  • OAuth 2.1 / RFC 9700 non-conformance: deployments shipping the defaults are non-compliant with current standards.

Credit

Reported by @subhanUmer.

Resources

Affected versions

npm

PackageVulnerableFixed in
better-auth
< 1.6.11
1.6.11

How to fix

Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.

better-auth npm → 1.6.11

npm
npm install better-auth@1.6.11
yarn
yarn add better-auth@1.6.11
pnpm
pnpm add better-auth@1.6.11

Severity breakdown

CVSS 4.0 9.4

CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:N/SC:H/SI:H/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

Attack vector
Network
Attack complexity
High
Attack requirements
Present
Privileges required
None
User interaction
None
Confidentiality (vulnerable system)
High
Integrity (vulnerable system)
High
Availability (vulnerable system)
None
Confidentiality (subsequent systems)
High
Integrity (subsequent systems)
High
Availability (subsequent systems)
None
Exploit maturity
Not defined
Scope
X

CVSS 3.1 8.7

CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:N

Attack vector
Network
Attack complexity
High
Privileges required
None
User interaction
None
Scope
Changed
Confidentiality
High
Integrity
High
Availability
None

Weakness

CWE-1188: Initialization of a Resource with an Insecure Default

The product initializes or sets a resource with a default that is intended to be changed by the product's installer, administrator, or maintainer, but the default is not secure.

CWE-327: Use of a Broken or Risky Cryptographic Algorithm

The product uses a broken or risky cryptographic algorithm or protocol.

How to prevent it
  • Architecture and Design: When there is a need to store or transmit sensitive data, use strong, up-to-date cryptographic algorithms to encrypt that data. Select a well-vetted algorithm that is currently considered to be strong by experts in the field, and use well-tested implementations. As with all cryptographic mechanisms, the source code should be available for analysis. For example, US government systems require FIPS 140-2 certification [REF-1192]. Do not develop custom or private cryptographic algorithms. They will likely be exposed to attacks that are well-understood by cryptographers. Reverse engineering techniques are mature. If the algorithm can be compromised if attackers find out how it works, then it is especially weak. Periodically ensure that the cryptography has not become obsolete. Some older algorithms, once thought to require a billion years of computing time, can now be broken in days or hours. This includes MD4, MD5, SHA1, DES, and other algorithms that were once regarded as strong. [REF-267]
  • Architecture and Design: Ensure that the design allows one cryptographic algorithm to be replaced with another in the next generation or version. Where possible, use wrappers to make the interfaces uniform. This will make it easier to upgrade to stronger algorithms. With hardware, design the product at the Intellectual Property (IP) level so that one cryptographic algorithm can be replaced with another in the next generation of the hardware product.
  • Architecture and Design: Carefully manage and protect cryptographic keys (see CWE-320). If the keys can be guessed or stolen, then the strength of the cryptography itself is irrelevant.
  • Architecture and Design: Use a vetted library or framework that does not allow this weakness to occur or provides constructs that make this weakness easier to avoid [REF-1482]. Industry-standard implementations will save development time and may be more likely to avoid errors that can occur during implementation of cryptographic algorithms. Consider the ESAPI Encryption feature.
  • Implementation, Architecture and Design: When using industry-approved techniques, use them correctly. Don't cut corners by skipping resource-intensive steps (CWE-325). These steps are often essential for preventing common attacks.

CWE-757: Selection of Less-Secure Algorithm During Negotiation ('Algorithm Downgrade')

A protocol or its implementation supports interaction between multiple actors and allows those actors to negotiate which algorithm should be used as a protection mechanism such as encryption or authentication, but it does not select the strongest algorithm that is available to both parties.

References

Timeline

  1. Published
  2. Last updated by the source
  3. Latest EPSS score
AdvisorySeverityWhy it's related
GHSA-r4xp-prcw-77qf
better-auth: OAuth Proxy lets an attacker sign in as another user
Critical Also affects better-auth
GHSA-965c-763c-88jm
better-auth: OAuth state can be used as a Magic Link to sign in as another user
Critical Also affects better-auth
GHSA-44jh-23m7-hpcf
@better-auth/drizzle-adapter: Concurrent requests can exceed rate limits on PostgreSQL
Medium Also affects better-auth
GHSA-q84f-53jg-9ppm
better-auth: Device login approval does not show the requesting client or permissions
High Also affects better-auth
GHSA-qq9h-g4jm-xgf3
Better Auth: Account takeover via pre-account hijacking on magic-link and email-OTP sign-in
High Also affects better-auth
GHSA-2vg6-77g8-24mp
Better Auth: Stale sessions persist after user deletion across admin, anonymous, and SCIM flows
Medium Also affects better-auth
GHSA-7w99-5wm4-3g79
@better-auth/oauth-provider's OAuth authorization-code grant allows concurrent redemption when two token requests race the find-then-delete primitive
High Also affects better-auth
GHSA-392p-2q2v-4372
Better Auth: OAuth refresh-token rotation forks the token family on concurrent redemption
High Also affects better-auth
Is this in your code?

MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.

Check your repos with MergeHarbor