Skip to content

Commit

Permalink
Add expiration for refresh token
Browse files Browse the repository at this point in the history
  • Loading branch information
varuna-sd committed Oct 10, 2019
1 parent 0fdf128 commit d1c6c3a
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 2 deletions.
6 changes: 5 additions & 1 deletion oidc_provider/lib/endpoints/token.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,15 @@ def validate_params(self):
try:
self.token = Token.objects.get(refresh_token=self.params['refresh_token'],
client=self.client)

except Token.DoesNotExist:
logger.debug(
'[Token] Refresh token does not exist: %s', self.params['refresh_token'])
raise TokenError('invalid_grant')

if self.token.has_expired_refresh_token():
logger.debug(
'[Token] Refresh token expired: %s', self.params['refresh_token'])
raise TokenError('invalid_token')
elif self.params['grant_type'] == 'client_credentials':
if not self.client._scope:
logger.debug('[Token] Client using client credentials with empty scope')
Expand Down
21 changes: 20 additions & 1 deletion oidc_provider/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
import binascii
from hashlib import md5, sha256
import json
import datetime

from django.db import models
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
from django.conf import settings

from . import settings as oidc_settings

CLIENT_TYPE_CHOICES = [
('confidential', 'Confidential'),
Expand Down Expand Up @@ -217,6 +218,24 @@ class Meta:
def id_token(self):
return json.loads(self._id_token) if self._id_token else None

def has_expired_refresh_token(self):
if not oidc_settings.get('OIDC_REFRESH_TOKEN_EXPIRE'):
return False

if oidc_settings.get('OIDC_REFRESH_TOKEN_EXPIRE') < oidc_settings.get('OIDC_TOKEN_EXPIRE'):
raise ValueError('Invalid setting for OIDC_REFRESH_TOKEN_EXPIRE')

# Note: Increasing expiration time settings could make previously
# expired refresh tokens usable. Therefore, clear all the
# refresh tokens when increasing refresh token expire time.
offset = (
oidc_settings.get('OIDC_REFRESH_TOKEN_EXPIRE')
- oidc_settings.get('OIDC_TOKEN_EXPIRE')
)
expires_at = self.expires_at + datetime.timedelta(seconds=offset)

return timezone.now() >= expires_at

@id_token.setter
def id_token(self, value):
self._id_token = json.dumps(value)
Expand Down
13 changes: 13 additions & 0 deletions oidc_provider/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,19 @@ def OIDC_TOKEN_EXPIRE(self):
"""
return 60*60

@property
def OIDC_REFRESH_TOKEN_EXPIRE(self):
"""
OPTIONAL. Refresh token expiration time expressed in seconds.
Zero: Refresh token doesn't expire.
Note: Increasing expiration time settings could make previously
expired refresh tokens usable. Hence, increase expiry time with care.
Ex: delete existing refresh tokens before increasing
refresh token expire time.
"""
return 0

@property
def OIDC_USERINFO(self):
"""
Expand Down
42 changes: 42 additions & 0 deletions oidc_provider/tests/cases/test_token_endpoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
import uuid

from base64 import b64encode
from datetime import timedelta

from django.utils import timezone

try:
from urllib.parse import urlencode
Expand Down Expand Up @@ -43,6 +46,7 @@
TokenView,
userinfo,
)
from oidc_provider import settings


class TokenTestCase(TestCase):
Expand Down Expand Up @@ -429,6 +433,44 @@ def do_refresh_token_check(self, scope=None):
response = self._post_request(post_data)
self.assertIn('invalid_grant', response.content.decode('utf-8'))

# Refresh token doesn't expires with default settings
post_data = self._refresh_token_post_data(response_dic2['refresh_token'], scope)
expiry_time = timezone.now() + timedelta(
seconds=24*60*60,
)
with patch('oidc_provider.models.timezone.now') as time_func:
time_func.return_value = expiry_time
response = self._post_request(post_data)
self.assertIn('refresh_token', response.content.decode('utf-8'))

@override_settings(OIDC_IDTOKEN_INCLUDE_CLAIMS=True)
@override_settings(OIDC_REFRESH_TOKEN_EXPIRE=24 * 60 * 60)
def test_refresh_token_expiry(self, scope=None):
"""
Refresh token expiry
Make sure that refresh token expires properly.
"""
# Retrieve refresh token
code = self._create_code()
self.assertEqual(code.scope, ['openid', 'email'])
post_data = self._auth_code_post_data(code=code.code)
response = self._post_request(post_data)

response_dic1 = json.loads(response.content.decode('utf-8'))

# Use refresh token to obtain new token
post_data = self._refresh_token_post_data(
response_dic1['refresh_token'], scope
)
current_time = timezone.now() + timedelta(
seconds=24 * 60 * 60,
)
with patch('oidc_provider.models.timezone.now') as time_func:
time_func.return_value = current_time
response = self._post_request(post_data)
self.assertIn('invalid_token', response.content.decode('utf-8'))

def test_client_redirect_uri(self):
"""
Validate that client redirect URIs exactly match registered
Expand Down

0 comments on commit d1c6c3a

Please sign in to comment.