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

Make typ optional #644

Merged
merged 13 commits into from Aug 8, 2021
1 change: 1 addition & 0 deletions CHANGELOG.rst
Expand Up @@ -15,6 +15,7 @@ Fixed

- Prefer `headers["alg"]` to `algorithm` in `jwt.encode()`. `#673 <https://github.com/jpadilla/pyjwt/pull/673>`__
- Fix aud validation to support {'aud': null} case. `#670 <https://github.com/jpadilla/pyjwt/pull/670>`__
- Make `typ` optional in JWT to be compliant with RFC7519. `#644 <https://github.com/jpadilla/pyjwt/pull/644>`__

Added
~~~~~
Expand Down
2 changes: 2 additions & 0 deletions jwt/api_jws.py
Expand Up @@ -99,6 +99,8 @@ def encode(
if headers:
self._validate_headers(headers)
header.update(headers)
if not header["typ"]:
del header["typ"]

json_header = json.dumps(
header, separators=(",", ":"), cls=json_encoder
Expand Down
59 changes: 59 additions & 0 deletions tests/test_api_jws.py
Expand Up @@ -650,6 +650,65 @@ def test_encode_headers_parameter_adds_headers(self, jws, payload):
assert "testheader" in header_obj
assert header_obj["testheader"] == headers["testheader"]

def test_encode_with_typ(self, jws):
payload = """
{
"iss": "https://scim.example.com",
"iat": 1458496404,
"jti": "4d3559ec67504aaba65d40b0363faad8",
"aud": [
"https://scim.example.com/Feeds/98d52461fa5bbc879593b7754",
"https://scim.example.com/Feeds/5d7604516b1d08641d7676ee7"
],
"events": {
"urn:ietf:params:scim:event:create": {
"ref":
"https://scim.example.com/Users/44f6142df96bd6ab61e7521d9",
"attributes": ["id", "name", "userName", "password", "emails"]
}
}
}
"""
token = jws.encode(
payload.encode("utf-8"), "secret", headers={"typ": "secevent+jwt"}
)

header = token[0 : token.index(".")].encode()
header = base64url_decode(header)
header_obj = json.loads(header)

assert "typ" in header_obj
assert header_obj["typ"] == "secevent+jwt"

def test_encode_with_typ_empty_string(self, jws, payload):
token = jws.encode(payload, "secret", headers={"typ": ""})

header = token[0 : token.index(".")].encode()
header = base64url_decode(header)
header_obj = json.loads(header)

assert "typ" not in header_obj

def test_encode_with_typ_none(self, jws, payload):
token = jws.encode(payload, "secret", headers={"typ": None})

header = token[0 : token.index(".")].encode()
header = base64url_decode(header)
header_obj = json.loads(header)

assert "typ" not in header_obj

def test_encode_with_typ_without_keywords(self, jws, payload):
headers = {"foo": "bar"}
token = jws.encode(payload, "secret", "HS256", headers, None)

header = token[0 : token.index(".")].encode()
header = base64url_decode(header)
header_obj = json.loads(header)

assert "foo" in header_obj
assert header_obj["foo"] == "bar"

def test_encode_fails_on_invalid_kid_types(self, jws, payload):
with pytest.raises(InvalidTokenError) as exc:
jws.encode(payload, "secret", headers={"kid": 123})
Expand Down
27 changes: 27 additions & 0 deletions tests/test_api_jwt.py
Expand Up @@ -16,6 +16,7 @@
InvalidIssuerError,
MissingRequiredClaimError,
)
from jwt.utils import base64url_decode

from .utils import crypto_required, key_path, utc_timestamp

Expand Down Expand Up @@ -167,6 +168,32 @@ def test_encode_bad_type(self, jwt):
lambda: jwt.encode(t, "secret", algorithms=["HS256"]),
)

def test_encode_with_typ(self, jwt):
payload = {
"iss": "https://scim.example.com",
"iat": 1458496404,
"jti": "4d3559ec67504aaba65d40b0363faad8",
"aud": [
"https://scim.example.com/Feeds/98d52461fa5bbc879593b7754",
"https://scim.example.com/Feeds/5d7604516b1d08641d7676ee7",
],
"events": {
"urn:ietf:params:scim:event:create": {
"ref": "https://scim.example.com/Users/44f6142df96bd6ab61e7521d9",
"attributes": ["id", "name", "userName", "password", "emails"],
}
},
}
token = jwt.encode(
payload, "secret", algorithm="HS256", headers={"typ": "secevent+jwt"}
)
header = token[0 : token.index(".")].encode()
header = base64url_decode(header)
header_obj = json.loads(header)

assert "typ" in header_obj
assert header_obj["typ"] == "secevent+jwt"

def test_decode_raises_exception_if_exp_is_not_int(self, jwt):
# >>> jwt.encode({'exp': 'not-an-int'}, 'secret')
example_jwt = (
Expand Down