Magic links & passwordless auth

Status: 🟩 COMPLETE Last updated: 2026-06-19 Plain-English tagline: Login without passwords. Click a link in your email, or tap your phone’s fingerprint reader, and you’re in. The 2026 direction for consumer auth — better UX, harder to phish, no password to forget.


In plain English

For 30 years, the default way to log into a website was email + password. Passwords are problematic:

  • Users pick weak ones, or reuse them, or forget them
  • They’re phishable (attackers send fake login pages)
  • They’re stealable (data breaches)
  • They’re a UX bottleneck (typing on phones is painful)

Passwordless authentication dispenses with passwords. Common methods:

  • Magic links — Click a link in an email. The link is unique, single-use, and short-lived. Clicking it logs you in.
  • Email OTP — A one-time code sent to your email. Type the code; you’re in.
  • SMS OTP — Same but via SMS.
  • Passkeys (WebAuthn) — Cryptographic credentials stored on your device. Authenticate via Face ID, Touch ID, Windows Hello, or a hardware key.
  • Authenticator app codes — Like TOTP for 2FA but as the primary login.

Of these, passkeys are the future — phishing-resistant, no password to type, supported in every modern browser. Magic links and email OTPs are common today — easy to implement, low UX friction. SMS is fading due to SIM-swap attacks but still common.

Most modern auth providers (Supabase, Clerk, NextAuth, Auth0) support these out of the box. Implementing them yourself is rarely necessary.


Why it matters

  • Better UX. No password to remember, type, or reset.
  • Better security. No password to leak in a breach. Passkeys are phishing-resistant.
  • Higher conversion. Users complete passwordless signup at much higher rates than password signup.
  • Where consumer apps are heading. Apple, Google, Microsoft are pushing passkeys hard. Major sites (Best Buy, Amazon, eBay, GitHub, Shopify) support them.
  • For a project like Bible Quest, magic links lower the barrier to entry — no password adds friction, particularly for less-technical users.

How they work

  1. User enters their email and clicks “Send magic link”
  2. Server generates a random token, stores it with expiry, and sends an email with a link like:
    https://yourapp.com/login/verify?token=abc123...
    
  3. User opens the email and clicks the link
  4. Server validates the token (exists, not expired, not used), looks up the user, issues a session
  5. Redirect to logged-in homepage

Token requirements

  • Random and unguessable (cryptographically random, ~32 bytes)
  • Short-lived (15-60 minutes typically)
  • Single-use (delete or mark used after click)
  • Tied to the email (used to identify the user)

Pros

  • No password to forget
  • Built-in proof of email control (you must have access to the inbox)
  • Easy to implement
  • Familiar to users (similar to “Reset password” links they already use)

Cons

  • Requires a working email inbox
  • Email delivery delays
  • Click flow can break across devices (start on laptop, link opens on phone)
  • Phishing risk (attackers can send fake “Sign in” emails that look identical)

Email / SMS OTP (one-time passwords)

Similar to magic links, but instead of clicking, the user types a 6-digit code:

  1. User enters email or phone
  2. Server generates a 6-digit code, stores with expiry, sends via email/SMS
  3. User types the code on the same page
  4. Server validates and logs in
  • Stays on the same device/page (no cross-device link click)
  • More familiar to users (like 2FA codes)
  • Faster for power users

Cons

  • More typing
  • SMS specifically vulnerable to SIM-swap attacks (attacker convinces carrier to port victim’s number)

For SMS, prefer email OTP or TOTP (time-based codes from an authenticator app) when possible.


Passkeys (WebAuthn)

The new gold standard. A passkey is a cryptographic credential stored on the user’s device:

  • User’s device has a private key. The website has the matching public key.
  • To log in, the website challenges the device. The device signs the challenge with its private key (after user verification — Face ID, fingerprint, PIN).
  • The website verifies the signature with the public key.

Why it’s special

  • Phishing-resistant. Passkeys are scoped to the exact origin. An attacker’s fake site can’t trigger the real passkey.
  • No shared secret. The private key never leaves the device. The server only has the public key.
  • Convenient. Face ID is faster than typing a password.
  • Strong. No weak passkeys; no reused passkeys.
  • Synced via the OS. iCloud Keychain, Google Password Manager, Windows Hello all sync passkeys across user’s devices.

Setup flow (simplified)

  1. User logs in (initial method — could be email OTP or password)
  2. User clicks “Set up passkey”
  3. Browser prompts for biometric (Face ID, etc.)
  4. Device generates a keypair, sends public key to server
  5. Server stores it associated with the user

Login flow

  1. User clicks “Sign in with passkey” (or it’s auto-suggested)
  2. Browser prompts for biometric
  3. Device signs the challenge
  4. Server verifies; user is in

In 2026 this is supported in every major browser. Supabase added passkey support recently. Adoption is growing fast.

Caveats

  • Recovery: what if the user loses all devices with the passkey? Need fallback auth method.
  • Cross-device sharing: getting a passkey from your phone to a friend’s laptop is intentionally hard (security feature, sometimes UX problem)
  • Account portability: passkeys are tied to a device/ecosystem

"use client";
import { useState } from "react";
import { createClient } from "@/lib/supabase-client";
 
export function MagicLinkForm() {
  const supabase = createClient();
  const [email, setEmail] = useState("");
  const [sent, setSent] = useState(false);
 
  async function send() {
    await supabase.auth.signInWithOtp({
      email,
      options: { emailRedirectTo: `${window.location.origin}/auth/callback` }
    });
    setSent(true);
  }
 
  if (sent) return <p>Check your inbox for a sign-in link.</p>;
  return (
    <form onSubmit={(e) => { e.preventDefault(); send(); }}>
      <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} required />
      <button type="submit">Send magic link</button>
    </form>
  );
}

The callback handler:

// app/auth/callback/route.ts
import { NextResponse } from "next/server";
import { createClient } from "@/lib/supabase-server";
 
export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);
  const code = searchParams.get("code");
  if (code) {
    const supabase = await createClient();
    await supabase.auth.exchangeCodeForSession(code);
  }
  return NextResponse.redirect(`${origin}/`);
}

That’s the whole flow. ~30 lines total. Supabase handles email delivery (rate-limited free tier, recommend Resend/SendGrid for production), token generation, expiry, validation.


TOTP / authenticator apps

Time-based one-time passwords from apps like Google Authenticator, Authy, 1Password.

How it works:

  • User scans a QR code → app stores the secret
  • Every 30 seconds, app generates a new 6-digit code derived from the secret + current time
  • User types the code; server uses the same algorithm to verify

Mostly used as a second factor (after email/password) but can be primary auth too.

Pros: works offline, free, widely supported, more secure than SMS. Cons: user must have the app; “what if I lose my phone?” recovery needed.


The current sweet spot:

  1. First login: magic link to email. Quick, no password.
  2. After first login: prompt to set up a passkey.
  3. Subsequent logins: passkey (Face ID, etc.) — fast, phishing-resistant.
  4. Fallback: magic link still works if passkey unavailable.

This combo gives high security, low friction, graceful degradation. Modern auth services support all of this out of the box.


Common gotchas

  • Email delivery delays. Magic links can take 30 seconds to arrive. UX should communicate this (“Check your inbox; may take a minute”).

  • Magic link expiry timing. Too short (5 minutes) = frustrating; too long (24 hours) = security risk. 15-60 minutes is typical.

  • Magic links that don’t single-use. Token valid for entire expiry window even after click. Attacker who intercepts (e.g. browser history) can replay. Mark used on click.

  • Magic link opens in different browser. User clicks in iPhone Mail app, which opens Safari, not their default Chrome. Session lands in Safari; user expects Chrome. Confusing. Modern apps handle by establishing session in the email-opening browser and prompting if expected origin differs.

  • Email “preview” services clicking links automatically. Microsoft Defender for Office, anti-malware tools “click” links to scan them. Counts as a use. Mark single-use, but also have the link redirect to a confirmation page that user must click — protects against URL prefetching.

  • Phishing magic-link emails. Attackers send fake “Sign in to X” emails. Educate users to expect the link only when they requested it.

  • SMS-based 2FA / OTP is vulnerable. SIM-swap attacks are real. For high-value accounts, prefer TOTP, passkeys, or email OTP.

  • Passkey loss without recovery. User loses devices → locked out forever. Always provide recovery (email magic link, recovery codes, etc.).

  • Cross-device passkey use. New device first-time = use the cross-device flow (QR code on laptop, scan with phone). Modern but UX needs care.

  • Account enumeration. “If account exists, send link” vs. “If account doesn’t exist, error.” Reveal nothing. Always say “If an account exists, a link has been sent.”

  • Rate limiting magic links. Without rate limiting, attacker can spam a victim’s inbox. Limit to N per hour per email.

  • Magic link to wrong email. User mistypes email. Link goes to someone else. They can sign in as the typo’d user… if your verification is lax. Combine with first-login verification.

  • Magic link as account creation. If someone sends a magic link to an email they don’t own, and you auto-create the account on click, you’ve let them create an account they control for someone else’s email. Verify email control via the click; only then create.

  • TOTP secret in user’s QR code = QR code in their photo library forever. Some users screenshot the QR code “just in case.” If their photo library leaks, the secret leaks. Tell users to delete the screenshot after setup.

  • MFA fatigue / push bombing. If you implement push notifications for 2FA, attackers can spam push prompts until victim hits “Approve” out of frustration. Add number-matching or other verification.

  • Edge case: user signs up with magic link, never logs in again, gets your weekly email. Pattern: don’t auto-create persistent accounts for one-time link uses unless intent is clear.

  • Magic link UX for SaaS sharing. Sharing a magic link with someone else = sharing access. Make it clear in copy that these links are personal.

  • No-password reset flow. Without passwords, “I forgot my password” still happens — usually “I changed my email and lost access to the old one.” Plan for it.

  • Account merging from multiple methods. User signs up with Google, later with magic link to same email. Are they the same account? Implement linking.

  • Provider rate limits. Free email-sending tiers (Supabase Auth’s built-in) are very limited. For production, configure SMTP via Resend, SendGrid, Postmark.


See also

Sources