Skills api-auth-nextauth
Auth.js (NextAuth v5) authentication patterns - configuration, providers, session strategies, middleware, database adapters, role-based access, Edge compatibility
git clone https://github.com/agents-inc/skills
T=$(mktemp -d) && git clone --depth=1 https://github.com/agents-inc/skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/src/skills/api-auth-nextauth" ~/.claude/skills/agents-inc-skills-api-auth-nextauth-ead662 && rm -rf "$T"
src/skills/api-auth-nextauth/SKILL.mdAuth.js (NextAuth v5) Patterns
Quick Guide: Configure Auth.js in a root
file exportingauth.tsfrom{ auth, handlers, signIn, signOut }. Use the unifiedNextAuth()function everywhere (Server Components, Route Handlers, middleware). Default session strategy is JWT (cookie-based); add a database adapter for persistent sessions. Protect routes via middleware or per-pageauth()checks.auth()
<critical_requirements>
CRITICAL: Before Using This Skill
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
, named constants)import type
(You MUST configure Auth.js in a root
file and export auth.ts
from { auth, handlers, signIn, signOut }
)NextAuth()
(You MUST use the unified
function for server-side session access - NOT the deprecated auth()
, getServerSession()
, or getSession()
)getToken()
(You MUST use
environment variable - AUTH_SECRET
is deprecated in v5)NEXTAUTH_SECRET
(You MUST use
prefixed environment variables for provider credentials (e.g., AUTH_
, AUTH_GITHUB_ID
) - they are auto-detected)AUTH_GITHUB_SECRET
(You MUST split auth config into
(Edge-compatible) and auth.config.ts
(with adapter) when using database sessions with middleware)auth.ts
(You MUST check session inside Server Actions and API routes - middleware alone is NOT sufficient for authorization)
</critical_requirements>
Auto-detection: Auth.js, NextAuth, next-auth, authjs, auth.ts, auth.config.ts, NextAuth(), signIn, signOut, auth(), handlers, SessionProvider, useSession, AUTH_SECRET, OAuth provider, credentials provider, database adapter, @auth/prisma-adapter, @auth/drizzle-adapter, authorized callback, jwt callback, session callback, proxy auth, middleware auth
When to use:
- Adding authentication to Next.js, SvelteKit, Express, or Qwik apps
- Implementing OAuth login (GitHub, Google, Discord, etc.) with 80+ built-in providers
- Building email/magic link authentication flows
- Need JWT or database-backed session management
- Projects requiring Edge-compatible middleware authentication
When NOT to use:
- Building a custom auth system from scratch (Auth.js is opinionated)
- Need fine-grained organization/team management out of the box
- Mobile-only apps without web frontend
- Need self-hosted auth with plugin architecture
Key patterns covered:
- Auth configuration (
,auth.ts
)auth.config.ts - OAuth providers (GitHub, Google, Credentials, Email)
- Session strategies (JWT vs database)
- Session access (Server Components, Route Handlers, Client Components)
- Middleware/proxy route protection
- Database adapters (Prisma, Drizzle)
- Callbacks (jwt, session, signIn, redirect)
- Role-based access control
- Edge compatibility split configuration
Detailed Resources:
- For decision frameworks and anti-patterns, see reference.md
Core patterns:
- examples/core.md - Auth configuration, providers, callbacks
- examples/session.md - Session strategies, session access patterns
- examples/middleware.md - Route protection, middleware, Edge compatibility
- examples/database.md - Database adapters, Prisma, Drizzle
- examples/patterns.md - Role-based access, magic links, account linking
<philosophy>
Philosophy
Auth.js (v5) consolidates authentication into a single, unified API. The
auth() function replaces getServerSession, getSession, withAuth, and getToken from v4 for server-side use. useSession() remains the correct client-side API. Configuration lives in a root file, not in API routes.
Core principles:
- Framework-agnostic - Works with Next.js, SvelteKit, Express, Qwik
- Unified API - Single
function for all server-side contextsauth() - Provider ecosystem - 80+ built-in OAuth providers with auto-detection of
env varsAUTH_* - JWT by default - Stateless sessions in encrypted cookies, no database required
- Edge-compatible - Middleware runs on Edge runtime with split configuration (Next.js 16 proxy runs on Node.js)
- Progressive complexity - Start with OAuth, add database adapter, then customize callbacks
<patterns>
Core Patterns
Pattern 1: Auth Configuration
The central configuration file exports everything you need from
NextAuth().
Basic OAuth Setup
// auth.ts import NextAuth from "next-auth"; import GitHub from "next-auth/providers/github"; import Google from "next-auth/providers/google"; export const { auth, handlers, signIn, signOut } = NextAuth({ providers: [ GitHub, // Auto-detects AUTH_GITHUB_ID and AUTH_GITHUB_SECRET Google, // Auto-detects AUTH_GOOGLE_ID, AUTH_GOOGLE_SECRET ], });
// app/api/auth/[...nextauth]/route.ts import { handlers } from "@/auth"; export const { GET, POST } = handlers;
Why good: Single config file exports all auth utilities, providers auto-detect
AUTH_* env vars, API route is minimal
Environment Variables
# .env.local AUTH_SECRET="generate-with-npx-auth-secret" # Required AUTH_GITHUB_ID="your-github-client-id" # Auto-detected by GitHub provider AUTH_GITHUB_SECRET="your-github-secret" # Auto-detected by GitHub provider AUTH_GOOGLE_ID="your-google-id" # Auto-detected by Google provider AUTH_GOOGLE_SECRET="your-google-secret"
Why good:
AUTH_ prefix is standardized in v5, AUTH_SECRET replaces deprecated NEXTAUTH_SECRET, providers auto-detect credentials
Pattern 2: Providers
Auth.js supports OAuth, email/magic link, and credentials authentication. 80+ built-in OAuth providers auto-detect
AUTH_* env vars.
// OAuth: customize profile mapping GitHub({ profile(profile) { return { id: String(profile.id), name: profile.name ?? profile.login, role: "user", }; }, }); // Credentials: validate input, return null on failure Credentials({ async authorize(credentials) { const parsed = LoginSchema.safeParse(credentials); if (!parsed.success) return null; const user = await getUserByEmail(parsed.data.email); if ( !user || !(await verifyPassword(parsed.data.password, user.hashedPassword)) ) return null; return { id: user.id, name: user.name, email: user.email }; }, });
Key rules: Validate input before DB lookup, always hash passwords, return
null on failure (never throw - it leaks info). See examples/core.md for complete implementations.
Pattern 3: Callbacks
Four callbacks customize auth behavior. Data flows: jwt callback (enrich token) -> session callback (expose to client).
callbacks: { jwt({ token, user }) { if (user) { token.id = user.id; token.role = user.role ?? "user"; } return token; }, session({ session, token }) { session.user.id = token.id as string; session.user.role = token.role as string; return session; }, }
Key rules: JWT callback runs on EVERY
auth() call (keep lightweight), user param is only present at sign-in, never expose OAuth tokens to client. See examples/core.md for complete callback implementations including signIn and redirect.
Pattern 4: Session Access
The unified
auth() function replaces getServerSession, getSession, and getToken from v4 for server-side use. useSession() remains for Client Components.
| Context | How to access session |
|---|---|
| Server Component | |
| Route Handler | |
| Server Action | |
| Middleware/Proxy | or callback |
| Client Component | (requires in layout) |
Key rules: Server-side imports come from
@/auth, client-side imports from next-auth/react. Never call auth() in Client Components. See examples/session.md for complete implementations.
Pattern 5: Sign In / Sign Out Actions
Two approaches: Server Actions (recommended, progressive enhancement) or client-side.
// Server-side (recommended): import from @/auth, use Server Actions in forms import { signIn, signOut } from "@/auth"; // In form action: await signIn("github", { redirectTo: "/dashboard" }) // In form action: await signOut({ redirectTo: "/" }) // Client-side: import from next-auth/react, use onClick handlers import { signIn, signOut } from "next-auth/react"; // onClick: signIn("github", { callbackUrl: "/dashboard" })
Key rules: Server-side uses
redirectTo, client-side uses callbackUrl. signIn() throws a NEXT_REDIRECT exception internally -- don't wrap in try/catch expecting a return value. See examples/core.md for complete implementations.
Pattern 6: TypeScript Extensions
Extend session and JWT types via declaration merging in
types/next-auth.d.ts. Declare custom fields (e.g., id, role) on Session, User, and JWT interfaces using & DefaultSession["user"] to preserve defaults. See examples/core.md for the complete type declaration example.
</patterns>
<integration>
Integration Guide
Auth.js is the authentication layer. It handles identity verification, session management, and route protection. It does NOT handle authorization logic (role checks, permission systems) -- that is application code.
Framework support: Auth.js works with multiple web frameworks via framework-specific packages (
next-auth, @auth/sveltekit, @auth/express).
Database adapters: For database sessions, Auth.js provides adapter packages (
@auth/prisma-adapter, @auth/drizzle-adapter, etc.) that integrate with your ORM. See examples/database.md.
Session strategy depends on your needs:
- JWT (default) - No database needed, works on Edge, stateless
- Database - Requires adapter, server-side session store, supports immediate revocation
Auth.js does NOT handle: fine-grained authorization/RBAC, rate limiting, or database queries beyond auth -- those are application-level concerns.
</integration><red_flags>
RED FLAGS
- Using
-- deprecated in v5; usegetServerSession(authOptions)
from yourauth()auth.ts - Using
orNEXTAUTH_SECRET
-- deprecated; useNEXTAUTH_URL
(URL is auto-detected)AUTH_SECRET - Credentials provider without rate limiting -- vulnerable to brute-force attacks
- Exposing OAuth tokens to client via session callback -- keep
/accessToken
server-side onlyrefreshToken - Middleware/proxy as sole authorization -- runs before rendering but does not replace per-route checks in Server Actions/API routes
- Database adapter imported in middleware -- database ORMs can't run on Edge runtime (Next.js 14/15); split config into
+auth.config.tsauth.ts - Wrapping
in try/catch -- it throws a NEXT_REDIRECT exception internally (this is intentional)signIn() - JWT callback querying database on every call -- runs on EVERY
invocation; keep it lightweightauth()
See reference.md for the complete anti-pattern list, gotchas, and migration table.
</red_flags>
<critical_reminders>
CRITICAL REMINDERS
All code must follow project conventions in CLAUDE.md
(You MUST configure Auth.js in a root
file and export auth.ts
from { auth, handlers, signIn, signOut }
)NextAuth()
(You MUST use the unified
function for server-side session access - NOT the deprecated auth()
, getServerSession()
, or getSession()
)getToken()
(You MUST use
environment variable - AUTH_SECRET
is deprecated in v5)NEXTAUTH_SECRET
(You MUST use
prefixed environment variables for provider credentials (e.g., AUTH_
, AUTH_GITHUB_ID
) - they are auto-detected)AUTH_GITHUB_SECRET
(You MUST split auth config into
(Edge-compatible) and auth.config.ts
(with adapter) when using database sessions with middleware)auth.ts
(You MUST check session inside Server Actions and API routes - middleware alone is NOT sufficient for authorization)
Failure to follow these rules will cause authentication failures, expose deprecated patterns, or create security vulnerabilities.
</critical_reminders>