Repository: jpadilla/pyjwt

Issue:
Ability to remove dictionary value "typ": "JWT" from jwt header
Suggest an idea for this project.
Hi, I'm hitting a limitation where I cannot use this library as it is adding the "typ": "JWT" dict to the header automatically.

A similar issue has been reported below.
https://stackoverflow.com/questions/61511738/pyjwt-custom-header-remove-type-from-jwt-header

Can you please add "typ" as optional?

Thank you

Discussion:
dajiaji: There were no practical problems, but the order of the parameters(typ, alg) in the JWT header changed, resulting in a different output from the sample code. I fixed it.

Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index b049e9e..f122fe7 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -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
 ~~~~~
diff --git a/docs/installation.rst b/docs/installation.rst
index 6da2ccd..925da97 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -7,6 +7,9 @@ You can install ``PyJWT`` with ``pip``:
 
     $ pip install pyjwt
 
+
+.. _installation_cryptography
+
 Cryptographic Dependencies (Optional)
 -------------------------------------
 
diff --git a/docs/usage.rst b/docs/usage.rst
index c5372af..2eacb96 100644
--- a/docs/usage.rst
+++ b/docs/usage.rst
@@ -17,6 +17,8 @@ Encoding & Decoding Tokens with HS256
 Encoding & Decoding Tokens with RS256 (RSA)
 -------------------------------------------
 
+RSA encoding and decoding require the ``cryptography`` module. See :ref:`installation_cryptography`.
+
 .. code-block:: pycon
 
     >>> import jwt
diff --git a/jwt/api_jws.py b/jwt/api_jws.py
index 6cefb2c..3a16294 100644
--- a/jwt/api_jws.py
+++ b/jwt/api_jws.py
@@ -99,6 +99,8 @@ class PyJWS:
         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


Test patch:
diff --git a/tests/test_api_jws.py b/tests/test_api_jws.py
index fbeda02..07d1511 100644
--- a/tests/test_api_jws.py
+++ b/tests/test_api_jws.py
@@ -650,6 +650,65 @@ class TestJWS:
         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})
diff --git a/tests/test_api_jwt.py b/tests/test_api_jwt.py
index 1faa05f..a6230b3 100644
--- a/tests/test_api_jwt.py
+++ b/tests/test_api_jwt.py
@@ -16,6 +16,7 @@ from jwt.exceptions import (
     InvalidIssuerError,
     MissingRequiredClaimError,
 )
+from jwt.utils import base64url_decode
 
 from .utils import crypto_required, key_path, utc_timestamp
 
@@ -167,6 +168,32 @@ class TestJWT:
                 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 = (