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?