Skip to main content

Magic Links

Magic links provide passwordless authentication by sending a one-time login link to the user's email. This requires the Messaging wire to be loaded for email delivery.

Configuration

import { FluxKit } from "@fluxkitdev/core";
import { auth } from "@fluxkitdev/auth";
import { messaging } from "@fluxkitdev/messaging";

const app = new FluxKit({
wires: [
auth({
magicLink: {
enabled: true,
expiresIn: "15m", // Link expiration
redirectUrl: "http://localhost:5173/auth/verify", // Frontend redirect
},
}),
messaging(), // Required for sending emails
],
database: { uri: process.env.MONGODB_URI },
});

Flow

  1. User requests a magic link by providing their email.
  2. FluxKit generates a one-time token and sends a login link via email.
  3. User clicks the link in their email.
  4. Your frontend sends the token to FluxKit for verification.
  5. FluxKit returns a JWT if the token is valid.

Programmatic

await app.auth.sendMagicLink("alice@example.com");

REST API

curl -X POST http://localhost:3000/auth/magic-link \
-H "Content-Type: application/json" \
-d '{"email": "alice@example.com"}'

Response (200):

{
"message": "Magic link sent to alice@example.com"
}

The email contains a link like:

http://localhost:5173/auth/verify?token=abc123...

Programmatic

const result = await app.auth.verifyMagicLink(token);
console.log(result.user); // User object
console.log(result.token); // JWT string

REST API

curl -X POST http://localhost:3000/auth/verify-magic-link \
-H "Content-Type: application/json" \
-d '{"token": "abc123..."}'

Response (200):

{
"user": {
"_id": "65a1b2c3d4e5f6a7b8c9d0e1",
"email": "alice@example.com",
"name": "Alice Johnson"
},
"token": "eyJhbGciOiJIUzI1NiIs..."
}

Response (401):

{
"error": {
"code": "INVALID_MAGIC_LINK",
"message": "Magic link is expired or already used"
}
}

Customizing the Email

You can customize the magic link email template:

auth({
magicLink: {
enabled: true,
email: {
subject: "Sign in to My App",
template: (link) => `
<h1>Sign In</h1>
<p>Click the link below to sign in to your account:</p>
<a href="${link}">Sign In</a>
<p>This link expires in 15 minutes.</p>
`,
},
},
});

Auto-Registration

By default, if a user requests a magic link and no account exists for that email, one is created automatically. You can disable this behavior:

auth({
magicLink: {
enabled: true,
autoRegister: false, // Reject unknown emails
},
});

Environment Variables

VariableDefaultDescription
MAGIC_LINK_EXPIRY15mToken expiration time
MAGIC_LINK_REDIRECT/auth/verifyFrontend redirect URL