Understanding Browser 302 Redirect Flow in OAuth/SSO

Hoang Vu,3 min read

March 31, 2026

Most OAuth/SSO production issues are not caused by 302 itself, but by misunderstanding the browser redirect flow.

This article focuses on the primary flow:

  1. How browser executes redirect chain in OAuth/SSO.
  2. How callback sets cookie/session.
  3. Why popup login can succeed while parent tab still looks logged out.

1. Standard OAuth redirect chain

Common flow:

App -> (302) -> Auth -> (login) -> (302) -> App

OAUTH 302 LOGIN FLOW

1. App initiates /oauth/login
2. 302 -> Auth Server
3. User login on Auth Server
4. 302 -> /oauth/callback
5. App sets session and redirects
6. User lands on success page

The key point is: browser is not guessing redirects. It follows HTTP response semantics directly.

Example response from Server A:

HTTP/1.1 302 Found
Location: https://auth.example.com/oauth2/authorize?client_id=...&redirect_uri=...
Cache-Control: no-store

When browser receives this in a page navigation context (or popup navigation), it will:

  1. Read status code 302.
  2. Read Location header.
  3. Automatically create a new request to that Location URL.
  4. Update current URL as redirect chain progresses.

That is why App -> Auth -> callback -> success feels automatic even though frontend code does not manually call window.location at every step.

It also explains why the same endpoint may not change UI when called via fetch: fetch is not a top-level navigation.

OAuth 302 Sequence Diagram

Browser
App (Server A)
Auth Server (B)
Browser
1) GET /auth/login
App (Server A)
App (Server A)
2) 302 Location: https://auth-b/login
Browser
Browser
3) Navigate to Auth B
Auth Server (B)
Auth Server (B)
4) 302 Location: /oauth/callback?code=...
Browser
Browser
5) GET /oauth/callback
App (Server A)
App (Server A)
6) Set-Cookie + 302 /success
Browser

OAuth 302 Node Diagram

BBrowser context drives navigation

Browser follows each 302 Location and moves to the next node until session is established.

App (A)

Start: /auth/login

302 redirect

Auth Server (B)

Login + consent

302 redirect

App (A)

Callback: set session

302 redirect

App Success

User now signed in

What is usually inside Server A callback 302 response?

After Auth Server redirects back to App callback (/auth/callback), Server A typically does:

  1. Validate state (anti-CSRF for login flow).
  2. Exchange authorization_code for tokens server-to-server.
  3. Create internal app session (DB/Redis/session token).
  4. Return set-cookie + final redirect to app page.

A common callback response:

HTTP/1.1 302 Found
Set-Cookie: sid=abc123; Path=/; HttpOnly; Secure; SameSite=Lax
Location: /auth/success
Cache-Control: no-store

Browser stores cookie (if policy allows), then continues automatically to next Location. So the user sees a smooth redirect flow, while callback backend logic is doing the heavy lifting between hops.

Key outcomes:

  1. URL really changes in address bar.
  2. Session cookies are commonly set in callback response from Server A.
  3. Browser renders the final destination page.

2. Popup SSO timeline in detail

Typical popup flow:

Popup Browser -> Server A (/auth/login)
Server A -> 302 -> Auth Server (B)
Auth Server login -> 302 -> callback to A
A set cookie/session -> 302 -> success page

POPUP SSO REDIRECT FLOW

1. Popup opens: /auth/login on Server A
2. Server A responds 302 to Auth Server B
3. Auth Server B renders login
4. Auth Server B 302 -> callback on A
5. Server A sets cookie/session
6. Server A 302 -> success page

In this model, redirect chain runs inside popup context, not the main tab. Parent app must sync auth state explicitly (postMessage, polling, or session check endpoint).


3. Why 302 instead of 301?

In OAuth/SSO, login redirects are temporary and session-dependent, not permanent URL migrations.

301 Moved Permanently signals a stable permanent move and can be cached aggressively by browsers/CDNs. That is risky for auth flows because:

  1. Browser may remember old redirect behavior and skip important runtime auth transitions.
  2. Login/callback endpoints are stateful by session and should not be treated as permanently relocated.
  3. Infrastructure changes (callback path/domain updates) can be masked by stale 301 cache and become hard-to-debug failures.

That is why most auth systems use 302 (or 303 when explicitly forcing a GET after a form submit) in login redirect chains.

  1. 301: long-term SEO/site migration use cases.
  2. 302/303: temporary runtime navigation such as OAuth/SSO.

4. Common integration mistakes

  1. Triggering login endpoint without browser navigation context and expecting automatic page redirect UX.
  2. Missing final success redirect after callback session setup.
  3. Popup completes auth but never notifies opener tab.
  4. Cookie attributes are wrong (domain, path, SameSite, Secure).

5. Recommended implementation pattern

Main login (full-page)

window.location.assign('/auth/login');

Popup SSO

  1. Parent tab opens popup to /auth/login.
  2. Popup executes full redirect chain.
  3. Popup success page sends postMessage('auth:success') to opener.
  4. Parent tab receives message and refreshes auth session from backend.

6. Fast debugging checklist for redirect issues

  1. Inspect full redirect chain in DevTools Network.
  2. Confirm callback response sets cookies correctly.
  3. Verify primary flow runs through browser navigation/popup navigation.
  4. If popup is used, verify opener sync logic.
  5. Confirm success route is reachable in current environment.

7. Corner case: when 302 appears in fetch

302 IN BROWSER VS FETCH

Browser navigation

1. Browser requests /oauth/login
2. 302 detected by browser
3. Address bar navigates to IdP
4. User login page is rendered
5. 302 callback updates top-level page

fetch/XHR flow

1. fetch('/oauth/login') runs in JS
2. 302 often auto-followed in fetch stack
3. No top-level navigation by default
4. May be blocked by CORS/opaque redirect
5. You must navigate manually (window.location)

fetch is a secondary corner case here, not the core OAuth redirect model.

Top-level navigation naturally follows 302 and updates page state.

fetch('/oauth/login') may follow redirects at network level, but it does not automatically navigate your UI.

  1. This creates the common confusion: "network succeeded, UI stayed still".
  2. Cross-origin redirects in fetch depend on mode, credentials, CORS, and can become opaque.
  3. For OAuth/SSO login, browser navigation or popup navigation remains the safer default.

Conclusion

In OAuth/SSO, 302 is first and foremost a browser redirect-flow topic.

fetch behavior should be treated as a corner case so teams do not design login entrypoints around the wrong execution model.

Once browser redirect flow is clear, most OAuth/SSO production redirect bugs become straightforward to debug.

2026 © @hoag/blog.