Skip to content

Commit

Permalink
properly respect REFRESH_TOKEN_EXPIRE_SECONDS when generating new tokens
Browse files Browse the repository at this point in the history
  • Loading branch information
ryanpetrello committed Apr 7, 2020
1 parent d40143a commit 03d9153
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 1 deletion.
19 changes: 19 additions & 0 deletions awx/api/urls/oauth2_root.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
# Copyright (c) 2017 Ansible, Inc.
# All Rights Reserved.
from datetime import timedelta

from django.utils.timezone import now
from django.conf import settings
from django.conf.urls import url

from oauthlib import oauth2
from oauth2_provider import views

from awx.main.models import RefreshToken
from awx.api.views import (
ApiOAuthAuthorizationRootView,
)
Expand All @@ -14,6 +18,21 @@
class TokenView(views.TokenView):

def create_token_response(self, request):
# Django OAuth2 Toolkit has a bug whereby refresh tokens are *never*
# properly expired (ugh):
#
# https://github.com/jazzband/django-oauth-toolkit/issues/746
#
# This code detects and auto-expires them on refresh grant
# requests.
if request.POST.get('grant_type') == 'refresh_token' and 'refresh_token' in request.POST:
refresh_token = RefreshToken.objects.filter(
token=request.POST['refresh_token']
).first()
if refresh_token:
expire_seconds = settings.OAUTH2_PROVIDER.get('REFRESH_TOKEN_EXPIRE_SECONDS', 0)
if refresh_token.created + timedelta(seconds=expire_seconds) < now():
return request.build_absolute_uri(), {}, 'The refresh token has expired.', '403'
try:
return super(TokenView, self).create_token_response(request)
except oauth2.AccessDeniedError as e:
Expand Down
36 changes: 35 additions & 1 deletion awx/main/tests/functional/api/test_oauth.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import pytest
import base64
import json
import time

import pytest

from django.db import connection
from django.test.utils import override_settings
Expand Down Expand Up @@ -326,6 +328,38 @@ def test_refresh_accesstoken(oauth_application, post, get, delete, admin):
assert original_refresh_token.revoked # is not None


@pytest.mark.django_db
def test_refresh_token_expiration_is_respected(oauth_application, post, get, delete, admin):
response = post(
reverse('api:o_auth2_application_token_list', kwargs={'pk': oauth_application.pk}),
{'scope': 'read'}, admin, expect=201
)
assert AccessToken.objects.count() == 1
assert RefreshToken.objects.count() == 1
refresh_token = RefreshToken.objects.get(token=response.data['refresh_token'])
refresh_url = drf_reverse('api:oauth_authorization_root_view') + 'token/'
short_lived = {
'ACCESS_TOKEN_EXPIRE_SECONDS': 1,
'AUTHORIZATION_CODE_EXPIRE_SECONDS': 1,
'REFRESH_TOKEN_EXPIRE_SECONDS': 1
}
time.sleep(1)
with override_settings(OAUTH2_PROVIDER=short_lived):
response = post(
refresh_url,
data='grant_type=refresh_token&refresh_token=' + refresh_token.token,
content_type='application/x-www-form-urlencoded',
HTTP_AUTHORIZATION='Basic ' + smart_str(base64.b64encode(smart_bytes(':'.join([
oauth_application.client_id, oauth_application.client_secret
]))))
)
assert response.status_code == 403
assert b'The refresh token has expired.' in response.content
assert RefreshToken.objects.filter(token=refresh_token).exists()
assert AccessToken.objects.count() == 1
assert RefreshToken.objects.count() == 1



@pytest.mark.django_db
def test_revoke_access_then_refreshtoken(oauth_application, post, get, delete, admin):
Expand Down

0 comments on commit 03d9153

Please sign in to comment.