CVE-2026-35511: Authorizer Zero-Click Account Takeover via OAuth Identity Linking
Authorizer's OAuth callback handler links a Google or GitHub login to any existing account with a matching email address, even if that account's email was never verified, letting an attacker who…

The problem
In internal/http_handlers/oauth_callback.go, when an OAuth login arrives for an email that already exists in the database, Authorizer fetches the existing account and merges the OAuth identity into it unconditionally. It never checks whether the existing account's EmailVerifiedAt field is set before performing the merge.
The result is a pre-account-hijacking primitive. An attacker registers with a victim's email and a known password but never verifies it. When the real user later logs in via Google or GitHub (a normal action), the callback silently links Google to the attacker's account, verifies the email, and leaves the attacker's password intact and valid.
The attacker then logs in with the original password and has full access to the victim's session and data.
Proof of concept
A working proof-of-concept for CVE-2026-35511 in github.com/authorizerdev/authorizer, with the exact payload below.
# Step 1: attacker pre-registers with victim's email (no email verification clicked)
POST /graphql
Content-Type: application/json
{"query":"mutation { signup(params: { email: \"victim@company.com\", password: \"Attacker1!\", confirm_password: \"Attacker1!\" }) { message } }"}
# Step 2: victim logs in normally via Google OAuth (zero attacker interaction)
# GET /oauth_callback?provider=google&code=<google-auth-code>
# Authorizer finds the existing (unverified) account, merges Google into it,
# sets EmailVerifiedAt = now, and never clears the attacker's password.
# Step 3: attacker logs in with the password they originally set
POST /graphql
Content-Type: application/json
{"query":"mutation { login(params: { email: \"victim@company.com\", password: \"Attacker1!\" }) { access_token user { id email } } }"}The root cause is a missing verification gate in the account-linking branch of oauth_callback.go. The handler at line 125 looks up an existing user by email, and at line 164 unconditionally replaces the OAuth user object with the existing one. Lines 179-181 then auto-verify the email (EmailVerifiedAt = &now) if it was previously nil, removing the only visible signal that the account was pre-staged.
Critically, no step in the merge logic invalidates or clears the existing password. The fix (commit 66fe488fd2a4) adds a guard: before merging, the handler checks existingUser.EmailVerifiedAt == nil and, if true, rejects the link rather than proceeding. This closes the trust-boundary violation where OAuth email trust was incorrectly extended to validate a password set by an unverified attacker (CWE-287).
The fix
Upgrade to github.com/authorizerdev/authorizer version 0.0.0-20260807033110-66fe488fd2a4 (release tag 2.4.0-rc.16) or later. The patch adds a verification check before any OAuth identity is linked to an existing account: if the existing account's email is not already verified, the link is rejected.
If immediate upgrade is not possible, disable email/password signup to prevent pre-registration staging.
Reported by Koda Reef.
Related research
- critical · 9.3CVE-2026-54072CVE-2026-54072: Authorizer Open Redirect Leaks OAuth2 Tokens via Unvalidated redirect_uri
- critical · 9.1kin-openapi: ValidationHandler Fail-Open Authentication Bypass
- high · 7.7CVE-2026-58423CVE-2026-58423: Gitea LFS SSH Sub-Verb Authentication Bypass
- highCVE-2026-56654CVE-2026-56654: Gitea Privilege Escalation via Access Token Scope Bypass