---
id: "GHSA-r4xp-prcw-77qf"
kind: "vulnerability"
aliases: ["GHSA-r4xp-prcw-77qf"]
title: "better-auth: OAuth Proxy lets an attacker sign in as another user"
severity: "high"
cvss: 7.4
epss: null
exploited: false
status: "open"
category: "package"
products: [{"name": "better-auth", "fixed": ["1.7.7"], "affected": ">= 1.5.0-beta.12, < 1.7.7", "ecosystem": "npm"}]
ecosystems: ["npm"]
tags: ["better-auth", "cwe-287", "cwe-345", "node", "oauth-proxy", "package", "self-hosted"]
not_affected_if: ["database-state-storage", "default-config", "distinct-proxy-secret", "pre-1.5.0-beta.12"]
published_at: "2026-10-27T15:32:16+00:00"
first_seen_at: "2026-10-27T15:34:04+00:00"
updated_at: "2026-10-27T15:35:43+00:00"
sources: [{"source": "ghsa", "url": "https://github.com/advisories/GHSA-r4xp-prcw-77qf", "first_seen_at": "2026-10-27T15:34:04+00:00"}]
---

# better-auth: OAuth Proxy lets an attacker sign in as another user

The OAuth Proxy plugin in affected better-auth releases trusts any user profile it can decrypt, and when the proxy secret matches the global Better Auth secret and sign-in state is stored in cookies, anyone can build a fake profile for a victim's email. The attacker then signs in as that user without an account or victim action.

_Summary, check and fix written by a model from the sources; labelled as such._

## Affected

- npm:better-auth affected >= 1.5.0-beta.12, < 1.7.7 fixed 1.7.7

## How to check

- Run `npm ls better-auth` and confirm the installed version is below `1.7.7` and at least `1.5.0-beta.12`.
- Check whether the OAuth Proxy plugin is configured and whether its `secret` is unset or equal to the global Better Auth secret.
- Check whether `account.storeStateStrategy` is set to `"cookie"`, and whether implicit account linking is enabled (the default).

## Fix

- Upgrade `better-auth` to `1.7.7` once released, across every node and environment in the proxy flow, including preview and development deployments.
- Until then, set a distinct strong OAuth Proxy `secret` in every participating environment, or set `account.storeStateStrategy: "database"`, or remove the OAuth Proxy plugin.
- Restart pending sign-in and account-linking flows after upgrading, since pre-upgrade state cookies are rejected.

## Timeline

- 2026-10-27T15:34:04+00:00 ghsa https://github.com/advisories/GHSA-r4xp-prcw-77qf

## From the sources

## 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.

---

_Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai_
