---
title: "better-auth before 1.7.7: an email is enough to take over an account"
description: "A critical better-auth flaw lets an attacker who knows a user's email open a session in their account when Magic Link and OAuth sign-in are both on. Fixed in 1.7.7, with interim config workarounds."
url: https://ihsanalapsi.dev/articles/better-auth-magic-link-takeover
lang: en
date: 2026-10-29
updated: 2026-10-29
tags: ["App security", "Web development", "Security updates"]
author: Ihsan Alapsi (إحسان العبسي)
---
# better-auth before 1.7.7: an email is enough to take over an account

If your project uses better-auth with email sign-in links (Magic Link) and social sign-in or any OAuth provider, an attacker who knew a user's email could open a session in their account. Here you will find out whether your configuration is affected, and how to fix it today.

## What is the better-auth flaw?

The flaw is that better-auth accepted an OAuth sign-in state value as if it were a Magic Link token. GitHub's advisory lists it as CVE-2026-114607 and rates it critical, 9.1 on CVSS 3.1.

The state is the value that ties the start of an OAuth sign-in to its callback. The Magic Link token is the one sent by email that opens the session. So the library accepted a value anyone can request in place of a token that should only reach the account owner's mailbox.

The original advisory was published in the better-auth repository on September 30, 2026, and the fix shipped in 1.7.7. On October 27, 2026 advisory GHSA-965c-763c-88jm was added to the GitHub Advisory Database after review.

## How does an attacker get in with just an email?

The attacker needs no mailbox access, no OAuth provider sign-in and no OAuth callback. According to the advisory, the attack goes like this:

1. Start an OAuth sign-in for any email address they choose.
2. Take the state value that comes back.
3. Use it to complete a Magic Link sign-in for the same address, which gives a normal session with that account's permissions.

If Magic Link sign-up is enabled and no account exists for the address, the attacker can create a new account wrongly marked as email verified. The advisory says that false flag can also bypass the library's default protections for unproven accounts when the real owner signs in later.

## Is my project affected?

Your project is affected if your version is from 1.4.0-beta.18 through versions before 1.7.7, and all three of these are true:

1. Magic Link is enabled with the default `storeToken: "plain"`, or with `storeToken: "hashed"` and the global `verification.storeIdentifier: "hashed"`.
2. At least one built-in social provider or a Generic OAuth provider is enabled.
3. OAuth state is stored in the database (`account.storeStateStrategy: "database"`), which is the default when a database or secondary storage is configured.

In other words, a simple setup: a database, Magic Link and a social sign-in button. If forged tokens are on your mind, we covered [the Postiz flaw that lets a user take over a whole organization](/articles/postiz-forged-token-takeover).

## How do you fix it?

Update better-auth to 1.7.7, together with its adapters, plugins and integrations. The release notes say the version fixed a critical Magic Link account-takeover vulnerability.

Mind these points from the advisory before you deploy:

- If several servers share the same verification storage, upgrade them all in the same cutover.
- Magic Links and OAuth or SAML states created before the upgrade cannot complete, so users request new ones.
- If you have custom `verification.storeIdentifier.overrides` rules, update them to match the `magic-link:` and `auth-state:` prefixes.
- No user or account data migration is needed.

If your project is built on Next.js, also see [the Next.js security update we covered](/articles/nextjs-16-4-1-security-update) if you have not updated yet.

## What if you can't update now?

The advisory gives three interim workarounds until you upgrade:

1. Keep the state in a cookie instead of the database. If you use OAuth Proxy, first set a dedicated OAuth Proxy secret across all environments, different from each environment's global secret.
2. Set the Magic Link plugin's `storeToken` to `"hashed"`, and leave the global `verification.storeIdentifier` unset or `"plain"`.
3. Disable Magic Link sign-in, or disable social and Generic OAuth sign-in.

```typescript
export const auth = betterAuth({
  account: {
    storeStateStrategy: "cookie",
  },
});
```

Setting `disableSignUp: true` on the Magic Link plugin is not enough: it only blocks new accounts, not the takeover of an existing one.

## Where do you start now?

Start with your version and configuration, since they decide whether your project is exposed at all.

1. Run `npm ls better-auth` and check your version.
2. Open your `betterAuth` configuration and check the three conditions above.
3. Update to 1.7.7 with all plugins, or apply a workaround today and update later.
4. Tell your users that old sign-in links no longer work.

If you want someone to test your platform's sign-in flow the way an attacker would, that is the job of [Muhassan](/muhassan).

All our app security articles are in one place: [app security](/articles?tag=app-security).

## FAQ

**Does the attacker need access to the victim's mailbox?**

No. According to the advisory, knowing the email address is enough. The attacker needs no mailbox access, no OAuth provider sign-in and no OAuth callback.

**If I set disableSignUp, am I safe?**

No. The advisory says disableSignUp only blocks new verified accounts. It does not stop the takeover of existing accounts.

**What happens to old Magic Links after the update?**

Magic Links and OAuth or SAML states created before the upgrade cannot complete, so users request new ones. No user or account data migration is needed.

**Which versions are affected?**

From 1.4.0-beta.18 through versions before 1.7.7. Version 1.7.7 has the fix.
