From 56c394680ac6dfc07c611a9eb1e030e32edd4fe1 Mon Sep 17 00:00:00 2001 From: Jonathan Beaulieu <123.jonathan@gmail.com> Date: Thu, 15 Apr 2021 04:28:04 -0400 Subject: [PATCH] fix: Allow multiple audiences for id_token.verify_token (#733) * feat: Allow multiple audiences for id_token.verify_token (#732) * running black Co-authored-by: arithmetic1728 <58957152+arithmetic1728@users.noreply.github.com> --- google/auth/jwt.py | 11 +++++++---- google/oauth2/id_token.py | 4 ++-- tests/test_jwt.py | 19 +++++++++++++++++++ 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/google/auth/jwt.py b/google/auth/jwt.py index 8165ddad7..892f3a88a 100644 --- a/google/auth/jwt.py +++ b/google/auth/jwt.py @@ -219,8 +219,9 @@ def decode(token, certs=None, verify=True, audience=None): in the token's header. verify (bool): Whether to perform signature and claim validation. Verification is done by default. - audience (str): The audience claim, 'aud', that this JWT should - contain. If None then the JWT's 'aud' parameter is not verified. + audience (str or list): The audience claim, 'aud', that this JWT should + contain. Or a list of audience claims. If None then the JWT's 'aud' + parameter is not verified. Returns: Mapping[str, str]: The deserialized JSON payload in the JWT. @@ -279,9 +280,11 @@ def decode(token, certs=None, verify=True, audience=None): # Check audience. if audience is not None: claim_audience = payload.get("aud") - if audience != claim_audience: + if isinstance(audience, str): + audience = [audience] + if claim_audience not in audience: raise ValueError( - "Token has wrong audience {}, expected {}".format( + "Token has wrong audience {}, expected one of {}".format( claim_audience, audience ) ) diff --git a/google/oauth2/id_token.py b/google/oauth2/id_token.py index 5e3626017..5fbb6a133 100644 --- a/google/oauth2/id_token.py +++ b/google/oauth2/id_token.py @@ -112,8 +112,8 @@ def verify_token(id_token, request, audience=None, certs_url=_GOOGLE_OAUTH2_CERT id_token (Union[str, bytes]): The encoded token. request (google.auth.transport.Request): The object used to make HTTP requests. - audience (str): The audience that this token is intended for. If None - then the audience is not verified. + audience (str or list): The audience or audiences that this token is + intended for. If None then the audience is not verified. certs_url (str): The URL that specifies the certificates to use to verify the token. This URL should return JSON in the format of ``{'key id': 'x509 certificate'}``. diff --git a/tests/test_jwt.py b/tests/test_jwt.py index 7b5ba5cdc..c5290eb07 100644 --- a/tests/test_jwt.py +++ b/tests/test_jwt.py @@ -144,6 +144,17 @@ def test_decode_valid_with_audience(token_factory): assert payload["metadata"]["meta"] == "data" +def test_decode_valid_with_audience_list(token_factory): + payload = jwt.decode( + token_factory(), + certs=PUBLIC_CERT_BYTES, + audience=["audience@example.com", "another_audience@example.com"], + ) + assert payload["aud"] == "audience@example.com" + assert payload["user"] == "billy bob" + assert payload["metadata"]["meta"] == "data" + + def test_decode_valid_unverified(token_factory): payload = jwt.decode(token_factory(), certs=OTHER_CERT_BYTES, verify=False) assert payload["aud"] == "audience@example.com" @@ -211,6 +222,14 @@ def test_decode_bad_token_wrong_audience(token_factory): assert excinfo.match(r"Token has wrong audience") +def test_decode_bad_token_wrong_audience_list(token_factory): + token = token_factory() + audience = ["audience2@example.com", "audience3@example.com"] + with pytest.raises(ValueError) as excinfo: + jwt.decode(token, PUBLIC_CERT_BYTES, audience=audience) + assert excinfo.match(r"Token has wrong audience") + + def test_decode_wrong_cert(token_factory): with pytest.raises(ValueError) as excinfo: jwt.decode(token_factory(), OTHER_CERT_BYTES)