Open Harold-D opened 2 years ago
I've tracked it down further to dj_rest's PasswordResetSerializer
@property
def password_reset_form_class(self):
if 'allauth' in settings.INSTALLED_APPS:
return AllAuthPasswordResetForm
else:
return PasswordResetForm
Overridden the serializer, like below, solves the problem.
from django.contrib.auth.forms import PasswordResetForm
from dj_rest_auth.serializers import PasswordResetSerializer
class PasswordResetSerializer(PasswordResetSerializer):
@property
def password_reset_form_class(self):
return PasswordResetForm
Add to setings.py
REST_AUTH_SERIALIZERS = {
'PASSWORD_RESET_SERIALIZER': '**YOUR_APP_NAME***.serializers.PasswordResetSerializer'
}
Now, although the reset URLs look similar, according to the contrib view, the link is invalid.
Allright, find the problem.
The dj_rest_auth PasswordResetSerializer
save method also has an if statement for allauth to select a token generator. In my case I would like to use the default django.contrib token generator because that is also the one which is going to be used by the view behind the password_reset_confirm
url.
So overriding the serializer's safe method like so:
def save(self):
from django.contrib.auth.tokens import default_token_generator
request = self.context.get('request')
# Set some values to trigger the send_email method.
opts = {
'use_https': request.is_secure(),
'from_email': getattr(settings, 'DEFAULT_FROM_EMAIL'),
'request': request,
'token_generator': default_token_generator,
}
opts.update(self.get_email_options())
self.reset_form.save(**opts)
solved the problem.
My question however still remains. What is the purpose of the allauth
if statments.
Is my use case really this unique? Using DJ_rest to send the password reset email and using the default contrib views to handle the actual password reset?
I have the same issue...It seems to have been introduced by the changes made for: https://github.com/iMerica/dj-rest-auth/pull/276 I am not sure what the purpose of creating the custom form was but the implemented solution does not take into account that the generated url for the reset should be the url of client-side rendered framework routed with something like React Router. The reverse call to get the rest url assumes too much and should be modifiable. The old Django Contrib form used just generate the reset tokens and we could format them into the desired url in the registration/password_reset_email.html template
The reverse call to get the rest url assumes too much and should be modifiable.
You put it well, and I'm having the same issue. Let me know if you come up with a good work around for this. I think we can override allauth's password reset form to insert the client side URL but it doesn't seem possible without some code duplication.
The same issue was encountered.
I can confirm this issue I also have the problem that when i try to use the uid and token that i get the a "invalid value" response:
curl -X 'POST' \
'http://0.0.0.0:5005/dj-rest-auth/password/reset/confirm/' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-H 'X-CSRFTOKEN: 9d9pF8KeTDAxrRLMNbQdD14ejFuxYnQLMSa8lmbvzwek0EI0V4m1JodQppvgKRWN' \
-d '{
"new_password1": "xxx",
"new_password2": "xxx",
"uid": "MQ",
"token": "bp8bxn-8599feb2adb3f3921c7ee544708d1ab7"
}'
{
"uid": [
"Invalid value"
]
}
Thanks @Harold-D I had the same problem as @kaiserls The problem was in dj_rest_auth.serializers.PasswordResetConfirmSerializer :
from allauth.account.utils import url_str_to_user_pk as uid_decoder
The solution I found :
from dj_rest_auth.views import PasswordResetConfirmView
re_path(r'^password-reset/confirm/$', PasswordResetConfirmView.as_view(),
name='password_reset_confirm'),
in settings.py :
REST_AUTH = {
"PASSWORD_RESET_CONFIRM_SERIALIZER": "accounts.serializers.PasswordResetConfirmSerializerv1",
}
my serializer (I'm forcing " from django.utils.http import urlsafe_base64_decode as uid_decoder")
class PasswordResetConfirmSerializerv1(PasswordResetConfirmSerializer):
"""
Customize default reset password serializer to change the uid decoder
"""
def validate(self, attrs):
from allauth.account.forms import default_token_generator
from django.utils.http import urlsafe_base64_decode as uid_decoder
# Decode the uidb64 (allauth use base36) to uid to get User object
try:
uid = force_str(uid_decoder(attrs['uid']))
self.user = User.objects.get(pk=uid)
except (TypeError, ValueError, OverflowError, User.DoesNotExist):
raise ValidationError({'uid': [_('Invalid value')]})
if not default_token_generator.check_token(self.user, attrs['token']):
raise ValidationError({'token': [_('Invalid value')]})
self.custom_validation(attrs)
# Construct SetPasswordForm instance
self.set_password_form = self.set_password_form_class(
user=self.user, data=attrs,
)
if not self.set_password_form.is_valid():
raise serializers.ValidationError(self.set_password_form.errors)
return attrs
I have the password reset sequence working using the django.contrib.auth views. Now I would like to be able for the user to initiate a password reset from within my app, so through a REST call, hence dj-rest-auth. Than, I would like the user to use the HTML interface to actually change the password. So only dj-rest-auth view to be able to sent the reset email after a REST call. Than the normal django.contrib HTML views to change the password.
Both REST and web work to the point of sending the email containing the reset URL. However, the link itself is the problem. Using the contrib view / HTML form, the link looks like this:
http://127.0.0.1:8000/reset/MQ/bcu19d-e0b431da6abd39711d58cbf89e16df19/
(notice the MQ part). When requesting a password reset as a REST call, the link looks like this:http://127.0.0.1:8000/reset/1/bcu19p-5accea3a393f9e902cfd96467cd6a652/
(MQ replaced by 1, the users primary key)I have tracked the issue down to the fact that I'm also using
allauth
because of token authentication. Without allauth in installed apps, the password reset link is correct with REST.So somehow, dj-rest-auth and allauth are not getting along. How can I make the REST call generate a correct reset URL so I can use the contrib views for the rest of the reset sequence?
Basic out of the box Django 4.1.2 setup
settings.py
urls.py