GitHub Advisoryadvisory · npm

GHSA-9h47-pqcx-hjr4 Better Auth has insecure cryptographic defaults in oidcProvider: alg=none advertised and plain PKCE accepted by default

HIGH Published 2026-07-07 Modified 2026-07-07 Official page ↗
Ecosystem
npm
Packages
better-auth
Severity
High
CVSS vector
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:N
Weaknesses
CWE-1188, CWE-327, CWE-757
Reviewed
2026-07-07

Affected packages

• npm better-auth: < 1.6.11 → fixed in 1.6.11

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 `[email protected]` 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 `[email protected]`. 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 • CWE-327: Use of a Broken or Risky Cryptographic Algorithm • CWE-757: Selection of Less-Secure Algorithm During Negotiation • CWE-1188: Insecure Default Initialization of Resource • RFC 9700 §2.1.1: PKCE • RFC 9700 §2.1.2: Token Replay Prevention • RFC 8414 §2: Authorization Server Metadata

Related

Weakness · CWE (3)

  • CWE-1188 Initialization of a Resource with an Insecure Default weakness
  • CWE-327 Use of a Broken or Risky Cryptographic Algorithm weakness
  • CWE-757 Selection of Less-Secure Algorithm During Negotiation weakness

References