Magic-link QA · follow the authentication handoff
Follow the Link, Then Follow the Session: A QA Map for Magic-Link Login
Test passwordless magic-link login across the full trust path: request, email, destination host, callback, expiry, replay, authenticated identity, session creation, and final redirect.
A magic link is an authentication handoff disguised as an ordinary URL. The email can render perfectly, the button can open, and the flow can still authenticate the wrong identity, cross into the wrong environment, lose the intended redirect, fail on another browser, or leave a reusable credential behind. Test it by following trust from the login request all the way to the session that exists after the click.
Trace the link across six trust boundaries
| Boundary | What crosses it | What the test must prove |
|---|---|---|
| 01 · Login request | An email address and a passwordless-login intent | The request belongs to the identity and environment under test |
| 02 · Inbox | A message carrying the current authentication action | This email belongs to this request, not an older attempt |
| 03 · URL | A credential-bearing link leaves the email client | HTTPS, intended host, intended route, and no unexpected environment crossing |
| 04 · Callback | The authentication service or application consumes the link | Valid, expired, stale, and repeated actions follow explicit policy |
| 05 · Session | The successful handoff becomes authenticated state | The right user receives the right session |
| 06 · Redirect | The authenticated user lands back in the product | The final destination is allowed and preserved when intended |
Boundary 01 — freeze the login attempt before the email exists
Start with one known identity and one intended environment. Record the address, the page or client that initiated login, and any return destination the product is expected to preserve. If several magic-link requests are active for the same identity, you have already created a separate supersession test; do not let that complexity leak unnoticed into the baseline case.
The request context matters because some passwordless implementations bind the transaction to browser or device state. Auth0, for example, documents configurations where a magic link must be opened in the same browser session that initiated the request. That behavior is provider- and configuration-specific, so QA should test the cross-browser and cross-device contract the product actually promises rather than assuming every magic link is portable.
Boundary 02 — prove that this email belongs to this login attempt
Match recipient, sender, subject, arrival window, and any safe request context before following the link. A long-lived inbox with several passwordless emails can make an old credential look current, especially when delivery order differs from request order.
Avoid making 'newest message in the inbox' the only selector. A resend can arrive before the original message, and mail ordering is not an authentication rule. The test should understand which login attempt it created and which message is evidence for that attempt.
Boundary 03 — inspect the URL before you spend it
| URL property | Question | Why it matters |
|---|---|---|
| Scheme | Does the authentication path use HTTPS? | The link carries authentication material while it is valid |
| Hostname | Does staging stay in staging and production stay in production? | A valid credential on the wrong environment is still a broken trust boundary |
| Route/callback | Is this the intended authentication endpoint? | A malformed or stale callback can make a correct-looking email unusable |
| Return destination | Is the post-login target expected and constrained? | Authentication should not become an arbitrary redirect mechanism |
| Secret material | Can diagnostics identify the route without permanently logging the live credential? | Screenshots and CI logs can outlive the link's intended exposure |
Treat a live magic link like a credential, not a harmless hyperlink
For the period in which the link can authenticate a user, possession may be enough to complete login. Keep full live URLs out of unnecessary long-lived logs, tickets, screenshots, analytics, and copied diagnostics. Preserve the host, route shape, request time, and result when those are sufficient to debug the flow.
Boundary 04 → 05 — watch authentication become a session
- 1
Open the current link through the supported user path
Use the browser and device behavior the product claims to support. If same-browser binding is intentional, test it explicitly and give cross-browser failure a useful recovery path.
- 2
Observe the callback, not just the first HTTP success
Follow the complete redirect chain. A 200 response somewhere in the middle does not prove that authentication completed.
- 3
Assert the authenticated identity
After the handoff, verify which user the application believes is signed in. Delivery to the right address does not by itself prove the resulting session belongs to the right account record.
- 4
Compare session state before and after authentication
Successful authentication should transition into the application's intended authenticated session model. Include session-fixation checks appropriate to the stack; OWASP recommends renewing the session identifier after successful authentication.
- 5
Assert the promised session properties
Check persistence, authenticated-only access, expected account context, and any session duration or device behavior that belongs to the product contract.
Boundary 06 — redirects deserve their own assertions
| Case | What to test | Failure hidden by a simple click test |
|---|---|---|
| Default login | Successful authentication lands on the documented default destination | Session exists but user is stranded on a callback page |
| Deep-link return | An allowed pre-login destination is restored after authentication | The app forgets where the user was going |
| Unsafe return target | Untrusted external destinations are rejected or constrained | Login callback becomes an open-redirect path |
| Mobile/deep link | Supported app or browser handoff reaches the intended client | Tracking or redirect layers break universal or deep-link behavior |
| Error callback | Expired or invalid links produce a recoverable destination | User receives a generic dead end with no route to request a new link |
Now break the credential: expiry, replay, and supersession
A magic link that works once in the happy path has not yet proven its lifecycle. Test a link after the documented validity window and verify that authentication does not occur. Auth0 documents expired passwordless links returning an authentication error through the callback path, which is a useful reminder that expiry has both a security result and a user-experience result: reject the credential and provide a safe route to start again.
If the design promises single-use links, use a valid link successfully and then replay the exact same link. The second attempt should follow the documented consumed-link behavior without silently creating another authenticated session. Also request a second magic link and test what that does to the first one; supersession semantics should be explicit rather than guessed.
Do not hard-code another provider's lifetime or supersession rule into your product test. The invariant is that the test knows the policy, controls the request timeline, and proves that expired, consumed, stale, and current links are distinguishable.
A compact magic-link attack-the-edges matrix
| Probe | Expected property | Evidence to capture |
|---|---|---|
| Current link | Authenticates the intended identity according to policy | Request time, sanitized route, identity, resulting session |
| Expired link | Does not authenticate | Expiry response and recovery path |
| Replay after success | Follows one-time or consumption policy | Second-use result and session state |
| Older link after resend | Follows documented supersession policy | Which request each link belongs to and each result |
| Wrong browser/device | Matches documented portability or binding policy | Initiating context, opening context, recovery message |
| Wrong environment host | Cannot silently authenticate across the wrong environment boundary | Host, callback, and resulting state |
| Tampered return destination | Cannot redirect to an untrusted arbitrary target | Rejected or sanitized destination and final route |
Evidence bundle for a failed passwordless login
- Identity and environment used to initiate the login.
- Request time and whether another magic link was requested during the scenario.
- Message sender, recipient, subject, and arrival window.
- Sanitized authentication hostname and callback route without needlessly preserving the live credential.
- Browser or device context when the implementation binds the transaction to it.
- Redirect chain and final destination.
- Authenticated identity and session state after the callback.
- Result of expiry, replay, or supersession when that is the case under test.
Passwordless does not automatically mean phishing-resistant
Email magic links remove a memorized password from this interaction, but the authentication action still depends on access to the email channel and possession of the live link. Keep the QA claim narrow: test the passwordless flow your product ships without presenting email magic links as equivalent to phishing-resistant mechanisms such as properly deployed passkeys or WebAuthn.
A magic-link test is complete only when trust survives the whole trip. The right login attempt produces the right email; the link stays inside the intended environment; the callback consumes it according to lifecycle policy; the right identity receives a fresh authenticated state; and the user lands somewhere the application intentionally allowed. The clickable button in the email is only the bridge between those assertions.
Need a clean recipient for manual email QA?
Use MailOnce to inspect real signup, OTP, magic-link, password-reset, and notification messages without putting a personal inbox into the test fixture.
Create temporary emailSources & further reading
Primary security and test-runner documentation used to ground the testing guidance.
Continue testing
Move to the next boundary in the same email-testing problem space.