Easy Password Reset With Django

I’ve recently been playing around quite a bit with Django and full stack development. While this has not been primarily affiliated to my research, it has helped me work with other research groups that need custom web applications in order to deploy their work.

Django as a web framework is really powerful. With easy URI schemas, a built-in ORM with supercharged capabilities, and a plug-and-play, modular architecture, there seems to be no limit to what django can accomplish. However, I recently came across a stumbling block where I needed to do user password recovery and it for some reason, I find the documentation sorely lacking, while other blog posts have conflicting (and many times, incorrect) opinions on how to accomplish this seemingly simple task.

Here I’ll show you the django-ic way of doing password recovery for your site, which is both and easy and, more importantly, secure. Note that by recovery, we always mean a password reset, since showing you your old password in plain text is akin to asking someone to hijack your account. Assuming you have your user models set up and user registration and authentication working well, let’s step through it.

Models

This is the easiest bit. django handles password recovery with the User model directly, so let’s move on.

URL Conf

We first modify the urlconf so that we can naturally move onto the views from there. This is the most involved step of adding user password recovery, but it is more of configuration rather than code, keeping in line with django’s batteries included  policy. First we import django’s auth module to make our life a lot easier

from django.contrib.auth import views as auth_views

Password Reset

We add a URL that the browser connects to when the user clicks on a password recovery button.

url(r'^account/password_reset/$', auth_views.password_reset, {'post_reset_redirect': '/account/password_reset/done/', 'template_name': 'account/password_reset.html', 'email_template_name': 'account/password_reset_email.html'}, name="password_reset")

This creates a link /account/password_reset which calls django auth’s built-in password_reset view. The password_reset view accepts custom templates for the page as well as the email that gets sent out, so we pass those in as a dictionary (note the key values).

Password Reset Done

This is the page that you see that notifies you that your recovery email has been sent out.

url(r'^account/password_reset/done/$', auth_views.password_reset_done, {'template_name': 'account/password_reset_done.html'})

This is similar to the above, but we just change the template that we will use to one that gives the user the message clearly. Don’t worry about the link in the email being insecure, django creates a URL with a unique UIDB64 hashed identifier and manages it for you.

Password Reset Confirm

This is where we do the actual password reset. The user is presented with a form that asks them to input their new password and confirm it. django takes this form and updates the password securely without you having to touch a line of code.

url(r'^account/password_reset/(?P[0-9A-Za-z]+)-(?P.+)/$', auth_views.password_reset_confirm, {'template_name': 'account/password_reset_confirm.html', 'post_reset_redirect': '/account/password_reset/complete/'}, name="password_reset_confirm")

In the above URL definition, we accept the link that django sent out in its email with the unique UIDB64 hash and the unique token. To customize the response, we pass in a dictionary indicating what is the template with the form we wish to serve as well as where to submit the POST request of the form. The form gets posted to the link we define below.

Password Reset Complete

Once django takes in the new password and updates your password, it will redirect to this page to show that the password reset was successful.

url(r'^account/password_reset/complete/$', auth_views.password_reset_complete, {'template_name': 'account/password_reset_complete.html'})

The final URL, this one accepts the password recovery form as a POST request and serves up the confirmation template that displays to the user if the password was successfully reset or no.

Templates

Finally, we look at the design and tidbits of the templates whose names we were passing into the URLs above.

  • “Password reset” only has a form to accept the email address or username of the account whose password we wish to recover.
  • “Password reset done” is a simple message indicating that a recovery email has been sent out.
  • “Password reset email” is the template where you can define how your password recovery email looks like so that your organization looks more professional.
  • “Password reset confirm” should just be a template with an if block checking for `validlink`. If `validlink` returns True, we define a form that takes two fields, `form.new_password1` and `form.new_password2`. If not `validlink`, we display an error message stating that the recovery link is no longer valid.
  • “Password reset complete” is again a simple notification template which is displayed if the password recovery was successful.

Emails

The last part is configuring the email account from which to send the recovery email. Although django comes with great helper functions for sending emails, it does not have its own SMTP server which is needed to send emails.

Thankfully, we can save ourselves the trouble of setting one up by using a service such as Gmail or Outlook. Since Gmail is wildly popular, let’s use that.

Firstly, go ahead and create an account on Gmail which will serve as the account from which the emails will be sent out. Once you have the email ID ready, go to your settings.py in the main django app and put in the following settings –

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
# Indicates that we are using gmail
EMAIL_HOST = 'smtp.gmail.com'
# The port number for the gmail SMTP server
EMAIL_PORT = 587
EMAIL_HOST_USER = ''
EMAIL_HOST_PASSWORD = ''
EMAIL_USE_TLS = True
DEFAULT_FROM_EMAIL = ''

Please note that you should not be putting the password to your email account (or any password for that matter) in plain text in a code file, and then checking that into source control for the world to see. You should be inserting that password via environment variables or some other secure mechanism.

Conclusion

And that’s it! We’re done. Without writing any sort of complex cryptography code, we have incorporated a full password recovery system into our web app. This is one of the many cool things you can do with django, and hopefully, this guide has explained this feature in a way which was easy and concise.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s