Setting Up Auth.js in Next.js 15
Auth.js (formerly NextAuth.js v5) is the standard authentication library for Next.js. It supports OAuth providers (Google, GitHub, Discord), email/password credentials, and magic links with minimal configuration.
Installation
npm install next-auth@beta
npx auth secret # Generates NEXTAUTH_SECRET in .env.local
Configuration
// auth.ts (root of project)
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 { db } from '@/lib/db';
import bcrypt from 'bcryptjs';
export const { handlers, auth, signIn, signOut } = NextAuth({
providers: [
GitHub({
clientId: process.env.GITHUB_ID!,
clientSecret: process.env.GITHUB_SECRET!,
}),
Google({
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
}),
Credentials({
credentials: {
email: { label: 'Email', type: 'email' },
password: { label: 'Password', type: 'password' },
},
async authorize(credentials) {
if (!credentials?.email || !credentials?.password) {
return null;
}
const user = await db.user.findUnique({
where: { email: credentials.email as string },
});
if (!user || !user.password) return null;
const passwordMatch = await bcrypt.compare(
credentials.password as string,
user.password
);
return passwordMatch ? user : null;
},
}),
],
callbacks: {
async session({ session, token }) {
if (token.sub) {
session.user.id = token.sub;
session.user.role = token.role as string;
}
return session;
},
async jwt({ token, user }) {
if (user) {
token.role = user.role;
}
return token;
},
},
pages: {
signIn: '/login',
error: '/auth/error',
},
});
Route Handler
// app/api/auth/[...nextauth]/route.ts
import { handlers } from '@/auth';
export const { GET, POST } = handlers;
Sign In and Out Server Actions
// app/actions/auth.ts
'use server';
import { signIn, signOut } from '@/auth';
export async function signInWithGitHub() {
await signIn('github', { redirectTo: '/dashboard' });
}
export async function signOutUser() {
await signOut({ redirectTo: '/' });
}
// components/auth-buttons.tsx
import { signInWithGitHub, signOutUser } from '@/actions/auth';
export function SignInButton() {
return (
<form action={signInWithGitHub}>
<button type="submit" className="flex items-center gap-2 px-4 py-2 bg-gray-900 text-white rounded-lg">
Sign in with GitHub
</button>
</form>
);
}