Skip to content

Commit

Permalink
Allow audience claims to be arrays
Browse files Browse the repository at this point in the history
This means that if a server checking a token provides an array as the
check_claims argument it intends that the server is handling multiple
identities and the token checks will result valid as long as one of the
audiences matches one of the identities the server impersonates.

Signed-off-by: Simo Sorce <simo@redhat.com>
  • Loading branch information
simo5 committed May 10, 2022
1 parent 7dcc162 commit 5dc1aee
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 13 deletions.
25 changes: 17 additions & 8 deletions jwcrypto/jwt.py
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ def _check_default_claims(self, claims):
def _check_check_claims(self, check_claims):
self._check_string_claim('iss', check_claims)
self._check_string_claim('sub', check_claims)
self._check_string_claim('aud', check_claims)
self._check_array_or_string_claim('aud', check_claims)
self._check_integer_claim('exp', check_claims)
self._check_integer_claim('nbf', check_claims)
self._check_integer_claim('iat', check_claims)
Expand Down Expand Up @@ -398,14 +398,23 @@ def _check_provided_claims(self):

elif name == 'aud':
if value is not None:
if value == claims[name]:
continue
if isinstance(claims[name], list):
if value in claims[name]:
continue
raise JWTInvalidClaimValue(
"Invalid '%s' value. Expected '%s' to be in '%s'" % (
name, claims[name], value))
tclaims = claims[name]
else:
tclaims = [claims[name]]
if isinstance(value, list):
cclaims = value
else:
cclaims = [value]
found = False
for v in cclaims:
if v in tclaims:
found = True
break
if not found:
raise JWTInvalidClaimValue(
"Invalid '{}' value. Expected '{}' in '{}'".format(
name, claims[name], value))

elif name == 'exp':
if value is not None:
Expand Down
23 changes: 18 additions & 5 deletions jwcrypto/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -1657,14 +1657,27 @@ def test_Issue_209(self):
# the oct key before hitting the ES one
jwt.JWT(jwt=token, key=ks)

def test_Issue_239(self):
claims = {"aud": "www.example.com"}
check_claims = {"aud": ["www.example.com", "account"]}
def test_Issue_277(self):
claims = {"aud": ["www.example.com", "www.test.net"]}
key = jwk.JWK(generate='oct', size=256)
token = jwt.JWT(header={"alg": "HS256"}, claims=claims)
token.make_signed_token(key)
self.assertRaises(jwt.JWTInvalidClaimFormat, jwt.JWT, key=key,
jwt=token.serialize(), check_claims=check_claims)
sertok = token.serialize()
jwt.JWT(key=key, jwt=sertok, check_claims={"aud": "www.example.com"})
jwt.JWT(key=key, jwt=sertok, check_claims={"aud": "www.test.net"})
jwt.JWT(key=key, jwt=sertok, check_claims={"aud": ["www.example.com"]})
jwt.JWT(key=key, jwt=sertok, check_claims={"aud": ["www.test.net"]})
jwt.JWT(key=key, jwt=sertok, check_claims={"aud": ["www.example.com",
"www.test.net"]})
jwt.JWT(key=key, jwt=sertok, check_claims={"aud": ["www.example.com",
"nomatch"]})
self.assertRaises(jwt.JWTInvalidClaimValue, jwt.JWT, key=key,
jwt=sertok, check_claims={"aud": "nomatch"})
self.assertRaises(jwt.JWTInvalidClaimValue, jwt.JWT, key=key,
jwt=sertok, check_claims={"aud": ["nomatch"]})
self.assertRaises(jwt.JWTInvalidClaimValue, jwt.JWT, key=key,
jwt=sertok, check_claims={"aud": ["nomatch",
"failmatch"]})


class ConformanceTests(unittest.TestCase):
Expand Down

0 comments on commit 5dc1aee

Please sign in to comment.