Skip to content

Commit b559c35

Browse files
committed
refactor: support legacy tokens
1 parent 5fb1e59 commit b559c35

File tree

3 files changed

+47
-20
lines changed

3 files changed

+47
-20
lines changed

oauth2_provider/models.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,25 @@ class AbstractRefreshToken(models.Model):
387387
updated = models.DateTimeField(auto_now=True)
388388
revoked = models.DateTimeField(null=True)
389389

390+
@property
391+
def is_expired(self):
392+
"""Determine if RefreshToken is expired."""
393+
expire_seconds = self.application.refresh_token_expire_seconds
394+
expires = self.created + timedelta(seconds=expire_seconds)
395+
396+
now = timezone.now()
397+
is_refresh_token_expired = now >= expires
398+
399+
# RefreshToken should not outlive AccessToken.
400+
# NOTE: Check AccessToken expiration for backwards compatibility with
401+
# long-lived tokens.
402+
access_token_expires = self.access_token.expires
403+
is_access_token_expired = now >= access_token_expires
404+
405+
# RefreshToken expired if and only if both refresh and access tokens
406+
# are expired.
407+
return is_refresh_token_expired and is_access_token_expired
408+
390409
def revoke(self):
391410
"""
392411
Mark this refresh token revoked and revoke related access token

oauth2_provider/oauth2_validators.py

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -475,9 +475,19 @@ def save_bearer_token(self, token, request, *args, **kwargs):
475475
if "scope" not in token:
476476
raise FatalClientError("Failed to renew access token: missing scope")
477477

478-
# "authenticate_client" sets the client (Application) on request
479-
application = request.client
480-
access_token_expire_seconds = application.access_token_expire_seconds
478+
# "authenticate_client" sets the client (Application) on request.
479+
app = request.client
480+
481+
# Users on older app versions should get long-lived tokens for
482+
# backwards compatibility.
483+
is_legacy_token = request.POST.get('is_legacy_token', False)
484+
485+
if is_legacy_token:
486+
access_token_expire_seconds = (
487+
settings.LEGACY_ACCESS_TOKEN_EXPIRE_SECONDS,
488+
)
489+
else:
490+
access_token_expire_seconds = app.access_token_expire_seconds
481491

482492
# expires_in is passed to Server on initialization
483493
# custom server class can have logic to override this
@@ -654,24 +664,18 @@ def validate_refresh_token(self, refresh_token, client, request, *args, **kwargs
654664
seconds=oauth2_settings.REFRESH_TOKEN_GRACE_PERIOD_SECONDS
655665
)
656666
)
657-
rt = RefreshToken.objects.filter(null_or_recent, token=refresh_token).select_related(
658-
"access_token",
659-
"application",
660-
).first()
667+
rt = (
668+
RefreshToken.objects
669+
.filter(null_or_recent, token=refresh_token)
670+
.select_related("user", "access_token", "application")
671+
.first()
672+
)
661673

662674
if not rt:
663675
return False
664676

665-
# Access and refresh token expiration is configurable by Application
666-
# Determine refresh token expiration datetime by adding the timedelta
667-
# of "refresh_token_expire_seconds" to the "created" datetime
668-
expire_seconds = rt.application.refresh_token_expire_seconds
669-
expires = rt.created + timedelta(seconds=expire_seconds)
670-
671-
is_expired = timezone.now() >= expires
672-
673-
# Revoke token if expired
674-
if is_expired:
677+
# Revoke token if expired.
678+
if rt.is_expired:
675679
try:
676680
rt.revoke()
677681
# Catch exception in case access or refresh token do not exist
@@ -686,7 +690,7 @@ def validate_refresh_token(self, refresh_token, client, request, *args, **kwargs
686690
# Token is valid if it refers to the right client AND is not expired
687691
is_valid = (
688692
rt.application == client and
689-
not is_expired
693+
not rt.is_expired
690694
)
691695

692696
return is_valid

oauth2_provider/settings.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,12 @@
4444
"READ_SCOPE": "read",
4545
"WRITE_SCOPE": "write",
4646
"AUTHORIZATION_CODE_EXPIRE_SECONDS": 60,
47-
"ACCESS_TOKEN_EXPIRE_SECONDS": 36000, # 10 hours in seconds
48-
"REFRESH_TOKEN_EXPIRE_SECONDS": 31556952, # 1 year in seconds
47+
"ACCESS_TOKEN_EXPIRE_SECONDS": 36000, # 10 hours in seconds
48+
"REFRESH_TOKEN_EXPIRE_SECONDS": 31556952, # 1 year in seconds
49+
50+
# Older app versions should get long-lived auth tokens.
51+
"LEGACY_ACCESS_TOKEN_EXPIRE_SECONDS": 315569520, # 10 years
52+
4953
"REFRESH_TOKEN_GRACE_PERIOD_SECONDS": 0,
5054
"ROTATE_REFRESH_TOKEN": True,
5155
"ERROR_RESPONSE_WITH_SCOPES": False,

0 commit comments

Comments
 (0)