Browse docs

Next.js quickstart

Add KolayLogin to a Next.js App Router project. The SDK talks to the hosted API at api.kolaylogin.com; you only need a publishable key to identify your environment.

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

Roles & Permissions
Once you enable Organizations from Configure → Organizations, every instance gets seeded built-in roles (org:admin, org:member) plus an editable system-permission catalog. Use useAuth().has({`permission: 'org:sys_billing:manage'`}) to gate UI on the role's permissions — they ride on the session JWT (org_perms claim) and refresh automatically when the user switches orgs via <OrganizationSwitcher />.

1. Install the SDKs

npm install @kolaylogin/nextjs @kolaylogin/react

2. Set your publishable key

Grab the pk_live_… string from your app overview (Dashboard → your app → "Publishable key" card). Paste it into .env.local:

# .env.local
NEXT_PUBLIC_KOLAYLOGIN_PUBLISHABLE_KEY=pk_live_RN-WUn5wTD6p9n3BW2rlXg

That's the only KolayLogin env var you need. The SDK ships with https://api.kolaylogin.combaked in as the default API URL — you don't configure that.

What about a secret key?
Customer-side server code (Next route handlers, server components) verifies the user's session via JWKS — that's a public operation, no secret needed. You only need a sk_live_… when you want to call admin endpoints (provisioning users from a script, calling the Admin SDK). For the auth flow itself, the publishable key is enough.

3. Mount the same-origin proxy

Cookies set by api.kolaylogin.com are scoped to that origin, so without a proxy your browser would refuse to send them back. The SDK ships a one-line proxy you export from app/api/kl/[...path]/route.ts:

app/api/kl/[...path]/route.ts
export {
  GET, POST, PUT, PATCH, DELETE, OPTIONS,
} from '@kolaylogin/nextjs/proxy';

4. Add the middleware

Declare which routes require auth. kolayloginMiddlewareverifies the session cookie via JWKS and forwards decoded claims as headers so server components don't re-verify. In Next 16 the file is proxy.ts and the export is proxy; in Next 14/15 it's middleware.ts and a default export.

proxy.ts (Next 16) / middleware.ts (Next 14/15)
import { kolayloginMiddleware, createRouteMatcher } from '@kolaylogin/nextjs';

const isProtected = createRouteMatcher([
  '/dashboard(.*)',
  '/api/private(.*)',
]);

export const proxy = kolayloginMiddleware({
  isProtectedRoute: isProtected,
  signInUrl: '/sign-in',
});

export const config = {
  matcher: ['/((?!_next|.*\\..*).*)'],
};

5. Wrap your app with the provider

app/providers.tsx
'use client';

import { KolayLoginProvider } from '@kolaylogin/react';

export function Providers({ children }: { children: React.ReactNode }) {
  // The provider reads NEXT_PUBLIC_KOLAYLOGIN_PUBLISHABLE_KEY automatically.
  return <KolayLoginProvider>{children}</KolayLoginProvider>;
}
app/layout.tsx
import { Providers } from './providers';

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

6. Drop in the prebuilt forms

<SignIn /> and <SignUp /> read your instance config (GET /v1/auth/config) and render exactly the methods + fields + social/web3 providers you've enabled in the dashboard. Toggle Google in the dashboard, the button appears. Toggle SMS off, the SMS tab disappears. Add a Privacy Policy URL on the Legal page, the link shows up under the form.

app/sign-in/page.tsx
import { SignIn } from '@kolaylogin/react';

export default function Page() {
  return <SignIn redirectUrl="/dashboard" signUpUrl="/sign-up" />;
}
app/sign-up/page.tsx
import { SignUp } from '@kolaylogin/react';

export default function Page() {
  return <SignUp redirectUrl="/dashboard" signInUrl="/sign-in" />;
}

7. Read the session server-side

app/dashboard/page.tsx
import { redirect } from 'next/navigation';
import { auth } from '@kolaylogin/nextjs';

export default async function DashboardPage() {
  const { userId, isSignedIn } = await auth();
  if (!isSignedIn) redirect('/sign-in');
  return <p>Hi {userId}</p>;
}

8. Conditional UI

import { SignedIn, SignedOut, UserButton, useUser } from '@kolaylogin/react';

export function Navbar() {
  const { user } = useUser();
  return (
    <header>
      <SignedIn>
        <span>{user?.email}</span>
        <UserButton afterSignOutUrl="/" />
      </SignedIn>
      <SignedOut>
        <a href="/sign-in">Sign in</a>
      </SignedOut>
    </header>
  );
}

Environment variables — what you actually need

KolayLogin runs on api.kolaylogin.com; you don't host the auth server, you just point your app at it. The env vars below are the complete list — everything else (OAuth credentials, webhook secrets, branding, legal URLs, every auth toggle) lives in the dashboard so the same code works unchanged across dev / staging / prod.

# .env.local
# Required — the publishable key (think OAuth client_id) for this
# environment. Public, browser-safe. Get it from the app overview card
# in the dashboard.
NEXT_PUBLIC_KOLAYLOGIN_PUBLISHABLE_KEY=pk_test_xxxxxxxxxxxxxxxxxxxxxx

# Required for any server-to-server / admin SDK call (creating users
# programmatically, listing all sessions, revoking tokens). Think OAuth
# client_secret — never ship this in the browser bundle. Get it from
# Settings → API keys.
KOLAYLOGIN_SECRET_KEY=sk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

# Optional — only set this if your instance signs JWTs with a custom
# issuer claim (Settings → JWT templates). The default matches the
# hosted SaaS issuer and works out of the box.
# KL_JWT_ISSUER=https://your-custom-issuer

Next steps