Skip to content
This repository was archived by the owner on Feb 24, 2026. It is now read-only.

Commit 6e7d01d

Browse files
feat: Raise meaningful exception when oauth callback times out
- Adds WSGITimeout exception to flow.py - Raises WSGITimeout when run_local_server times out instead of crashing with AttributeError - Adds unit test to verify timeout behavior Co-authored-by: chalmerlowe <7291104+chalmerlowe@users.noreply.github.com>
1 parent 715ff5a commit 6e7d01d

2 files changed

Lines changed: 29 additions & 2 deletions

File tree

google_auth_oauthlib/flow.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -410,8 +410,9 @@ def run_local_server(
410410
in the user's browser.
411411
redirect_uri_trailing_slash (bool): whether or not to add trailing
412412
slash when constructing the redirect_uri. Default value is True.
413-
timeout_seconds (int): It will raise an error after the timeout timing
414-
if there are no credentials response. The value is in seconds.
413+
timeout_seconds (int): It will raise a WSGITimeout exception after the
414+
timeout timing if there are no credentials response. The value is in
415+
seconds.
415416
When set to None there is no timeout.
416417
Default value is None.
417418
token_audience (str): Passed along with the request for an access
@@ -425,6 +426,10 @@ def run_local_server(
425426
Returns:
426427
google.oauth2.credentials.Credentials: The OAuth 2.0 credentials
427428
for the user.
429+
430+
Raises:
431+
WSGITimeout: If there is a timeout when waiting for the response from the
432+
authorization server.
428433
"""
429434
wsgi_app = _RedirectWSGIApp(success_message)
430435
# Fail fast if the address is occupied
@@ -452,6 +457,10 @@ def run_local_server(
452457
local_server.timeout = timeout_seconds
453458
local_server.handle_request()
454459

460+
if wsgi_app.last_request_uri is None:
461+
# Timeout occurred
462+
raise WSGITimeout("Timed out waiting for response from authorization server")
463+
455464
# Note: using https here because oauthlib is very picky that
456465
# OAuth 2.0 should only occur over https.
457466
authorization_response = wsgi_app.last_request_uri.replace("http", "https")
@@ -505,3 +514,7 @@ def __call__(self, environ, start_response):
505514
start_response("200 OK", [("Content-type", "text/plain; charset=utf-8")])
506515
self.last_request_uri = wsgiref.util.request_uri(environ)
507516
return [self._success_message.encode("utf-8")]
517+
518+
519+
class WSGITimeout(Exception):
520+
"""Raised when the WSGI server times out waiting for a response."""

tests/unit/test_flow.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -455,3 +455,17 @@ def test_local_server_socket_cleanup(
455455
instance.run_local_server()
456456

457457
server_mock.server_close.assert_called_once()
458+
459+
@mock.patch("google_auth_oauthlib.flow.webbrowser", autospec=True)
460+
@mock.patch("wsgiref.simple_server.make_server", autospec=True)
461+
def test_run_local_server_timeout(
462+
self, make_server_mock, webbrowser_mock, instance, mock_fetch_token
463+
):
464+
mock_server = mock.Mock()
465+
make_server_mock.return_value = mock_server
466+
467+
# handle_request does nothing (simulating timeout), so last_request_uri remains None
468+
mock_server.handle_request.return_value = None
469+
470+
with pytest.raises(flow.WSGITimeout):
471+
instance.run_local_server(timeout_seconds=1)

0 commit comments

Comments
 (0)