Skip to content

Commit 304819c

Browse files
Setup initial PyJWT 1.7.1 support (#536)
1 parent 09d6599 commit 304819c

File tree

4 files changed

+56
-17
lines changed

4 files changed

+56
-17
lines changed

rest_framework_simplejwt/backends.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,17 @@
11
import jwt
22
from django.utils.translation import gettext_lazy as _
3-
from jwt import InvalidAlgorithmError, InvalidTokenError, PyJWKClient, algorithms
3+
from jwt import InvalidAlgorithmError, InvalidTokenError, algorithms
44

55
from .exceptions import TokenBackendError
66
from .utils import format_lazy
77

8+
try:
9+
from jwt import PyJWKClient
10+
11+
JWK_CLIENT_AVAILABLE = True
12+
except ImportError:
13+
JWK_CLIENT_AVAILABLE = False
14+
815
ALLOWED_ALGORITHMS = {
916
"HS256",
1017
"HS384",
@@ -37,7 +44,10 @@ def __init__(
3744
self.audience = audience
3845
self.issuer = issuer
3946

40-
self.jwks_client = PyJWKClient(jwk_url) if jwk_url else None
47+
if JWK_CLIENT_AVAILABLE:
48+
self.jwks_client = PyJWKClient(jwk_url) if jwk_url else None
49+
else:
50+
self.jwks_client = None
4151
self.leeway = leeway
4252

4353
def _validate_algorithm(self, algorithm):

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@
5454
install_requires=[
5555
"django",
5656
"djangorestframework",
57-
"pyjwt>=2,<3",
57+
"pyjwt>=1.7.1,<3",
5858
],
5959
python_requires=">=3.7",
6060
extras_require=extras_require,

tests/test_backends.py

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33
from unittest.mock import patch
44

55
import jwt
6+
import pytest
67
from django.test import TestCase
7-
from jwt import PyJWS, algorithms
8+
from jwt import PyJWS
9+
from jwt import __version__ as jwt_version
10+
from jwt import algorithms
811

9-
from rest_framework_simplejwt.backends import TokenBackend
12+
from rest_framework_simplejwt.backends import JWK_CLIENT_AVAILABLE, TokenBackend
1013
from rest_framework_simplejwt.exceptions import TokenBackendError
1114
from rest_framework_simplejwt.utils import aware_utcnow, datetime_to_epoch, make_utc
1215
from tests.keys import (
@@ -28,6 +31,8 @@
2831

2932
LEEWAY = 100
3033

34+
IS_OLD_JWT = jwt_version == "1.7.1"
35+
3136

3237
class TestTokenBackend(TestCase):
3338
def setUp(self):
@@ -159,7 +164,7 @@ def test_decode_with_expiry(self):
159164
def test_decode_with_invalid_sig(self):
160165
self.payload["exp"] = aware_utcnow() - timedelta(seconds=1)
161166
for backend in self.backends:
162-
with self.subTest("Test decode with invalid sig for f{backend.algorithm}"):
167+
with self.subTest(f"Test decode with invalid sig for {backend.algorithm}"):
163168
payload = self.payload.copy()
164169
payload["exp"] = aware_utcnow() + timedelta(days=1)
165170
token_1 = jwt.encode(
@@ -170,6 +175,10 @@ def test_decode_with_invalid_sig(self):
170175
payload, backend.signing_key, algorithm=backend.algorithm
171176
)
172177

178+
if IS_OLD_JWT:
179+
token_1 = token_1.decode("utf-8")
180+
token_2 = token_2.decode("utf-8")
181+
173182
token_2_payload = token_2.rsplit(".", 1)[0]
174183
token_1_sig = token_1.rsplit(".", 1)[-1]
175184
invalid_token = token_2_payload + "." + token_1_sig
@@ -189,8 +198,12 @@ def test_decode_with_invalid_sig_no_verify(self):
189198
token_2 = jwt.encode(
190199
payload, backend.signing_key, algorithm=backend.algorithm
191200
)
192-
# Payload copied
193-
payload["exp"] = datetime_to_epoch(payload["exp"])
201+
if IS_OLD_JWT:
202+
token_1 = token_1.decode("utf-8")
203+
token_2 = token_2.decode("utf-8")
204+
else:
205+
# Payload copied
206+
payload["exp"] = datetime_to_epoch(payload["exp"])
194207

195208
token_2_payload = token_2.rsplit(".", 1)[0]
196209
token_1_sig = token_1.rsplit(".", 1)[-1]
@@ -210,9 +223,13 @@ def test_decode_success(self):
210223
token = jwt.encode(
211224
self.payload, backend.signing_key, algorithm=backend.algorithm
212225
)
213-
# Payload copied
214-
payload = self.payload.copy()
215-
payload["exp"] = datetime_to_epoch(self.payload["exp"])
226+
if IS_OLD_JWT:
227+
token = token.decode("utf-8")
228+
payload = self.payload
229+
else:
230+
# Payload copied
231+
payload = self.payload.copy()
232+
payload["exp"] = datetime_to_epoch(self.payload["exp"])
216233

217234
self.assertEqual(backend.decode(token), payload)
218235

@@ -223,11 +240,18 @@ def test_decode_aud_iss_success(self):
223240
self.payload["iss"] = ISSUER
224241

225242
token = jwt.encode(self.payload, PRIVATE_KEY, algorithm="RS256")
226-
# Payload copied
227-
self.payload["exp"] = datetime_to_epoch(self.payload["exp"])
243+
if IS_OLD_JWT:
244+
token = token.decode("utf-8")
245+
else:
246+
# Payload copied
247+
self.payload["exp"] = datetime_to_epoch(self.payload["exp"])
228248

229249
self.assertEqual(self.aud_iss_token_backend.decode(token), self.payload)
230250

251+
@pytest.mark.skipif(
252+
not JWK_CLIENT_AVAILABLE,
253+
reason="PyJWT 1.7.1 doesn't have JWK client",
254+
)
231255
def test_decode_rsa_aud_iss_jwk_success(self):
232256
self.payload["exp"] = aware_utcnow() + timedelta(days=1)
233257
self.payload["foo"] = "baz"
@@ -261,6 +285,8 @@ def test_decode_rsa_aud_iss_jwk_success(self):
261285

262286
def test_decode_when_algorithm_not_available(self):
263287
token = jwt.encode(self.payload, PRIVATE_KEY, algorithm="RS256")
288+
if IS_OLD_JWT:
289+
token = token.decode("utf-8")
264290

265291
pyjwt_without_rsa = PyJWS()
266292
pyjwt_without_rsa.unregister_algorithm("RS256")
@@ -276,6 +302,8 @@ def _decode(jwt, key, algorithms, options, audience, issuer, leeway):
276302

277303
def test_decode_when_token_algorithm_does_not_match(self):
278304
token = jwt.encode(self.payload, PRIVATE_KEY, algorithm="RS256")
305+
if IS_OLD_JWT:
306+
token = token.decode("utf-8")
279307

280308
with self.assertRaisesRegex(TokenBackendError, "Invalid algorithm specified"):
281309
self.hmac_token_backend.decode(token)

tox.ini

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
[tox]
22
envlist=
3-
py{37,38,39}-dj22-drf310-tests
4-
py{37,38,39,310}-dj{22,32}-drf{311,312,313}-tests
5-
py{38,39,310}-dj{40,main}-drf313-tests
3+
py{37,38,39}-dj22-drf310-pyjwt{171,2}-tests
4+
py{37,38,39,310}-dj{22,32}-drf{311,312,313}-pyjwt{171,2}-tests
5+
py{38,39,310}-dj{40,main}-drf313-pyjwt{171,2}-tests
66
docs
77

88
[gh-actions]
@@ -25,7 +25,6 @@ DRF=
2525
3.13: drf313
2626

2727
[testenv]
28-
usedevelop=True
2928
commands = pytest {posargs:tests} --cov-append --cov-report=xml --cov=rest_framework_simplejwt
3029
extras=
3130
test
@@ -40,6 +39,8 @@ deps=
4039
drf311: djangorestframework>=3.11,<3.12
4140
drf312: djangorestframework>=3.12,<3.13
4241
drf313: djangorestframework>=3.13,<3.14
42+
pyjwt171: pyjwt>=1.7.1,<1.8
43+
pyjwt2: pyjwt>=2,<3
4344
djmain: https://github.com/django/django/archive/main.tar.gz
4445
allowlist_externals=make
4546

0 commit comments

Comments
 (0)