better-auth: OAuth Proxy lets an attacker sign in as another user
CVE-2026-110864 · GHSA-r4xp-prcw-77qf · published 14 Oct 2026
How serious is it
- Severity: high, CVSS score 7.4 out of 10.
- No exploit prediction (EPSS) score yet.
- Not on the CISA list of exploited vulnerabilities.
- No public exploit in Exploit-DB.
Is your app affected by CVE-2026-110864?
Paste this prompt into Claude Code, Cursor or any AI coding tool: it runs a free Shippable scan of your project and fixes what it finds. Built with Lovable, Bolt or v0? Scan your live app. Or upload your package file.
Who is affected
better-auth(npm) >= 1.5.0-beta.12, < 1.7.7
How to check
- Run
npm ls better-authin your project to see the installed version, including copies pulled in by other packages. - Or paste your lockfile into the checker: it lists every package with a known vulnerability, this one included.
- Or run
npx shippable-dev scan localhost:3000in your project folder. It reads your lockfile and flags this vulnerability along with every other known one.
How to fix
Copies a prompt for Claude Code, Cursor or Codex: it checks your lockfiles for this and upgrades it.
- Upgrade
better-authto 1.7.7 or later, commit the lockfile and redeploy. - If a vulnerable version was live, check your logs and rotate the secrets that server could read.
The advisory
## Am I affected?
You may be affected if you use `better-auth` from 1.5.0-beta.12 through versions before 1.7.7 with the OAuth Proxy plugin. You are affected only if **all** of these are also true:
- On a participating environment, the OAuth Proxy plugin has no dedicated `secret`, or its `secret` equals that environment's global Better Auth secret.
- Sign-in state is stored in a cookie (`account.storeStateStrategy: "cookie"`). With a database or secondary storage, this is only the case if you chose it explicitly.
- Implicit account linking is on (the default).
You are not affected by this path if every participating environment uses a proxy `secret` distinct from its global Better Auth secret, if sign-in state is stored in the database, or if you use a version before 1.5.0-beta.12.
## Summary
The OAuth Proxy plugin trusts any user profile it can decrypt. In the affected setup, Better Auth encrypts sign-in state, including data the caller chose, with the same secret and hands it to anyone who starts a social sign-in. An attacker can use this to get a fake profile for another user's email address that the proxy accepts as real.
## Impact
An attacker without an account who knows a user's verified email address can sign in as that user, with that user's permissions. They can read private data, change the account, and link a provider account of their choice to it. They need no provider sign-in and no action from the victim. No availability impact is established, and a reported open redirect was not confirmed.
## Patches
The fix is merged for the planned `better-auth` 1.7.7 release. That package is not available yet.
Upgrade all nodes handling the same cookie-backed OAuth or SAML state flow and all OAuth Proxy participants together, including the production coordinator and preview or development deployments. Keep installed Better Auth packages released alongside `better-auth` on the same release version. Restart pending sign-in and account-linking flows; pre-upgrade state cookies and proxy payloads are rejected. Public options and API signatures are unchanged.
## Workarounds
Until you upgrade to the fixed release, apply one of these:
- Set one strong OAuth Proxy `secret` across every environment that takes part in the proxy flow, and keep it different from each environment's global Better Auth secret.
- Set `account.storeStateStrategy: "database"`. This is already the default when a database or secondary storage is configured. If Magic Link is also enabled, switching can expose a separate cross-purpose verification-record path in affected releases. Ensure the two flows resolve to distinct stored identifiers; with built-in options, use Magic Link `storeToken: "hashed"` with global `verification.storeIdentifier` unset or `"plain"`, or disable Magic Link or both built-in social and Generic OAuth sign-in.
- Remove the OAuth Proxy plugin if you do not need it.
Setting `account.accountLinking.disableImplicitLinking: true` stops this path from taking over an existing account. It does not fix the underlying flaw: the callback can still accept attacker-made data as a provider identity. Apply one of the steps above as well.
Check your whole app
Dependencies are only half of it. Free and read-only: before you push, run npx shippable-dev scan localhost:3000 in your project folder, no account needed. For your live app, you prove it's yours with a one-line tag first.
References
- github.com/better-auth/better-auth/security/advisories/GHSA-r4xp-prcw-77qf
- www.vulncheck.com/advisories/better-auth-1.5.0-beta.12-before-1.7.7-authentication-bypass-via-oauth-proxy
- github.com/better-auth/better-auth/pull/11494
- github.com/better-auth/better-auth/commit/ac54bfd67821a7607533af2059858b12bc54e26a
- github.com/better-auth/better-auth/releases/tag/v1.7.7
- github.com/advisories/GHSA-r4xp-prcw-77qf
- github.com/better-auth/better-auth
Merged from NVD, GitHub Advisory Database, OSV. Last updated 28 Oct 2026. All vulnerabilities · RSS · JSON