NextAuth.js (nay là Auth.js) là thư viện authentication phổ biến nhất cho Next.js, hỗ trợ OAuth (Google, GitHub, Facebook…), Credentials, Email magic link, và nhiều hơn nữa — chỉ với vài dòng config.
1. Cài đặt
npm install --save next-auth@beta
Tạo auth secret:
npx auth secret
Thêm vào .env:
AUTH_SECRET=your-generated-secret
AUTH_GOOGLE_ID=your-google-client-id
AUTH_GOOGLE_SECRET=your-google-client-secret
NEXTAUTH_URL=http://localhost:3000
2. Cấu hình Auth
Tạo file auth.ts ở root project:
// 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';
export const { handlers, signIn, signOut, auth } = NextAuth({
providers: [
Google,
GitHub,
Credentials({
credentials: {
email: { label: 'Email', type: 'email' },
password: { label: 'Password', type: 'password' },
},
async authorize(credentials) {
// Xác thực với backend API
const res = await fetch(`${process.env.API_URL}/auth/login`, {
method: 'POST',
body: JSON.stringify(credentials),
headers: { 'Content-Type': 'application/json' },
});
if (!res.ok) return null;
const user = await res.json();
return user; // { id, name, email, token }
},
}),
],
callbacks: {
// Thêm custom data vào token
async jwt({ token, user, account }) {
if (user) {
token.accessToken = (user as any).token;
token.id = user.id;
}
return token;
},
// Thêm custom data vào session
async session({ session, token }) {
session.user.id = token.id as string;
(session as any).accessToken = token.accessToken;
return session;
},
},
pages: {
signIn: '/login', // Trang login tùy chỉnh
error: '/error', // Trang lỗi tùy chỉnh
},
});
3. Tạo Route Handler
// app/api/auth/[...nextauth]/route.ts
import { handlers } from '@/auth';
export const { GET, POST } = handlers;
4. Bảo vệ Route với Middleware
// middleware.ts
import { auth } from '@/auth';
export default auth((req) => {
const isLoggedIn = !!req.auth;
const isAuthPage = req.nextUrl.pathname.startsWith('/login');
if (!isLoggedIn && !isAuthPage) {
return Response.redirect(new URL('/login', req.url));
}
});
export const config = {
matcher: ['/dashboard/:path*', '/profile/:path*'],
};
5. Trang Login tùy chỉnh
// app/login/page.tsx
'use client';
import { signIn } from 'next-auth/react';
import { useState } from 'react';
import { useRouter } from 'next/navigation';
export default function LoginPage() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const router = useRouter();
async function handleSubmit(e: React.FormEvent) {
e.preventDefault();
setError('');
const result = await signIn('credentials', {
email,
password,
redirect: false,
});
if (result?.error) {
setError('Email hoặc mật khẩu không đúng');
} else {
router.push('/dashboard');
}
}
return (
<div className="flex min-h-screen items-center justify-center">
<form onSubmit={handleSubmit} className="w-full max-w-sm space-y-4">
<h1 className="text-2xl font-bold">Đăng nhập</h1>
{error && <p className="text-red-500">{error}</p>}
<input
type="email"
placeholder="Email"
value={email}
onChange={e => setEmail(e.target.value)}
className="w-full border rounded p-2"
required
/>
<input
type="password"
placeholder="Mật khẩu"
value={password}
onChange={e => setPassword(e.target.value)}
className="w-full border rounded p-2"
required
/>
<button type="submit" className="w-full bg-blue-600 text-white py-2 rounded">
Đăng nhập
</button>
<div className="space-y-2">
<button
type="button"
onClick={() => signIn('google', { callbackUrl: '/dashboard' })}
className="w-full border py-2 rounded flex items-center justify-center gap-2"
>
Đăng nhập với Google
</button>
<button
type="button"
onClick={() => signIn('github', { callbackUrl: '/dashboard' })}
className="w-full border py-2 rounded"
>
Đăng nhập với GitHub
</button>
</div>
</form>
</div>
);
}
6. Lấy session trong Server Component
// 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>Xin chào, {session.user?.name}!</h1>
<p>Email: {session.user?.email}</p>
<img src={session.user?.image ?? ''} alt="Avatar" className="w-12 h-12 rounded-full" />
</div>
);
}
7. Lấy session trong Client Component
'use client';
import { useSession, signOut } from 'next-auth/react';
export function Navbar() {
const { data: session, status } = useSession();
if (status === 'loading') return <p>Đang tải...</p>;
return (
<nav className="flex items-center justify-between p-4">
<span>My App</span>
{session ? (
<div className="flex items-center gap-3">
<span>{session.user?.name}</span>
<button onClick={() => signOut({ callbackUrl: '/login' })}>
Đăng xuất
</button>
</div>
) : (
<a href="/login">Đăng nhập</a>
)}
</nav>
);
}
Bọc app với SessionProvider:
// app/layout.tsx
import { SessionProvider } from 'next-auth/react';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html>
<body>
<SessionProvider>{children}</SessionProvider>
</body>
</html>
);
}
8. Kết luận
NextAuth.js giúp thêm authentication vào Next.js cực nhanh:
- OAuth (Google, GitHub…): Chỉ cần thêm provider và client ID/secret
- Credentials: Kết nối với backend API tùy chỉnh
- Middleware: Bảo vệ route mà không cần code trong từng page
auth()trong Server Component,useSession()trong Client Component
Đây là giải pháp authentication đầy đủ, production-ready chỉ với vài chục dòng config.