django user logged out after password change
Question:
I am having an issue with Django users changing passwords – I have built a few production sites in Django, just none in about a year (or in 1.8), but I don’t recall having this issue before.
Summary
When a user changes their password, the user is logged out, but the password is successfully changed.
Details
I have a view that allows a user to change a password, I am using standard django forms and the auth framework, and to stress: changing the password works, it just logs the user out so that they have to login again.
I actually don’t mind this terribly, I would prefer that the user be redirected to their dashboard with a message update, if i need to reauth the user in the code, then I will, just seems kind of clunky.
here is my view function:
@login_required
def user_change_password(request):
"""Allows a user to change their password"""
if request.method == "POST":
form = SubscriberPasswordForm(request.POST)
if form.is_valid():
try:
request.user.set_password(form.cleaned_data['password'])
request.user.save()
except Exception, err:
print "Error changing password: {}".format(err)
messages.add_message(request, messages.ERROR, 'The password could not be changed, please try again '
'later. This admins have been notified of this error.')
else:
#this outputs True
print request.user.is_authenticated()
messages.add_message(request, messages.INFO, 'Your password has been changed successfully')
return HttpResponseRedirect("/accounts/dashboard/")
else:
form = SubscriberPasswordForm()
return render(request, "accounts/change-password.html", {"form": form})
So the password is changed, the user gets redirected to the dashboard page, the @login_required decorator then redirects them back to the login screen.
The password form is here, though it is pretty straightforward.
class SubscriberPasswordForm(forms.Form):
password = forms.CharField(widget=forms.PasswordInput)
cpassword = forms.CharField(widget=forms.PasswordInput)
def clean_cpassword(self):
password1 = self.cleaned_data.get("password")
password2 = self.cleaned_data.get("cpassword")
if password1 and password2 and password1 != password2:
raise forms.ValidationError(
self.error_messages['password_mismatch'],
code='password_mismatch',
)
Answers:
My understanding is being logged out after password change is new in Django 1.7. So you will need to re-auth user in your code as you said.
See Release Notes:
https://docs.djangoproject.com/en/1.8/releases/1.7/#django-contrib-auth
Here is the specific note:
“The AbstractBaseUser.get_session_auth_hash() method was added and if your AUTH_USER_MODEL inherits from AbstractBaseUser, changing a user’s password now invalidates old sessions if the SessionAuthenticationMiddleware is enabled. See Session invalidation on password change for more details including upgrade considerations when enabling this new middleware.”
See Documentation:
https://docs.djangoproject.com/en/1.7/topics/auth/default/#session-invalidation-on-password-change
For Django 1.8
Simply call update_session_auth_hash
after set_password
like so:
from django.contrib.auth import update_session_auth_hash
request.user.set_password(form.cleaned_data['password'])
update_session_auth_hash(request, request.user)
For django 1.9:
from django.contrib.auth import update_session_auth_hash
def password_change(request):
if request.method == 'POST':
form = PasswordChangeForm(user=request.user, data=request.POST)
if form.is_valid():
form.save()
update_session_auth_hash(request, form.user)
The following fields must be supplied in the POST request:
- old_password
- new_password1
- new_password2
See detailed docs at update_session_auth_hash
for Django 3, Django 1.8+ use this link:
https://docs.djangoproject.com/en/3.2/topics/auth/default/#django.contrib.auth.update_session_auth_hash
or use this code:
from django.contrib.auth import update_session_auth_hash
def password_change(request):
if request.method == 'POST':
form = PasswordChangeForm(user=request.user, data=request.POST)
if form.is_valid():
form.save()
update_session_auth_hash(request, form.user)
else:
...
As of Django 1.11 you can use post_reset_login=True
. See details here: https://stackoverflow.com/a/47535448/10039005
I am having an issue with Django users changing passwords – I have built a few production sites in Django, just none in about a year (or in 1.8), but I don’t recall having this issue before.
Summary
When a user changes their password, the user is logged out, but the password is successfully changed.
Details
I have a view that allows a user to change a password, I am using standard django forms and the auth framework, and to stress: changing the password works, it just logs the user out so that they have to login again.
I actually don’t mind this terribly, I would prefer that the user be redirected to their dashboard with a message update, if i need to reauth the user in the code, then I will, just seems kind of clunky.
here is my view function:
@login_required
def user_change_password(request):
"""Allows a user to change their password"""
if request.method == "POST":
form = SubscriberPasswordForm(request.POST)
if form.is_valid():
try:
request.user.set_password(form.cleaned_data['password'])
request.user.save()
except Exception, err:
print "Error changing password: {}".format(err)
messages.add_message(request, messages.ERROR, 'The password could not be changed, please try again '
'later. This admins have been notified of this error.')
else:
#this outputs True
print request.user.is_authenticated()
messages.add_message(request, messages.INFO, 'Your password has been changed successfully')
return HttpResponseRedirect("/accounts/dashboard/")
else:
form = SubscriberPasswordForm()
return render(request, "accounts/change-password.html", {"form": form})
So the password is changed, the user gets redirected to the dashboard page, the @login_required decorator then redirects them back to the login screen.
The password form is here, though it is pretty straightforward.
class SubscriberPasswordForm(forms.Form):
password = forms.CharField(widget=forms.PasswordInput)
cpassword = forms.CharField(widget=forms.PasswordInput)
def clean_cpassword(self):
password1 = self.cleaned_data.get("password")
password2 = self.cleaned_data.get("cpassword")
if password1 and password2 and password1 != password2:
raise forms.ValidationError(
self.error_messages['password_mismatch'],
code='password_mismatch',
)
My understanding is being logged out after password change is new in Django 1.7. So you will need to re-auth user in your code as you said.
See Release Notes:
https://docs.djangoproject.com/en/1.8/releases/1.7/#django-contrib-auth
Here is the specific note:
“The AbstractBaseUser.get_session_auth_hash() method was added and if your AUTH_USER_MODEL inherits from AbstractBaseUser, changing a user’s password now invalidates old sessions if the SessionAuthenticationMiddleware is enabled. See Session invalidation on password change for more details including upgrade considerations when enabling this new middleware.”
See Documentation:
https://docs.djangoproject.com/en/1.7/topics/auth/default/#session-invalidation-on-password-change
For Django 1.8
Simply call update_session_auth_hash
after set_password
like so:
from django.contrib.auth import update_session_auth_hash
request.user.set_password(form.cleaned_data['password'])
update_session_auth_hash(request, request.user)
For django 1.9:
from django.contrib.auth import update_session_auth_hash
def password_change(request):
if request.method == 'POST':
form = PasswordChangeForm(user=request.user, data=request.POST)
if form.is_valid():
form.save()
update_session_auth_hash(request, form.user)
The following fields must be supplied in the POST request:
- old_password
- new_password1
- new_password2
See detailed docs at update_session_auth_hash
for Django 3, Django 1.8+ use this link:
https://docs.djangoproject.com/en/3.2/topics/auth/default/#django.contrib.auth.update_session_auth_hash
or use this code:
from django.contrib.auth import update_session_auth_hash
def password_change(request):
if request.method == 'POST':
form = PasswordChangeForm(user=request.user, data=request.POST)
if form.is_valid():
form.save()
update_session_auth_hash(request, form.user)
else:
...
As of Django 1.11 you can use post_reset_login=True
. See details here: https://stackoverflow.com/a/47535448/10039005