Giới thiệu
Trong thế giới phát triển ứng dụng hiện đại, việc bảo vệ dữ liệu nhạy cảm và đảm bảo rằng chỉ những người dùng đã xác thực mới có thể truy cập vào tài nguyên là điều cực kỳ quan trọng. Bài viết này sẽ hướng dẫn bạn cách xây dựng một giải pháp xác thực an toàn bằng cách tích hợp Microsoft Entra ID (trước đây là Azure Active Directory) với Next.js và Node.js/TypeScript.
Nội dung chính
- Kiến trúc tổng quan
- Cấu hình Entra ID
- Frontend Next.js với NextAuth
- Backend Node/TypeScript
- Các thực tiễn tốt nhất
- Kết luận
- Câu hỏi thường gặp
Kiến trúc tổng quan
Giải pháp này có thể được hiểu qua năm bước chính:
- Người dùng truy cập ứng dụng Next.js và bắt đầu quá trình đăng nhập.
- NextAuth.js sẽ chuyển hướng người dùng đến Microsoft Entra ID để xác thực.
- Sau khi xác thực thành công, Entra ID sẽ trả về một ID Token và một Access Token cho Next.js.
- Frontend sẽ gửi Access Token đến backend trong header
Authorization: Bearer <token>. - Backend sẽ xác thực token đó với dịch vụ JWKS của Entra ID.
- Nếu token hợp lệ, người dùng sẽ được truy cập vào các route bảo vệ.
- Nếu token không hợp lệ hoặc đã hết hạn, backend sẽ trả về mã lỗi
401 Unauthorized.
Cấu hình Entra ID
Để bắt đầu, bạn cần thực hiện một số bước cấu hình trong Azure Portal:
- Truy cập vào Microsoft Entra ID → Đăng ký ứng dụng → Đăng ký mới.
- Điền thông tin vào các trường sau:
- Tên:
nextjs-auth-app - Redirect URI:
http://localhost:3000/api/auth/callback - Bật ID tokens và Access tokens.
- Tên:
- Lưu các thông tin sau:
- Application (client) ID
- Directory (tenant) ID
- Client Secret (tạo thủ công trong phần Certificates & secrets).
Frontend Next.js với NextAuth
Cài đặt
Để bắt đầu, bạn cần cài đặt các thư viện cần thiết:
bash
npm install next-auth @azure/msal-node
Biến môi trường
Tạo một file .env.local và thêm các biến sau:
plaintext
AZURE_AD_CLIENT_ID=<Application ID> # ID của ứng dụng đã đăng ký trong Entra ID
AZURE_AD_CLIENT_SECRET=<Client Secret> # Bí mật của ứng dụng (không được tiết lộ ở frontend)
AZURE_AD_TENANT_ID=<Directory ID> # Tenant (thư mục tổ chức)
NEXTAUTH_URL=http://localhost:3000 # URL của ứng dụng Next.js
NEXTAUTH_SECRET=<chave-aleatória> # Khóa để ký JWT phiên
Cấu hình NextAuth
Sau đó, tạo một file cấu hình cho NextAuth:
javascript
import NextAuth from "next-auth";
import AzureADProvider from "next-auth/providers/azure-ad";
export default NextAuth({
providers: [
AzureADProvider({
clientId: process.env.AZURE_AD_CLIENT_ID!,
clientSecret: process.env.AZURE_AD_CLIENT_SECRET!,
tenantId: process.env.AZURE_AD_TENANT_ID!,
}),
],
session: { strategy: "jwt" },
callbacks: {
async jwt({ token, account }) {
if (account) token.accessToken = account.access_token;
return token;
},
async session({ session, token }) {
session.accessToken = token.accessToken as string;
return session;
},
},
});
Thành phần Đăng nhập/Đăng xuất (AuthButton.tsx)
javascript
"use client";
import { signIn, signOut, useSession } from "next-auth/react";
export default function AuthButton() {
const { data: session } = useSession();
if (session) {
return (
<button onClick={() => signOut()}>
Đăng xuất ({session.user?.name})
</button>
);
}
return (
<button onClick={() => signIn("azure-ad")}>Đăng nhập với Entra ID</button>
);
}
Trang chính (page.tsx)
javascript
import AuthButton from "./components/AuthButton";
import Link from "next/link";
export default function Home() {
return (
<main style={{ padding: "2rem" }}>
<h1>🔐 Demo xác thực với Entra ID</h1>
<AuthButton />
<hr style={{ margin: "2rem 0" }} />
<Link href="/protected">
<button>Truy cập trang bảo vệ</button>
</Link>
</main>
);
}
Backend Node/TypeScript
Phụ thuộc
Cài đặt các phụ thuộc cần thiết cho backend:
bash
npm install express jwks-rsa express-jwt
Mã nguồn (src/index.ts)
javascript
import express from "express";
import { expressjwt as jwt } from "express-jwt";
import jwksRsa from "jwks-rsa";
const app = express();
const port = 4000;
const checkJwt = jwt({
secret: jwksRsa.expressJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 10,
jwksUri: `https://login.microsoftonline.com/${process.env.AZURE_AD_TENANT_ID}/discovery/v2.0/keys`,
}) as any,
audience: process.env.AZURE_AD_CLIENT_ID,
issuer: `https://login.microsoftonline.com/${process.env.AZURE_AD_TENANT_ID}/v2.0`,
algorithms: ["RS256"],
});
app.get("/api/secure-data", checkJwt, (req, res) => {
res.json({
message: "Đã xác thực ✅",
claims: (req as any).auth,
});
});
app.listen(port, () =>
console.log(`🚀 Backend chạy tại http://localhost:${port}`)
);
Các thực tiễn tốt nhất
- Bảo vệ bí mật: Không bao giờ đưa
clientSecretra frontend. - Phiên bảo mật: Sử dụng cookie
httpOnlyhoặc JWT từ NextAuth. - Xác thực nghiêm ngặt: Cấu hình đúng
audiencevàissuer. - Giám sát: Ghi lại các lỗi xác thực để phân tích.
- Mở rộng: Mô hình này cho phép nhiều dịch vụ xác thực token mà không phụ thuộc vào frontend.
Kết luận
Với kiến trúc này, chúng ta đã tạo ra một quy trình xác thực an toàn:
- Next.js quản lý xác thực và phiên của người dùng.
- Backend Node/TypeScript xác thực các token trước khi cung cấp dữ liệu nhạy cảm.
- Microsoft Entra ID cung cấp việc quản lý danh tính với tính bảo mật cao.
Mô hình này có thể mở rộng cho các microservices hoặc tích hợp với API của Microsoft (Graph API).
Câu hỏi thường gặp
Microsoft Entra ID là gì?
Microsoft Entra ID là một dịch vụ quản lý danh tính và truy cập (IAM) giúp bảo vệ và quản lý quyền truy cập vào ứng dụng.
Tại sao tôi nên sử dụng NextAuth?
NextAuth giúp đơn giản hóa việc xác thực người dùng trong các ứng dụng Next.js, cung cấp các phương pháp xác thực an toàn và dễ dàng tích hợp với nhiều nhà cung cấp.
Tôi có thể mở rộng mô hình này cho các dịch vụ khác không?
Có, bạn có thể mở rộng mô hình này cho nhiều dịch vụ khác nhau, bao gồm cả các microservices và các API khác.