allowSignup option enabled, an account is created automatically when the link or code is used for an email that is not registered yet — just like OAuth sign-in. This is disabled by default (see Sign-Up Behavior).
The magic link email delivery setup below (provider, sender, template, redirect URL) is configured under theemail:key ofstartApp. To run code on related auth events, register callbacks underauth:— see Auth Hooks.
Configuration
Magic link authentication is disabled by default. Enable it under theauth: key of startApp and configure the email delivery under the email: key:
redirectUrl is the page in your app that completes the sign-in. Modelence redirects here after validating the token and storing it in an httpOnly cookie, so this URL never carries the token. This is also where you call loginWithMagicLink().
How It Works
- User requests a magic link by providing their email
- A secure single-use link token and a 6-digit one-time code are generated, hashed, and stored in the database (only the hashes are stored, never the raw values). Both expire after 15 minutes, and using either one invalidates both
- An email with the sign-in link and the code is sent to the address. The response is always the same generic message, so it never reveals whether an email is registered (with
allowSignupdisabled, unknown emails get the same response but no email is sent) - The link points at a Modelence server route, not your SPA page. That route validates the token, stores it in a short-lived
httpOnlycookie, and redirects to your configuredredirectUrlwithout the token in the URL. The token is not consumed at this step, so email security scanners that prefetch links cannot burn it - On that page, you call
loginWithMagicLink()— the token is read server-side from the cookie, atomically consumed (single-use), and the user is signed in. Alternatively, the user types the code anywhere and you callloginWithOneTimeCode({ email, code }) - If no account exists for the email and
allowSignupis enabled, one is created first (see Sign-Up Behavior) - The email address is marked as verified — using the emailed link or code proves ownership
The magic link token never reaches your client code or appears in a browser URL — it is exchanged server-side through an
httpOnly cookie, the same pattern used by password reset. This closes the window where a token could leak via the address bar, browser history, or the Referer header.Base URL requirement
The magic link is built from your site’s base URL, so you must set it via the_system.site.url config (or the MODELENCE_SITE_URL environment variable). If it isn’t set, sending a magic link fails with a clear error rather than emailing a broken link.
Sender address requirement
email.from must be set to an address on your own domain. There is no default sender — if it isn’t configured, sending a magic link fails with a clear error rather than sending from an address your email provider can’t authenticate (SPF/DKIM).
Client Implementation
Request a Magic Link
Complete the Sign-In
The user lands on yourredirectUrl page after clicking the email link. The token is already held in an httpOnly cookie, so no arguments are needed:
Sign In with the One-Time Code
The same email carries a 6-digit code, so users can complete the sign-in without clicking the link — the natural path for native apps (Expo / React Native) where deep links aren’t set up, or when the email is opened on a different device than the one signing in:482 193 and 482-193 both work. Each code tolerates only a few wrong guesses before it is invalidated, and using the code invalidates the link (and vice versa).
Sign-Up Behavior
Automatic account creation is a separate opt-in from enabling magic links, and it is disabled by default: unknown emails receive no email (the response stays the same generic message), and a link or code can never create an account. Enable it withallowSignup:
allowSignup enabled, when a magic link is used for an email with no existing account, Modelence creates one automatically:
- The account is created with the email already marked as verified
- The handle is derived from the email local-part (or via your
generateHandlehook) - The
onBeforeSignuphook runs first and can reject the signup by throwing;onAfterSignupfires after the account is created - No password is set — the user can keep signing in with magic links, add a password later via password reset, or link an OAuth provider
onAfterLogin fires as usual. All magic link hook invocations use provider: 'magicLink'.
Custom Email Template
The template receives both credentials —magicLinkUrl (the clickable link) and code (the typed one-time code) — so you can render either or both:
Security
- Single-use, short-lived credentials — the link and code work exactly once (using either invalidates both) and expire after 15 minutes
- Hashed at rest — only SHA-256 hashes are stored, so a database leak does not expose usable links
- Scanner-safe — the emailed link only stashes the token in a cookie; it is consumed by the follow-up client call, so corporate email scanners that prefetch links do not invalidate them
- Guess-resistant codes — each code allows only a handful of wrong guesses before it is invalidated, and code attempts are separately rate limited per IP and per email
- No enumeration — requesting a link always returns the same generic response, whether or not the email is registered; a wrong code returns the same error as an unknown email
- Rate limited — magic link requests and code attempts are limited per IP and per email address; see Rate Limiting to customize the
magicLinkandoneTimeCodebuckets