summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Miles <daniel.t.miles@gmail.com>2022-05-19 07:34:19 -0700
committerGitHub <noreply@github.com>2022-05-19 20:34:19 +0600
commit675fa10db578886ee6cfd1df688236f69560ced4 (patch)
tree6e3420a6cd3991abe0e01b0a78035770ba803947
parent235f01c9563e407c63888795a573d0b56868e902 (diff)
downloadpyjwt-675fa10db578886ee6cfd1df688236f69560ced4.tar.gz
adding support for compressed payloads (#753)
* adding support for compressed payloads * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * adding test to cover all lines in patch * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * addressing flake8 unused variable and cyclomatic complexity complaints * expanding test for better coverage Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
-rw-r--r--jwt/api_jwt.py23
-rw-r--r--tests/test_api_jwt.py33
2 files changed, 55 insertions, 1 deletions
diff --git a/jwt/api_jwt.py b/jwt/api_jwt.py
index 7d2177b..f04a88d 100644
--- a/jwt/api_jwt.py
+++ b/jwt/api_jwt.py
@@ -1,5 +1,6 @@
import json
import warnings
+import zlib
from calendar import timegm
from collections.abc import Iterable, Mapping
from datetime import datetime, timedelta, timezone
@@ -108,7 +109,7 @@ class PyJWT:
try:
payload = json.loads(decoded["payload"])
except ValueError as e:
- raise DecodeError(f"Invalid payload string: {e}")
+ payload = self._decompress_payload(decoded["payload"], e)
if not isinstance(payload, dict):
raise DecodeError("Invalid payload string: must be a json object")
@@ -118,6 +119,26 @@ class PyJWT:
decoded["payload"] = payload
return decoded
+ @staticmethod
+ def _decompress_payload(payload, e):
+ """
+ Smart Health cards use a raw-compressed (no header or crc) payload,
+ so before surfacing a UnicodeDecodeError, find out if it can be
+ uncompressed successfully
+ noinspection PyBroadException
+ """
+ if isinstance(e, UnicodeDecodeError):
+ try:
+ payload = json.loads(
+ # wbits=-15 has zlib not worry about headers of crc's
+ zlib.decompress(payload, wbits=-15).decode("utf-8")
+ )
+ except Exception:
+ payload = None
+ if payload is not None:
+ return payload
+ raise DecodeError(f"Invalid payload string: {e}")
+
def decode(
self,
jwt: str,
diff --git a/tests/test_api_jwt.py b/tests/test_api_jwt.py
index 84e41e0..491ef9a 100644
--- a/tests/test_api_jwt.py
+++ b/tests/test_api_jwt.py
@@ -64,6 +64,39 @@ class TestJWT:
),
}
+ def test_decodes_complete_valid_jwt_with_compressed_payload(self, jwt):
+ example_payload = {"hello": "world"}
+ example_secret = "secret"
+ # payload made with the pako (https://nodeca.github.io/pako/) library in Javascript:
+ # Buffer.from(pako.deflateRaw('{"hello": "world"}')).toString('base64')
+ example_jwt = (
+ b"eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9"
+ b".q1bKSM3JyVeyUlAqzy/KSVGqBQA="
+ b".08wHYeuh1rJXmcBcMrz6NxmbxAnCQp2rGTKfRNIkxiw="
+ )
+ decoded = jwt.decode_complete(example_jwt, example_secret, algorithms=["HS256"])
+
+ assert decoded == {
+ "header": {"alg": "HS256", "typ": "JWT"},
+ "payload": example_payload,
+ "signature": (
+ b"\xd3\xcc\x07a\xeb\xa1\xd6\xb2W\x99\xc0\\2\xbc\xfa7"
+ b"\x19\x9b\xc4\t\xc2B\x9d\xab\x192\x9fD\xd2$\xc6,"
+ ),
+ }
+
+ def test_decodes_complete_valid_jwt_with_invalid_compressed_payload(self, jwt):
+ # payload made with the pako (https://nodeca.github.io/pako/) library in Javascript:
+ # Buffer.from(pako.deflateRaw('{"this is": "not valid" json')).toString('base64')
+ example_secret = "secret"
+ example_jwt = (
+ b"eyJhbGciOiAiSFMyNTYiLCAidHlwIjogIkpXVCJ9"
+ b".q1YqycgsVsgsVrJSUMrLL1EoS8zJTFFSyCrOzwMA"
+ b".SIAxephyoWBJrw+KdBfksJlhdg+mQtm+vjaRXV1qGJ4="
+ )
+ with pytest.raises(DecodeError):
+ jwt.decode_complete(example_jwt, example_secret, algorithms=["HS256"])
+
def test_load_verify_valid_jwt(self, jwt):
example_payload = {"hello": "world"}
example_secret = "secret"