Nextauth
Build with NextAuth.js (Auth.js) for authentication in Next.js and other frameworks. Use this skill when the project needs self-hosted auth with OAuth providers, credentials, magic links, database sessions, JWT, callbacks, or middleware protection. Covers Auth.js v5, providers, adapters, callbacks, middleware, and session management.
You are an auth specialist who integrates NextAuth.js (Auth.js v5) into projects. Auth.js is an open-source, self-hosted authentication library for Next.js and other frameworks, supporting OAuth, credentials, magic links, and database-backed sessions. ## Key Points - Use JWT strategy for serverless (no database session lookups) - Use database strategy when you need to revoke sessions instantly - Extend the session via callbacks to include user ID and roles - Use middleware for route protection — cleaner than checking in every page - Always set `AUTH_SECRET` — required for token encryption - Use the Credentials provider only with proper password hashing (bcrypt/argon2) - Type-augment the Session and JWT interfaces for type safety - Storing sensitive data in the JWT — it's readable (base64), not encrypted - Using Credentials provider without rate limiting — vulnerable to brute force - Not setting custom pages — the default pages are functional but unstyled - Calling `useSession()` without `<SessionProvider>` — returns undefined - Not handling the loading state — `status` starts as `"loading"` ## Quick Example ```bash npm install next-auth@beta ``` ```env AUTH_SECRET=<random-32-byte-string> AUTH_GOOGLE_ID=your-google-client-id AUTH_GOOGLE_SECRET=your-google-client-secret AUTH_GITHUB_ID=your-github-client-id AUTH_GITHUB_SECRET=your-github-client-secret ```
skilldb get auth-services-skills/nextauthFull skill: 321 linesNextAuth.js (Auth.js) Integration
You are an auth specialist who integrates NextAuth.js (Auth.js v5) into projects. Auth.js is an open-source, self-hosted authentication library for Next.js and other frameworks, supporting OAuth, credentials, magic links, and database-backed sessions.
Core Philosophy
Self-hosted, zero vendor lock-in
Auth.js runs in your application — no external auth service, no per-user pricing. You own the user data, the session storage, and the auth flow. Switch providers or databases without changing your auth code.
Provider-agnostic
Auth.js supports 80+ OAuth providers (Google, GitHub, Discord, etc.), email/magic link, and custom credentials. Adding a provider is a few lines of config. Removing one is deleting those lines.
Adapter pattern
Auth.js uses adapters to persist users and sessions to any database — Prisma, Drizzle, MongoDB, Supabase, DynamoDB. The adapter interface is simple: implement a few functions and your database is wired in.
Setup
Install (Auth.js v5)
npm install next-auth@beta
Environment variables
AUTH_SECRET=<random-32-byte-string>
AUTH_GOOGLE_ID=your-google-client-id
AUTH_GOOGLE_SECRET=your-google-client-secret
AUTH_GITHUB_ID=your-github-client-id
AUTH_GITHUB_SECRET=your-github-client-secret
Configuration
// auth.ts
import NextAuth from 'next-auth';
import Google from 'next-auth/providers/google';
import GitHub from 'next-auth/providers/github';
import Credentials from 'next-auth/providers/credentials';
import { PrismaAdapter } from '@auth/prisma-adapter';
import { prisma } from './lib/prisma';
import bcrypt from 'bcryptjs';
export const { handlers, auth, signIn, signOut } = NextAuth({
adapter: PrismaAdapter(prisma),
providers: [
Google,
GitHub,
Credentials({
credentials: {
email: { label: 'Email', type: 'email' },
password: { label: 'Password', type: 'password' },
},
authorize: async (credentials) => {
const user = await prisma.user.findUnique({
where: { email: credentials.email as string },
});
if (!user?.hashedPassword) return null;
const valid = await bcrypt.compare(
credentials.password as string,
user.hashedPassword
);
return valid ? user : null;
},
}),
],
session: { strategy: 'jwt' },
pages: {
signIn: '/login',
error: '/login',
},
});
Route handler
// app/api/auth/[...nextauth]/route.ts
import { handlers } from '@/auth';
export const { GET, POST } = handlers;
Middleware
// middleware.ts
import { auth } from './auth';
export default auth((req) => {
if (!req.auth && req.nextUrl.pathname !== '/login') {
const newUrl = new URL('/login', req.nextUrl.origin);
return Response.redirect(newUrl);
}
});
export const config = {
matcher: ['/dashboard/:path*', '/api/protected/:path*'],
};
Key Techniques
Server-side session
import { auth } from '@/auth';
// In Server Components
export default async function DashboardPage() {
const session = await auth();
if (!session?.user) redirect('/login');
return <div>Welcome {session.user.name}</div>;
}
// In API routes
export async function GET() {
const session = await auth();
if (!session?.user) {
return new Response('Unauthorized', { status: 401 });
}
const data = await prisma.post.findMany({
where: { authorId: session.user.id },
});
return Response.json(data);
}
Client-side session
'use client';
import { useSession, signIn, signOut } from 'next-auth/react';
function AuthButton() {
const { data: session, status } = useSession();
if (status === 'loading') return <div>Loading...</div>;
if (session) {
return (
<div>
<p>{session.user?.email}</p>
<button onClick={() => signOut()}>Sign out</button>
</div>
);
}
return (
<div>
<button onClick={() => signIn('google')}>Sign in with Google</button>
<button onClick={() => signIn('github')}>Sign in with GitHub</button>
</div>
);
}
Session provider (layout)
// app/layout.tsx
import { SessionProvider } from 'next-auth/react';
import { auth } from '@/auth';
export default async function RootLayout({ children }: { children: React.ReactNode }) {
const session = await auth();
return (
<html>
<body>
<SessionProvider session={session}>
{children}
</SessionProvider>
</body>
</html>
);
}
Callbacks (customize tokens and sessions)
export const { handlers, auth, signIn, signOut } = NextAuth({
// ...providers, adapter
callbacks: {
// Add user ID and role to JWT
jwt({ token, user }) {
if (user) {
token.id = user.id;
token.role = user.role;
}
return token;
},
// Add user ID and role to session
session({ session, token }) {
session.user.id = token.id as string;
session.user.role = token.role as string;
return session;
},
// Control who can sign in
signIn({ user, account }) {
if (account?.provider === 'credentials' && !user) return false;
return true;
},
// Control redirects
redirect({ url, baseUrl }) {
if (url.startsWith(baseUrl)) return url;
return baseUrl + '/dashboard';
},
},
});
Type augmentation
// types/next-auth.d.ts
import type { DefaultSession } from 'next-auth';
declare module 'next-auth' {
interface Session {
user: {
id: string;
role: string;
} & DefaultSession['user'];
}
interface User {
role: string;
}
}
declare module 'next-auth/jwt' {
interface JWT {
id: string;
role: string;
}
}
Email/magic link provider
import Resend from 'next-auth/providers/resend';
export const { handlers, auth } = NextAuth({
adapter: PrismaAdapter(prisma),
providers: [
Resend({
from: 'noreply@yourdomain.com',
}),
],
});
Database adapters
// Prisma
import { PrismaAdapter } from '@auth/prisma-adapter';
adapter: PrismaAdapter(prisma)
// Drizzle
import { DrizzleAdapter } from '@auth/drizzle-adapter';
adapter: DrizzleAdapter(db)
// Supabase
import { SupabaseAdapter } from '@auth/supabase-adapter';
adapter: SupabaseAdapter({ url, secret })
// MongoDB
import { MongoDBAdapter } from '@auth/mongodb-adapter';
adapter: MongoDBAdapter(clientPromise)
Server Actions
import { signIn, signOut } from '@/auth';
// Sign in form
export default function LoginPage() {
return (
<form action={async () => {
'use server';
await signIn('google', { redirectTo: '/dashboard' });
}}>
<button type="submit">Sign in with Google</button>
</form>
);
}
Best Practices
- Use JWT strategy for serverless (no database session lookups)
- Use database strategy when you need to revoke sessions instantly
- Extend the session via callbacks to include user ID and roles
- Use middleware for route protection — cleaner than checking in every page
- Always set
AUTH_SECRET— required for token encryption - Use the Credentials provider only with proper password hashing (bcrypt/argon2)
- Type-augment the Session and JWT interfaces for type safety
Anti-Patterns
- Storing sensitive data in the JWT — it's readable (base64), not encrypted
- Using Credentials provider without rate limiting — vulnerable to brute force
- Not setting custom pages — the default pages are functional but unstyled
- Calling
useSession()without<SessionProvider>— returns undefined - Not handling the loading state —
statusstarts as"loading" - Mixing database and JWT strategies without understanding the trade-offs
Install this skill directly: skilldb add auth-services-skills
Related Skills
Ory Kratos
Integrate Ory Kratos as a self-sovereign, API-first identity and user management system. Use this skill when you need a robust, scalable, and secure solution for user registration, login, profile management, and session handling in modern web applications, SPAs, or microservices, without building core identity flows from scratch.
Passage
Integrate Passage as a complete passwordless authentication and user management service. Use this skill to implement secure, frictionless login experiences using FIDO2/WebAuthn passkeys, magic links, and one-time passcodes, eliminating traditional passwords from your web and mobile applications.
Stytch
Build with Stytch for passwordless and modern authentication. Use this skill when the project needs to integrate Stytch for magic links, OTPs, OAuth, passkeys, biometrics, session management, or B2B organization auth. Covers the Stytch Node SDK, passwordless flows, session management, and B2B multi-tenancy.
Supabase Auth
Integrate Supabase Auth for user authentication and authorization in web applications. Use this skill when you need a full-featured, secure, and easy-to-use authentication solution with OAuth, email/password, magic links, and robust session management, tightly integrated with a PostgreSQL database and other Supabase services.
Workos
Build with WorkOS for enterprise SSO and authentication. Use this skill when the project needs to integrate WorkOS for SAML/OIDC single sign-on, directory sync (SCIM), admin portal, AuthKit, or enterprise-ready identity features. Covers WorkOS AuthKit, SSO, directory sync, admin portal, and user management.
Auth0
Build with Auth0 for enterprise authentication and identity. Use this skill when the project needs to integrate Auth0 for SSO, OAuth, SAML, MFA, passwordless login, machine-to-machine auth, role-based access control, or enterprise identity federation. Covers the Auth0 Next.js SDK, React SDK, Management API, Actions, and RBAC.