Use case

Add magic link login to Next.js

Magic link login in Next.js needs two things: an auth layer that issues and verifies single-use tokens, and an email path that reliably delivers them. AscendKit provides both, so you enable one provider and the link email goes out through your verified domain on AWS SES. Below: the full App Router setup, how magic link compares with passwords and OAuth, and the delivery failures that break passwordless auth in production.

How it works

  1. 1. Install and configure the SDK
    Add @ascendkit/nextjs, run ascendkit init, and mount the auth route handler. The CLI writes your keys into .env.local.
  2. 2. Enable magic link as a provider
    Turn magic link on for the environment. It is a configuration change, not a code change — the SDK loads the Better Auth magic-link plugin when the provider is enabled.
  3. 3. Verify your sending domain
    Magic link auth is only as reliable as its email. Verify your domain so links send from your address with DKIM and SPF, not a shared subdomain.

How do you add magic link login to a Next.js App Router app?

Three files. Mount the AscendKit auth runtime, expose the catch-all auth route, and wrap your app in the provider — magic link then works through the same components as every other provider.

The provider list is environment configuration rather than code. When magic link is enabled, the SDK dynamically imports Better Auth's magic-link plugin and wires its sendMagicLink callback to AscendKit's email sender, so the token email routes through your verified domain automatically.

That is the part teams usually underestimate. Issuing a signed token is easy; getting it into an inbox within seconds, from a domain with correct DKIM and SPF, is the work. Because auth and email are the same platform here, there is no webhook between an auth vendor and an email vendor for the link to fall through.

app/api/auth/[...all]/route.ts
import { createAscendKitAuthRuntime } from "@ascendkit/nextjs/server";
import { createAuthRouteHandlers } from "@ascendkit/nextjs/server";

export const authRuntime = createAscendKitAuthRuntime();

export const { GET, POST } = createAuthRouteHandlers(authRuntime);

How do you render the sign-in form?

Wrap the app in AscendKitProvider and drop in a sign-in surface. The provider reads which providers are enabled for the environment and renders the matching form, so turning magic link on in the dashboard changes the UI without a deploy.

You have three UX patterns available: a global modal mounted by the provider, individual SignInButton and SignUpButton components placed anywhere in your layout, or the full AscendKitAuthCard embedded on a dedicated route. Magic link works identically across all three — the user enters an email, receives a link, and lands back authenticated.

app/layout.tsx
import { AscendKitProvider } from "@ascendkit/nextjs";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <AscendKitProvider>{children}</AscendKitProvider>
      </body>
    </html>
  );
}

Magic link vs passwords vs OAuth: which should you use?

Magic link removes the password from your threat model entirely — no hashes to leak, no reset flow to build, no credential stuffing. The cost is that every sign-in depends on email delivery, and users on a different device from their inbox hit friction.

One constraint specific to AscendKit is worth knowing before you choose: enabling magic link disables credentials sign-in for that environment. They are alternative primary factors, not complementary ones. OAuth can be combined with either.

Trade-offs across the three primary sign-in methods.
Magic linkPasswordOAuth
Credential to stealNone storedPassword hashNone stored
Reset flow neededNoYesNo
Depends on email deliveryEvery sign-inReset onlyNo
Works offline from inboxNoYesYes
Setup costDomain verificationLowestOAuth app registration
Combines with AscendKit credentialsNo — mutually exclusiven/aYes

Why do magic links land in spam, and how do you prevent it?

Because the email is transactional but looks promotional to filters, and because unverified domains have no authentication signal. The fix is domain verification with DKIM and SPF before you launch, not after users complain.

AscendKit runs delivery on AWS SES and automates the setup that teams usually defer: domain verification, DKIM record generation, SPF alignment, and DNS provider detection. Sending from your own verified domain rather than a shared subdomain is the single largest factor in whether magic links arrive.

The second factor is content. Keep the link email short, transactional, and free of marketing copy or tracking-heavy markup. A one-line message with a single button outperforms a designed template for this specific email type.

Troubleshooting

The magic link says it has expired even though it was just sent

Cause: The link was opened by an email security scanner before the user clicked it. Corporate mail gateways pre-fetch URLs to check for malware, which consumes a single-use token.

Fix: This is a known trade-off of single-use tokens in enterprise environments. If your users are on corporate mail, prefer OAuth or credentials as the primary method for those accounts.

Clicking the link signs in on the wrong device

Cause: The user requested the link on desktop but opened their email on a phone. The session is created wherever the link is opened.

Fix: Expected behaviour, not a bug. Set expectations in the UI copy on the check-your-email screen so users know to open the link on the device they want to be signed in on.

Emails never arrive during local development

Cause: The sending domain is unverified, so delivery is restricted.

Fix: Verify your domain in the dashboard before testing sign-in flows. Check the email delivery logs to confirm whether a send was attempted and what the provider returned.

FAQ

Do I need a separate email service for the magic link?

No. Email is part of the platform, so the link is delivered from your verified domain on AWS SES without adding a vendor or a webhook between auth and email.

Can I combine magic link with passwords?

No. Enabling magic link disables credentials sign-in for that environment — they are alternative primary factors. OAuth providers can be combined with either.

How long is a magic link valid?

Links are single-use and short-lived, following Better Auth's magic-link plugin defaults. Opening a link consumes it, so a second click on the same link will fail.

Does magic link work outside Next.js?

The React hooks and components work in Vite, Remix, and Astro. The server-side auth runtime requires Next.js; other backends verify access tokens through the Python SDK instead.

Related guides

Start with one API key

Auth, email, surveys, and journeys share one user record, so you ship this without stitching vendors together.

Start free