Browse docs

Magic link

Passwordless sign-in via a one-time link delivered by email. Links are 15-minute, single-use, and wipe on consumption.

Copy this quickstart guide as a prompt for LLMs to implement KolayLogin in your application.

Prereqs

  • Enable in instance config: authConfig.magic_link = true (dashboard → auth methods).
  • Configure Resend (KL_RESEND_API_KEY + KL_EMAIL_FROM) so the email actually delivers.

Flow

┌───────── client ─────────┐      ┌──────── KolayLogin ───────┐
│ POST /sign-in/magic-link │ ───▶ │ store tokenHash, send mail │
│                          │      │                            │
│  user clicks link        │ ◀──  │  email { verifyUrl }        │
│  GET /magic-link/verify  │ ───▶ │ exchange token → cookies   │
└──────────────────────────┘      └────────────────────────────┘

Send the link

curl -X POST $KL_API/v1/auth/sign-in/magic-link \
  -H 'content-type: application/json' \
  -d '{"email":"ada@example.com"}'

The API returns { ok: true } regardless of whether the email is registered (user enumeration defense).

Complete the flow

The email contains a URL like /v1/auth/magic-link/verify?token=.... When clicked, the API sets the session cookies and responds with the created user/session ids. Your app should land on the magic-link handler route and then redirect to the dashboard.

app/magic-link/page.tsx (Next.js handler)
export default async function MagicLinkRedirect({
  searchParams,
}: { searchParams: Promise<{ token?: string }> }) {
  const { token } = await searchParams;
  if (!token) return <p>Invalid link.</p>;
  const res = await fetch(
    `${"https://api.kolaylogin.com"}/v1/auth/magic-link/verify?token=${encodeURIComponent(token)}`,
    { credentials: 'include', cache: 'no-store' },
  );
  if (!res.ok) return <p>Link expired.</p>;
  return <meta httpEquiv="refresh" content="0;url=/dashboard" />;
}

Errors

  • 401 magic_link_disabled — feature off on the instance.
  • 401 invalid_token — token consumed, expired, or never issued.