Fix OAuth redirect loops caused by stale sessions - #13641
Conversation
🪼 branch checks and previews
Install Gradio from this PR pip install https://huggingface.co/buckets/gradio/pypi-previews/resolve/761af727a89879356971c76ca6a7cadbb8b8e13d/gradio-6.20.0-py3-none-any.whlInstall Gradio Python Client from this PR pip install "gradio-client @ git+https://github.com/gradio-app/gradio@761af727a89879356971c76ca6a7cadbb8b8e13d#subdirectory=client/python"Import Gradio JS Client from this PR via CDN import { Client } from "https://huggingface.co/buckets/gradio/npm-previews/resolve/761af727a89879356971c76ca6a7cadbb8b8e13d/browser.js"; |
🦄 change detectedThis Pull Request includes changes to the following packages.
|
There was a problem hiding this comment.
Pull request overview
This PR fixes OAuth login redirect loops in Hugging Face Spaces by ensuring stale/invalid OAuth session cookies can’t be reused after a state mismatch, and by preserving the _nb_redirects retry counter through the /login/callback redirect URI so the “too many redirects” escape hatch can work reliably.
Changes:
- Clear/reset OAuth session state on
MismatchingStateErrorand explicitly expire thesessioncookie in the callback response. - Preserve
_nb_redirectswhen generating the callback redirect URI. - Add FastAPI
TestClientregressions covering retry count preservation and explicit cookie expiration.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
gradio/oauth.py |
Clears session on OAuth state mismatch, explicitly deletes the session cookie, and propagates _nb_redirects into the callback URL generation. |
test/test_routes.py |
Adds regression tests for _nb_redirects preservation and for expiring stale session cookies on state mismatch. |
.changeset/clean-oauth-retries.md |
Adds a patch-level changeset entry describing the OAuth retry/session reset fix. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # Reset the session so the next attempt cannot reuse stale OAuth state. | ||
| request.session.clear() |
| host = os.environ.get("SPACE_HOST") | ||
| if host is None: # cannot happen in a Space | ||
| raise RuntimeError( | ||
| "Gradio is not running in a Space (SPACE_HOST environment variable is not set)." | ||
| " Cannot redirect to non-iframe view." |
|
I don't think the repro spaces are any good here, for example, they don't even set oauth: true in the space readme or have a gr.LoginButton? |
Description
OAuth state mismatches could keep reusing a stale session cookie. Starlette also cannot expire a session cookie when its signature is invalid, and Gradio dropped
_nb_redirectswhile generating the next callback URL.This PR resets the failed OAuth session, explicitly expires the cookie with matching security attributes, and carries the retry count through the callback URL. It adds focused FastAPI
TestClientregressions for both behaviors.Before / after Spaces
Closes: #13634
AI Disclosure
Testing and Formatting Your Code
python -m pytest test/test_routes.py::TestOAuthSecurity -q(5 passed)ruff format --check gradio/oauth.py test/test_routes.pyruff check gradio/oauth.pyty check gradio/oauth.py test/test_routes.py(no errors; existing unused-ignore warnings)python demo_issue_13634.pyThe full
test/test_routes.pysuite was attempted locally, but tests that bind local ports are blocked in the sandbox. No Playwright tests were added.Minimal reproduction
Before this fix, the callback query has no
_nb_redirectsand the stale callback has noSet-Cookieheader. After this fix, the callback preserves_nb_redirects=2and the response explicitly expiressession.The reproduction file is temporary and was not committed.