Ứng dụng quản lý cần đăng nhập và phân quyền. Bài này xây dựng xác thực bằng Auth.js (NextAuth) với đăng nhập email/mật khẩu và Google, đọc phiên trong Server Component, bảo vệ route bằng middleware (proxy) và kiểm tra quyền trong Server Action.
Bạn sẽ học được
- Cài đặt và cấu hình Auth.js
- Đăng nhập bằng mật khẩu (bcrypt) và Google
- Đọc phiên, hiện thông tin người dùng
- Bảo vệ trang và action theo vai trò
Một số khái niệm
- Xác thực: xác định người dùng là ai (đăng nhập).
- Phân quyền: người đó được làm gì (admin, giảng viên, sinh viên).
- Phiên (session): Auth.js lưu trong cookie mã hóa (JWT) hoặc CSDL.
- Provider: cách đăng nhập: Credentials, Google, GitHub, Microsoft...
Cấu hình Auth.js
npm install next-auth@beta bcryptjs
npx auth secret # tạo AUTH_SECRET trong .env.local// src/auth.ts
import NextAuth from 'next-auth';
import Credentials from 'next-auth/providers/credentials';
import Google from 'next-auth/providers/google';
import bcrypt from 'bcryptjs';
import { db } from '@/lib/db';
export const { handlers, auth, signIn, signOut } = NextAuth({
providers: [
Google,
Credentials({
credentials: { email: {}, mat_khau: {} },
async authorize(c) {
const u = await db.nguoiDung.findUnique({ where: { email: String(c.email) } });
if (!u || !(await bcrypt.compare(String(c.mat_khau), u.mat_khau))) return null;
return { id: String(u.id), name: u.ho_ten, email: u.email, role: u.vai_tro };
},
}),
],
callbacks: {
jwt({ token, user }) { if (user) token.role = (user as any).role; return token; },
session({ session, token }) { (session.user as any).role = token.role; return session; },
},
pages: { signIn: '/dang-nhap' },
});// src/app/api/auth/[...nextauth]/route.ts
export { GET, POST } from '@/auth'; // hoặc: import { handlers } ... export const { GET, POST } = handlers;Dùng phiên
// Server Component
import { auth, signOut } from '@/auth';
export default async function ThanhTaiKhoan() {
const session = await auth();
if (!session) return <a href="/dang-nhap">Đăng nhập</a>;
return (
<form action={async () => { 'use server'; await signOut(); }}>
Xin chào {session.user?.name} <button>Đăng xuất</button>
</form>
);
}Bảo vệ trang và action
// src/middleware.ts (Next.js 16: proxy.ts)
export { auth as middleware } from '@/auth';
export const config = { matcher: ['/quan-tri/:path*', '/sinhvien/them'] };// src/lib/quyen.ts
import { auth } from '@/auth';
import { redirect } from 'next/navigation';
export async function canVaiTro(...vaiTro: string[]) {
const s = await auth();
if (!s) redirect('/dang-nhap');
if (!vaiTro.includes((s.user as any).role)) throw new Error('Không có quyền');
return s;
}
// trong Server Action: await canVaiTro('admin', 'giangvien');Mẹo hay
Middleware chỉ là lớp chặn sớm; vẫn phải kiểm tra quyền ở nơi đọc/ghi dữ liệu (Server Component, Server Action, Route Handler).
Lưu ý
Không lưu mật khẩu dạng thô; luôn băm bằng bcrypt. Giữ AUTH_SECRET và khóa Google trong biến môi trường, không đưa lên GitHub.
Thực hành
- Tạo trang /dang-nhap với form Credentials.
- Thêm đăng nhập Google.
- Bảo vệ khu /quan-tri chỉ cho admin.
- Hiển thị nút Sửa/Xóa theo vai trò.
Tóm tắt bài học
- Auth.js: providers, callbacks, pages.
- auth() đọc phiên ở máy chủ.
- Middleware chặn sớm, kiểm tra quyền tại chỗ.
- bcrypt, AUTH_SECRET, biến môi trường.