+3

Authentication nhập môn cho modern website

Authentication (xác thực) là trái tim của mọi ứng dụng web hiện nay. Dù bạn đang xây dựng một trang thương mại điện tử, sản phẩm SaaS hay dashboard nội bộ, thì việc đăng nhập an toàn – mượt mà luôn là chuyện sống còn. Hôm nay, chúng ta cùng tìm hiểu ba “ông lớn” trong làng xác thực: JWT, OAuth và SSO, và xem cách token “chạy qua chạy lại” giữa frontend (FE) và backend (BE) như thế nào nhé 😎

1. Vì sao phải xác thực?

  • Authentication = Xác định bạn là ai.
  • Authorization = Quyết định bạn được làm gì.

Nếu xác thực kém → bạn dễ gặp:

  • Lộ dữ liệu, truy cập trái phép
  • Trải nghiệm người dùng tệ (phải login 7749 lần)
  • Các lỗi bảo mật như token bị đánh cắp, replay attack, CSRF

2. Các chiến lược xác thực phổ biến

a) JWT (JSON Web Token)

JWT là kiểu xác thực “quốc dân”, được dùng cực kỳ nhiều hiện nay.

Cách hoạt động:

  1. Người dùng nhập email + mật khẩu.
  2. Backend kiểm tra, nếu đúng → tạo JWT (ký bằng secret key).
  3. Frontend lưu token (thường trong cookie HTTP-only hoặc storage an toàn).
  4. Mỗi request, FE gửi token kèm theo (Authorization: Bearer <token>).
  5. BE kiểm tra chữ ký → xác định user.

Ưu điểm:

  • Không cần lưu session trên server (stateless).
  • Dễ dùng với API/microservice.

Nhược điểm:

  • Khó thu hồi token (nếu bị lộ).
  • Nếu lưu trong localStorage → dễ bị XSS “chôm”.

b) OAuth 2.0

Đây là chuẩn xác thực cực phổ biến khi bạn thấy nút “Login with Google / Facebook / GitHub”.

Cách hoạt động (Authorization Code Flow):

  1. User nhấn “Login with Google”.
  2. FE chuyển hướng đến trang đăng nhập của Google.
  3. User đồng ý cấp quyền.
  4. Google trả về một “authorization code”.
  5. FE gửi code đó cho BE.
  6. BE đổi code lấy access token (và có thể cả refresh token).
  7. BE dùng token để lấy thông tin user từ Google → tạo session.

Ưu điểm:

  • User không cần mật khẩu mới → tiện cực.
  • An toàn, dễ tích hợp nhiều dịch vụ.

Nhược điểm:

  • Hơi phức tạp hơn JWT.
  • Phụ thuộc bên thứ ba (Google, Facebook, …).

c) SSO (Single Sign-On)

SSO = Đăng nhập 1 lần – xài mọi nơi 🧙‍♂️ Thường dùng trong công ty, hệ thống nhiều ứng dụng.

Cách hoạt động:

  1. User đăng nhập vào hệ thống trung tâm (IdP như Okta, Azure AD).
  2. IdP cấp một token (JWT hoặc SAML).
  3. Các ứng dụng “cùng nhà” tin tưởng token đó → cho user truy cập luôn.

Ưu điểm:

  • Một lần đăng nhập → truy cập cả hệ sinh thái.
  • Phù hợp cho doanh nghiệp.

Nhược điểm:

  • Cấu hình hơi phức tạp.
  • IdP mà sập là… cả nhà nghỉ 😅

3. Luồng token: từ FE → BE

Một trong những cách phổ biến và an toàn là dùng JWT + cookie HTTP-only (thay vì lưu token trong localStorage).

Vì sao? 👉 Cookie HTTP-only không thể bị truy cập bởi JavaScript, nên XSS bó tay.

Bước 1: FE gửi thông tin đăng nhập đến BE

    // frontend (Next.js)
const login = async (email, password) => {
  const res = await fetch("/api/auth/login", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ email, password }),
    credentials: "include", // cho phép cookie
  });
  return res.json();
};

Bước 2: BE xác thực và gửi JWT qua cookie

// backend (Next.js route)
import { NextResponse } from "next/server";
import jwt from "jsonwebtoken";

export async function POST(req) {
const { email, password } = await req.json();

if (email !== "user@example.com" || password !== "123456") {
  return NextResponse.json({ error: "Sai thông tin!" }, { status: 401 });
}

const token = jwt.sign({ email, role: "user" }, process.env.JWT_SECRET, { expiresIn: "15m" });

const res = NextResponse.json({ success: true });
res.cookies.set("token", token, {
  httpOnly: true,
  secure: process.env.NODE_ENV === "production",
  sameSite: "strict",
  maxAge: 60 * 15,
  path: "/",
});

return res;
}

Bước 3: FE gọi API có bảo vệ

Cookie sẽ được gửi tự động cùng request.

const fetchProfile = async () => {
  const res = await fetch("/api/user/profile", {
    method: "GET",
    credentials: "include", // gửi cookie
  });
  return res.json();
};

Bước 4: BE kiểm tra token mỗi khi gọi API

// backend
import { NextRequest, NextResponse } from "next/server";
import jwt from "jsonwebtoken";

export async function GET(req) {
  const token = req.cookies.get("token")?.value;
  if (!token) return NextResponse.json({ error: "Chưa đăng nhập!" }, { status: 401 });

  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    return NextResponse.json({ user: decoded });
  } catch {
    return NextResponse.json({ error: "Token hết hạn hoặc sai!" }, { status: 403 });
  }
}

Bước 5: Hết hạn & Refresh Token

Access token (JWT) nên ngắn hạn (ví dụ 15 phút). Khi hết hạn, bạn có thể dùng refresh token (lưu riêng, cũng trong cookie an toàn) để xin token mới – user không phải login lại.

Ưu điểm:

  • Không dính XSS.
  • Cookie tự động gửi cùng request.
  • Cực hợp với Next.js App Router.

4. Best Practice

  • Access token ngắn hạn + refresh token dài hạn
  • Dùng cookie HTTP-only
  • Luôn bật HTTPS
  • Có luồng logout + revoke token
  • Với OAuth, nên dùng PKCE để tăng bảo mật cho SPA

5. Nên chọn chiến lược nào?

Mục đích Gợi ý
App nhỏ / API đơn giản JWT
Cần đăng nhập Google, GitHub... OAuth 2.0 / OIDC
Doanh nghiệp / nhiều app SSO + IdP

Kết luận

Xác thực không chỉ là đăng nhập, mà còn là xây dựng trải nghiệm an toàn, tiện lợi, đáng tin cậy. Hiểu rõ JWT, OAuth, SSO và dòng chảy token FE → BE sẽ giúp bạn thiết kế hệ thống vừa an toàn, vừa “ngọt nước” 😋

👉 Tóm gọn lại:

  • Bảo vệ token thật kỹ
  • Duy trì phiên ngắn hạn
  • Dùng nhà cung cấp xác thực uy tín

Nếu bạn đọc đến đây mà vẫn chưa ngáp, thì xin chúc mừng: bạn vừa hiểu được gần hết mớ rối rắm mà dân dev gọi là “authentication hell” 🔥

Column 1 Column 2 Column 3
Text Text Text

All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí