Tuesday, November 27, 2012

Re: How to log a user in automatically after a password reset?



On Wed, Nov 28, 2012 at 4:07 AM, JC Briar <jcbriar.walkabout@gmail.com> wrote:
When a user has forgotten their password, I use django.contrib.auth.views.password_reset to send an email containing a temporary link. When the user follows that link, I use password_reset_confirm to let them set a new password. So far, so good.

But password_reset_confirm doesn't automatically log the user in – as a result, immediately after resetting their password the user has to enter it again in order to log in. Most users, I suspect, would find this to be obnoxious.

How could this be fixed? According to the auth documentation, I need to call authenticate() and login(). But where? I could supply my own SetPasswordForm subclass to password_reset_confirm, and make the calls in its save() method... except save() doesn't have access to the HttpRequest object that login() requires. Or I could define my own password_reset_complete view... but then I wouldn't have access to the username and password anymore. Are there other options?

What you need to do is write your own password_reset_confirm() method.

You could tackle this three ways, depending on how much custom code you want to write.

Firstly, reinvent the wheel. Copy and paste the original, and insert your code to log in the user on success. Simple, and it works, but it's ugly.

Secondly, don't reinvent the wheel :-) The wonderful thing about Django is that views are just methods - which means you can invoke them:

def my_password_reset_confirm(request):
    response = password_reset_confirm(request)
    do_stuff()
    return response

So - write a custom password_reset_confirm method that does all the usual reset confirmation, and integrates the calls to authenticate and login(). If the original method returns a HTTPRedirect, you know the reset was successful, so you can reprocess the POST data to extract the username and password.

Thirdly, don't reinvent the wheel, and don't reprocess form data either -- exploit duck typing. The existing password_reset_confirm takes the form as an argument; the only problem is that the form doesn't have access to the request. password_reset_confirm takes the form class as an argument, but through the wonders of duck typing, there's nothing that says you need to provide a form class -- you just need to provide something that takes the same arguments as a form class constructor. 

So, subclass the form so that it *does* take the request as an argument, and put the login code as part of the form save() method. Then, write a method that has the same prototype as the constructor for the form that the password_reset_confirm() method is expecting, but returns an instance of *your* form, bound with the current request. Abracadabra - you've got the request object provided to the password reset form!

Yours,
Russ Magee %-)
 

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To post to this group, send email to django-users@googlegroups.com.
To unsubscribe from this group, send email to django-users+unsubscribe@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-users?hl=en.

No comments:

Post a Comment