Magic-link authentication: secure passwordless sign-in
Secure magic-link authentication creates a random, short-lived, single-use token, stores only its protected hash, emails the raw token inside a link, and verifies it in the application backend before creating a session. The token is a credential, so it must never be logged, stored in analytics, or verified in browser-only code.
Request the same response for every email
await auth.requestMagicLink({
email,
redirectUrl: "/dashboard",
});
return { message: "If that address can sign in, a link has been sent." };A fixed response prevents the endpoint from revealing whether an email belongs to an account. Rate-limit requests by normalized email and by the surrounding network or application policy. Do not return internal token expiry or delivery details to an unauthenticated caller.
Keep the raw token out of storage
- Generate the token with a cryptographically secure random source.
- Hash it with the configured token pepper.
- Store the hash, type, subject, and expiry.
- Place the raw token only in the email URL.
- Consume the matching record atomically during verification.
Verify in the backend and create the session once
const result = await auth.verifyMagicLink({ token });
if (result.status === "mfa_required") {
return beginSecondFactor(result);
}
return setSessionCookie(result.sessionToken, result.session.expiresAt);After verification, remove the token from the browser URL and keep only the opaque session cookie. A refresh, copied URL, or second click must not create another session from the consumed link.
Use HTTPS links on mobile
For iOS and Android, send an HTTPS link and configure Universal Links or App Links. A hosted bridge can continue to an app destination and provide a web fallback without verifying the token itself. The application backend remains responsible for consuming the token.
Monitor the flow safely
- Count requests, accepted sends, verification successes, expiry, and rate limits.
- Do not record raw URLs, tokens, email bodies, or query strings.
- Alert on abnormal request volume and repeated delivery failures.
- Keep external errors generic while preserving safe internal event names.