Генерация одиночного токена доступа с помощью Django OAuth2 Toolkit

Я использую последний Django OAuth2 Toolkit (0.10.0) с Python 2.7, Django 1.8 и Django REST Framework 3.3

При использовании grant_type=passwordЯ заметил странное поведение, которое каждый раз, когда пользователь запрашивает новый токен доступа:

curl -X POST -d "grant_type=password&username=<user_name>&password=<password>" -u"<client_id>:<client_secret>" http://localhost:8000/o/token/

Новый токен доступа и токен обновления созданы. Старый токен доступа и обновления все еще можно использовать до истечения времени ожидания токена!

Мои проблемы:

  • Мне нужно, чтобы каждый раз, когда пользователь запрашивал новый токен доступа, старый становился недействительным, непригодным для использования и удалялся.
  • Кроме того, есть способ, которым тип пароля не будет создавать токен обновления. Я не пользуюсь этим в моем приложении.

Одно решение, которое я нашел, заключается в том, что REST Framework OAuth предоставляет конфигурацию для одного токена доступа за раз. Я не хочу использовать этого провайдера, но у меня не будет выбора.

4 ответа

Решение

Если вы хотите удалить все предыдущие токены доступа перед выпуском нового, есть простое решение этой проблемы: создайте свой собственный поставщик просмотра токенов!

Код ниже, вероятно, поможет вам достичь такой функциональности:

from oauth2_provider.models import AccessToken, Application
from braces.views import CsrfExemptMixin
from oauth2_provider.views.mixins import OAuthLibMixin
from oauth2_provider.settings import oauth2_settings

class TokenView(APIView, CsrfExemptMixin, OAuthLibMixin):
    permission_classes = (permissions.AllowAny,)

    server_class = oauth2_settings.OAUTH2_SERVER_CLASS
    validator_class = oauth2_settings.OAUTH2_VALIDATOR_CLASS
    oauthlib_backend_class = oauth2_settings.OAUTH2_BACKEND_CLASS

    def post(self, request):
        username = request.POST.get('username')
        try:
            if username is None:
                raise User.DoesNotExist
            AccessToken.objects.filter(user=User.objects.get(username=username), application=Application.objects.get(name="Website")).delete()
        except Exception as e:
            return Response(e.message,status=400)

        url, headers, body, status = self.create_token_response(request)
        return Response(body, status=status, headers=headers)

Часть, которую вы должны заметить, это блок Try-Except. Там мы находим токены доступа и удаляем их. Все, прежде чем мы создаем новый.

Вы можете посмотреть, как создать свой собственный провайдер, используя OAuthLib. Также это может быть полезно: TokenView в django-oauth-toolkit. Вы можете увидеть там оригинальный Apiview. Как вы сказали, вы использовали этот пакет.

Что касается refresh_token, как уже упоминалось в других ответах, вы не можете делать то, что просите. При взгляде на код oauthlib тип grunt пароля, вы увидите, что при его инициализации refresh_token имеет значение True. Пока вы сами не измените тип Grunt, это невозможно.

Но вы можете сделать то же самое, что мы сделали выше с токенами доступа. Создайте токен, а затем удалите токен обновления.

Мне нужно, чтобы каждый раз, когда пользователь запрашивал новый токен доступа, старый становился недействительным, непригодным для использования и удалялся.

Давать новый токен, когда вы спрашиваете его, кажется ожидаемым поведением. Разве вы не можете отменить существующее, прежде чем просить новое?

Обновить


Если вы решили оставить только один токен - класс OAuth2Validator наследует OAuthLib RequestValidatorи переопределяет метод save_bearer_token. В этом методе перед кодом, связанным с созданием экземпляра модели AccessToken и его методом.save(), вы можете запросить (аналогично этому), чтобы проверить, существует ли уже сохраненный AccessToken в БД для этого пользователя. Если найден существующий токен, его можно удалить из базы данных.

Я настоятельно рекомендую сделать это изменение настраиваемым на случай, если вы передумаете в будущем (в конце концов, по таким причинам будет выпущено несколько токенов)

Более простое решение - иметь собственный класс валидатора, который, вероятно, наследует oauth2_provider.oauth2_validators.OAuth2Validator и переопределяет save_bearer_token, Этот новый класс должен быть дан дляOAUTH2_VALIDATOR_CLASSвsettings.py


Кроме того, есть способ, которым тип пароля не будет создавать токен обновления. Я не пользуюсь этим в моем приложении.

Django OAuth Toolkit зависит от OAuthLib.

Делая refresh_token необязательным, сводится к create_tokenметод вBearerToken класс oAuthLib в этой строке и класс для предоставления пароля здесь. Как вы можете видеть __init__Метод для этого класса занимает refresh_token аргумент, который по умолчанию установлен вTrue, Это значение используется вcreate_token_responseметод того же класса на линии

token = token_handler.create_token(request, self.refresh_token)

create_token_responseметод вOAuthLibCore класс Django OAuth, я полагаю, называет соответствующий create_token_response в OAuthLib. Соблюдайте использование self.server и его инициализация в __init__ метод этого класса, который имеет только валидатор, переданный в качестве аргумента, но ничего не связанного с refresh_token,

Сравните это с OAuthLib. Неявный тип предоставления. create_token_response метод, который явно делает

token = token_handler.create_token(request, refresh_token=False)

не создавать refresh_token совсем

Так что, если я что-то здесь не упустил, tldr, я не думаю, что инструментарий Django OAuth предоставляет возможность опционального refresh_token,

Вот пример того, как сделать это напрямую:

      from oauthlib.common import generate_token
from oauth2_provider.models import AccessToken, Application
from django.utils import timezone
from dateutil.relativedelta import relativedeltatok = generate_token()

tok = generate_token()
app = Application.objects.first9)
user = User.objects.first()
access_token = AccessToken.objects.create(user=user, application=app, expires=timezone.now() + relativedelta(hours=1), token=tok)

Принятый ответ по-прежнему не очищает RefreshToken. Код ниже должен отменить как токен обновления, так и токен доступа.

from oauth2_provider.models import RefreshToken
def clear_token(user):
"""
Clear all user authorized tokens.
"""
for token in RefreshToken.objects.filter(user=user, revoked__isnull=True):
    token.revoke()
Другие вопросы по тегам