Magic Links
Passwordless email sign-in with time-limited magic link tokens.
The magiclink plugin provides passwordless sign-in via a one-time link sent to the user's email address. No password is required. Users click the link and are immediately authenticated with a full session.
Magic links are ideal for:
- Applications where users sign in infrequently (reducing password friction)
- B2B tools where users may not remember a password set months ago
- Supplementing password auth as an alternative sign-in method
Setup
import (
"github.com/xraph/authsome"
"github.com/xraph/authsome/plugins/magiclink"
"github.com/xraph/authsome/bridge/maileradapter"
)
mailer := maileradapter.NewResend("re_your_resend_api_key")
eng, err := authsome.New(
authsome.WithStore(store),
authsome.WithMailer(mailer), // required: magic links are sent via email
authsome.WithPlugin(magiclink.New(magiclink.Config{
TokenTTL: 15 * time.Minute, // how long the link stays valid
AutoCreate: true, // create user on first sign-in if not found
})),
)A Mailer bridge is required. If no mailer is configured, magic link requests succeed on the server side (the token is generated) but the email is never delivered. Always configure a mailer before deploying to production.
How it works
Request a magic link: The user submits their email address.
POST /v1/auth/magic-link/request
{"email": "alice@example.com", "app_id": "myapp"}The engine:
- Looks up the user by email. If
AutoCreate: trueand the user does not exist, creates a new user account automatically. - Generates a cryptographically random token.
- Stores the token with a TTL (default 15 minutes).
- Calls the
Mailerbridge to deliver the email.
The response is always 200 OK regardless of whether the email exists, preventing email enumeration.
User clicks the link: Your application receives the request with the token in the URL.
The link format is determined by your application. A typical pattern:
https://myapp.com/auth/magic-link?token=ml_abc123xyz&app_id=myappYour frontend extracts the token and calls the confirm endpoint.
Confirm the token: Exchange the token for a session.
POST /v1/auth/magic-link/confirm
{"token": "ml_abc123xyz", "app_id": "myapp"}On success, a full user + session pair is returned:
{
"user": {"id": "ausr_01j9...", "email": "alice@example.com", ...},
"session": {"token": "a3f8c9d4...", "refresh_token": "d72b1ef8...", ...}
}The token is consumed (one-time use) and invalidated after use.
Configuration
type Config struct {
// TokenTTL is the lifetime of the magic link token (default: 15 minutes).
TokenTTL time.Duration
// AutoCreate creates a new user account if the email is not found.
// Default: false — returns 404 for unknown emails.
AutoCreate bool
// RedirectURL is the base URL for the magic link.
// The token is appended as a query parameter: RedirectURL + "?token=" + token.
// Required if you want the plugin to construct the link automatically.
RedirectURL string
}Email template customization
The magic link email is sent through the Mailer bridge. To customize the email content, implement the bridge.Mailer interface and return your own HTML/text for the magic_link template type:
type myMailer struct{}
func (m *myMailer) Send(ctx context.Context, msg bridge.MailMessage) error {
switch msg.Template {
case "magic_link":
// msg.Data["token"] contains the magic link token.
// msg.Data["link"] contains the full redirect URL (if RedirectURL is configured).
return sendCustomEmail(msg.To, msg.Data)
}
return nil
}The bridge.MailMessage struct contains:
type MailMessage struct {
To string // recipient email
Template string // "magic_link", "verify_email", "reset_password"
Subject string // suggested subject line
Data map[string]string // template variables
}Security considerations
- Tokens are single-use. Once a token is consumed, it cannot be reused. Re-requesting a magic link generates a new token and invalidates all previous tokens for that email.
- Tokens expire. The default TTL is 15 minutes. Set a shorter TTL for higher-security applications.
- Tokens are not guessable. Tokens are generated using
crypto/randand are URL-safe base64-encoded. - Rate limiting. Use
RateLimitConfig.SignInLimitto prevent abuse. Magic link requests that succeed server-side but fail email delivery are counted toward the rate limit.
API routes
| Method | Path | Description |
|---|---|---|
POST | /magic-link/request | Generate a magic link token and send email |
POST | /magic-link/confirm | Confirm the token and return a session |