Auth.js (NextAuth v5) authentication patterns - configuration, providers, session strategies, middleware, database adapters, role-based access, Edge compatibility
Scanned 9/5/2026
Install to Claude Code
npx -y skills add agents-inc/skills --skill api-auth-nextauth --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Api Auth Nextauth?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/agents-inc-api-auth-nextauth-skills)More formats (shields.io, HTML) on the badges page.
---
name: api-auth-nextauth
description: Auth.js (NextAuth v5) authentication patterns - configuration, providers, session strategies, middleware, database adapters, role-based access, Edge compatibility
---
# Auth.js (NextAuth v5) Patterns
> **Quick Guide:** Configure Auth.js in a root `auth.ts` file exporting `{ auth, handlers, signIn, signOut }` from `NextAuth()`. Use the unified `auth()` 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-page `auth()` checks.
---
<critical_requirements>
## CRITICAL: Before Using This Skill
> **All code must follow project conventions in CLAUDE.md** (kebab-case, named exports, import ordering, `import type`, named constants)
**(You MUST configure Auth.js in a root `auth.ts` file and export `{ auth, handlers, signIn, signOut }` from `NextAuth()`)**
**(You MUST use the unified `auth()` function for server-side session access - NOT the deprecated `getServerSession()`, `getSession()`, or `getToken()`)**
**(You MUST use `AUTH_SECRET` environment variable - `NEXTAUTH_SECRET` is deprecated in v5)**
**(You MUST use `AUTH_` prefixed environment variables for provider credentials (e.g., `AUTH_GITHUB_ID`, `AUTH_GITHUB_SECRET`) - they are auto-detected)**
**(You MUST split auth config into `auth.config.ts` (Edge-compatible) and `auth.ts` (with adapter) when using database sessions with middleware)**
**(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](reference.md)
**Core patterns:**
- [examples/core.md](examples/core.md) - Auth configuration, providers, callbacks
- [examples/session.md](examples/session.md) - Session strategies, session access patterns
- [examples/middleware.md](examples/middleware.md) - Route protection, middleware, Edge compatibility
- [examples/database.md](examples/database.md) - Database adapters, Prisma, Drizzle
- [examples/patterns.md](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:**
1. **Framework-agnostic** - Works with Next.js, SvelteKit, Express, Qwik
2. **Unified API** - Single `auth()` function for all server-side contexts
3. **Provider ecosystem** - 80+ built-in OAuth providers with auto-detection of `AUTH_*` env vars
4. **JWT by default** - Stateless sessions in encrypted cookies, no database required
5. **Edge-compatible** - Middleware runs on Edge runtime with split configuration (Next.js 16 proxy runs on Node.js)
6. **Progressive complexity** - Start with OAuth, add database adapter, then customize callbacks
</philosophy>
---
<patterns>
## Core Patterns
### Pattern 1: Auth Configuration
The central configuration file exports everything you need from `NextAuth()`.
#### Basic OAuth Setup
```typescript
// 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
],
});
```
```typescript
// 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
```bash
# .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.
```typescript
// 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](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).
```typescript
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](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 | `const session = await auth()` |
| Route Handler | `export const GET = auth(function GET(req) { req.auth })` |
| Server Action | `const session = await auth()` |
| Middleware/Proxy | `export { auth as middleware }` or `authorized` callback |
| Client Component | `useSession()` (requires `SessionProvider` 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](examples/session.md) for complete implementations.
---
### Pattern 5: Sign In / Sign Out Actions
Two approaches: Server Actions (recommended, progressive enhancement) or client-side.
```typescript
// 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](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](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](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 `getServerSession(authOptions)`** -- deprecated in v5; use `auth()` from your `auth.ts`
- **Using `NEXTAUTH_SECRET` or `NEXTAUTH_URL`** -- deprecated; use `AUTH_SECRET` (URL is auto-detected)
- **Credentials provider without rate limiting** -- vulnerable to brute-force attacks
- **Exposing OAuth tokens to client via session callback** -- keep `accessToken`/`refreshToken` server-side only
- **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.ts` + `auth.ts`
- **Wrapping `signIn()` in try/catch** -- it throws a NEXT_REDIRECT exception internally (this is intentional)
- **JWT callback querying database on every call** -- runs on EVERY `auth()` invocation; keep it lightweight
See [reference.md](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 `auth.ts` file and export `{ auth, handlers, signIn, signOut }` from `NextAuth()`)**
**(You MUST use the unified `auth()` function for server-side session access - NOT the deprecated `getServerSession()`, `getSession()`, or `getToken()`)**
**(You MUST use `AUTH_SECRET` environment variable - `NEXTAUTH_SECRET` is deprecated in v5)**
**(You MUST use `AUTH_` prefixed environment variables for provider credentials (e.g., `AUTH_GITHUB_ID`, `AUTH_GITHUB_SECRET`) - they are auto-detected)**
**(You MUST split auth config into `auth.config.ts` (Edge-compatible) and `auth.ts` (with adapter) when using database sessions with middleware)**
**(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>
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!