2025, Oct 17 23:16

Как выдать JWT в HTTP-only куки при регистрации в dj-rest-auth

Как привести регистрацию в dj-rest-auth к поведению входа: выдача JWT в HTTP-only куки. Пошаговый пример для Django REST Framework и django-allauth с кодом.

При настройке аутентификации в Django REST Framework с dj-rest-auth и django-allauth на базе JWT часто используют HTTP-only куки для хранения токенов. Встроенная точка входа для входа в систему уже ведёт себя именно так: она выставляет куки для access- и refresh-токенов. Неожиданность возникает при регистрации: стандартная конечная точка регистрации возвращает токены только в теле ответа и не устанавливает JWT-куки. Ниже — краткое руководство, как привести регистрацию в соответствие со входом, чтобы оба сценария выдавали JWT через HTTP-only куки.

Что идёт не так

Эндпоинт входа из dj_rest_auth автоматически записывает JWT в HTTP-only куки и дополнительно возвращает access-токен в теле ответа. Эндпоинт регистрации, напротив, отдаёт access- и refresh-токены только в теле ответа и не прикрепляет их как куки. Если ваш фронтенд рассчитывает на тот же сценарий работы с токенами через куки после регистрации, что и после входа, такая несогласованность становится реальной проблемой интеграции.

Минимальный пример несоответствия

Если использовать стандартное представление регистрации, получится именно описанное поведение — токены попадают только в тело ответа, но не в куки.

# urls.py
from django.urls import path
from dj_rest_auth.registration.views import RegisterView as SignupBaseView
urlpatterns = [
    path("auth/register/", SignupBaseView.as_view(), name="register"),
]

Почему так происходит

В представлении регистрации нет логики, которая устанавливает куки, — она есть только у представления входа. Иными словами, RegisterView не выставляет JWT-куки, а LoginView — выставляет. В этом и причина расхождения.

Как сделать, чтобы регистрация тоже ставила HTTP-only куки

Расширьте представление регистрации и явно устанавливайте куки при успешном создании учётной записи. Идея проста: вызвать родительский create(), проверить, что вернулся статус 201, взять из тела ответа access- и refresh-токены и применить set_jwt_cookies.

# views.py
from dj_rest_auth.registration.views import RegisterView
from dj_rest_auth.jwt_auth import set_jwt_cookies as bind_jwt_cookies
from rest_framework import status as http_status
class SignupCookieView(RegisterView):
    def create(self, request, *args, **kwargs):
        reply = super().create(request, *args, **kwargs)
        if reply.status_code == http_status.HTTP_201_CREATED:
            token_access = reply.data.get("access")
            token_refresh = reply.data.get("refresh")
            if token_access and token_refresh:
                bind_jwt_cookies(reply, token_access, token_refresh)
        return reply
# urls.py
from django.urls import path
from .views import SignupCookieView
urlpatterns = [
    path("auth/register/", SignupCookieView.as_view(), name="signup_with_cookies"),
]

Небольшое пояснение: почему это не работает из коробки?

Если коротко, в представлении регистрации просто нет ветки, которая устанавливает куки, в отличие от представления входа. Вместо неявной связи между сценариями регистрации и входа этот подход делает поведение явным: вы переопределяете представление регистрации и сами выставляете куки.

Почему это важно

Единообразие между регистрацией и входом уменьшает число пограничных сценариев на клиенте и помогает последовательно придерживаться модели работы с JWT через куки от начала до конца. Если стратегия вашего приложения — хранить токены в HTTP-only куки, согласованная регистрация избавляет от «особых случаев» для новых пользователей и не позволяет refresh-токену попасть куда-либо ещё, помимо задуманного вами места.

Итоги

Сохраняйте единообразие пути пользователя: после входа и после регистрации клиент должен оказаться в одинаковом состоянии аутентификации. В связке dj-rest-auth и django-allauth это означает, что нужно расширить представление регистрации, чтобы оно устанавливало JWT-куки при успешном создании аккаунта. Изменение минимальное, но оно убирает лишнее трение в клиентском коде и приводит ваши эндпоинты в соответствие с выбранной моделью работы с JWT через куки.

Статья основана на вопросе на StackOverflow от Sebastian Power и ответе Razia Khan.