In this article
September 2, 2026
September 2, 2026

How to add AuthKit to a Next.js app

Go from no auth to a full hosted sign-in flow, either in one command or step by step.

Explore with AI
Open in ChatGPT
Open in Claude
Open in Perplexity

Authentication is one of those features that looks small on a roadmap and then eats a sprint. Sign-up and sign-in are the easy part. Then come password resets, email verification, MFA, social login, session refresh, and the security review that follows all of it.

AuthKit hands you those screens and the session handling behind them, so your Next.js app gets a production-ready auth flow without you designing a single form. There are two ways to wire it up, and this tutorial covers both. The CLI installer does the integration for you in about two minutes and is where most people should start. The manual walkthrough follows, for when you want to understand or control every line.

Before you start

You'll need:

  • A WorkOS account
  • Your WorkOS API key and client ID
  • Node.js 20 or later
  • A Next.js app using the App Router

The fastest option: the CLI installer

The WorkOS CLI ships an AI installer that detects your framework, installs the right SDK, writes the routes, configures your dashboard, and validates the build. Run one command from your project root:

  
npx workos@latest install
  

The installer will:

  1. Detect your framework and version from your dependencies and file structure
  2. Open your browser to authenticate your WorkOS account
  3. Configure your dashboard, including redirect URIs, CORS origins, and homepage URL
  4. Install @workos-inc/authkit-nextjs
  5. Create /app/callback/route.ts and proxy.ts, and wrap your root layout in AuthKitProvider
  6. Write your keys to .env.local
  7. Run your build to confirm everything compiles

Here's what a run looks like:

  
◆  Detected Next.js 15.3.1 (App Router)
│
◇  Opening browser for WorkOS authentication...
│  Authenticated as nick@example.com
│
◇  Configuring your WorkOS dashboard...
│  ✓ Redirect URI set to http://localhost:3000/callback
│  ✓ Homepage URL set to http://localhost:3000
│
◇  Installing @workos-inc/authkit-nextjs...
│  ✓ Package installed
│
◇  Analyzing project structure...
│  ✓ Created /app/callback/route.ts
│  ✓ Created proxy.ts
│  ✓ Updated /app/layout.tsx with AuthKitProvider
│  ✓ Created .env.local
│
◇  Validating integration...
│  ✓ Build completed successfully
│
◆  AuthKit is ready. Run `npm run dev` to get started.
  

If you already have middleware or configuration in place, the installer composes with it rather than replacing it. Run git diff afterward to review every change.

A few useful flags:

Flag What it does
--integration nextjs Skip auto-detection and name your framework
--redirect-uri <uri> Use a custom callback URI instead of http://localhost:3000/callback
--no-validate Skip the post-install build check
--debug Verbose logging for troubleshooting

Make sure your project builds cleanly before you run the installer. Pre-existing build errors will fail validation.

Once the installer finishes, skip ahead to Validate the flow. If you'd rather wire things up yourself, keep reading.

Manual integration

Step 1: Install the SDK

  
npm install @workos-inc/authkit-nextjs
  

Step 2: Configure a redirect URI

The redirect URI is the callback endpoint WorkOS sends users to after they authenticate. That endpoint exchanges the authorization code for an authenticated user object.

In the WorkOS Dashboard, open your application under Applications, go to the Redirects tab, and add a redirect URI. Use http://localhost:3000/callback for local development.

While you're on that tab, set a Sign-out URI as well. Without one, users see an error when they sign out.

Step 3: Configure an initiate login URL

Sign-in requests are meant to start in your app, but they don't always. Someone might bookmark the hosted sign-in page, or arrive there from a password reset or invitation email.

When AuthKit detects a sign-in request that didn't originate in your app, it redirects to your initiate login URL, an endpoint you define that kicks off an AuthKit sign-in. Password reset and invitation details survive the redirect, so users land in the right place, as long as your initiate login URL starts an AuthKit sign-in rather than rendering your own sign-in page.

Set it on the same Redirects tab in the dashboard.

Step 4: Set your secrets

Add these to .env.local:

  
WORKOS_API_KEY='sk_example_123456789'
WORKOS_CLIENT_ID='client_123456789'
WORKOS_COOKIE_PASSWORD='<your password>'

# configured in the WorkOS dashboard
NEXT_PUBLIC_WORKOS_REDIRECT_URI='http://localhost:3000/callback'
  

The NEXT_PUBLIC prefix on the redirect URI makes the value available in edge functions and proxy configurations, which matters for things like Vercel preview deployments.

The cookie password encrypts your session cookies and must be at least 32 characters. Generate one with:

  
openssl rand -base64 32
  

Step 5: Wrap your app in the provider

AuthKitProvider handles auth edge cases and is required around your app layout.

  
// app/layout.tsx
import { AuthKitProvider } from '@workos-inc/authkit-nextjs/components';

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        <AuthKitProvider>{children}</AuthKitProvider>
      </body>
    </html>
  );
}
  

Step 6: Add the proxy

Next.js needs a proxy to determine which routes require authentication. (This was called middleware before Next 16.)

You have two options. Use authkitMiddleware if your proxy only handles auth. Use the composable authkit method if your proxy needs to do other work too.

Option A: the complete proxy

With authkitMiddleware, session management and redirects are handled for you. There are two modes.

Page based auth. Protected routes are decided by where you call withAuth({ ensureSignedIn: true }), covered in step 9.

  
// proxy.ts
import { authkitMiddleware } from '@workos-inc/authkit-nextjs';

export default authkitMiddleware();

// Match against pages that require authentication.
// Leave this out to apply authentication to every page.
export const config = { matcher: ['/'] };
  

Middleware auth. Every route is protected by default, with exceptions listed explicitly.

  
// proxy.ts
import { authkitMiddleware } from '@workos-inc/authkit-nextjs';

export default authkitMiddleware({
  middlewareAuth: {
    enabled: true,
    unauthenticatedPaths: ['/'],
  },
});

export const config = { matcher: ['/', '/account/:page*'] };
  

Here the home page is public, and /account and everything under it requires a signed-in user.

Option B: the composable proxy

The authkit method manages the session and leaves route protection to you.

  
// proxy.ts
import { authkit } from '@workos-inc/authkit-nextjs';
import { NextResponse } from 'next/server';

export default async function proxy(request) {
  // The auth object contains the session, response headers, and an
  // authorization URL for when the session isn't valid. This method sets
  // the cookie and refreshes the session for you.
  const {
    session,
    headers: authkitHeaders,
    authorizationUrl,
  } = await authkit(request, { debug: true });

  const { pathname } = new URL(request.url);

  // You decide what happens when there's no session on a protected route.
  if (pathname.startsWith('/account') && !session.user) {
    const response = NextResponse.redirect(authorizationUrl);
    for (const [key, value] of authkitHeaders) {
      if (key.toLowerCase() === 'set-cookie') {
        response.headers.append(key, value);
      } else {
        response.headers.set(key, value);
      }
    }
    return response;
  }

  const response = NextResponse.next({
    request: { headers: new Headers(request.headers) },
  });

  for (const [key, value] of authkitHeaders) {
    if (key.toLowerCase() === 'set-cookie') {
      response.headers.append(key, value);
    } else {
      response.headers.set(key, value);
    }
  }

  return response;
}

export const config = { matcher: ['/', '/account'] };
  

Preserving the AuthKit headers on redirects matters. Drop them and you drop the session cookie.

Step 7: Add the callback route

This route must match both your NEXT_PUBLIC_WORKOS_REDIRECT_URI and the redirect URI in your dashboard.

  
// app/callback/route.ts
import { handleAuth } from '@workos-inc/authkit-nextjs';

// Redirects to `/` after a successful sign-in.
// Customize with `handleAuth({ returnPathname: '/foo' })`.
export const GET = handleAuth();
  

Step 8: Add the login route

This is the endpoint you configured as your initiate login URL. It generates an AuthKit authorization URL server side and redirects the user to it.

  
// app/login/route.ts
import { getSignInUrl } from '@workos-inc/authkit-nextjs';
import { redirect } from 'next/navigation';

export const GET = async () => {
  const signInUrl = await getSignInUrl();
  return redirect(signInUrl);
};
  

Step 9: Read the authenticated user

AuthKit works in both server and client components.

In a server component, use withAuth:

  
// app/page.jsx
import Link from 'next/link';
import { getSignUpUrl, withAuth } from '@workos-inc/authkit-nextjs';

export default async function HomePage() {
  // Returns the user from the session, or null if nobody is signed in.
  const { user } = await withAuth();
  const signUpUrl = await getSignUpUrl();

  if (!user) {
    return (
      <main>
        <h1>Welcome</h1>
        <p>Please sign in to continue.</p>
        <Link href="/login">Sign in</Link>
        {' | '}
        <Link href={signUpUrl}>Sign up</Link>
      </main>
    );
  }

  return (
    <main>
      <h1>Welcome back{user.firstName && `, ${user.firstName}`}</h1>
      <p>Email: {user.email}</p>
    </main>
  );
}
  

In a client component, use the useAuth hook:

  
'use client';

import { useAuth } from '@workos-inc/authkit-nextjs/components';

export default function HomePage() {
  const { user, loading } = useAuth();

  if (loading) return <div>Loading...</div>;

  return <p>Welcome back{user.firstName && `, ${user.firstName}`}</p>;
}
  

Step 10: Protect routes

For pages that require a signed-in user, pass ensureSignedIn. Anyone without a session is redirected to AuthKit automatically.

In a server component:

  
// app/protected/page.tsx
import { withAuth } from '@workos-inc/authkit-nextjs';

export default async function ProtectedPage() {
  const { user } = await withAuth({ ensureSignedIn: true });

  return <p>Welcome back{user.firstName && `, ${user.firstName}`}</p>;
}
  

In a client component:

  
'use client';

import { useAuth } from '@workos-inc/authkit-nextjs/components';

export default function ProtectedPage() {
  const { user, loading } = useAuth({ ensureSignedIn: true });

  if (loading) return <div>Loading...</div>;

  return <p>Welcome back{user.firstName && `, ${user.firstName}`}</p>;
}
  

Step 11: Let users sign out

Call signOut from a server action. After signing out, users land on the sign-out URI you configured in the dashboard.

  
import { signOut, withAuth } from '@workos-inc/authkit-nextjs';

export default async function HomePage() {
  const { user } = await withAuth();

  return (
    <main>
      <h1>Welcome back{user.firstName && `, ${user.firstName}`}</h1>
      <form
        action={async () => {
          'use server';
          await signOut();
        }}
      >
        <button type="submit">Sign out</button>
      </form>
    </main>
  );
}
  

Validate the flow

Start your dev server:

  
npm run dev
  

Go to localhost:3000 and sign up for an account. Sign out, then sign back in with the credentials you just created. The new user should appear under Users in the WorkOS Dashboard.

Troubleshooting

  • Users see an error on sign-out. You haven't configured a sign-out URI in the dashboard. Add one on the Redirects tab.
  • Sessions don't persist. Check that WORKOS_COOKIE_PASSWORD is at least 32 characters, and that your composable proxy forwards AuthKit's set-cookie headers on redirects.
  • Callback errors after sign-in. Your callback route, NEXT_PUBLIC_WORKOS_REDIRECT_URI, and the dashboard redirect URI all have to match exactly.
  • Something else. Run workos doctor from your project root. It checks your SDK version, environment configuration, connectivity, dashboard settings, and auth patterns.

What's next

  • Give your coding agent WorkOS context with workos skills install, supported in Claude Code, Codex, Cursor, and Goose
  • Read up on session management
  • Match AuthKit to your product with branding
  • Browse the example apps