I use standard Django view, password_reset_confirm(), to reset user's password. After user follows password reset link in the letter, he enters new password and then view redirects him to the site root:
urls.py
url(r'^password-reset/confirm/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
'django.contrib.auth.views.password_reset_confirm', {
'template_name': 'website/auth/password_reset_confirm.html',
'post_reset_redirect': '/',
}, name='password_reset_confirm'),
After Django redirects user, he is not authenticated. I don't want him to type password again, instead, I want to authenticate him right after he set new password.
To implement this feature, I created a delegate view. It wraps standard one and handles its output. Because standard view redirects user only if password reset succeeded, I check status code of response it returns, and if it's a redirect, retrieve user from DB again and authenticate him.
urls.py
url(r'^password-reset/confirm/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
views.password_reset_confirm_delegate, {
'template_name': 'website/auth/password_reset_confirm.html',
'post_reset_redirect': '/',
}, name='password_reset_confirm'),
views.py
@sensitive_post_parameters()
@never_cache
def password_reset_confirm_delegate(request, **kwargs):
response = password_reset_confirm(request, **kwargs)
# TODO Other way?
if response.status_code == 302:
try:
uid = urlsafe_base64_decode(kwargs['uidb64'])
user = User.objects.get(pk=uid)
except (TypeError, ValueError, OverflowError, User.DoesNotExist):
pass
else:
user = authenticate(username=user.username, passwordless=True)
login(request, user)
return response
backends.py
class PasswordlessAuthBackend(ModelBackend):
"""Log in to Django without providing a password.
"""
def authenticate(self, username, passwordless=False):
if not passwordless:
return None
try:
return User.objects.get(username=username)
except User.DoesNotExist:
return None
def get_user(self, user_id):
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None
settings.py
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBackend',
'website.backends.PasswordlessAuthBackend'
)
Are there any better ways to do this?
from django.contrib.auth import authenticate, login def my_view(request): username = request.POST['username'] password = request.POST['password'] user = authenticate(request, username=username, password=password) if user is not None: login(request, user) # Redirect to a success page. ... else: # Return an 'invalid ...
The Django authentication system handles both authentication and authorization. Briefly, authentication verifies a user is who they claim to be, and authorization determines what an authenticated user is allowed to do. Here the term authentication is used to refer to both tasks.
By calling request. user, we are able to access the user that is currently logged in. We then can use the is_authenticated property to determine if a user is currently authenticated (logged into the account). Basically, we do this all in the views.py file, which is the file where our main Python code always goes.
Starting Django 1.11 your can do this by using the class based view. You need to override the password_reset_confirm url to pass post_reset_login=True
and success_url
to PasswordResetConfirmView:
urlpatterns += [
url(
r'^reset/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
views.PasswordResetConfirmView.as_view(
post_reset_login=True,
success_url=reverse_lazy('studygroups_login_redirect')
),
name='password_reset_confirm'
),
]
Since password_reset_confirm
is not class-based-view, you cant cleanly customize it in any significant way without resorting to middleware-type tricks. Therefore what you are doing seems to be the most efficient way at the moment.
If django would be been passing request
to the SetPasswordForm
(similar to how DRF passes request
to serializers), you could of overwritten the form's save()
to login the user there however as now that is also not possible.
You can also look into other 3rd party libs which implement auth as class based views. From a quick google search, the most promising are:
auth.views
as class based views. You can overwrite form_valid
in order to authenticate user.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With