workos-authkit-nextjs

Original🇺🇸 English
Not Translated

Integrate WorkOS AuthKit with Next.js App Router (13+). Server-side rendering required.

2installs
Added on

NPX Install

npx skill4agent add workos/skills workos-authkit-nextjs

SKILL.md Content

WorkOS AuthKit for Next.js

Step 1: Fetch SDK Documentation (BLOCKING)

STOP. Do not proceed until complete.
WebFetch:
https://github.com/workos/authkit-nextjs/blob/main/README.md
The README is the source of truth. If this skill conflicts with README, follow README.

Step 2: Pre-Flight Validation

Project Structure

  • Confirm
    next.config.js
    or
    next.config.mjs
    exists
  • Confirm
    package.json
    contains
    "next"
    dependency

Environment Variables

Check
.env.local
for:
  • WORKOS_API_KEY
    - starts with
    sk_
  • WORKOS_CLIENT_ID
    - starts with
    client_
  • NEXT_PUBLIC_WORKOS_REDIRECT_URI
    - valid callback URL
  • WORKOS_COOKIE_PASSWORD
    - 32+ characters

Step 3: Install SDK

Detect package manager, install SDK package from README.
Verify: SDK package exists in node_modules before continuing.

Step 4: Version Detection (Decision Tree)

Read Next.js version from
package.json
:
Next.js version?
  |
  +-- 16+ --> Create proxy.ts at project root
  |
  +-- 15   --> Create middleware.ts (cookies() is async - handlers must await)
  |
  +-- 13-14 --> Create middleware.ts (cookies() is sync)
Critical: File MUST be at project root (or
src/
if using src directory). Never in
app/
.
Next.js 15+ async note: All route handlers and middleware accessing cookies must be async and properly await cookie operations. This is a breaking change from Next.js 14.
Middleware/proxy code: See README for
authkitMiddleware()
export pattern.

Existing Middleware (IMPORTANT)

If
middleware.ts
already exists with custom logic (rate limiting, logging, headers, etc.), use the
authkit()
composable function
instead of
authkitMiddleware
.
Pattern for composing with existing middleware:
typescript
import { NextRequest, NextResponse } from 'next/server';
import { authkit, handleAuthkitHeaders } from '@workos-inc/authkit-nextjs';

export default async function middleware(request: NextRequest) {
  // 1. Get auth session and headers from AuthKit
  const { session, headers, authorizationUrl } = await authkit(request);
  const { pathname } = request.nextUrl;

  // 2. === YOUR EXISTING MIDDLEWARE LOGIC ===
  // Rate limiting, logging, custom headers, etc.
  const rateLimitResult = checkRateLimit(request);
  if (!rateLimitResult.allowed) {
    return new NextResponse('Too Many Requests', { status: 429 });
  }

  // 3. Protect routes - redirect to auth if needed
  if (pathname.startsWith('/dashboard') && !session.user && authorizationUrl) {
    return handleAuthkitHeaders(request, headers, { redirect: authorizationUrl });
  }

  // 4. Continue with AuthKit headers properly handled
  return handleAuthkitHeaders(request, headers);
}
Key functions:
  • authkit(request)
    - Returns
    { session, headers, authorizationUrl }
    for composition
  • handleAuthkitHeaders(request, headers, options?)
    - Ensures AuthKit headers pass through correctly
  • For rewrites, use
    partitionAuthkitHeaders()
    and
    applyResponseHeaders()
    (see README)
Critical: Always return via
handleAuthkitHeaders()
to ensure
withAuth()
works in pages.

Step 5: Create Callback Route

Parse
NEXT_PUBLIC_WORKOS_REDIRECT_URI
to determine route path:
URI path          --> Route location
/auth/callback    --> app/auth/callback/route.ts
/callback         --> app/callback/route.ts
Use
handleAuth()
from SDK. Do not write custom OAuth logic.
CRITICAL for Next.js 15+: The route handler MUST be async and properly await handleAuth():
typescript
// CORRECT - Next.js 15+ requires async route handlers
export const GET = handleAuth();

// If handleAuth returns a function, ensure it's awaited in request context
Check README for exact usage. If build fails with "cookies outside request scope", the handler is likely missing async/await.

Step 6: Provider Setup (REQUIRED)

CRITICAL: You MUST wrap the app in
AuthKitProvider
in
app/layout.tsx
.
This is required for:
  • Client-side auth state via
    useAuth()
    hook
  • Consistent auth UX across client/server boundaries
  • Proper migration from Auth0 (which uses client-side auth)
tsx
// app/layout.tsx
import { AuthKitProvider } from '@workos-inc/authkit-nextjs';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <AuthKitProvider>{children}</AuthKitProvider>
      </body>
    </html>
  );
}
Check README for exact import path - it may be a subpath export like
@workos-inc/authkit-nextjs/components
.
Do NOT skip this step even if using server-side auth patterns elsewhere.

Step 7: UI Integration

Add auth UI to
app/page.tsx
using SDK functions. See README for
getUser
,
getSignInUrl
,
signOut
usage.

Verification Checklist (ALL MUST PASS)

Run these commands to confirm integration. Do not mark complete until all pass:
bash
# 1. Check middleware/proxy exists (one should match)
ls proxy.ts middleware.ts src/proxy.ts src/middleware.ts 2>/dev/null

# 2. CRITICAL: Check AuthKitProvider is in layout (REQUIRED)
grep "AuthKitProvider" app/layout.tsx || echo "FAIL: AuthKitProvider missing from layout"

# 3. Check callback route exists
find app -name "route.ts" -path "*/callback/*"

# 4. Build succeeds
npm run build
If check #2 fails: Go back to Step 6 and add AuthKitProvider. This is not optional.

Error Recovery

"cookies was called outside a request scope" (Next.js 15+)

Most common cause: Route handler not properly async or missing await.
Fix for callback route:
  1. Check that
    handleAuth()
    is exported directly:
    export const GET = handleAuth();
  2. If using custom wrapper, ensure it's
    async
    and awaits any cookie operations
  3. Verify authkit-nextjs SDK version supports Next.js 15+ (check README for compatibility)
  4. Never call
    cookies()
    at module level - only inside request handlers
This error causes OAuth codes to expire ("invalid_grant"), so fix the handler first.

"middleware.ts not found"

  • Check: File at project root or
    src/
    , not inside
    app/
  • Check: Filename matches Next.js version (proxy.ts for 16+, middleware.ts for 13-15)

"Cannot use getUser in client component"

  • Check: Component has no
    'use client'
    directive, or
  • Check: Move auth logic to server component/API route

"Module not found" for SDK import

  • Check: SDK installed before writing imports
  • Check: SDK package directory exists in node_modules

"withAuth route not covered by middleware"

  • Check: Middleware/proxy file exists at correct location
  • Check: Matcher config includes the route path

Build fails after AuthKitProvider

  • Check: README for correct import path (may be subpath export)
  • Check: No client/server boundary violations

NEXTPUBLIC prefix issues

  • Client components need
    NEXT_PUBLIC_*
    prefix
  • Server components use plain env var names