Inbox
Back to Developers & QA

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

BoundaryWhat crosses itWhat the test must prove
01 · Login requestAn email address and a passwordless-login intentThe request belongs to the identity and environment under test
02 · InboxA message carrying the current authentication actionThis email belongs to this request, not an older attempt
03 · URLA credential-bearing link leaves the email clientHTTPS, intended host, intended route, and no unexpected environment crossing
04 · CallbackThe authentication service or application consumes the linkValid, expired, stale, and repeated actions follow explicit policy
05 · SessionThe successful handoff becomes authenticated stateThe right user receives the right session
06 · RedirectThe authenticated user lands back in the productThe 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 propertyQuestionWhy it matters
SchemeDoes the authentication path use HTTPS?The link carries authentication material while it is valid
HostnameDoes staging stay in staging and production stay in production?A valid credential on the wrong environment is still a broken trust boundary
Route/callbackIs this the intended authentication endpoint?A malformed or stale callback can make a correct-looking email unusable
Return destinationIs the post-login target expected and constrained?Authentication should not become an arbitrary redirect mechanism
Secret materialCan 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. 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. 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. 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. 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. 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

CaseWhat to testFailure hidden by a simple click test
Default loginSuccessful authentication lands on the documented default destinationSession exists but user is stranded on a callback page
Deep-link returnAn allowed pre-login destination is restored after authenticationThe app forgets where the user was going
Unsafe return targetUntrusted external destinations are rejected or constrainedLogin callback becomes an open-redirect path
Mobile/deep linkSupported app or browser handoff reaches the intended clientTracking or redirect layers break universal or deep-link behavior
Error callbackExpired or invalid links produce a recoverable destinationUser 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

ProbeExpected propertyEvidence to capture
Current linkAuthenticates the intended identity according to policyRequest time, sanitized route, identity, resulting session
Expired linkDoes not authenticateExpiry response and recovery path
Replay after successFollows one-time or consumption policySecond-use result and session state
Older link after resendFollows documented supersession policyWhich request each link belongs to and each result
Wrong browser/deviceMatches documented portability or binding policyInitiating context, opening context, recovery message
Wrong environment hostCannot silently authenticate across the wrong environment boundaryHost, callback, and resulting state
Tampered return destinationCannot redirect to an untrusted arbitrary targetRejected 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 email

Sources & 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.