Django: неверный токен для сброса пароля после создания учетной записи

в приложении пользователь с ролью администратора через конечную точку DRF может создавать новые учетные записи пользователей.

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

Я определил url:

    path('v1/account/register/',
         AccountCreationView.as_view(),
         name='custom_account_creation'),

представление, которое в первую очередь проверяет, что роль пользователя позволяет создавать новых пользователей:

class AccountCreationView(RegisterView):
    """
    Accounts Creation
    """
    serializer_class = RegisterWithMailSendSerializer

    def get_response_data(self, user):
        # print('get_response_data', user)
        self.user = user

    def create(self, request, *args, **kwargs):
        role_section = 'UsersAdmins'
        #
        rights_check = role_rights_check(
            request.user,
            role_section,
            "R",
        )
        if rights_check[0] == False:
            return Response({"error": rights_check[1]},
                            status=status.HTTP_401_UNAUTHORIZED)
        response = super().create(request, *args, **kwargs)

и пользовательский сериализатор для этого представления, где после проверки данных, сохранить, а затем создать ссылку сброса пароля и отправить по электронной почте только что созданному пользователю:

class RegisterWithMailSendSerializer(RegisterSerializer):
    def save(self, request, **kwargs):
        adapter = get_adapter()
        user = adapter.new_user(request)
        self.cleaned_data = self.get_cleaned_data()
        user = adapter.save_user(request, user, self, commit=False)
        if "password1" in self.cleaned_data:
            try:
                adapter.clean_password(self.cleaned_data['password1'],
                                       user=user)
            except DjangoValidationError as exc:
                raise serializers.ValidationError(
                    detail=serializers.as_serializer_error(exc))
        user.save()
        self.custom_signup(request, user)
        setup_user_email(request, user, [])

        pg = PasswordResetTokenGenerator()
        pg_token = pg.make_token(user)
        print('>>> pg_token', pg_token)

        frontend_site = settings.FRONTEND_APP_BASE_URL
        token_generator = kwargs.get('token_generator',
                                     default_token_generator)
        temp_key = token_generator.make_token(user)
        path = reverse(
            'password_reset_confirm',
            args=[user_pk_to_url_str(user), temp_key],
        )
        full_url = frontend_site + path
        context = {
            'current_site': frontend_site,
            'user': user,
            'password_reset_url': full_url,
            'request': request,
        }
        if app_settings.AUTHENTICATION_METHOD != app_settings.AuthenticationMethod.EMAIL:
            context['username'] = user_username(user)

        email = self.get_cleaned_data()['email']
        get_adapter(request).send_mail('password_reset_key', email, context)
        return user

в settings.py CSRF_COOKIE_SECURE не установлен и имеет значение по умолчанию False.

все вроде бы работает, пользователь создан и ссылка с uid и токеном отправляется на относительный email НО токен кажется недействительным, когда пользователь пытается сбросить свой пароль...

Печатный 'pg_token' - это тот же самый, который заложен в отправленном URL.

Для полноты картины вот пользовательский сериализатор, используемый для сброса пароля:

в settings.py

REST_AUTH_SERIALIZERS = {
    'PASSWORD_RESET_SERIALIZER':
    'api.serializers.serializers_auth.CustomPasswordResetSerializer',
    'TOKEN_SERIALIZER': 'api.serializers.serializers_auth.TokenSerializer',
}

serializers_auth.py

class CustomAllAuthPasswordResetForm(AllAuthPasswordResetForm):

    def save(self, request, **kwargs):
        frontend_site = settings.FRONTEND_APP_BASE_URL
        email = self.cleaned_data['email']
        token_generator = kwargs.get('token_generator',
                                     default_token_generator)
        for user in self.users:
            temp_key = token_generator.make_token(user)
            path = reverse(
                'password_reset_confirm',
                args=[user_pk_to_url_str(user), temp_key],
            )
            full_url = frontend_site + path
            context = {
                'current_site': frontend_site,
                'user': user,
                'password_reset_url': full_url,
                'request': request,
            }
            if app_settings.AUTHENTICATION_METHOD != app_settings.AuthenticationMethod.EMAIL:
                context['username'] = user_username(user)
            get_adapter(request).send_mail('password_reset_key', email,
                                           context)
        return self.cleaned_data['email']


class CustomPasswordResetSerializer(PasswordResetSerializer):
    @property
    def password_reset_form_class(self):
        return CustomAllAuthPasswordResetForm

Я перепробовал все, включая те же вызовы для создания и сброса через Postman, думая, что по какой-то причине токен был аннулирован автоматическим входом в веб-интерфейс DRF после создания пользователя, но я не понимаю, почему токен не действителен.

Если я попробую вручную POST адрес электронной почты на /api/v1/auth/password/reset/ и затем использую предоставленный uid/token на /api/v1/auth/password/reset/confirm/ сброс пароля работает как ожидалось.

Опыт и советы будут очень признательны.

вы можете легко реализовать полную аутентификацию пользователей с помощью Django Djoser Проверьте документацию: https://djoser.readthedocs.io/en/latest/getting_started.html

Доступные конечные точки

/users/

/users/me/

/users/confirm/

/users/resend_activation/

/users/set_password/

/users/reset_password/

/users/reset_password_confirm/

/users/set_username/

/users/reset_username/

/users/reset_username_confirm/

/token/login/ (Аутентификация на основе токенов)

/token/logout/ (Аутентификация на основе токенов)

/jwt/create/ (JSON Web Token Authentication)

/jwt/refresh/ (JSON Web Token Authentication)

/jwt/verify/ (JSON Web Token Authentication)

Решается вызовом конечной точки сброса пароля с параметром email сразу после создания пользователя, без какой-либо пользовательской логики или переопределений:

from rest_framework.test import APIClient
if settings.SEND_EMAIL_PWD_CHANGE_TO_NEW_USERS == True:
                client = APIClient()
                client.post('/api/v1/auth/password/reset/', {'email': user.email}, format='json')

Теперь письмо со ссылкой на сброс содержит действительный токен для сброса пароля.

Вернуться на верх