diff --git a/src/joserfc/_rfc7519/claims.py b/src/joserfc/_rfc7519/claims.py index e35c973..1d1421e 100644 --- a/src/joserfc/_rfc7519/claims.py +++ b/src/joserfc/_rfc7519/claims.py @@ -31,7 +31,7 @@ def convert_claims(claims: Claims, encoder_cls: type[JSONEncoder] | None = None) class ClaimsOption(TypedDict, total=False): essential: bool allow_blank: bool | None - value: str | int | bool + value: str | int | bool | list[str | int | bool] values: list[str | int | bool] | list[str] | list[int] | list[bool] @@ -64,20 +64,25 @@ def check_value(self, claim_name: str, value: Any) -> None: option_values = option.get("values") - if option_values is None: - option_value = option.get("value") - if option_value is not None: - option_values = [option_value] + if option_values: + if isinstance(value, list): + if not any(v in value for v in option_values): + raise InvalidClaimError(claim_name) + elif value not in option_values: + raise InvalidClaimError(claim_name) + return - if not option_values: + option_value = option.get("value") + if option_value is None: return + # "value" is the expected claim value itself; a list claim also + # matches when one of its elements is the expected value if isinstance(value, list): - if not any(v in value for v in option_values): - raise InvalidClaimError(claim_name) - else: - if value not in option_values: + if value != option_value and option_value not in value: raise InvalidClaimError(claim_name) + elif value != option_value: + raise InvalidClaimError(claim_name) def validate(self, claims: dict[str, Any]) -> None: """ diff --git a/tests/jwt/test_claims.py b/tests/jwt/test_claims.py index d5bf46a..fb6d12e 100644 --- a/tests/jwt/test_claims.py +++ b/tests/jwt/test_claims.py @@ -129,6 +129,22 @@ def test_option_value(self): claims_requests.validate({"foo": False}) self.assertRaises(InvalidClaimError, claims_requests.validate, {"foo": True}) + def test_option_value_with_list(self): + # a list "value" is the expected claim value itself + claims_requests = jwt.JWTClaimsRegistry(aud={"essential": True, "value": ["id1"]}) + claims_requests.validate({"aud": ["id1"]}) + self.assertRaises(InvalidClaimError, claims_requests.validate, {"aud": ["id2"]}) + + claims_requests = jwt.JWTClaimsRegistry(aud={"essential": True, "value": ["id1", "id2"]}) + claims_requests.validate({"aud": ["id1", "id2"]}) + self.assertRaises(InvalidClaimError, claims_requests.validate, {"aud": "id1"}) + self.assertRaises(InvalidClaimError, claims_requests.validate, {"aud": ["id1"]}) + + # a scalar "value" still matches any element of a list claim + claims_requests = jwt.JWTClaimsRegistry(foo={"essential": True, "value": "id1"}) + claims_requests.validate({"foo": ["id1"]}) + self.assertRaises(InvalidClaimError, claims_requests.validate, {"foo": ["id2"]}) + def test_option_values(self): claims_requests = jwt.JWTClaimsRegistry(foo={"essential": True, "values": ["1", "2", True, False]}) self.assertRaises(InvalidClaimError, claims_requests.validate, {"foo": "a"})