Skip to main content

Magic Link

Passwordless email login — users click a link to sign in, no password required.

Captcha Protection

When captcha is enabled, the Magic Link endpoint is automatically protected by Cloudflare Turnstile. All client SDKs handle token acquisition transparently — no code changes needed.

How It Works

  1. User enters their email address
  2. Server sends an email with a one-time magic link
  3. User clicks the link
  4. Your app extracts the token from the URL and calls verifyMagicLink
  5. User is signed in with full session tokens
Auto-Create

When autoCreate is enabled (default), users who don't have an account are automatically registered when they request a magic link. The created account has no password and is marked as verified.

Configuration

Enable magic link in your edgebase.config.ts:

export default {
auth: {
magicLink: {
enabled: true, // default: false
autoCreate: true, // auto-register unknown emails (default: true)
tokenTTL: '15m', // link expiration (default: '15m')
},
},
email: {
provider: 'resend',
apiKey: 'your-api-key',
from: 'noreply@yourapp.com',
// Optional fallback URL template ({token} placeholder)
// Per-request redirectUrl/redirectTo overrides this.
magicLinkUrl: 'https://yourapp.com/auth/magic?token={token}',
},
} satisfies EdgeBaseConfig;

Request a magic link email for a user:

await client.auth.signInWithMagicLink({
email: 'user@example.com',
redirectUrl: `${window.location.origin}/auth/magic`,
state: 'checkout',
});

The server always responds 200 OK regardless of whether the email exists. This prevents email enumeration attacks.

Per-Request Redirects

On the Web SDK, signInWithMagicLink() also accepts:

  • redirectUrl or redirectTo
  • state

If you pass them, EdgeBase uses that redirect for this request instead of the static email.magicLinkUrl template. The clicked link includes:

  • token
  • type=magic-link
  • state if provided

If your project sets auth.allowedRedirectUrls, the redirect must match that allowlist.

After the user clicks the link, extract the token from the URL and verify it:

// Extract token from URL (e.g., https://yourapp.com/auth/magic?token=abc123)
const params = new URLSearchParams(window.location.search);
const token = params.get('token');
const state = params.get('state');

const { user, accessToken, refreshToken } = await client.auth.verifyMagicLink(token);
console.log('Signed in as:', user.email);
console.log('Resume flow:', state);

Full Example (React)

import { useEffect, useState } from 'react';
import { client } from './edgebase';

function MagicLinkLogin() {
const [email, setEmail] = useState('');
const [sent, setSent] = useState(false);

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
await client.auth.signInWithMagicLink({
email,
redirectUrl: `${window.location.origin}/auth/magic`,
state: 'dashboard',
});
setSent(true);
};

if (sent) {
return <p>Check your email for the sign-in link!</p>;
}

return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="your@email.com"
/>
<button type="submit">Send Magic Link</button>
</form>
);
}

// Callback page — handles the magic link redirect
function MagicLinkCallback() {
useEffect(() => {
const params = new URLSearchParams(window.location.search);
const token = params.get('token');
const state = params.get('state');
if (token) {
client.auth.verifyMagicLink(token).then(({ user }) => {
window.location.href = state === 'dashboard' ? '/dashboard' : '/';
});
}
}, []);

return <p>Signing you in...</p>;
}

Security

  • Single-use tokens — Each token can only be used once. After verification, the token is deleted.
  • Expiration — Tokens expire after tokenTTL (default 15 minutes).
  • No email enumeration — The server returns the same response whether or not the email exists.
  • Rate limiting — Auth rate limits apply to prevent abuse.
  • Auth hooksbeforeSignIn and afterSignIn hooks fire during magic link verification.
  • Redirect allowlist — If auth.allowedRedirectUrls is set, only approved redirect URLs can be used for per-request links.

Compatibility

Magic link works alongside other auth methods:

ScenarioBehavior
Email/password user requests magic linkWorks — signs in without password
Magic link auto-created user tries password sign-inFails — no password set (use magic link or set password via admin)
Magic link user links OAuth providerWorks — via account linking

REST API

POST /api/auth/signin/magic-link
Content-Type: application/json

{ "email": "user@example.com" }

Response: 200 OK

{ "ok": true }
POST /api/auth/verify-magic-link
Content-Type: application/json

{ "token": "abc123..." }

Response: 200 OK

{
"user": { "id": "...", "email": "user@example.com", "verified": true },
"accessToken": "eyJ...",
"refreshToken": "..."
}

Error Responses:

StatusCondition
400Missing or invalid email / missing token
400Token expired, invalid, or already used
403beforeSignIn hook rejected the sign-in
404Magic link authentication is not enabled