Kno2gether kno2gether.com ↗ Try Knotie free
Build Recipe + Env Checklist

Magic-Link Login for Client Apps: Passwordless, Domain-Locked, Done in an Afternoon

Stop making clients remember another password. Here's the exact stack — Next.js + NextAuth (Auth.js) + an email service — to ship a passwordless, company-domain-locked login, with the env-setup checklist so your keys never get hardcoded.

Start free with Knotie
01

The login nobody complains about

Every password you hand a client is a small liability you now own. They forget it (support ticket). They reset it (another support ticket). They write it on a sticky note or reuse it from another site (your security hole). Multiply that across every client and every login, and password management quietly becomes a job you never agreed to do.

A magic link removes the whole category of problem. The client types their email, you email them a one-time sign-in link, they click it, and they're in — no password to create, remember, reset, or leak. There's nothing for them to manage, and crucially, nothing for you to store: no password hashes sitting in your database waiting to be breached. It's the same pattern you've already used a hundred times when a site emails you a 'click here to log in' link.
02

Why it's quietly more secure (an honest framing)

To complete a magic-link login, the person has to open the actual email in the client's inbox. That means access to the account is gated on possession of that inbox — which is, in practice, a second factor for free: you can't log in just by knowing or guessing a password, because there is no password.

Be precise about this, though: a magic link is effectively a possession factor (you control the inbox), not a literal TOTP/authenticator-app two-factor setup. It's stronger than a password alone because it removes password reuse, guessing and credential-stuffing as attack paths — but its security rests on the client's email account being secure. If you need true MFA on top, NextAuth supports adding it; the magic link itself is the passwordless base, not a replacement for a hardware key in high-risk contexts.
03

The stack — boring in the best way

You don't need anything exotic. Three pieces, all of which you've probably already used:
  • Next.js (or any framework Auth.js supports) — your app and its API routes.
  • NextAuth / Auth.js — the open-source auth library. It ships the passwordless email flow, generates and verifies the one-time sign-in token, and manages the session. (NextAuth and Auth.js are the same project — Auth.js is the framework-agnostic name; in Next.js you still install next-auth.)
  • An email provider — to actually send the link. Auth.js has built-in providers for Resend, Nodemailer (any SMTP), Sendgrid, Postmark, Loops and more. Resend is the simplest modern option.
  • A database adapter — required for the email/magic-link flow, because Auth.js stores the verification token to validate the link when it's clicked. Any supported adapter (Postgres, MySQL, SQLite, Prisma, Drizzle, etc.) works.
04

Lock sign-in to one company domain

For a client dashboard you usually want exactly one company's people to get in — and nobody else. Auth.js gives you a hook for this: the signIn callback. Return true to allow the sign-in, false to reject it. Restricting to a single domain is one line:
The official docs show this pattern with profile.email.endsWith("@company.com") for OAuth providers; for the email/magic-link provider the address comes through on the user/email argument. Either way the rule is the same: reject any address that isn't on the allowed company domain. Want multiple clients? Swap the single check for an allow-list of domains.
05

The env-setup checklist (keys never hardcoded)

Authentication breaks the moment a secret leaks, so every key lives in environment variables — never in the committed code. Run this checklist before you ship:
  • Generate the auth secret: npx auth secret (the documented Auth.js way — it writes AUTH_SECRET into your .env for you). Prefer a manual command? openssl rand -base64 33 produces an equivalent high-entropy value.
  • AUTH_SECRET — the value above. Used to sign/encrypt the session. Different value per environment.
  • AUTH_RESEND_KEY (or your provider's key) — the email-sending API key, from the Resend dashboard.
  • The from address — a verified sender on a domain you've authenticated in Resend (SPF/DKIM set), so the magic-link emails actually land in the inbox and not in spam.
  • Database URL — for the adapter that stores verification tokens + sessions.
  • Never commit .env: confirm it's in .gitignore, and set the same keys in your host's encrypted env (Vercel/Cloudflare/Fly env vars) for production.
06

Ship it in an afternoon — the build order

Put together, the path from empty project to working domain-locked login is short:
  • 1. npm i next-auth + your chosen adapter + the email provider package.
  • 2. Run npx auth secret and add your provider key + from address to .env.
  • 3. Configure the provider (Resend) and the adapter in auth.ts.
  • 4. Add the signIn callback with your company-domain rule.
  • 5. Drop a 'Sign in with email' form on the page; on submit it triggers the magic-link email.
  • 6. Test: a matching-domain address gets the link and gets in; a non-matching one is rejected at the callback.
07

One honest caveat

Magic links are an excellent default for client dashboards, but they're not magic in the literal sense. Deliverability matters — if the email lands in spam, the client can't log in, so authenticate your sending domain properly. And the security model leans entirely on the client's email account being secure; for genuinely high-risk systems, layer real MFA on top rather than treating the inbox as the only gate. For the everyday client portal, though, this is the login that generates zero support tickets and gives you no password database to lose sleep over.

Watch the 60-second version

Get the magic-link setup checklist + the next builder breakdown

I'll send the env + stack checklist for this build, plus a new builder concept broken down like this on a regular cadence. No spam, unsubscribe anytime.

By submitting you agree to our Privacy Policy & Terms. Unsubscribe anytime.

Frequently asked questions

Is NextAuth the same thing as Auth.js?
Yes. Auth.js is the framework-agnostic evolution of NextAuth — same project, broader scope (works beyond Next.js). In a Next.js app you still install the next-auth package; the magic-link email flow, providers and callbacks described here are all part of it.
Do I really need a database for magic links?
For the email/magic-link (passwordless) flow, yes — Auth.js requires a database adapter because it stores the one-time verification token so it can validate the link when the user clicks it. Any supported adapter works (Postgres, MySQL, SQLite, Prisma, Drizzle, and more).
Is a magic link really 'two-factor authentication'?
Not literally. It's effectively a possession factor — to log in you must control the email inbox the link was sent to — which is stronger than a password alone because there's no password to guess, reuse or steal. But it isn't a TOTP/authenticator-app 2FA setup. If you need true MFA, add it on top; the magic link is the passwordless base layer.
How do I restrict sign-in to just one company's domain?
Use the Auth.js signIn callback and return whether the email ends with the allowed domain — e.g. user.email?.endsWith('@theirCompany.com'). Return false for anything else and that address is rejected. For several clients, check against an allow-list of domains instead of one.
How is the AUTH_SECRET generated?
The documented way is npx auth secret, which generates the value and writes it into your .env. A manual equivalent is openssl rand -base64 33. Use a different secret per environment and keep all of them in env vars — never in committed code.
Sources · · · ·

Want this running under YOUR brand?

Knotie is a white-label AI platform — resell voice agents, chat agents, and automations under your own brand, your domain, your prices. Built-in credit billing means you keep the margin. Start free.

Start free with Knotie