2
votes

I'm trying to implement password reset by sending an email to the user with a link which will redirect him/her to a new password form.

I took by example this question and this site.

But my problem is a bit different. I don't have a local database containing the users, so I cannot perform operations over their attributes. I receive user data via an API (user id, user email, user password).

So, which is the best way to generate a unique link to send via email to user so that this link would tell me who the user is and allow me to reset his/her password? And also, how could I redirect it in urls.py? I wish that this link could be used only a single time.

My views.py is like this:

def password_reset_form(request):

if request.method == 'GET':
    form = PasswordResetForm()
else:
    form = PasswordResetForm(request.POST) 

    if form.is_valid():
        email = form.cleaned_data['email']

        content_dict = {
            'email': email,
            'domain': temp_data.DOMAIN,
            'site_name': temp_data.SITE_NAME,
            'protocol': temp_data.PROTOCOL,
            }

        subject = content_dict.get('site_name')+ ' - Password Reset'
        content = render_to_string('portal/password_reset_email.html', content_dict)
        send_mail(subject, content, temp_data.FIBRE_CONTACT_EMAIL, [email])

        return render(request, 'portal/password_reset_done.html', {'form': form,})

return render(request, 'portal/password_reset_form.html', {'form': form,})

And the template the e-mail I'm sending is:

{% autoescape off %}

You're receiving this e-mail because we got a request to reset the password for your user account at {{ site_name }}.

Please go to the following page and choose a new password:

{% block reset_link %}
{{ protocol }}://{{ domain }}/[***some unique link***]
{% endblock %}

If you didn't request a password reset, let us know. 

Thank you.

The {{ site_name }} team.

{% endautoescape %}

Thanks, guys.

3
@KlausD., no it's a salt of the password.revy

3 Answers

2
votes

Don't reinvent the wheel. You should use django-allauth for this kind of stuff. The library is well maintained and under active development.

1
votes

I’d suggest to borrow the general logic from Django and adapt it to your specific conditions:

As you can see from PasswordResetTokenGenerator._make_token_with_timestamp(), its algo relies on user’s last login timestamp, so the APIs you consume would need to accommodate that.

You could import the same utility functions used by the above—where cryptography is concerned it’s better to rely on well-tested solutions. Deep internals are prone to change without a release note though when you update to newer Django versions, so take care.

You could also look into simply storing some carefully randomly generated reset codes along with usernames in your local DB and deleting them when user accesses the reset form, but that is less elegant while being more brittle and infosec-issue prone.

1
votes

The problem is not to generate unique link, you need to store somewhere info about user id or email, and generated token. Otherwise you will not know which user should use which token. After user resets his password you can delete his record (his token).

You can write the most simple model even in sqlite, which basically could look like this:

class UserTokens(model.Models):
    email = models.EmailField(max_length=50)
    token = models.CharField(max_length=50)

Afterwards when you send mail make something like this:

def password_reset_form(request):

    #your logic here
    # form post

    usr, created = UserToken.objects.get_or_create(email=form.email)
    if usr.token:
        #send this token to him
     else:
        usr.token = ''.join(
    random.choice(string.ascii_uppercase + string.digits) for _ in range(50))
         usr.save()
         #also send this token to him

Then you create a new view or api view which searches for that token, if found, let him reset the password. If not, raise a 404 error, or just let him know that there is no such link to reset password.

Please not that, this was written from my phone so care for typos.

PS. you also asked about urls

just make something like this:

url(r'unsubscribe/(?P<quit_hash>[\w\d]+)/$', 'quit_newsletter_or_somethin', name='quit_newsletter_or_somethin')