Search code examples
pythondjangodjango-rest-frameworkdjango-rest-auth

Django Rest Auth - UID invalid value error


I have a DRF server, and I've been trying to send a password reset email, using the PasswordResetForm.

I receive an email as expected, though when trying to send a reset the password, I receive:

{
    "uid": [
        "Invalid value"
    ]
}

After some investigation, I've found that in Django's PasswordResetConfirmSerializer's file, there is a conditional import which is responsible for the issue - I'm using allauth and so it imports the wrong uid_decoder module (it works using the other module):

# this imports the wrong uid_decoder
if 'allauth' in settings.INSTALLED_APPS:
    from allauth.account.forms import default_token_generator
    from allauth.account.utils import url_str_to_user_pk as uid_decoder
else:
    from django.contrib.auth.tokens import default_token_generator 
    from django.utils.http import urlsafe_base64_decode as uid_decoder

Is there a better way to handle the issue than editing the dj_rest_auth file?

Thanks!

Email send code, if needed:

from django.conf import settings
from django.contrib.auth.forms import PasswordResetForm


def send_user_invite(email):
    # send invitation to reset password & join the platform
    form_options = {
        "use_https": True,
        "from_email": getattr(settings, "DEFAULT_FROM_EMAIL"),
        # "request": request,
        "subject_template_name": "registration/password_reset_subject.txt",
        "email_template_name": "users/invite_with_password_reset.html",
        "extra_email_context": {"reset_base_url": settings.RESET_BASE_URL},
    }
    form = PasswordResetForm(data={"email": email})
    if form.is_valid():
        form.save(**form_options)


Solution

  • My workaround was to write a custom subclass for ResetPasswordForm, as follows:

    # helpers.py
    from server.users.forms import SendInviteForm
    
    
    def send_user_invite(email):
        # send invitation to reset password & join the platform
        form = SendInviteForm(data={"email": email})
        if form.is_valid():
            form.save(None)
    
    from os import path
    
    from allauth.account.forms import (
        EmailAwarePasswordResetTokenGenerator,
        ResetPasswordForm,
    )
    from allauth.account.utils import user_pk_to_url_str
    from django.conf import settings
    from django.contrib.auth import forms as admin_forms
    from django.contrib.auth import get_user_model
    from django.core.mail import send_mail
    from django.template.loader import render_to_string
    from django.utils.translation import gettext_lazy as _
    
    
    
    class SendInviteForm(ResetPasswordForm):
        """
        used to send an invitation to onboard the platform and reset the password
        """
    
        default_token_generator = EmailAwarePasswordResetTokenGenerator()
    
        def send_email_invite(self, email, uri, uid, token):
            context = {
                "uri": uri,
                "uid": uid,
                "token": token,
            }
            msg_plain = render_to_string("users/invite_with_password_reset.txt", context)
            msg_html = render_to_string("users/invite_with_password_reset.html", context)
            send_mail(
                "Welcome!",
                msg_plain,
                None,
                [email],
                html_message=msg_html,
            )
    
        def save(self, request, **kwargs):
            email = self.cleaned_data["email"]
            token_generator = kwargs.get("token_generator", self.default_token_generator)
            for user in self.users:
                temp_key = token_generator.make_token(user)
                uri = path.join(settings.CLIENT_BASE_URL, "he/welcome/reset-password")
                self.send_email_invite(email, uri, user_pk_to_url_str(user), temp_key)
            return self.cleaned_data["email"]