Better Auth: OAuth Sign-In State Can Be Replayed as a Magic Link to Hijack Any Account
What happened
A GitHub Security Advisory (GHSA-965c-763c-88jm, tracked as CVE-2026-114607, CVSS 9.1) reports that better-auth versions from 1.4.0-beta.18 up to but not including 1.7.7 can accept an OAuth sign-in state value as a valid Magic Link token. According to the advisory, an attacker can start an OAuth sign-in with any target email address. They then take the returned state value and redeem it as a Magic Link to finish authentication for that address. They never read the victim's inbox and never complete the OAuth callback. The advisory lists the exploit status as public PoC.
Affected setups require all of the following: Magic Link enabled with the default or compatible storeToken and storeIdentifier settings, at least one social or Generic OAuth provider enabled, and account.storeStateStrategy set to "database". Database state is the default when a database or secondary storage is configured. Cookie-backed state is not vulnerable to this path, though the advisory flags a separate OAuth Proxy caveat.
Why it matters
This is an unauthenticated account-takeover issue for applications using better-auth with the affected combination. The attacker needs only a known email address. No credentials, no mailbox access, and no OAuth provider interaction beyond starting the flow. Per the advisory, if Magic Link sign-up is also enabled and no account exists for that email, an attacker can create a new account that Better Auth wrongly marks as email-verified and is signed in to it. That false flag can also bypass Better Auth's default unproven-account protections when the real mailbox owner later signs in, leaving the attacker's session in the account. Any application that trusts verified-email status or email domain for authorization is at higher risk.
Who is affected
- Any npm project depending on
better-authin the vulnerable version range - Deployments with Magic Link plus social or Generic OAuth plus database-backed OAuth state, which is the default when a database or secondary storage is configured
- Multi-server deployments sharing verification storage
What defenders should do now
- Inventory exposure: check whether your
better-authversion falls in1.4.0-beta.18through<1.7.7and whether Magic Link and social or Generic OAuth are both enabled withstoreStateStrategy: "database". - Apply workarounds now. The fix is merged but
1.7.7is not yet released. Setaccount.storeStateStrategyto"cookie"(note the separate OAuth Proxy secret caveat if you use OAuth Proxy), or set Magic Link'sstoreTokento"hashed"while leavingverification.storeIdentifierunset or"plain", or temporarily disable Magic Link or OAuth sign-in. - Hunting angle: look for OAuth sign-in starts followed quickly by Magic Link verification completions for the same account or email, especially where no matching OAuth callback from the provider occurred.
- Post-upgrade hygiene: once patched, request new Magic Links and restart pending OAuth or SAML sign-ins. The advisory states that links and state created before the upgrade cannot complete. Review accounts marked email-verified via Magic Link or OAuth flows during the exposure window for signs of illegitimate creation or takeover.
- Note:
disableSignUp: trueon the Magic Link plugin is called out as insufficient. It blocks new verified-account creation but does not stop takeover of existing accounts.
Developing intel
This is a same-day advisory. The fix is merged but not yet shipped in a released better-auth package, so details, patch availability, and workaround guidance may change. Track the original advisory for the latest status: GHSA-965c-763c-88jm.