Authentication in Next.js using Auth.js¶
What is Auth.js?¶
Auth.js (formerly known as NextAuth.js) is a flexible and secure authentication solution for Next.js applications.
It supports multiple authentication methods — OAuth providers, Credentials, Email, and more — and integrates seamlessly with Next.js App Router (v13+).
Key Features¶
- Simple integration with App Router and API routes
- Supports OAuth, Credentials, Magic Links, Email, and WebAuthn
- Built-in support for databases (PostgreSQL, MongoDB, etc.)
- JWT and Session-based authentication
- Highly customizable with callbacks, events, and adapters
- Compatible with TypeScript
Installation¶
npm install next-auth
Folder Structure (App Router)¶
/app
/api
/auth
[...nextauth]
/route.js
/dashboard
page.js
/login
page.js
Basic Setup¶
1. Create the Auth Route¶
File: /app/api/auth/[...nextauth]/route.js
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,
}),
],
secret: process.env.NEXTAUTH_SECRET,
};
const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };
2. Add Auth.js Client in Components¶
You can use NextAuth React Hooks to handle login and logout.
npm install next-auth @auth/react
Then wrap your app with the provider.
File: /app/layout.js
import { SessionProvider } from "next-auth/react";
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<SessionProvider>{children}</SessionProvider>
</body>
</html>
);
}
3. Sign In / Sign Out Buttons¶
"use client";
import { signIn, signOut, useSession } from "next-auth/react";
export default function AuthButton() {
const { data: session } = useSession();
if (session) {
return (
<>
<p>Signed in as {session.user.email}</p>
<button onClick={() => signOut()}>Sign out</button>
</>
);
}
return <button onClick={() => signIn("google")}>Sign in with Google</button>;
}
Securing Pages¶
You can protect server components or routes using getServerSession().
Example: Protected Page¶
import { getServerSession } from "next-auth";
import { authOptions } from "../api/auth/[...nextauth]/route";
export default async function Dashboard() {
const session = await getServerSession(authOptions);
if (!session) {
return <p>Access Denied</p>;
}
return <h1>Welcome, {session.user.name}</h1>;
}
Supported Providers¶
Auth.js supports many providers out of the box:
- GitHub
- Discord
- Auth0
- Apple
- Credentials (Custom username/password)
- Email (Magic link)
Example (Credentials Provider):
import CredentialsProvider from "next-auth/providers/credentials";
providers: [
CredentialsProvider({
name: "Credentials",
credentials: {
username: { label: "Username", type: "text" },
password: { label: "Password", type: "password" },
},
async authorize(credentials) {
const user = { id: 1, name: "Admin", email: "admin@example.com" };
if (credentials.username === "admin" && credentials.password === "1234") {
return user;
}
return null;
},
}),
];
Session Management¶
You can access session data from both server and client:
Client Side¶
import { useSession } from "next-auth/react";
const { data: session } = useSession();
Server Side¶
import { getServerSession } from "next-auth";
const session = await getServerSession();
Database Integration (Optional)¶
Auth.js can store users, sessions, and tokens in a database using Adapters.
Example for Prisma:
npm install @auth/prisma-adapter @prisma/client
import { PrismaAdapter } from "@auth/prisma-adapter";
import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
export const authOptions = {
adapter: PrismaAdapter(prisma),
providers: [
/* ... */
],
};
Environment Variables¶
Set these in .env.local:
GOOGLE_CLIENT_ID=your_client_id
GOOGLE_CLIENT_SECRET=your_client_secret
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=your_random_secret
Middleware for Route Protection¶
You can protect routes using middleware:
File: /middleware.js
export { default } from "next-auth/middleware";
export const config = {
matcher: ["/dashboard/:path*"],
};