Skip to content

Commit ea6d5d0

Browse files
authored
Merge pull request #10 from eadwinCode/jazz_merge_march4_2022
Jazzband recent updates
2 parents 6e78835 + 0ca57a6 commit ea6d5d0

File tree

10 files changed

+243
-119
lines changed

10 files changed

+243
-119
lines changed

ninja_jwt/backends.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,28 @@
22

33
import jwt
44
from django.utils.translation import gettext_lazy as _
5-
from jwt import InvalidAlgorithmError, InvalidTokenError, PyJWKClient, algorithms
5+
from jwt import InvalidAlgorithmError, InvalidTokenError, algorithms
66

77
from .exceptions import TokenBackendError
88
from .utils import format_lazy
99

10+
try:
11+
from jwt import PyJWKClient
12+
13+
JWK_CLIENT_AVAILABLE = True
14+
except ImportError:
15+
JWK_CLIENT_AVAILABLE = False
16+
1017
ALLOWED_ALGORITHMS = (
1118
"HS256",
1219
"HS384",
1320
"HS512",
1421
"RS256",
1522
"RS384",
1623
"RS512",
24+
"ES256",
25+
"ES384",
26+
"ES512",
1727
)
1828

1929

@@ -36,7 +46,10 @@ def __init__(
3646
self.audience = audience
3747
self.issuer = issuer
3848

39-
self.jwks_client = PyJWKClient(jwk_url) if jwk_url else None
49+
if JWK_CLIENT_AVAILABLE:
50+
self.jwks_client = PyJWKClient(jwk_url) if jwk_url else None
51+
else:
52+
self.jwks_client = None
4053
self.leeway = leeway
4154

4255
if algorithm.startswith("HS"):

ninja_jwt/models.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,7 @@ def is_authenticated(self) -> bool:
105105

106106
def get_username(self) -> str:
107107
return self.username
108+
109+
def __getattr__(self, attr):
110+
"""This acts as a backup attribute getter for custom claims defined in Token serializers."""
111+
return self.token.get(attr, None)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Generated by Django 3.2.10 on 2022-01-24 06:42
2+
3+
import django.db.models.deletion
4+
from django.conf import settings
5+
from django.db import migrations, models
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
dependencies = [
11+
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12+
("token_blacklist", "0011_linearizes_history"),
13+
]
14+
15+
operations = [
16+
migrations.AlterField(
17+
model_name="outstandingtoken",
18+
name="user",
19+
field=models.ForeignKey(
20+
blank=True,
21+
null=True,
22+
on_delete=django.db.models.deletion.SET_NULL,
23+
to=settings.AUTH_USER_MODEL,
24+
),
25+
),
26+
]

ninja_jwt/token_blacklist/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
class OutstandingToken(models.Model):
66
id = models.BigAutoField(primary_key=True, serialize=False)
77
user = models.ForeignKey(
8-
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True, blank=True
8+
settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True
99
)
1010

1111
jti = models.CharField(unique=True, max_length=255)

ninja_jwt/tokens.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,8 @@ def check_exp(self, claim: str = "exp", current_time: Optional[datetime] = None)
170170
raise TokenError(format_lazy(_("Token has no '{}' claim"), claim))
171171

172172
claim_time = datetime_from_epoch(claim_value)
173-
if claim_time <= current_time:
173+
leeway = self.get_token_backend().leeway
174+
if claim_time <= current_time - timedelta(seconds=leeway):
174175
raise TokenError(format_lazy(_("Token '{}' claim has expired"), claim))
175176

176177
@classmethod
@@ -190,11 +191,16 @@ def for_user(cls, user: Type[AbstractBaseUser]) -> Union["Token", Type["Token"]]
190191

191192
_token_backend = None
192193

193-
def get_token_backend(self):
194+
@property
195+
def token_backend(self):
194196
if self._token_backend is None:
195197
self._token_backend = import_string("ninja_jwt.state.token_backend")
196198
return self._token_backend
197199

200+
def get_token_backend(self):
201+
# Backward compatibility.
202+
return self.token_backend
203+
198204

199205
class BlacklistMixin:
200206
"""
@@ -277,6 +283,11 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
277283
)
278284

279285

286+
class AccessToken(Token):
287+
token_type: str = "access"
288+
lifetime: timedelta = api_settings.ACCESS_TOKEN_LIFETIME
289+
290+
280291
class RefreshToken(BlacklistMixin, Token):
281292
token_type: str = "refresh"
282293
lifetime: timedelta = api_settings.REFRESH_TOKEN_LIFETIME
@@ -290,6 +301,7 @@ class RefreshToken(BlacklistMixin, Token):
290301
api_settings.JTI_CLAIM,
291302
"jti",
292303
)
304+
access_token_class = AccessToken
293305

294306
@property
295307
def access_token(self) -> "AccessToken":
@@ -298,7 +310,7 @@ def access_token(self) -> "AccessToken":
298310
claims present in this refresh token to the new access token except
299311
those claims listed in the `no_copy_claims` attribute.
300312
"""
301-
access = AccessToken()
313+
access = self.access_token_class()
302314

303315
# Use instantiation time of refresh token as relative timestamp for
304316
# access token "exp" claim. This ensures that both a refresh and
@@ -315,11 +327,6 @@ def access_token(self) -> "AccessToken":
315327
return access
316328

317329

318-
class AccessToken(Token):
319-
token_type: str = "access"
320-
lifetime: timedelta = api_settings.ACCESS_TOKEN_LIFETIME
321-
322-
323330
class UntypedToken(Token):
324331
token_type: str = "untyped"
325332
lifetime: timedelta = timedelta(seconds=0)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ classifiers = [
4848

4949
dependencies = [
5050
"Django >= 2.1",
51-
"pyjwt>=2,<3",
51+
"pyjwt>=1.7.1,<3",
5252
"pyjwt[crypto]",
5353
"ninja-schema >= 0.12.8",
5454
"django-ninja-extra >= 0.14.2",

tests/keys.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,3 +110,19 @@
110110
E01hmaHk9xlOpo73IjUxhXUCAwEAAQ==
111111
-----END PUBLIC KEY-----
112112
"""
113+
114+
115+
ES256_PRIVATE_KEY = """
116+
-----BEGIN EC PRIVATE KEY-----
117+
MHcCAQEEIMtBPxiLHcJCrAGdz4jHvTtAh6Rw7351AckG3whXq2WOoAoGCCqGSM49
118+
AwEHoUQDQgAEMZHyNxbkr7+zqQ1dQk/zug2pwYdztmjhpC+XqK88q5NfIS1cBYYt
119+
zhHUS4vGpazNqbW8HA3ZIvJRmx4L96O6/w==
120+
-----END EC PRIVATE KEY-----
121+
"""
122+
123+
ES256_PUBLIC_KEY = """
124+
-----BEGIN PUBLIC KEY-----
125+
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEMZHyNxbkr7+zqQ1dQk/zug2pwYdz
126+
tmjhpC+XqK88q5NfIS1cBYYtzhHUS4vGpazNqbW8HA3ZIvJRmx4L96O6/w==
127+
-----END PUBLIC KEY-----
128+
"""

0 commit comments

Comments
 (0)