Skip to content

Commit 8b863e8

Browse files
authored
Merge pull request #136 from zejn/deterministic-headers
Sort headers when serializing to allow for headless JWT.
2 parents aac4c32 + bdccde7 commit 8b863e8

File tree

3 files changed

+30
-0
lines changed

3 files changed

+30
-0
lines changed

CHANGELOG.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ Major
1717
`#121 <https://github.com/mpdavis/python-jose/pull/121>`_
1818
* Make pyca/cryptography backend the preferred backend if multiple backends are present.
1919
`#122 <https://github.com/mpdavis/python-jose/pull/122>`_
20+
* Allow for headless JWT by sorting headers when serializing.
21+
`#136 <https://github.com/mpdavis/python-jose/pull/136>`_
2022

2123
Bugfixes
2224
""""""""

jose/jws.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,7 @@ def _encode_header(algorithm, additional_headers=None):
144144
json_header = json.dumps(
145145
header,
146146
separators=(',', ':'),
147+
sort_keys=True,
147148
).encode('utf-8')
148149

149150
return base64url_encode(json_header)

tests/test_jwt.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,33 @@ def test_non_default_headers(self, claims, key, headers):
107107
for k, v in headers.items():
108108
assert all_headers[k] == v
109109

110+
def test_deterministic_headers(self):
111+
from collections import OrderedDict
112+
from jose.utils import base64url_decode
113+
114+
claims = {"a": "b"}
115+
key = "secret"
116+
117+
headers1 = OrderedDict((
118+
('kid', 'my-key-id'),
119+
('another_key', 'another_value'),
120+
))
121+
encoded1 = jwt.encode(claims, key, algorithm='HS256', headers=headers1)
122+
encoded_headers1 = encoded1.split('.', 1)[0]
123+
124+
headers2 = OrderedDict((
125+
('another_key', 'another_value'),
126+
('kid', 'my-key-id'),
127+
))
128+
encoded2 = jwt.encode(claims, key, algorithm='HS256', headers=headers2)
129+
encoded_headers2 = encoded2.split('.', 1)[0]
130+
131+
assert encoded_headers1 == encoded_headers2
132+
133+
# manually decode header to compare it to known good
134+
decoded_headers1 = base64url_decode(encoded_headers1.encode('utf-8'))
135+
assert decoded_headers1 == b"""{"alg":"HS256","another_key":"another_value","kid":"my-key-id","typ":"JWT"}"""
136+
110137
def test_encode(self, claims, key):
111138

112139
expected = (

0 commit comments

Comments
 (0)