Open SaadatAliKlasra opened 1 year ago
This issue is addressed in the documentation FAQ.
You need to add
password_reset_confirm
url into your urls.py (at the top of any other included urls). Please check the urls.py module inside demo app example for more details.
I hope this can help.
This issue is addressed in the documentation FAQ.
You need to add
password_reset_confirm
url into your urls.py (at the top of any other included urls). Please check the urls.py module inside demo app example for more details.I hope this can help.
it is not working also
Did you solve the problem?
Can someone explain how password_reset_confirm should look like in the urls.py? (Is this view not allready implemented in django-allauth somewhere? Do we have to implement in on our own?)
I think I got it working by copying the following path from urls.py
in the demo app:
# this url is used to generate email content
re_path(
r"^password-reset/confirm/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,32})/$",
TemplateView.as_view(template_name="password_reset_confirm.html"),
name="password_reset_confirm",
),
Note that in that file, there are two similarly-named URLs defined. One is called password_reset_confirm
(with underscores), while the other is called password-reset-confirm
(with dashes). You want the one with underscores.
The
password_reset_confirm.html
template in thisTemplateView
doesn't appear to be important for REST operations--here it looks just to be a custom template for the demo app showing a password reset form that asks you to paste in the token you got in the email. But it's probably not necessary, and thisTemplateView
appears to exist mostly as a way to construct the password reset URL that will be sent in the email.
Edit: The password_reset_confirm
view serves to both (1) generate the password reset link that you get in the email given a user ID and unique token and (2) allow for custom handling of the request when the user clicks on that link. In the demo
app, it just shows a password reset form, but you can also replace the TemplateView
with a redirect if you want to handle the password reset form on a different frontend. There's also the REST_AUTH["PASSWORD_RESET_USE_SITES_DOMAIN"]
option you can set in settings.py
to use your site's domain (when using django.contrib.site
and the SITE_ID
setting) as the base of this password reset URL, so all you'd have to do is set up a matching URL pattern on your frontend router.
When you POST to the /dj-rest-auth/password/reset/
endpoint and also have allauth
as an installed app in settings.py
, Django eventually loads the AllAuthPasswordResetForm
form. The .save
method of this form reverses the password_reset_confirm
URL to construct the URL that will be sent in the password reset email, like this:
path = reverse(
'password_reset_confirm',
args=[user_pk_to_url_str(user), temp_key],
)
That's why it needs to have a path with name password_reset_confirm
defined in your app's urls.py
.
This is definitely a bug.
The error is here
When using allauth, the default view to use should be allauth's "account_reset_password_from_key"
view.
Here is a workaround.
# settings.py
REST_AUTH = {
...
# Fix dj-rest-auth weird issue https://github.com/iMerica/dj-rest-auth/issues/494
'PASSWORD_RESET_SERIALIZER': 'path.to.MyPasswordResetSerializer',
}
def my_reset_password_url_generator(request, user, temp_key):
"""
Same code as dj_rest_auth.forms.default_url_generator but we reverse to 'account_reset_password_from_key' (the allauth view)
instead of 'password_reset_confirm' (undefined view, see https://github.com/iMerica/dj-rest-auth/issues/494)
"""
path = reverse(
'account_reset_password_from_key', # see in allauth/account/urls.py
args=[user_pk_to_url_str(user), temp_key],
)
if api_settings.PASSWORD_RESET_USE_SITES_DOMAIN:
url = build_absolute_uri(None, path)
else:
url = build_absolute_uri(request, path)
url = url.replace('%3F', '?')
return url
class MyPasswordResetSerializer(PasswordResetSerializer):
"""
Fix dj-rest-auth issue https://github.com/iMerica/dj-rest-auth/issues/494
"""
def get_email_options(self):
"""This hook thankfully gives us chance to use our url generator."""
return {
'url_generator': my_reset_password_url_generator
}
I do think the current solution proposed in the docs isn't a great solution... It looks like it's copying a similar implementation from django-allauth
with how it generates the activate_url
for the new user email verification process. But my view is that django-allauth
assumes the Django app is serving all URLs whereas dj-rest-auth
should really assume there's a separate frontend serving views...
For what it's worth, my approach using the recommends "urls" approach from the docs, taken from the demo app for reference:
# urls.py
from dj_rest_auth.views import PasswordResetConfirmView
from django.urls import re_path
urlpatterns = [
# This URL is never actually accessed, and could be replaced with a redirect to the frontend using
# Sites Domain. It's purely used by dj-rest-auth to build an absolute URL to inject into the email
# that goes to the user. This path is actually accessed by the frontend.
# The view being used (PasswordResetConfrimView) is never accessed and might be better served putting an actual redirect view here to the frontend in case someone did ever inadvertently access this URL
re_path(r'^auth/password/reset/confirm/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,32})/$',
PasswordResetConfirmView.as_view(),
name='password_reset_confirm' # this path name is what dj-rest-auth is using to construct the URL
),
... other paths
]
Then in settings.py
:
REST_AUTH = {
... other settings
'PASSWORD_RESET_USE_SITES_DOMAIN': True,
}
I then use Sites Domain to actually redirect the user to my frontend application.
This does seem like a bit of a hack/workaround, as the sole purpose of the URL in urls.py
is just to generate a URL to inject into the email for the user to direct to. In my scenario I don't actually want or need Django serving any HTML templates (which I assume most don't considering the common use cases of dj-rest-auth
.
I think a better way would be to simply provide uid
and token
as variables as context to the email template (which they already are anyway), and let the developer construct their preferred URL/path in the email template. And then not even bother with trying to build password_reset_url
within forms.py
and just let the developer manage that in the email template.
Example would then just be:
# templates/account/email/password_reset_key_message.txt
Hey user, click here:
{{ current_site.domain }}/auth/password/reset/confirm/{{ uid }}/{{ token }}/
from dj_rest_auth.views import PasswordResetConfirmView from django.urls import path, include, re_path from django.views.generic import TemplateView
urlpatterns = [
path('', include("dj_rest_auth.urls")),
path('registration/', include('dj_rest_auth.registration.urls')),
repath(
r"^password-reset/confirm/(?P
this does all the magic
I'm using Email instead of a username to log in to my website. I'm using ACCOUNT_EMAIL_VERIFICATION = "mandatory" from django-allauth. I've also the following URL pattern as suggested in the docs. from dj_rest_auth.registration.views import VerifyEmailView path('dj-rest-auth/account-confirm-email/', VerifyEmailView.as_view(), name='account_email_verification_sent') but still getting this error.