Skip to content

The iss value isn't validated when encoding payload #1039

@webknjaz

Description

@webknjaz

I was looking into what PyGitHub does and writing test for it, invoking its JWT methods. Then I attempted decoding that JWT with jwt.decode(..., issuer=123) since there was 'iss': 123 in the object. The validator treats anything non-str as iterables and crashes:

    def _validate_iss(self, payload: dict[str, Any], issuer: Any) -> None:
        if issuer is None:
            return
    
        if "iss" not in payload:
            raise MissingRequiredClaimError("iss")
    
        if isinstance(issuer, str):
            if payload["iss"] != issuer:
                raise InvalidIssuerError("Invalid issuer")
        else:
>           if payload["iss"] not in issuer:
E           TypeError: argument of type 'int' is not iterable

issuer     = 123
payload    = {'exp': 1739881471, 'iat': 1739881111, 'iss': 123}
self       = <jwt.api_jwt.PyJWT object at 0x7f3101bc93a0>

When I attempted jwt.decode(..., issuer='123') it hit another code branch and crashed in the equality check:

    def _validate_iss(self, payload: dict[str, Any], issuer: Any) -> None:
        if issuer is None:
            return
    
        if "iss" not in payload:
            raise MissingRequiredClaimError("iss")
    
        if isinstance(issuer, str):
            if payload["iss"] != issuer:
>               raise InvalidIssuerError("Invalid issuer")
E               jwt.exceptions.InvalidIssuerError: Invalid issuer

issuer     = '123'
payload    = {'exp': 1739881584, 'iat': 1739881224, 'iss': 123}
self       = <jwt.api_jwt.PyJWT object at 0x7ff5d86cd2e0>

I understand why this is happening and I'm not the one creating the payload with iss being of int type. I believe that the root cause is that PyGitHub creates it with an integer and the bug is that PyJWT allows it, not validating the input.

I checked the spec @ https://datatracker.ietf.org/doc/html/rfc7519#section-4.1 and it says this is supposed to be a string:

4.1.1. "iss" (Issuer) Claim

The "iss" (issuer) claim identifies the principal that issued the
JWT. The processing of this claim is generally application specific.
The "iss" value is a case-sensitive string containing a StringOrURI
value. Use of this claim is OPTIONAL.

(emphasis mine)

So here we are — PyJWT allows putting arbitrary values into payload on encoding but expects them to be spec-compliant on decoding.

I think, there are two things necessary to maintain consistency here — input validation with jwt.encode() checking that the values provided are of legal types, and maybe better type checking and error messages in jwt.decode() reporting illegal data types.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions