Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit adc3ee6

Browse filesBrowse files
fix: Raise meaningful exception when oauth callback times out (#363)
I found that when `timeout_seconds` parameter is used, the code raises an exception such as: ``` File "/usr/lib/python3.12/site-packages/google_auth_oauthlib/flow.py", line 520, in run_local_server authorization_response = wsgi_app.last_request_uri.replace("http", "https") ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ AttributeError: 'NoneType' object has no attribute 'replace' ``` This PR adds a custom exception such that a caller can handle this case. I'm unable to run the full `nox` suite locally, but it looks like nothing has broken. I'll make any changes needed when the CI tests run. --------- Co-authored-by: Chalmer Lowe <chalmerlowe@google.com>
1 parent 0994627 commit adc3ee6
Copy full SHA for adc3ee6

2 files changed

+37-3Lines changed: 37 additions & 3 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎packages/google-auth-oauthlib/google_auth_oauthlib/flow.py‎

Copy file name to clipboardExpand all lines: packages/google-auth-oauthlib/google_auth_oauthlib/flow.py
+20-3Lines changed: 20 additions & 3 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 WSGITimeoutError 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+
WSGITimeoutError: 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
@@ -455,7 +460,15 @@ def run_local_server(
455460

456461
# Note: using https here because oauthlib is very picky that
457462
# OAuth 2.0 should only occur over https.
458-
authorization_response = wsgi_app.last_request_uri.replace("http", "https")
463+
try:
464+
authorization_response = wsgi_app.last_request_uri.replace(
465+
"http", "https"
466+
)
467+
except AttributeError as e:
468+
raise WSGITimeoutError(
469+
"Timed out waiting for response from authorization server"
470+
) from e
471+
459472
self.fetch_token(
460473
authorization_response=authorization_response, audience=token_audience
461474
)
@@ -506,3 +519,7 @@ def __call__(self, environ, start_response):
506519
start_response("200 OK", [("Content-type", "text/plain; charset=utf-8")])
507520
self.last_request_uri = wsgiref.util.request_uri(environ)
508521
return [self._success_message.encode("utf-8")]
522+
523+
524+
class WSGITimeoutError(AttributeError):
525+
"""Raised when the WSGI server times out waiting for a response."""
Collapse file

‎packages/google-auth-oauthlib/tests/unit/test_flow.py‎

Copy file name to clipboardExpand all lines: packages/google-auth-oauthlib/tests/unit/test_flow.py
+17Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,3 +497,20 @@ def test_run_local_server_logs_and_prints_url(
497497
urllib.parse.quote(instance.redirect_uri, safe="")
498498
in print_mock.call_args[0][0]
499499
)
500+
501+
@mock.patch("google_auth_oauthlib.flow.webbrowser", autospec=True)
502+
@mock.patch("wsgiref.simple_server.make_server", autospec=True)
503+
def test_run_local_server_timeout(
504+
self, make_server_mock, webbrowser_mock, instance, mock_fetch_token
505+
):
506+
mock_server = mock.Mock()
507+
make_server_mock.return_value = mock_server
508+
509+
# handle_request does nothing (simulating timeout), so last_request_uri remains None
510+
mock_server.handle_request.return_value = None
511+
512+
with pytest.raises(flow.WSGITimeoutError):
513+
instance.run_local_server(timeout_seconds=1)
514+
515+
webbrowser_mock.get.assert_called_with(None)
516+
webbrowser_mock.get.return_value.open.assert_called_once()

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.