nextauth-authentication
Guidelines for implementing NextAuth.js (Auth.js v5) authentication in Next.js applications with session management and security best practices
What this skill does
# NextAuth Authentication
You are an expert in NextAuth.js (Auth.js v5) authentication implementation. Follow these guidelines when integrating authentication in Next.js applications.
## Core Principles
- Use Auth.js v5 patterns and the universal `auth()` function
- Implement proper session management strategy based on your needs
- Always validate sessions server-side for sensitive operations
- Configure environment variables correctly with the `AUTH_` prefix
## Installation
```bash
npm install next-auth@beta
```
## Environment Variables
```bash
# Required
AUTH_SECRET=your-32-byte-secret-here # Generate with: openssl rand -base64 32
# Provider credentials (auto-detected if named correctly)
AUTH_GITHUB_ID=your-github-client-id
AUTH_GITHUB_SECRET=your-github-client-secret
AUTH_GOOGLE_ID=your-google-client-id
AUTH_GOOGLE_SECRET=your-google-client-secret
# Optional: Custom URL (auto-detected in most environments)
AUTH_URL=https://your-domain.com
```
## Basic Configuration
### auth.ts (Root Configuration)
```typescript
import NextAuth from 'next-auth';
import GitHub from 'next-auth/providers/github';
import Google from 'next-auth/providers/google';
import Credentials from 'next-auth/providers/credentials';
import { PrismaAdapter } from '@auth/prisma-adapter';
import { prisma } from '@/lib/prisma';
export const { handlers, auth, signIn, signOut } = NextAuth({
adapter: PrismaAdapter(prisma),
providers: [
GitHub, // Credentials auto-detected from AUTH_GITHUB_ID/SECRET
Google,
Credentials({
credentials: {
email: { label: 'Email', type: 'email' },
password: { label: 'Password', type: 'password' },
},
async authorize(credentials) {
// Validate credentials
const user = await validateCredentials(credentials);
if (!user) return null;
return user;
},
}),
],
session: {
strategy: 'jwt', // or 'database'
maxAge: 30 * 24 * 60 * 60, // 30 days
},
pages: {
signIn: '/auth/signin',
error: '/auth/error',
},
callbacks: {
async jwt({ token, user, account }) {
if (user) {
token.id = user.id;
token.role = user.role;
}
return token;
},
async session({ session, token }) {
if (token) {
session.user.id = token.id as string;
session.user.role = token.role as string;
}
return session;
},
async authorized({ auth, request }) {
const isAuthenticated = !!auth?.user;
const isProtectedRoute = request.nextUrl.pathname.startsWith('/dashboard');
if (isProtectedRoute && !isAuthenticated) {
return false; // Redirect to sign-in
}
return true;
},
},
});
```
### Route Handlers (app/api/auth/[...nextauth]/route.ts)
```typescript
import { handlers } from '@/auth';
export const { GET, POST } = handlers;
```
### Middleware (middleware.ts)
```typescript
import { auth } from '@/auth';
export default auth((req) => {
const isAuthenticated = !!req.auth;
const isAuthPage = req.nextUrl.pathname.startsWith('/auth');
const isProtectedRoute = req.nextUrl.pathname.startsWith('/dashboard');
// Redirect authenticated users away from auth pages
if (isAuthenticated && isAuthPage) {
return Response.redirect(new URL('/dashboard', req.nextUrl));
}
// Redirect unauthenticated users from protected routes
if (!isAuthenticated && isProtectedRoute) {
return Response.redirect(new URL('/auth/signin', req.nextUrl));
}
});
export const config = {
matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
};
```
## Session Strategies
### JWT Strategy (Default without adapter)
Best for: Serverless, Edge runtime, minimal database queries
```typescript
export const { auth } = NextAuth({
session: {
strategy: 'jwt',
maxAge: 30 * 24 * 60 * 60, // 30 days
},
});
```
Characteristics:
- Sessions stored in encrypted cookies
- No database query per request
- Cannot be invalidated before expiration
- Works with Edge middleware
### Database Strategy
Best for: Immediate session invalidation, "sign out everywhere"
```typescript
export const { auth } = NextAuth({
adapter: PrismaAdapter(prisma),
session: {
strategy: 'database',
maxAge: 30 * 24 * 60 * 60, // 30 days
},
});
```
Characteristics:
- Sessions stored in database
- Database query on every request
- Can be invalidated immediately
- Incompatible with Edge middleware (use split config)
### Split Configuration for Edge + Database
```typescript
// auth.config.ts - Edge-compatible config
import type { NextAuthConfig } from 'next-auth';
export const authConfig: NextAuthConfig = {
pages: {
signIn: '/auth/signin',
},
callbacks: {
authorized({ auth, request }) {
return !!auth?.user;
},
},
providers: [], // Configured in auth.ts
};
// auth.ts - Full config with adapter
import NextAuth from 'next-auth';
import { PrismaAdapter } from '@auth/prisma-adapter';
import { authConfig } from './auth.config';
export const { handlers, auth, signIn, signOut } = NextAuth({
...authConfig,
adapter: PrismaAdapter(prisma),
providers: [GitHub, Google],
});
// middleware.ts - Uses edge-compatible config
import NextAuth from 'next-auth';
import { authConfig } from './auth.config';
export default NextAuth(authConfig).auth;
```
## Authentication in Components
### Server Components
```typescript
import { auth } from '@/auth';
import { redirect } from 'next/navigation';
export default async function DashboardPage() {
const session = await auth();
if (!session?.user) {
redirect('/auth/signin');
}
return (
<div>
<h1>Welcome, {session.user.name}!</h1>
<p>Email: {session.user.email}</p>
</div>
);
}
```
### Client Components
```typescript
'use client';
import { useSession } from 'next-auth/react';
export function UserProfile() {
const { data: session, status } = useSession();
if (status === 'loading') {
return <Skeleton />;
}
if (status === 'unauthenticated') {
return <SignInPrompt />;
}
return (
<div>
<img src={session.user.image} alt={session.user.name} />
<p>{session.user.name}</p>
</div>
);
}
```
### Session Provider Setup
```typescript
// app/providers.tsx
'use client';
import { SessionProvider } from 'next-auth/react';
export function Providers({ children }: { children: React.ReactNode }) {
return <SessionProvider>{children}</SessionProvider>;
}
// 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>
);
}
```
## Server Actions
```typescript
'use server';
import { auth, signIn, signOut } from '@/auth';
// Sign in action
export async function handleSignIn(provider: string) {
await signIn(provider, { redirectTo: '/dashboard' });
}
// Sign out action
export async function handleSignOut() {
await signOut({ redirectTo: '/' });
}
// Protected action
export async function createPost(formData: FormData) {
const session = await auth();
if (!session?.user) {
throw new Error('Unauthorized');
}
const title = formData.get('title') as string;
await prisma.post.create({
data: {
title,
authorId: session.user.id,
},
});
revalidatePath('/posts');
}
```
## API Route Protection
```typescript
import { auth } from '@/auth';
import { NextResponse } from 'next/server';
export async function GET() {
const session = await auth();
if (!session?.user) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const data = await fetchUserData(session.user.id);
return NextResponse.json(data);
}
```
## Role-Based Access Control
### Type Extensions
```typescript
// types/next-auth.d.ts
import { DefaultSession, DefaultUser } from 'next-auth';
import { JWT, DefaultJWT } from 'next-auth/jwt';
dRelated in Web Dev
generating-lwc-components
IncludedLightning Web Components with PICKLES methodology and 165-point scoring. Use this skill when the user creates or edits LWC components, builds wire service patterns, or writes Jest tests for LWC. TRIGGER when: user creates/edits LWC components, touches lwc/**/*.js, .html, .css, .js-meta.xml files, or asks about wire service, SLDS, or Jest LWC tests. DO NOT TRIGGER when: Apex classes (use generating-apex), Aura components, or Visualforce.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Set up queries with useQuery, mutations with useMutation, configure QueryClient caching strategies, implement optimistic updates, and handle infinite scroll with useInfiniteQuery. Use when: setting up data fetching in React projects, migrating from v4 to v5, or fixing object syntax required errors, query callbacks removed issues, cacheTime renamed to gcTime, isPending vs isLoading confusion, keepPreviousData removed problems.
document-processor-api
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
nutrient-document-processing
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Covers useMutationState, simplified optimistic updates, throwOnError, network mode (offline/PWA), and infiniteQueryOptions. Use when setting up data fetching, fixing v4→v5 migration errors (object syntax, gcTime, isPending, keepPreviousData), or debugging SSR/hydration issues with streaming server components.
accelint-nextjs-best-practices
IncludedNext.js performance optimization and best practices. Use when writing Next.js code (App Router or Pages Router); implementing Server Components, Server Actions, or API routes; optimizing RSC serialization, data fetching, or server-side rendering; reviewing Next.js code for performance issues; fixing authentication in Server Actions; or implementing Suspense boundaries, parallel data fetching, or request deduplication.