Skip to content

Commit fe08531

Browse files
authored
feat: pass thru OAuth audience during InstalledAppFlow.run_local_server (#300)
1 parent a49216b commit fe08531

File tree

2 files changed

+49
-1
lines changed

2 files changed

+49
-1
lines changed

google_auth_oauthlib/flow.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,7 @@ def run_local_server(
378378
open_browser=True,
379379
redirect_uri_trailing_slash=True,
380380
timeout_seconds=None,
381+
token_audience=None,
381382
**kwargs
382383
):
383384
"""Run the flow using the server strategy.
@@ -412,6 +413,9 @@ def run_local_server(
412413
if there are no credentials response. The value is in seconds.
413414
When set to None there is no timeout.
414415
Default value is None.
416+
token_audience (str): Passed along with the request for an access
417+
token. Determines the endpoints with which the token can be
418+
used. Optional.
415419
kwargs: Additional keyword arguments passed through to
416420
:meth:`authorization_url`.
417421
@@ -444,7 +448,9 @@ def run_local_server(
444448
# Note: using https here because oauthlib is very picky that
445449
# OAuth 2.0 should only occur over https.
446450
authorization_response = wsgi_app.last_request_uri.replace("http", "https")
447-
self.fetch_token(authorization_response=authorization_response)
451+
self.fetch_token(
452+
authorization_response=authorization_response, audience=token_audience
453+
)
448454

449455
# This closes the socket
450456
local_server.server_close()

tests/unit/test_flow.py

+42
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ def test_authorized_session(self, instance):
242242
class TestInstalledAppFlow(object):
243243
SCOPES = ["email", "profile"]
244244
REDIRECT_REQUEST_PATH = "/?code=code&state=state"
245+
AUDIENCE = "dummy-audience"
245246

246247
@pytest.fixture
247248
def instance(self):
@@ -312,6 +313,46 @@ def test_run_local_server(self, webbrowser_mock, instance, mock_fetch_token, por
312313
client_secret=CLIENT_SECRETS_INFO["web"]["client_secret"],
313314
authorization_response=expected_auth_response,
314315
code_verifier=None,
316+
audience=None,
317+
)
318+
319+
@pytest.mark.webtest
320+
@mock.patch("google_auth_oauthlib.flow.webbrowser", autospec=True)
321+
def test_run_local_server_audience(
322+
self, webbrowser_mock, instance, mock_fetch_token, port
323+
):
324+
auth_redirect_url = urllib.parse.urljoin(
325+
f"http://localhost:{port}", self.REDIRECT_REQUEST_PATH
326+
)
327+
328+
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
329+
future = pool.submit(
330+
partial(
331+
instance.run_local_server, port=port, token_audience=self.AUDIENCE
332+
)
333+
)
334+
335+
while not future.done():
336+
try:
337+
requests.get(auth_redirect_url)
338+
except requests.ConnectionError: # pragma: NO COVER
339+
pass
340+
341+
credentials = future.result()
342+
343+
assert credentials.token == mock.sentinel.access_token
344+
assert credentials._refresh_token == mock.sentinel.refresh_token
345+
assert credentials.id_token == mock.sentinel.id_token
346+
assert webbrowser_mock.open.called
347+
assert instance.redirect_uri == f"http://localhost:{port}/"
348+
349+
expected_auth_response = auth_redirect_url.replace("http", "https")
350+
mock_fetch_token.assert_called_with(
351+
CLIENT_SECRETS_INFO["web"]["token_uri"],
352+
client_secret=CLIENT_SECRETS_INFO["web"]["client_secret"],
353+
authorization_response=expected_auth_response,
354+
code_verifier=None,
355+
audience=self.AUDIENCE,
315356
)
316357

317358
@pytest.mark.webtest
@@ -353,6 +394,7 @@ def test_run_local_server_code_verifier(
353394
client_secret=CLIENT_SECRETS_INFO["web"]["client_secret"],
354395
authorization_response=expected_auth_response,
355396
code_verifier="amanaplanacanalpanama",
397+
audience=None,
356398
)
357399

358400
@mock.patch("google_auth_oauthlib.flow.webbrowser", autospec=True)

0 commit comments

Comments
 (0)