0
0
Lập trình
TT

Xác thực An toàn với Microsoft Entra ID cho Developers TypeScript

Đăng vào 5 tháng trước

• 5 phút đọc

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

  1. Kiến trúc tổng quan
  2. Cấu hình Entra ID
  3. Frontend Next.js với NextAuth
  4. Backend Node/TypeScript
  5. Các thực tiễn tốt nhất
  6. Kết luận
  7. 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:

  1. Người dùng truy cập ứng dụng Next.js và bắt đầu quá trình đăng nhập.
  2. NextAuth.js sẽ chuyển hướng người dùng đến Microsoft Entra ID để xác thực.
  3. 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.
  4. Frontend sẽ gửi Access Token đến backend trong header Authorization: Bearer <token>.
  5. 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:

  1. Truy cập vào Microsoft Entra ID → Đăng ký ứng dụng → Đăng ký mới.
  2. Đ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 tokensAccess tokens.
  3. 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 Copy
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 Copy
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 Copy
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 Copy
"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 Copy
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 Copy
npm install express jwks-rsa express-jwt

Mã nguồn (src/index.ts)

javascript Copy
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 clientSecret ra frontend.
  • Phiên bảo mật: Sử dụng cookie httpOnly hoặc JWT từ NextAuth.
  • Xác thực nghiêm ngặt: Cấu hình đúng audienceissuer.
  • 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.

Tài liệu tham khảo

Gợi ý câu hỏi phỏng vấn
Không có dữ liệu

Không có dữ liệu

Bài viết được đề xuất
Bài viết cùng tác giả

Bình luận

Chưa có bình luận nào

Chưa có bình luận nào