Implementation of Forgot/Reset Password Feature in Django
Oct 21, 2014 · 5 Min Read · 5 Likes · 23 CommentsDjango has its own implementation for reset/forgot password for its admin site. We are going to use that code as reference to implement similar feature for a non admin-site authentication page. Although there are tons of good packages which will allow user to use their password resetting system. But if the system isn’t too complex and doesn’t need such authentication plugins, then reusing the django’s very own implementation can be a good option.
This implementation is going to divided into two parts. First part is sending an email with reset url, and the Second part is clicking the reset url attached in email and entering new password for reset completion.
Before starting anything, lets look at the django’s reset/forgot password’s implementation in django/contrib/auth/forms.py
) and django/contrib/auth/views.py
).
Implementation of sending an email for forgot password with reset url
We are going to make a reset password form where we are going to add an text field which will take either username or email address associated with the corresponding user.
from django import forms
class PasswordResetRequestForm(forms.Form):
email_or_username = forms.CharField(label=("Email Or Username"), max_length=254)
We are going to make a view which will check the input email/username and send an email to user’s email address(implementation reference: github source).
from django.contrib.auth.tokens import default_token_generator
from django.utils.encoding import force_bytes
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
from django.template import loader
from django.core.mail import send_mail
from settings import DEFAULT_FROM_EMAIL
from django.views.generic import FormView
from .forms import PasswordResetRequestForm
from django.contrib import messages
from django.contrib.auth.models import User
from django.db.models.query_utils import Q
class ResetPasswordRequestView(FormView):
template_name = "account/test_template.html"
success_url = '/account/login'
form_class = PasswordResetRequestForm
def form_valid(self, *args, **kwargs):
form = super(ResetPasswordRequestView, self).form_valid(*args, **kwargs)
data= form.cleaned_data["email_or_username"]
user= User.objects.filter(Q(email=data)|Q(username=data)).first()
if user:
c = {
'email': user.email,
'domain': self.request.META['HTTP_HOST'],
'site_name': 'your site',
'uid': urlsafe_base64_encode(force_bytes(user.pk)),
'user': user,
'token': default_token_generator.make_token(user),
'protocol': self.request.scheme,
}
email_template_name='registration/password_reset_email.html'
subject = "Reset Your Password"
email = loader.render_to_string(email_template_name, c)
send_mail(subject, email, DEFAULT_FROM_EMAIL , [user.email], fail_silently=False)
messages.success(self.request, 'An email has been sent to ' + data +" if it is a valid user.")
return form
As you see above, the code is fairly simple(although it looks long). An encoded user id has been generated here and a token by using. This user id is going to be used later to get the user, the token will be used for checking validity of the url for that user and both the token and the user id is going to be used as unique reference for reset password url. c is a dictionary which has user id, token and other related data etc. This dictionary is going to be blent with the template registration/password_reset_email.html
and send to the user’s email address. We will be using django’s message framework to show messages.
{% if messages %}
<ul class="messages">
{% for message in messages %}
<li>{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
{% endfor %}
{% endif %}
</ul>
<form action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
Two more things before wrapping up sending email part. One, making a url for using this view.
urlpatterns = patterns(
url(r'^account/reset_password', ResetPasswordRequestView.as_view(), name="reset_password"),
)
Two, create or edit the template inside TEMPLATES directory named registration/password_reset_email.html
.
{% load i18n %}{% autoescape off %}
{% blocktrans %}You're receiving this email because you requested a password reset for your user account at {{ site_name }}{%endblocktrans %}
{% trans "Please go to the following page and choose a new password:" %}
{% block reset_link %}
{{ domain }}{% url 'reset_password_confirm' uidb64=uid token=token %}
{% endblock %}
{% trans "Your username, in case you've forgotten:" %} {{ user.get_username }}
{% trans "Thanks for using our site!" %}
{% blocktrans %}The {{ site_name }} team{% endblocktrans %}
{% endautoescape %}
Now run the server and you will see forms like the screen shots below: (This screenshots look cool because django adminsite’s js/css have been used here.)
Screenshots password reset flow
Rendered template from PasswordResetRequestForm form
Rendered template from PasswordResetRequestForm form with error messages
Rendered template of login form with sent email confirmation message
Sent email look
Implementation of clicking the reset url and entering new password for reset completion
First, lets write a form which will have two fields new password and retype password field.
class SetPasswordForm(forms.Form):
error_messages = {
'password_mismatch': ("The two password fields didn't match."),
}
new_password1 = forms.CharField(label=("New password"), required=True,
widget=forms.PasswordInput)
new_password2 = forms.CharField(label=("New password confirmation"), required=True,
widget=forms.PasswordInput)
def clean_new_password2(self):
password1 = self.cleaned_data.get('new_password1')
password2 = self.cleaned_data.get('new_password2')
if password1 != password2:
raise forms.ValidationError(
self.error_messages['password_mismatch'],
code='password_mismatch',
)
return password2
It will take two password input and verify if they match, if those inputs match(in clean method), it will return password. Now using that form, we are going to write a view(reference for implementation).
class PasswordResetConfirmView(FormView):
template_name = "account/test_template.html"
success_url = '/admin/'
form_class = SetPasswordForm
def form_valid(self, *arg, **kwargs):
form = super(PasswordResetConfirmView, self).form_valid(*arg, **kwargs)
uidb64=self.kwargs['uidb64']
token=self.kwargs['token']
UserModel = get_user_model()
try:
uid = urlsafe_base64_decode(uidb64)
user = User._default_manager.get(pk=uid)
except (TypeError, ValueError, OverflowError, User.DoesNotExist):
user = None
if user is not None and default_token_generator.check_token(user, token):
new_password= form.cleaned_data['new_password2']
user.set_password(new_password)
user.save()
messages.success(self.request, 'Password reset has been successful.')
else:
messages.error(self.request, 'Password reset has not been unsuccessful.')
return form
URL for this view:
urlpatterns += patterns(
url(r'^account/reset_password_confirm/(?P<uidb64>[0-9A-Za-z]+)-(?P<token>.+)/$', PasswordResetConfirmView.as_view(), name='reset_password_confirm'),
)
Well PasswordResetConfirmView takes two parameter from urls, uidb64 and token, those were sent within email generated by ResetPasswordRequestView. We got user id hence the user by decoding uid64, and function default_token_generator
checks the token against the user. If they are valid, then we set new password for the user. If they are not valid, it will show an error message saying the url is no longer valid.
Screenshots reset successful
Rendered template for SetPasswordForm form
Reset successful
Alternative reference to this implementation
You can check the django authentication views for your own implementation or customize them.
In conclusion
Thus you implement your very own forgot or reset the password. For full project/implementation, please check/fork this repository. If you have any questions, please share in the comment section below.
Last updated: Dec 29, 2024
Hi, This template throwing the below error.
Reverse for 'password_reset_confirm' with arguments '()' and keyword arguments '{u'uidb64': 'Mw', u'token': u'4a1-0432bcab41a9fde28f68'}' not found. 0 pattern(s) tried: []
Please respond as soon as possible.. Thank you.Check this answer, which should solve your problem http://stackoverflow.com/qu… 😄
I can not see the screenshots..
also i can not see the template code of other templates
Thanks for noticing. I have updated the post 😄
Thank You! is very useful.
Great article, very useful! However, I cannot find
django/contrib/admin/templates/registration/password_reset_subject.txt
what is the username and password
You can create user in your system and use the user’s username and password
and if I want to use my own template? it does not look stylish with django admin, thanks
TemplateDoesNotExist at
/account/reset_password
why shows this error..Hi, can you please create an issue on the github repository with the error stacktrace? In that way, I can easily figure out if something is wrong. Also don’t forget to mention which django version you are using. Thanks
If i want to resend the email with that particular URL then how can i do it?
You can just use this code:
# somehow you got email or username and put it in data associated_users= User.objects.filter(Q(email=data)|Q(username=data)) if associated_users.exists(): for user in associated_users: c = { 'email': user.email, 'domain': request.META['HTTP_HOST'], 'site_name': 'your site', 'uid': urlsafe_base64_encode(force_bytes(user.pk)), 'user': user, 'token': default_token_generator.make_token(user), 'protocol': 'http', } subject_template_name='registration/password_reset_subject.txt' # copied from django/contrib/admin/templates/registration/password_reset_subject.txt to templates directory email_template_name='registration/password_reset_email.html' # copied from django/contrib/admin/templates/registration/password_reset_email.html to templates directory subject = loader.render_to_string(subject_template_name, c) # Email subject *must not* contain newlines subject = ''.join(subject.splitlines()) email = loader.render_to_string(email_template_name, c) send_mail(subject, email, DEFAULT_FROM_EMAIL , [user.email], fail_silently=False)
it giving error like The reset password link is no longer valid. in mail this link is going
http://127.0.0.1:8000/user/reset_password_confirm/(%3FPb'YTNlYWQyYWUtMTAyOC00ZjY1LTgwYTMtMWQ3NThiYjJjOTQ3'%5B0-9A-Za-z%5D+)-(%3FP505-9a4d4776744e45518e0e.+)/
disqus_0GqaZnP8Yj important
While i am doing this coding I got server 500 error. It is not showing successful message. for example message.success (“email sent please check”)
In registration/password_reset_email.html:
{{ domain }}{% url 'reset_password_confirm' uidb64=uid token=token %}
I want to show in email, this link is ‘Reset Password’. Please help meinstead of this can u tell me how to fetch user details and send it to his registered email…
You need to see the implementation of
ResetPasswordRequestView
’sform_valid
method. There we fetch the user and send user data viac = {..}
dictionary. You can modify this dictionary. Then insideregistration/password_reset_email.html
template, you can access those context variables and send them to user email.