Description
When the OAuth session cookie (set by the Starlette session middleware Gradio uses to store OAuth state/nonce/code_verifier during the login flow) becomes stale or invalid, the /login/callback handler does not clear it before redirecting back to retry. This causes an infinite redirect loop that persists even outside the iframe, which the existing mitigation in #8533 / PR #8951 does not cover.
PR #8951 detects "too many redirects" (MAX_REDIRECTS = 2) and, when exceeded, redirects the user to the non-iframe SPACE_HOST URL — under the assumption that the root cause is always third-party cookies being blocked inside the Spaces iframe. That escape hatch does nothing when the user is already on the direct, non-iframe *.hf.space URL: the loop just continues there with no further fallback, until the browser gives up with ERR_TOO_MANY_REDIRECTS.
Steps to reproduce
Hard to reproduce deterministically (it depends on the browser already holding a stale session cookie from a previous failed/interrupted OAuth attempt for that Space), but once a browser is in this state:
- Open a Gradio Space with
hf_oauth: true directly at its https://<space>.hf.space/ URL (not embedded in the huggingface.co/spaces/... iframe).
- Click "Sign in with Hugging Face".
- Observe the request chain in DevTools → Network (with "Preserve log"):
GET /login/huggingface?_nb_redirects=N&_target_url=... (on the Space)
GET /oauth/authorize?... → 303 (on huggingface.co)
GET /login/callback?_target_url=... → 307 (on huggingface.co)
- back to
GET /login/huggingface?_nb_redirects=N+1&... (on the Space)
- repeats indefinitely,
_nb_redirects climbing each cycle, never resolving
- Eventually Chrome shows: "
<space>.hf.space te redireccionó demasiadas veces. ERR_TOO_MANY_REDIRECTS"
Root cause (as far as I can tell without server-side access)
Checked in DevTools → Application → Cookies for the Space's own *.hf.space domain: a session cookie is present (separate from huggingface.co's own token/session cookies). This is the Starlette SessionMiddleware cookie Gradio's OAuth code uses to hold state across the redirect chain. Manually deleting only this one cookie for the Space's domain immediately fixes the login — no need to clear cookies for huggingface.co or anything else.
This strongly suggests the callback handler, on detecting invalid/mismatched OAuth state (or any failure during the authorize/callback round trip), redirects back to retry the flow without clearing/resetting the session cookie first — so every subsequent attempt fails identically against the same stale state, forever, until a human manually intervenes at the browser level.
Suggested fix
In the /login/callback handler (gradio/oauth.py), when OAuth state validation fails (or the token exchange otherwise fails) instead of only redirecting back to /login/huggingface with an incremented _nb_redirects, also clear/reset the session cookie (or at least the OAuth-related keys within it) so the next attempt starts from a clean state instead of repeating the same failure. The existing MAX_REDIRECTS/non-iframe-escape logic in PR #8951 could stay as a secondary safety net, but shouldn't be the only recovery path — it doesn't help when the loop is already happening outside the iframe.
Environment
- Gradio SDK version: 6.3.0
- Hosted on Hugging Face Spaces (ZeroGPU hardware)
hf_oauth: true, hf_oauth_scopes: [contribute-repos]
- Reproduced in Chrome (stable), both inside the
huggingface.co/spaces/... iframe and directly at the *.hf.space URL
\n\n## Before / after Spaces\n\n- Before fix\n- After fix — draft PR #13641
Before / after Spaces
Description
When the OAuth
sessioncookie (set by the Starlette session middleware Gradio uses to store OAuth state/nonce/code_verifier during the login flow) becomes stale or invalid, the/login/callbackhandler does not clear it before redirecting back to retry. This causes an infinite redirect loop that persists even outside the iframe, which the existing mitigation in #8533 / PR #8951 does not cover.PR #8951 detects "too many redirects" (
MAX_REDIRECTS = 2) and, when exceeded, redirects the user to the non-iframeSPACE_HOSTURL — under the assumption that the root cause is always third-party cookies being blocked inside the Spaces iframe. That escape hatch does nothing when the user is already on the direct, non-iframe*.hf.spaceURL: the loop just continues there with no further fallback, until the browser gives up withERR_TOO_MANY_REDIRECTS.Steps to reproduce
Hard to reproduce deterministically (it depends on the browser already holding a stale
sessioncookie from a previous failed/interrupted OAuth attempt for that Space), but once a browser is in this state:hf_oauth: truedirectly at itshttps://<space>.hf.space/URL (not embedded in thehuggingface.co/spaces/...iframe).GET /login/huggingface?_nb_redirects=N&_target_url=...(on the Space)GET /oauth/authorize?...→ 303 (on huggingface.co)GET /login/callback?_target_url=...→ 307 (on huggingface.co)GET /login/huggingface?_nb_redirects=N+1&...(on the Space)_nb_redirectsclimbing each cycle, never resolving<space>.hf.spacete redireccionó demasiadas veces.ERR_TOO_MANY_REDIRECTS"Root cause (as far as I can tell without server-side access)
Checked in DevTools → Application → Cookies for the Space's own
*.hf.spacedomain: asessioncookie is present (separate fromhuggingface.co's owntoken/session cookies). This is the StarletteSessionMiddlewarecookie Gradio's OAuth code uses to hold state across the redirect chain. Manually deleting only this one cookie for the Space's domain immediately fixes the login — no need to clear cookies forhuggingface.coor anything else.This strongly suggests the callback handler, on detecting invalid/mismatched OAuth state (or any failure during the authorize/callback round trip), redirects back to retry the flow without clearing/resetting the session cookie first — so every subsequent attempt fails identically against the same stale state, forever, until a human manually intervenes at the browser level.
Suggested fix
In the
/login/callbackhandler (gradio/oauth.py), when OAuth state validation fails (or the token exchange otherwise fails) instead of only redirecting back to/login/huggingfacewith an incremented_nb_redirects, also clear/reset thesessioncookie (or at least the OAuth-related keys within it) so the next attempt starts from a clean state instead of repeating the same failure. The existingMAX_REDIRECTS/non-iframe-escape logic in PR #8951 could stay as a secondary safety net, but shouldn't be the only recovery path — it doesn't help when the loop is already happening outside the iframe.Environment
hf_oauth: true,hf_oauth_scopes: [contribute-repos]huggingface.co/spaces/...iframe and directly at the*.hf.spaceURL\n\n## Before / after Spaces\n\n- Before fix\n- After fix — draft PR #13641
Before / after Spaces