Hiểu đúng flow Redirect 302 của Browser trong OAuth/SSO
Ngày 31 tháng 3, 2026
Phần lớn lỗi OAuth/SSO trong production không phải do 302 sai, mà do hiểu sai flow redirect của browser.
Bài này tập trung vào luồng chính:
- Browser đi qua chuỗi redirect như thế nào trong OAuth/SSO.
- Ở callback, cookie/session được set ra sao.
- Vì sao popup login xong nhưng tab chính vẫn có thể chưa có session.
1. Redirect 302 trong flow OAuth tiêu chuẩn
Flow quen thuộc:
App -> (302) -> Auth -> (login) -> (302) -> App
OAUTH 302 LOGIN FLOW
Điểm quan trọng nhất: browser không "đoán" redirect, mà làm đúng theo chuẩn HTTP response.
Ví dụ response từ Server A:
HTTP/1.1 302 Found
Location: https://auth.example.com/oauth2/authorize?client_id=...&redirect_uri=...
Cache-Control: no-storeKhi browser nhận response này trong ngữ cảnh điều hướng trang (hoặc popup navigation), nó sẽ:
- Đọc status code
302. - Đọc header
Location. - Tự tạo request mới đến URL trong
Location. - Cập nhật URL hiện tại theo hướng redirect chain.
Đó là lý do bạn thấy flow kiểu App -> Auth -> callback -> success chạy "tự động", dù frontend không cần window.location ở từng hop.
Điều này cũng giải thích vì sao cùng endpoint nhưng gọi bằng fetch thì UI có thể không đổi trang: fetch không phải top-level navigation.
OAuth 302 Sequence Diagram
OAuth 302 Node Diagram
Browser follows each 302 Location and moves to the next node until session is established.
App (A)
Start: /auth/login
Auth Server (B)
Login + consent
App (A)
Callback: set session
App Success
User now signed in
App (A)
Start: /auth/login
Auth Server (B)
Login + consent
App (A)
Callback: set session
App Success
User now signed in
Bên trong callback 302 của Server A thường có gì?
Khi Auth Server redirect về callback của App (/auth/callback), Server A thường làm chuỗi bước sau:
- Validate
stateđể chống CSRF login flow. - Đổi
authorization_codelấy token ở backend (server-to-server). - Tạo session nội bộ (DB/Redis/JWT session id).
- Trả response set cookie + redirect cuối về trang app.
Một response callback điển hình:
HTTP/1.1 302 Found
Set-Cookie: sid=abc123; Path=/; HttpOnly; Secure; SameSite=Lax
Location: /auth/success
Cache-Control: no-storeBrowser sẽ lưu cookie (nếu policy hợp lệ), rồi tiếp tục tự điều hướng sang Location kế tiếp. Vì vậy người dùng thấy "login xong là nhảy về app", nhưng thực chất giữa các bước có rất nhiều logic backend diễn ra ở callback.
Điểm mấu chốt:
- Top-level page hoặc popup thực sự đổi URL.
- Cookie/session thường được thiết lập ngay tại callback response của Server A.
- Sau redirect cuối, browser render trang đích mới.
2. SSO popup flow: timeline chi tiết
Ví dụ popup thường gặp:
Popup Browser -> Server A (/auth/login)
Server A -> 302 -> Auth Server (B)
Auth Server login -> 302 -> callback về A
A set cookie/session -> 302 -> page success
POPUP SSO REDIRECT FLOW
Trong flow này, popup là nơi chạy redirect chain, không phải tab chính. Vì vậy tab chính cần một cơ chế đồng bộ trạng thái (polling, postMessage, hoặc check session endpoint) sau khi popup hoàn tất.
3. Vì sao là 302 chứ không phải 301?
Trong OAuth/SSO, redirect login là điều hướng tạm thời theo trạng thái phiên, không phải ánh xạ URL cố định.
301 Moved Permanently mang ngữ nghĩa "đã chuyển vĩnh viễn" nên có thể bị cache mạnh bởi browser/CDN. Điều này nguy hiểm cho auth flow vì:
- Browser có thể ghi nhớ redirect cũ và bỏ qua một số bước động của phiên đăng nhập.
- Callback/login endpoint là endpoint theo ngữ cảnh phiên, không nên bị coi là route chuyển hướng vĩnh viễn.
- Khi hạ tầng đổi domain/path callback, cache từ 301 cũ có thể gây lỗi khó debug.
Vì vậy đa số hệ thống dùng 302 (hoặc 303 khi muốn ép method thành GET sau submit form) cho login redirect chain. Tóm lại:
301: dùng cho SEO/site migration ổn định lâu dài.302/303: dùng cho điều hướng tạm thời theo runtime state như OAuth/SSO.
4. Sai lầm phổ biến khi tích hợp OAuth
- Gọi endpoint login không theo browser navigation (ví dụ gọi API rồi chờ UI tự chuyển).
- Đặt callback xử lý session nhưng quên trả redirect cuối về trang success.
- Popup login thành công nhưng không notify tab chính.
- Cookie session set ở callback nhưng thuộc tính SameSite hoặc domain/path không đúng.
5. Pattern triển khai khuyến nghị
Với login chính (full page)
window.location.assign('/auth/login');Với popup SSO
- Tab chính mở popup tới
/auth/login. - Popup chạy full redirect chain.
- Trang success trong popup gửi
postMessage('auth:success')về opener. - Tab chính nhận message rồi gọi
/auth/sessionđể đồng bộ user state.
6. Checklist debug nhanh khi 302 "có vẻ đúng nhưng vẫn lỗi"
- Kiểm tra redirect chain trong DevTools Network (mỗi hop có
Location). - Kiểm tra response callback có
Set-Cookieđúng domain/path/samesite/secure. - Xác nhận flow chính có chạy bằng browser navigation/popup navigation.
- Nếu popup: xác nhận callback đã notify tab chính.
- Kiểm tra endpoint success có chạy được trong môi trường hiện tại (localhost/prod).
7. Corner case: khi nào nói về 302 trong fetch?
302 IN BROWSER VS FETCH
Browser navigation
fetch/XHR flow
fetch là góc phụ cần biết để tránh debug sai hướng, không phải trục chính của OAuth redirect flow.
Top-level navigation sẽ tự đi theo 302 và đổi trang.
Nhưng fetch('/oauth/login') thường chỉ theo redirect ở mức request nội bộ, không tự điều hướng UI.
- Đây là corner case thường gây hiểu nhầm "network OK nhưng UI đứng yên".
- Cross-origin redirect trong fetch còn phụ thuộc
mode,credentials, CORS và có thể thànhopaque. - Với login OAuth/SSO, ưu tiên browser navigation hoặc popup navigation.
Kết luận
302 trong OAuth/SSO trước hết là câu chuyện của browser redirect flow.
Phần fetch chỉ nên xem như corner case để tránh chọn sai cách gọi login endpoint.
Khi nắm chắc flow redirect của browser, phần lớn bug OAuth/SSO khó chịu trong production sẽ trở nên dễ debug hơn rất nhiều.