Magic Link Auth

Switch your site's login flow to passwordless magic links. Users enter their email, receive a one-time link, and click it to log in — no password required.

Configure login mode

Set loginMode in settings/auth.md:

---
provider: custom

# Login mode: password, magic-link
loginMode: magic-link

loginPage: /login
afterLogin: /account
afterLogout: /
---
Value Behavior
password Email + password form (default)
magic-link Email-only form, one-time login link sent by email

How it works

In magic-link mode, the built-in auth pages call window.__sitemdAuth.requestMagicLink(email). Each provider handles the flow differently using its native passwordless API — no custom backend required for third-party providers.

The user experience is the same across all providers: enter email, receive a link, click to log in.

What changes in your pages

When loginMode: magic-link is set, the built-in auth pages adapt automatically at runtime:

/login — shows an email field and "Send Login Link" button instead of the email+password form. After submission, replaces the form with a "check your email" confirmation.

/upgrade (purchase page) — shows email only, opens Stripe checkout in a new tab. After payment, the Stripe webhook creates the user account and sends a welcome email containing the magic link.

/forgot-password — not linked from the login page in magic link mode (there is no password to reset).

/account/settings — the change password section is not shown.

Email template

Magic link emails use the magic-link template from emails/auth/magic-link.md. Customize it like any other email template:

---
id: magic-link
subject: Your login link
# Variables available: {{loginUrl}}
---

Click to log in — this link expires in 30 minutes:

[Log in]({{loginUrl}})

See Email Templates for full template format and provider setup.

JavaScript API

The auth runtime exposes requestMagicLink on window.__sitemdAuth:

window.__sitemdAuth.requestMagicLink(email)
  .then(() => { /* show "check your email" */ })
  .catch(err => { /* show error */ })

The current loginMode value is available at runtime via window.__sitemdAuthCfg.loginMode. Use it in your own page scripts to conditionally show or hide password-related UI:

var cfg = window.__sitemdAuthCfg || {};
if (cfg.loginMode === 'magic-link') {
  // hide password fields
}

Provider support

Magic link mode works with all auth providers. Each uses its native passwordless API:

Provider How it works Dashboard setup
custom Your API implements /auth/magic-link/request and /auth/magic-link/verify endpoints (see below)
supabase Calls signInWithOtp — Supabase sends the magic link email Enable "Email OTP" in Authentication → Providers → Email
firebase Calls sendSignInLinkToEmail — Firebase sends the email link Enable "Email/Password" → toggle on "Email link (passwordless sign-in)" in Authentication → Sign-in method
clerk Creates a sign-in with email_link strategy — Clerk sends the link Enabled by default
auth0 Redirects to Auth0 Universal Login with passwordless email Enable the "Email" passwordless connection in Authentication → Passwordless

For third-party providers, the redirect URL / callback URL for your site must be allowed in the provider's dashboard settings.

Custom provider API endpoints

When using provider: custom, your API must implement two endpoints:

POST /auth/magic-link/request

Body: { email: string }

Generates a one-time token, stores it, and sends the email. Always returns { ok: true } regardless of whether the email exists (prevents enumeration).

GET /auth/magic-link/verify?token=<tokenId>

Validates the token, creates a session, deletes the token, and redirects to:

{SITE_URL}/account#magic=<sessionToken>

The auth runtime handles the rest client-side.

The dev server includes a built-in auth stub for the custom provider. Navigate to your login redirect URL with any magic code to log in instantly:

http://localhost:4747/account#magic=anytoken

The stub accepts any exchange code — no real API or email required. See Local Auth Testing for details.