Magic Links
A magic link is a URL, delivered by email or SMS, that authenticates a user or authorises an action the moment it is clicked โ no password required. They are convenient, which is exactly why they have become a preferred phishing vector: an attacker who can make a user click any link can just as easily make them click a malicious one, and the user has been trained by every magic-link flow they've ever used that clicking an emailed link to log in is normal, expected behaviour. As a general rule, email-delivered authentication links should not be used as a login mechanism. This article covers why, and how to handle the cases where a link-based flow is genuinely difficult to avoid.
Why Magic Links Are a Phishing Risk
They Train Users to Click Authentication Links in Email
The core problem is behavioural, not cryptographic. Every legitimate magic-link email reinforces the exact action a phishing email needs the victim to take: click this link to sign in. A well-executed phishing email that mimics the organisation's magic-link template is indistinguishable from the real thing to most users, because the real flow is itself "click a link from your inbox to get authenticated." Security awareness training that says "never click login links in email" is directly undermined by a product that routinely sends login links by email.
Email Is Not an Authentication Factor
A magic link is only as strong as the mailbox it is delivered to. Email accounts are frequently protected by weak or no MFA, are a common target for credential stuffing and OAuth consent phishing, and are often accessible from shared or unmanaged devices. Treating "possession of the email inbox" as equivalent to "possession of a security key" conflates a weak, frequently-compromised channel with a genuine authentication factor.
No Origin Binding โ Vulnerable to Real-Time Relay
Unlike FIDO2/WebAuthn (see Multi-Factor Authentication), a magic link is a bearer token: whoever has the URL, from whatever origin, is authenticated. An adversary-in-the-middle phishing kit can present a cloned login page, capture the email address, and โ if the attacker also has read access to the mailbox (a prior email compromise, a shared support inbox, a forwarding rule planted after an earlier breach) โ complete the flow without the victim ever suspecting anything.
Link Leakage Outside the User's Control
Magic links routinely leak through channels the sender does not control:
- Corporate email security gateways and link scanners (Proofpoint, Mimecast, Microsoft Defender for Office 365 Safe Links) pre-fetch links to check for malware. A single-use magic link can be consumed by the scanner before the user ever clicks it, breaking the flow or โ worse โ a link that is not single-use gets silently authenticated by the scanner's crawler.
- Referrer headers can leak the full link (including the token) to third-party resources if the destination page loads external assets before validating the token.
- Browser history, shared mailboxes, mailing list archives, and forwarded emails can all expose a still-valid link to someone other than the intended recipient.
- Server and proxy access logs capture the full URL, including the token, if it is placed in the query string.
Long-Lived, Replayable Tokens
Because magic links are designed for asynchronous delivery (the user may not check their inbox for minutes or hours), the tokens are often given a longer validity window than a typical session or OTP. A longer-lived, unrevoked bearer token sitting in an inbox is a standing credential โ exactly the kind of "collect now, use later" target that makes mailbox compromise valuable to an attacker.
General Guidance: Prohibit Email Links for Authentication
Do not use email-delivered links as a primary login or step-up authentication mechanism. Use phishing-resistant MFA (FIDO2/WebAuthn, passkeys) or, at minimum, a code the user must manually type into the application they are already on โ see the ranked factor guidance in Multi-Factor Authentication. A code that must be typed back into the original tab preserves origin binding in a way a clickable link does not: the user is proving they received something, on the site they explicitly navigated to, rather than being taken wherever the link points.
This prohibition should be a default in application design reviews and threat models: any proposal to email a clickable authentication link should be treated the same as a proposal to email a password, and should require an explicit, documented exception.
Legitimate Exceptions
Some flows are genuinely difficult to solve without an email link, because the entire premise of the flow is that the user does not yet have a working credential to authenticate with by any other means. These are the accepted exceptions โ not because the risk disappears, but because the alternative (no verification path at all, or a weaker one) is worse.
| Use Case | Why a Link Is Hard to Avoid |
|---|---|
| User enrolment / initial account activation | The user has no password, no registered MFA factor, and often no prior relationship with the system. Email is the only channel already proven to reach them (their invite, their corporate address). |
| Password reset | By definition the user cannot authenticate with the credential they are trying to recover. A secondary channel is required to re-establish trust before a new credential can be set. |
| Email address verification | The explicit purpose of the flow is to prove control of the mailbox โ a link sent to that mailbox is the mechanism, not a workaround. |
| B2B/B2C invitations (e.g. inviting an external user into a shared workspace) | The invitee has no account and no established authenticator; email is the identifier being invited. |
Even within these exceptions, the goal is to minimise and contain the risk rather than treat the link as a safe default. The controls below apply specifically to these accepted use cases.
Hardening Accepted Magic-Link Flows
Token Construction
| Requirement | Detail |
|---|---|
| Entropy | Minimum 128 bits, generated with a CSPRNG โ never a sequential ID, timestamp, or predictable value |
| Single use | Token is invalidated immediately on first successful use, regardless of outcome |
| Short expiry | 15 minutes for password reset/enrolment; as short as operationally possible |
| Storage | Store only a hash of the token (SHA-256), never the raw value โ same principle as password storage |
| Placement | Path segment or fragment, not a query parameter that gets logged by default in most web server configurations |
import secrets
import hashlib
def generate_reset_token() -> tuple[str, str]:
token = secrets.token_urlsafe(32) # sent to the user, never stored
token_hash = hashlib.sha256(token.encode()).hexdigest() # stored server-side
return token, token_hash
Defeating Link Pre-Fetch / Scanner Consumption
Email security gateways and client link-preview features will fetch a URL to inspect it before the user clicks. A naive single-use token gets burned by the scanner, not the user.
- Require an interstitial confirmation page for the first
GETon the link โ display a "Click to continue" button that performs the actual state-changing action viaPOST. Scanners that only fetch the link do not trigger the follow-on action. - Do not perform the sensitive action (password reset, session creation) on the initial page load. Treat the link
GETas "this token exists and is valid," and require an explicit user action afterward.
Binding the Link to Its Context
- Bind the token to the request context where possible: the IP range or device that initiated the reset/enrolment request, checked (not strictly enforced, to avoid breaking legitimate cross-device flows) against the IP/device that redeems it, with a step-up challenge on mismatch.
- Bind the token to the specific account/email address it was issued for โ never accept a valid token for a different identifier than the one embedded server-side.
- Re-authenticate for anything beyond the minimum purpose. A password-reset link should let the user set a new password and nothing else โ it must not become a general-purpose login session with full account access.
Session and Scope Limits
- The session established by consuming a magic link should be scoped to the single purpose (e.g. "set new password") and should expire immediately after that action completes.
- Do not treat a redeemed magic link as equivalent to a fully authenticated, long-lived session with access to sensitive account settings (payment methods, MFA enrolment changes) without an additional step-up check.
- Log every issuance and redemption event (timestamp, requesting IP, redeeming IP, outcome) โ feed to the same monitoring pipeline as failed login and MFA events.
Notify and Give an Escape Hatch
- Notify the user through a second channel (existing verified email, push notification to an already-enrolled device) whenever a reset or enrolment link is issued, so an attacker-initiated request is visible to the legitimate owner.
- Rate-limit link issuance per account and per IP to prevent an attacker from flooding a victim's inbox with reset requests as a precursor to social engineering ("just click the last link I sent you").
What Not to Do
- Do not use magic links for routine, repeated sign-in ("passwordless login" as a everyday convenience feature) โ this is the exact pattern that normalises clicking authentication links from email and is the highest-risk form of this pattern.
- Do not make the token long-lived "for user convenience" โ every extra hour of validity is an extra hour the token is a standing credential sitting in an inbox.
- Do not skip the interstitial confirmation step to "streamline the flow" โ this is the single most common way single-use tokens get silently burned by security scanners, and removing it also removes the last barrier between a pre-fetch and a completed account takeover.
- Do not reuse the same token generation/validation code path for high-privilege actions (e.g. changing a registered MFA factor, adding a payment method) as for low-privilege ones (e.g. verifying a newsletter subscription).
Hardening Checklist
| Control | Priority | Notes |
|---|---|---|
| Email links prohibited as a routine/repeated login mechanism | Critical | Use phishing-resistant MFA instead โ see Multi-Factor Authentication |
| Magic links restricted to enrolment, password reset, and email verification flows | Critical | Documented, explicit exceptions only |
| Tokens generated with a CSPRNG, โฅ128 bits entropy | Critical | Never sequential, timestamp-based, or predictable |
| Tokens single-use and invalidated immediately on redemption | Critical | Prevents replay from scanners or leaked copies |
| Token expiry โค 15 minutes | High | Shorter where operationally feasible |
| Only a hash of the token stored server-side | High | Same principle as password storage |
| Token not placed in a logged query parameter | Medium | Avoid leakage via server/proxy access logs |
| Interstitial confirmation step before the sensitive action executes | Critical | Prevents email security scanners from consuming single-use tokens |
| Redeemed session scoped to the single purpose, expires immediately after | High | Not a general-purpose authenticated session |
| Second-channel notification on link issuance | High | Surfaces attacker-initiated requests to the legitimate owner |
| Link issuance rate-limited per account and per IP | High | Prevents inbox flooding / reset-request social engineering |
| Issuance and redemption events logged and monitored | High | Same pipeline as failed login / MFA events |