Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Allow multiple audiences for id_token.verify_token #733

Merged
merged 4 commits into from Apr 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 7 additions & 4 deletions google/auth/jwt.py
Expand Up @@ -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.
Expand Down Expand Up @@ -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
)
)
Expand Down
4 changes: 2 additions & 2 deletions google/oauth2/id_token.py
Expand Up @@ -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'}``.
Expand Down
19 changes: 19 additions & 0 deletions tests/test_jwt.py
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand Down