auth-patterns

Authentication Patterns in Next.js

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "auth-patterns" with this command: npx skills add davepoon/buildwithclaude/davepoon-buildwithclaude-auth-patterns

Authentication Patterns in Next.js

Overview

Next.js supports multiple authentication strategies. This skill covers common patterns including NextAuth.js (Auth.js), middleware-based protection, and session management.

Authentication Libraries

Library Best For

NextAuth.js (Auth.js) Full-featured auth with providers

Clerk Managed auth service

Lucia Lightweight, flexible auth

Supabase Auth Supabase ecosystem

Custom JWT Full control

NextAuth.js v5 Setup

Installation

npm install next-auth@beta

Configuration

// auth.ts import NextAuth from 'next-auth' import GitHub from 'next-auth/providers/github' import Credentials from 'next-auth/providers/credentials'

export const { handlers, auth, signIn, signOut } = NextAuth({ providers: [ GitHub({ clientId: process.env.GITHUB_ID, clientSecret: process.env.GITHUB_SECRET, }), Credentials({ credentials: { email: { label: 'Email', type: 'email' }, password: { label: 'Password', type: 'password' }, }, authorize: async (credentials) => { const user = await getUserByEmail(credentials.email) if (!user || !verifyPassword(credentials.password, user.password)) { return null } return user }, }), ], callbacks: { authorized: async ({ auth }) => { return !!auth }, }, })

API Route Handler

// app/api/auth/[...nextauth]/route.ts import { handlers } from '@/auth'

export const { GET, POST } = handlers

Middleware Protection

// middleware.ts export { auth as middleware } from '@/auth'

export const config = { matcher: ['/dashboard/:path*', '/api/protected/:path*'], }

Getting Session Data

In Server Components

// app/dashboard/page.tsx import { auth } from '@/auth' import { redirect } from 'next/navigation'

export default async function DashboardPage() { const session = await auth()

if (!session) { redirect('/login') }

return ( <div> <h1>Welcome, {session.user?.name}</h1> </div> ) }

In Client Components

// components/user-menu.tsx 'use client'

import { useSession } from 'next-auth/react'

export function UserMenu() { const { data: session, status } = useSession()

if (status === 'loading') { return <div>Loading...</div> }

if (!session) { return <SignInButton /> }

return ( <div> <span>{session.user?.name}</span> <SignOutButton /> </div> ) }

Session Provider Setup

// 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 }) { return ( <html> <body> <Providers>{children}</Providers> </body> </html> ) }

Sign In/Out Components

// components/auth-buttons.tsx import { signIn, signOut } from '@/auth'

export function SignInButton() { return ( <form action={async () => { 'use server' await signIn('github') }} > <button type="submit">Sign in with GitHub</button> </form> ) }

export function SignOutButton() { return ( <form action={async () => { 'use server' await signOut() }} > <button type="submit">Sign out</button> </form> ) }

Middleware-Based Auth

Basic Pattern

// middleware.ts import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server'

const protectedRoutes = ['/dashboard', '/settings', '/api/protected'] const authRoutes = ['/login', '/signup']

export function middleware(request: NextRequest) { const token = request.cookies.get('session')?.value const { pathname } = request.nextUrl

// Redirect authenticated users away from auth pages if (authRoutes.some(route => pathname.startsWith(route))) { if (token) { return NextResponse.redirect(new URL('/dashboard', request.url)) } return NextResponse.next() }

// Protect routes if (protectedRoutes.some(route => pathname.startsWith(route))) { if (!token) { const loginUrl = new URL('/login', request.url) loginUrl.searchParams.set('callbackUrl', pathname) return NextResponse.redirect(loginUrl) } }

return NextResponse.next() }

export const config = { matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'], }

With JWT Verification

// middleware.ts import { NextResponse } from 'next/server' import { jwtVerify } from 'jose'

const secret = new TextEncoder().encode(process.env.JWT_SECRET)

export async function middleware(request: NextRequest) { const token = request.cookies.get('token')?.value

if (!token) { return NextResponse.redirect(new URL('/login', request.url)) }

try { const { payload } = await jwtVerify(token, secret) // Token is valid, continue return NextResponse.next() } catch { // Token is invalid return NextResponse.redirect(new URL('/login', request.url)) } }

Role-Based Access Control

Extending Session Types

// types/next-auth.d.ts import { DefaultSession } from 'next-auth'

declare module 'next-auth' { interface Session { user: { role: 'user' | 'admin' } & DefaultSession['user'] } }

// auth.ts export const { handlers, auth } = NextAuth({ callbacks: { session: ({ session, token }) => ({ ...session, user: { ...session.user, role: token.role, }, }), jwt: ({ token, user }) => { if (user) { token.role = user.role } return token }, }, })

Role-Based Component

// components/admin-only.tsx import { auth } from '@/auth' import { redirect } from 'next/navigation'

export async function AdminOnly({ children }: { children: React.ReactNode }) { const session = await auth()

if (session?.user?.role !== 'admin') { redirect('/unauthorized') }

return <>{children}</> }

// Usage export default async function AdminPage() { return ( <AdminOnly> <AdminDashboard /> </AdminOnly> ) }

Session Storage Options

JWT (Stateless)

// auth.ts export const { auth } = NextAuth({ session: { strategy: 'jwt' }, // JWT stored in cookies, no database needed })

Database Sessions

// auth.ts import { PrismaAdapter } from '@auth/prisma-adapter' import { prisma } from '@/lib/prisma'

export const { auth } = NextAuth({ adapter: PrismaAdapter(prisma), session: { strategy: 'database' }, // Sessions stored in database })

Custom Login Page

// app/login/page.tsx 'use client'

import { signIn } from 'next-auth/react' import { useSearchParams } from 'next/navigation'

export default function LoginPage() { const searchParams = useSearchParams() const callbackUrl = searchParams.get('callbackUrl') || '/dashboard'

return ( <div className="flex flex-col gap-4"> <button onClick={() => signIn('github', { callbackUrl })} className="btn" > Sign in with GitHub </button> <button onClick={() => signIn('google', { callbackUrl })} className="btn" > Sign in with Google </button> </div> ) }

Security Best Practices

  • Use HTTPS in production

  • Set secure cookie flags (HttpOnly, Secure, SameSite)

  • Implement CSRF protection (built into NextAuth)

  • Validate redirect URLs to prevent open redirects

  • Use environment variables for secrets

  • Implement rate limiting on auth endpoints

  • Hash passwords with bcrypt or argon2

Resources

For detailed patterns, see:

  • references/middleware-auth.md

  • Advanced middleware patterns

  • references/session-management.md

  • Session strategies

  • examples/nextauth-setup.md

  • Complete NextAuth.js setup

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

file-organizer

No summary provided by upstream source.

Repository SourceNeeds Review
General

server-actions

No summary provided by upstream source.

Repository SourceNeeds Review
General

xlsx

No summary provided by upstream source.

Repository SourceNeeds Review