Skip to content

OAuth 2.0 — Authorization Code Flow với PKCE ​

Đây là luồng (flow) OAuth 2.0 tiêu chuẩn hiện nay, dùng cho mọi ứng dụng có người dùng đăng nhập (web, SPA, mobile, desktop).

mermaid
sequenceDiagram
    participant U as Người dùng
    participant App as Ứng dụng (Client)
    participant AS as Authorization Server

    App->>App: Sinh code_verifier (chuỗi ngẫu nhiên bí mật)
    App->>App: Tính code_challenge = SHA256(code_verifier), mã hoá base64url
    App->>AS: GET /authorize?response_type=code&client_id=...&redirect_uri=...&code_challenge=...&code_challenge_method=S256&state=...
    AS->>U: Hiển thị màn hình đăng nhập / màn hình đồng ý cấp quyền
    U->>AS: Nhập thông tin đăng nhập, bấm "Đồng ý"
    AS->>App: Redirect về redirect_uri kèm authorization code + state
    App->>App: Kiểm tra state khớp với giá trị đã gửi (chống CSRF)
    App->>AS: POST /token (code, redirect_uri, client_id, code_verifier)
    AS->>AS: Tính lại SHA256(code_verifier), so khớp với code_challenge đã lưu
    AS->>App: Trả về access_token (+ id_token nếu là luồng OIDC, + refresh_token)

Diễn giải từng bước:

  1. Sinh cặp PKCE: trước khi bắt đầu, ứng dụng tự sinh một chuỗi ngẫu nhiên gọi là code_verifier (giữ bí mật, không rời khỏi bộ nhớ ứng dụng), rồi băm (hash) chuỗi này bằng SHA-256 và mã hoá base64url để ra code_challenge.
  2. Authorization request: ứng dụng redirect người dùng tới authorization server, gửi kèm code_challenge (không gửi code_verifier) cùng các tham số chuẩn: response_type=code, client_id, redirect_uri, state (chuỗi ngẫu nhiên để đối chiếu sau, chống giả mạo callback).
  3. Người dùng xác thực: authorization server hiển thị màn hình đăng nhập/đồng ý cấp quyền, không liên quan gì tới ứng dụng gốc — đây là lý do ứng dụng gốc không bao giờ nhìn thấy mật khẩu người dùng.
  4. Trả authorization code: sau khi người dùng đồng ý, authorization server redirect ngược lại redirect_uri kèm một code (chỉ dùng được một lần, sống rất ngắn) và state.
  5. Đổi code lấy token: ứng dụng gửi code cùng code_verifier (chuỗi gốc, chưa hash) lên authorization server qua request POST /token.
  6. Xác minh PKCE: authorization server tự tính lại SHA256(code_verifier) và so sánh với code_challenge đã nhận ở bước 2. Nếu ai đó chặn được code ở bước 4 nhưng không có code_verifier gốc (chỉ tồn tại trong bộ nhớ ứng dụng thật), họ không thể đổi được token — đây chính là cơ chế PKCE ngăn tấn công đánh cắp authorization code.
  7. Nhận token: nếu khớp, authorization server trả về access_token (và refresh_token nếu có, id_token nếu đây là luồng OpenID Connect).