diff --git a/django/contrib/auth/views.py b/django/contrib/auth/views.py index b84fae8..00e7447 100644 --- a/django/contrib/auth/views.py +++ b/django/contrib/auth/views.py @@ -303,6 +303,8 @@ def password_reset_done(request, return TemplateResponse(request, template_name, context) +SECURE_PASSWORD_RESET = True + # Doesn't need csrf_protect since no-one can guess the URL @sensitive_post_parameters() @never_cache @@ -333,6 +335,10 @@ def password_reset_confirm(request, uidb64=None, token=None, except (TypeError, ValueError, OverflowError, UserModel.DoesNotExist): user = None + internal_token = request.session.get('internal-reset-token') + if internal_token: + token = internal_token + if user is not None and token_generator.check_token(user, token): validlink = True title = _('Enter new password') @@ -342,11 +348,19 @@ def password_reset_confirm(request, uidb64=None, token=None, form.save() return HttpResponseRedirect(post_reset_redirect) else: + if SECURE_PASSWORD_RESET and not request.session.get('internal-reset-token'): + import uuid + # Saving a dummy password to invalidate user token. + user.set_password(str(uuid.uuid4())) + user.save() + request.session['internal-reset-token'] = token_generator.make_token(user) form = set_password_form(user) else: validlink = False form = None title = _('Password reset unsuccessful') + if request.session.get('internal-reset-token'): + del request.session['internal-reset-token'] context = { 'form': form, 'title': title,