Skip to content

Commit e93fd2b

Browse files
committed
fix(jwt): require string values for jti claims
1 parent 25e9526 commit e93fd2b

2 files changed

Lines changed: 36 additions & 0 deletions

File tree

‎src/joserfc/_rfc7519/claims.py‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,14 @@ def validate_sub(self, value: str) -> None:
143143
raise InvalidClaimError("sub", "Claim 'sub' must be a StringOrURI value")
144144
self.check_value("sub", value)
145145

146+
def validate_jti(self, value: str) -> None:
147+
"""The "jti" (JWT ID) claim provides a unique identifier for the JWT.
148+
Its value MUST be a case-sensitive string. Use of this claim is OPTIONAL.
149+
"""
150+
if not isinstance(value, str):
151+
raise InvalidClaimError("jti", "Claim 'jti' must be a string")
152+
self.check_value("jti", value)
153+
146154
def validate_aud(self, value: str | list[str]) -> None:
147155
"""The "aud" (audience) claim identifies the recipients that the JWT is
148156
intended for. Each principal intended to process the JWT MUST

‎tests/jwt/test_claims.py‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,34 @@ def test_claim_sub(self):
6868
self.assertRaises(InvalidClaimError, claims_requests.validate, {"sub": ["a"]})
6969
self.assertRaises(InvalidClaimError, claims_requests.validate, {"sub": {"a": "b"}})
7070

71+
def test_claim_jti(self):
72+
claims_requests = jwt.JWTClaimsRegistry()
73+
claims_requests.validate({"jti": "expected"})
74+
self.assertRaises(InvalidClaimError, claims_requests.validate, {"jti": ["expected"]})
75+
self.assertRaises(InvalidClaimError, claims_requests.validate, {"jti": 1})
76+
77+
for options in ({}, {"value": "expected"}, {"values": ["expected", "other"]}):
78+
claims_requests = jwt.JWTClaimsRegistry(jti=options)
79+
claims_requests.validate({})
80+
claims_requests.validate({"jti": "expected"})
81+
for value in (None, 1, True, ["other", "expected"], {"jti": "expected"}):
82+
with self.subTest(options=options, value=value):
83+
with self.assertRaises(InvalidClaimError) as cm:
84+
claims_requests.validate({"jti": value})
85+
self.assertEqual(cm.exception.claim, "jti")
86+
self.assertIn("'jti'", cm.exception.description)
87+
88+
for options in ({"value": "expected"}, {"values": ["expected", "other"]}):
89+
claims_requests = jwt.JWTClaimsRegistry(jti=options)
90+
self.assertRaises(InvalidClaimError, claims_requests.validate, {"jti": "wrong"})
91+
92+
claims_requests = jwt.JWTClaimsRegistry(jti={"essential": True})
93+
self.assertRaises(MissingClaimError, claims_requests.validate, {})
94+
self.assertRaises(InvalidClaimError, claims_requests.validate, {"jti": ""})
95+
claims_requests = jwt.JWTClaimsRegistry(jti={"allow_blank": True})
96+
claims_requests.validate({"jti": ""})
97+
self.assertRaises(InvalidClaimError, claims_requests.validate, {"jti": []})
98+
7199
def test_claim_aud(self):
72100
claims_requests = jwt.JWTClaimsRegistry(aud={"essential": True})
73101
claims_requests.validate({"aud": "a"})

0 commit comments

Comments
 (0)