OAuth2 — Authorization Code Grant

7 phút đọc

🔑 Authorization Code Grant

Đây là grant type phổ biến và an toàn nhất trong OAuth2. Được thiết kế cho các ứng dụng có backend server, và hiện nay (với PKCE) cũng là lựa chọn chuẩn cho SPA & mobile app.


1. Tổng quan

Thuộc tính Giá trị
RFC RFC 6749 §4.1
Dùng cho Web app (server-side), SPA + PKCE, Mobile app + PKCE
Có Refresh Token? ✅ Có
Client Secret cần thiết? ✅ (server-side) / ❌ thay bằng PKCE (public client)
Mức độ bảo mật ⭐⭐⭐⭐⭐ (8/9)

2. Các thành phần tham gia

  • Resource Owner — Người dùng cuối (end-user)
  • User-Agent — Trình duyệt / app của người dùng
  • Client — Ứng dụng của bạn (web app, SPA, mobile)
  • Authorization Server (AS) — Server xác thực & cấp token (Google, Facebook, Auth0…)
  • Resource Server (RS) — API server chứa dữ liệu được bảo vệ

3. Flow chi tiết (Standard — Có Client Secret)

sequenceDiagram
    participant U as 👤 User
    participant B as 🌐 Browser
    participant C as 🖥️ Client App (Backend)
    participant AS as 🔐 Authorization Server
    participant RS as 📦 Resource Server

    U->>B: Click "Login with Google"
    B->>AS: GET /authorize?response_type=code&client_id=...&redirect_uri=...&scope=...&state=xyz
    AS->>B: Hiển thị trang đăng nhập + consent
    U->>AS: Đăng nhập & đồng ý cấp quyền
    AS->>B: Redirect → redirect_uri?code=AUTH_CODE&state=xyz
    B->>C: Gửi AUTH_CODE lên backend
    C->>AS: POST /token (code + client_id + client_secret + redirect_uri)
    AS->>C: { access_token, refresh_token, expires_in }
    C->>RS: GET /api/data (Authorization: Bearer access_token)
    RS->>C: 200 OK + dữ liệu

Giải thích từng bước:

  1. Authorization Request — Client redirect user đến AS với các params:
    • response_type=code
    • client_id — định danh ứng dụng
    • redirect_uri — URL AS sẽ redirect sau khi xác thực
    • scope — quyền cần xin (vd: openid profile email)
    • state — random string để chống CSRF
  2. User Authentication & Consent — AS hiển thị login form, user đăng nhập và đồng ý.
  3. Authorization Code — AS redirect về redirect_uri kèm code (short-lived, ~10 phút, single-use).
    • Phân biết rõ với MFA

      Không giống nhau — đây là hai thứ hoàn toàn khác nhau, dù cùng có từ "code" và đều ngắn hạn. Để phân biệt rõ:


      🔢 Google Authenticator (6 số) — đây là TOTP/MFA

      • Là gì: Time-based One-Time Password (TOTP) — RFC 6238
      • Mục đích: Xác minh danh tính bạn (Authentication / MFA)
      • Ai tạo ra: App trên điện thoại bạn tự tính toán dựa trên secret key + thời gian hiện tại
      • Dạng: 6 chữ số, đổi mỗi 30 giây
      • Ai nhập: Bạn — nhìn app rồi gõ tay vào website
      • Ví dụ: Đăng nhập Gmail → nhập mật khẩu → nhập thêm 6 số từ Google Authenticator

      🔑 Authorization Code trong OAuth2

      • Là gì: Một chuỗi random dài (VD: 4/P7q7W91a3Bc...) do Authorization Server tạo ra
      • Mục đích: Cho phép backend đổi lấy Access Token một cách an toàn (Authorization)
      • Ai tạo ra: Authorization Server (Google, Facebook...) tạo ra phía server
      • Dạng: Chuỗi ký tự dài, random, single-use, sống ~10 phút
      • Ai nhập: Không ai nhập tay cả — nó được tự động đính vào URL redirect, browser mang đến backend hoàn toàn tự động
      • Ví dụ: https://yourapp.com/callback?code=4/P7q7W91a3Bc...

      So sánh nhanh

      TOTP (Google Authenticator) Authorization Code
      Mục đích Xác minh bạn là ai Đổi lấy Access Token
      Dạng 6 số, đổi 30 giây Chuỗi dài, single-use
      Ai tạo Điện thoại của bạn Authorization Server
      Ai nhập Bạn gõ tay Browser tự động qua redirect
      Tiêu chuẩn RFC 6238 (TOTP) RFC 6749 (OAuth2)

      Analogy đơn giản:

      • TOTP = Mã PIN rút tiền ATM — bạn nhớ và gõ tay để chứng minh bạn là chủ thẻ
      • Authorization Code = Phiếu lấy số ở ngân hàng — nhân viên đưa cho bạn, bạn đưa lại cho quầy khác để lấy dịch vụ, xong là vứt đi
  4. Token Exchange (Back-channel) — Backend gọi POST /token với code + client_secret. Đây là bước quan trọng: token được trao đổi qua server-to-server, không qua browser → an toàn.
  5. Access Token sử dụng — Dùng access_token để gọi Resource Server.
  6. Refresh Token — Khi access_token hết hạn, dùng refresh_token để lấy token mới mà không cần user đăng nhập lại.

4. Flow PKCE (Proof Key for Code Exchange) — Dành cho SPA & Mobile

sequenceDiagram
    participant App as 📱 SPA / Mobile App
    participant AS as 🔐 Authorization Server

    App->>App: Tạo code_verifier (random 43-128 chars)
    App->>App: code_challenge = BASE64URL(SHA256(code_verifier))
    App->>AS: GET /authorize?...&code_challenge=...&code_challenge_method=S256
    AS->>App: Redirect với authorization_code
    App->>AS: POST /token (code + code_verifier — KHÔNG có client_secret)
    AS->>AS: Verify: SHA256(code_verifier) == code_challenge?
    AS->>App: { access_token, refresh_token }

Tại sao PKCE an toàn?

  • Nếu attacker đánh cắp authorization_code, họ không có code_verifier → không thể đổi lấy token.
  • code_challenge là hash một chiều → không thể reverse.

5. Request/Response mẫu

Bước 1 — Authorization Request

GET https://accounts.google.com/o/oauth2/auth
  ?response_type=code
  &client_id=your_client_id
  &redirect_uri=https://yourapp.com/callback
  &scope=openid%20profile%20email
  &state=randomCSRFtoken
  &access_type=offline

Bước 2 — Token Exchange

POST https://oauth2.googleapis.com/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=4/P7q7W91
&redirect_uri=https://yourapp.com/callback
&client_id=your_client_id
&client_secret=your_client_secret

Response

{
  "access_token": "ya29.A0ARrdaM...",
  "expires_in": 3600,
  "refresh_token": "1//0gLd...",
  "scope": "openid profile email",
  "token_type": "Bearer",
  "id_token": "eyJhbGciOiJSUzI1NiJ9..."
}

6. Security Checklist

  • Luôn validate state parameter để chống CSRF attack
  • redirect_uri phải được whitelist chính xác tại Authorization Server
  • Dùng PKCE cho mọi public client (SPA, mobile)
  • Lưu refresh_token an toàn (không localStorage — dùng httpOnly cookie hoặc secure server-side storage)
  • Implement token rotation cho refresh_token
  • Đặt access_token TTL ngắn (15–60 phút)
  • Không log access_token hay refresh_token

7. Khi nào dùng?

Scenario Dùng Authorization Code? Variant
Web app có backend (Laravel, Rails…) ✅ Yes Standard (với client_secret)
Single Page App (React, Vue) ✅ Yes • PKCE (không có client_secret)
Mobile App (iOS, Android) ✅ Yes • PKCE
Server-to-server (không có user) ❌ No Dùng Client Credentials
CLI tool ⚠️ Maybe Device Flow hoặc PKCE

8. Tham khảo

Bài viết liên quan

Đang cập nhật...