Authentication Patterns with NextAuth.js
Authentication is a core part of nearly every modern web application. Whether you're building a blog, a SaaS platform, or a portfolio dashboard, ensuring secure and flexible user authentication is essential. NextAuth.js is the de facto authentication library for Next.js applications, offering robust and extensible authentication strategies out of the box.
In this post, we’ll explore the most effective authentication patterns with NextAuth.js—covering credentials, OAuth, role-based access, and session handling—so you can confidently implement secure authentication in your Next.js apps.
What is NextAuth.js?
NextAuth.js is a full-featured authentication library for Next.js. It supports:
- OAuth providers (Google, GitHub, etc.)
- Email/password (credentials)
- Magic links
- JWT and database sessions
- Role-based access control
It integrates easily with both Next.js App Router and Pages Router, and works well with React Server Components (RSC), making it a top choice for modern apps.
1. OAuth Authentication Pattern
Best for: Quick and secure sign-in using Google, GitHub, Facebook, etc.
// [...nextauth].ts
import NextAuth from 'next-auth';
import GoogleProvider from 'next-auth/providers/google';
export const authOptions = {
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
}),
],
};
export default NextAuth(authOptions);
Why use it?
- Easy for users—no password needed
- Reduces login friction
- Secure and maintained by major identity providers
2. Credentials Authentication Pattern
Best for: Custom login forms and internal user systems.
import CredentialsProvider from 'next-auth/providers/credentials';
CredentialsProvider({
name: 'Credentials',
credentials: {
email: { label: 'Email', type: 'text' },
password: { label: 'Password', type: 'password' }
},
async authorize(credentials) {
const user = await findUser(credentials.email, credentials.password);
if (user) return user;
return null;
}
});
Security Tip: Always hash and salt passwords, and validate on the server. Do not expose secrets in the client.
3. JWT-Based Sessions Pattern
Best for: Serverless apps and stateless APIs.
export const authOptions = {
session: {
strategy: 'jwt',
},
jwt: {
secret: process.env.JWT_SECRET,
},
// ...providers
};
Pros:
- Lightweight and stateless
- Ideal for distributed deployments (e.g., Vercel)
- Works well with APIs and mobile apps
4. Database Sessions Pattern
Best for: Traditional apps needing session persistence or server-side session invalidation.
export const authOptions = {
session: {
strategy: 'database',
},
adapter: PrismaAdapter(prisma),
// ...providers
};
Pros:
- Sessions are stored in your DB
- You can revoke or track sessions
- Useful for enterprise-level apps
5. Role-Based Access Control (RBAC)
Best for: Admin panels, dashboards, or tiered user systems.
callbacks: {
async session({ session, token }) {
session.user.role = token.role;
return session;
},
async jwt({ token, user }) {
if (user) token.role = user.role;
return token;
},
}
Then check user roles in your app:
if (session?.user?.role !== 'admin') {
return <p>Access Denied</p>;
}
6. Protecting Routes (Client + Server)
Client-side (React component):
import { useSession } from 'next-auth/react';
const Dashboard = () => {
const { data: session, status } = useSession({ required: true });
if (status === 'loading') return <p>Loading...</p>;
return <p>Welcome {session?.user?.email}</p>;
};
Server-side (App Router):
import { getServerSession } from 'next-auth/next';
export async function GET() {
const session = await getServerSession(authOptions);
if (!session) return new Response('Unauthorized', { status: 401 });
return new Response(JSON.stringify(session));
}
Refresh Token Rotation (Advanced Pattern)
If you're integrating with OAuth APIs (like Google APIs), use refresh tokens to maintain sessions. NextAuth supports custom logic for managing them securely.
Final Thoughts
NextAuth.js gives you a secure, modular, and developer-friendly way to handle authentication in your Next.js applications. Whether you're building a personal project or an enterprise-grade app, mastering these patterns will help you create robust and secure user experiences.