Debug JWT: checklist decode header/payload khi API trả 401【2026】

(Cập nhật: 19 tháng 7, 2026 ) JWT debug 401 header payload exp API auth OAuth
Kết luận

Debug JWT = checklist có thứ tự, không phải ‘dán lên jwt.io rồi đoán’. Khi 401: đọc header (alg, kid) → payload (exp → sub/aud/roles) → mới nghi chữ ký. Decode chỉ cho bạn thấy claims; verify mới chứng minh token chưa bị sửa. Làm nhanh trên trình duyệt với JWT decode Kawa — không gửi token lên server.

Bạn đang tích hợp OAuth / API gateway / NestJS / Laravel Sanctum-style JWT. Postman gọi được vài phút trước, giờ frontend đỏ 401. Đồng nghiệp bảo “token sai”. Bạn mở DevTools, copy chuỗi eyJ…, rồi… làm gì tiếp theo theo thứ tự nào?

Bài này là playbook debug: decode header + payload theo checklist cố định — góc dành cho dev Việt Nam làm freelance/outsource hay nhảy giữa nhiều project auth khác nhau trong tuần.

Bài viết này giúp bạn

  • Hiểu cấu trúc 3 phần JWT và vì sao payload không phải mã hóa
  • Checklist đọc header rồi payload khi gặp 401 / sai quyền
  • Bảng claim thường gặp và tín hiệu đỏ (alg=none, aud lệch, exp đã qua)
  • Case study: token “nhìn đúng” nhưng API vẫn từ chối
  • Dùng tool local thay vì paste production token lên site không rõ nguồn

JWT khi debug: bạn đang nhìn cái gì?

JWT (JSON Web Token) là chuỗi:

header.payload.signature

Ba phần ngăn bởi ., mỗi phần là Base64URL (không phải encryption):

PhầnMục đích khi debugBạn có đọc được không?
HeaderThuật toán, loại token, đôi khi kidCó — decode ngay
PayloadClaims: sub, exp, aud, role…Có — decode ngay
SignatureChứng minh header+payload chưa bị sửaChỉ có ý nghĩa sau verify với key đúng

Ai cầm token đều đọc được payload. Đó là lý do không đặt mật khẩu, số thẻ, API key production vào claims — kể cả khi “chỉ dùng nội bộ”.

// Ví dụ header sau decode
{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "auth-key-2026-03"
}

// Ví dụ payload sau decode
{
  "sub": "user_9182",
  "aud": "https://api.partner.vn",
  "exp": 1784505600,
  "roles": ["editor"]
}

Nhìn được JSON ≠ server chấp nhận token. Server còn kiểm tra chữ ký + policy claims.


Checklist debug theo thứ tự (in ra dán cạnh màn hình)

1

Lấy đúng chuỗi: bỏ tiền tố Bearer và khoảng trắng thừa; token phải có đúng 2 dấu chấm (3 phần)

2

Decode header trước: ghi alg, typ, kid — so với docs / JWKS của môi trường đang gọi

3

Decode payload: kiểm tra exp (và nbf nếu có) trước mọi thứ khác

4

Đối chiếu sub / aud / iss / scope|roles với API bạn đang hit

5

Xác nhận Authorization header trên request thật (Network tab), không nhầm access vs refresh

6

Claims ổn mà vẫn 401 → chuyển sang verify signature / đúng key / clock skew — không đoán mò trên frontend

Bước A — Header: 60 giây quyết định hướng

Header JWT — checklist debug
Field Ý nghĩa Khi lệch thì sao
alg HS256 / RS256 / ES256… Server expect RS256 mà token HS256 → verify fail. alg=none = từ chối ngay
typ Thường JWT Một số gateway bắt đúng typ; hiếm nhưng dễ bỏ sót
kid Key ID trong JWKS kid cũ sau rotate → 401 dù payload đúng user
cty Content type (hiếm) Nested JWT — đừng decode một lớp rồi kết luận

Ghi chú thực tế: team outsource hay nhận staging JWKS nhưng dán token production (hoặc ngược lại). kid không có trong JWKS → fail. Decode header giúp phát hiện trước khi soi Nest middleware 200 dòng.

Bước B — Payload: claim nào đọc trước?

Payload — thứ tự ưu tiên khi 401
Claim Nghĩa Câu hỏi debug
exp Hết hạn (Unix giây) now >= exp? → refresh / login lại
nbf Not before Đồng hồ máy lệch tương lai? Token chưa có hiệu lực
iat Issued at Token 'già' bất thường — nghi copy nhầm / replay
sub Subject / user id Đúng user bạn nghĩ đang test không?
aud Audience Token cho API A gửi sang API B?
iss Issuer Issuer auth-dev vs auth-prod?
scope / roles Quyền 200 nhưng 403 sau đó — thiếu scope, không phải 'JWT hỏng'

Mẹo chuyển exp: Unix giây → giờ đọc được bằng Unix timestamp hoặc new Date(exp * 1000) trong console. Đừng so với giờ máy đã set sai timezone mà quên UTC.


Decode ≠ verify (sai một bước = debug sai cả buổi)

Thao tácBạn biết được gìBạn chưa biết gì
DecodeNội dung header/payloadToken có bị sửa không; key có đúng không
VerifyChữ ký khớp key → chưa bị giả mạo header/payloadPrivileges business (vẫn cần check aud, revoke list…)

Frontend decode để hiện tên user trong UI là bình thường. Tin roles: ["admin"] từ payload chưa verify để mở trang admin trên client là lỗ hổng — attacker tự sửa Base64 rồi gửi lại; không có signature hợp lệ thì backend phải từ chối, nhưng UI đã lộ.

Khi debug, hãy nói rõ với team: “Tôi đã decode — exp còn 12 phút, aud khớp. Chưa verify.” Tránh báo cáo “JWT OK” chỉ vì JSON đẹp.

Tool JWT decode trên Kawa xử lý local: dán token → xem header/payload JSON, hiện trạng thái exp. Có thêm bước xác minh HS256 nếu bạn có secret dev — không dán secret production lên bất kỳ trang web nào. RS256/JWKS vẫn nên verify trên server hoặc CLI nội bộ.

🔍 Giải mã JWT ngay tại đây

Lưu ý: Công cụ này chỉ thực hiện giải mã. Nó không xác minh chữ ký. Không sử dụng để phát hiện giả mạo.

* Tự động giải mã khi nhập hoặc dán

 
 

Xác minh chữ ký (Signature Verification)


Case study 1: “Token mới lấy mà vẫn 401”

Bối cảnh: SPA React + API Nest. Login trả access token TTL 5 phút. Dev để tab mở, đi ăn trưa, quay lại bấm Save → 401.

Checklist áp dụng:

  1. Decode → exp đã qua 40 phút.
  2. Refresh token endpoint trả 200 nhưng frontend vẫn gắn access cũ trong axios interceptor (bug race).
  3. Sửa: clear queue + gắn token mới trước retry; thêm log exp - now khi 401.

Không cần đụng secret. Chỉ cần đọc exp đúng chỗ.

Case study 2: Payload đúng user, vẫn bị từ chối

Bối cảnh: Freelance nhận task gọi API đối tác. Token từ portal đối tác; sub đúng, exp còn hạn.

Checklist:

  1. Header: alg: RS256, kid: partner-2025-01.
  2. Docs mới: rotate sang partner-2026-06. Portal vẫn cấp kid cũ trong 24h transition.
  3. Gateway đã bỏ key cũ → verify fail → 401 generic.

Bài học: đọc kid trước khi nghi code của mình. Gửi screenshot header (đã che sub nếu cần) cho phía đối tác nhanh hơn đoán middleware.


Sai lầm hay gặp khi debug JWT

  1. Paste cả dòng Bearer eyJ… vào tool → cấu trúc không còn 3 phần → lỗi decode. Chỉ lấy phần sau Bearer .
  2. Nhầm access token với id_token / refresh. Decode được nhưng aud khác hẳn API resource.
  3. Tin role trên UI. Payload nói admin chưa verify → vẫn phải để backend quyết.
  4. Screenshot token đầy đủ lên group chat. Coi như credential đã lộ — rotate nếu là token thật.
  5. Bỏ qua nbf khi đồng hồ VM/WSL lệch. Token “tương lai” vài phút → từ chối khó hiểu.
  6. So exp bằng mili giây. Một số lib trả ms; JWT standard dùng giây — nhân nhầm ×1000 sẽ tưởng token sống tới năm 5000+.

Quy trình 5 phút trước khi @ backend

  1. Network: request có Authorization không? Status thật là 401 hay CORS giả dạng?
  2. Decode header + payload trên công cụ JWT decode.
  3. Ghi vào ticket: alg, kid, exp (ISO), aud, iss, sub (che một phần).
  4. Nếu hết hạn → reproduce với token mới trước khi mở PR “fix auth”.
  5. Nếu claims khớp env → nhờ backend log lý do verify fail (signature / audience / revoke).

Câu hỏi thường gặp

401 thì kiểm tra gì trước?
Bearer đúng chưa → expsub/aud/rolesalg/kid → mới verify chữ ký.

Decode có cần secret?
Không. Secret chỉ cho verify.

exp là gì?
Unix giây hết hạn; so với UTC now.

Header xem field nào?
alg, typ, kid — đặc biệt sau key rotation.

Debug an toàn thế nào?
Tool local trong trình duyệt; che PII; không dán token production lên dịch vụ lạ.

Frontend verify được không?
Không lấy verify client làm lớp bảo mật. Decode để UX; ủy quyền ở server.

Liên kết liên quan