How to implement forgot password feature in Flask?

I am using Python, Flask, SqlAlchemy for my project which is being developed in Ubuntu 14.04.

So, I am assuming you have the model called User which contains at least email address, password fields.

So, it looks like,

models.py

class User(db.Model, UserMixin):
  __tablename__ = 'myusers'

  id = db.Column(db.Integer, primary_key=True)
  first_name = db.Column(db.String(100))
  last_name = db.Column(db.String(100))
  active = db.Column(db.Boolean())
  email = db.Column(db.String(120), unique=True)
  password = db.Column(db.String(120))

We are going to create the password reset feature by generating the temporary access token which will expire within some duration.That token will be sent to your email to set the new password.

Timed and Signed Token Generator Algorithm

So, we have the library called itsdangerous and the method TimedJSONWebSignatureSerializer. Have a look on these beautiful libraries to proceed further.

Our inital version of forgot_password function goes in the following way:

Before that we need two forms.

  • User email id field form(This helps to get the email id from user to be sent reset url) (ResetPassword)
  • New password form (To enter new password by the user) (ResetPasswordSubmit)

So, I am using

forms.py

class ExistingUser(object):
    def __init__(self, message="Email doesn't exists"):
        self.message = message

    def __call__(self, form, field):
        if not User.query.filter_by(email=field.data).first():
            raise ValidationError(self.message)

reset_rules = [validators.Required(),
          validators.Email(),
          ExistingUser(message='Email address is not available')
         ]

class ResetPassword(Form):
    email = TextField('Email', validators=reset_rules)

class ResetPasswordSubmit(Form):
    password = PasswordField('Password', validators=custom_validators['edit_password'], )
    confirm = PasswordField('Confirm Password')

This above code contains two forms along with some validation rules.If you are aware of forms usage in Flask the above snippet can be swallowed easily.

views.py

Inital look

@mod.route('/reset-password', methods=('GET', 'POST',))
def forgot_password():
    token = request.args.get('token',None)
    form = ResetPassword(request.form) #form
    if form.validate_on_submit():
        email = form.email.data
        user = User.query.filter_by(email=email).first()
        if user:
            token = user.get_token()
            print token
    return render_template('users/reset.html', form=form)

We are asking user to enter his email id to forward reset token url to that mail account.

So, Now we are going to see how to implement generating access token and verification

Generating access token

from itsdangerous import TimedJSONWebSignatureSerializer as Serializer

def get_token(self, expiration=100):
        s = Serializer(current_app.config['SECRET_KEY'], expiration)
        return s.dumps({'user': self.id}).decode('utf-8')

The get_token class method helps to generate the token for it’s object.

verify the token

This is a static method, since this method is not bound to the object.Once it gets token from the user, it has to predict the object by it’s unique id.So, it is static method.

@staticmethod
def verify_token(token):
    s = Serializer(current_app.config['SECRET_KEY'])
    try:
        data = s.loads(token)
    except:
        return None
    id = data.get('user')
    if id:
        return User.query.get(id)
    return None

So, my User model should have the above two methods as it’s property.

class User(db.Model, UserMixin):
   #fields

    def get_token(self, expiration=1800):
        s = Serializer(current_app.config['SECRET_KEY'], expiration)
        return s.dumps({'user': self.id}).decode('utf-8')

    @staticmethod
    def verify_token(token):
        s = Serializer(current_app.config['SECRET_KEY'])
        try:
            data = s.loads(token)
        except:
            return None
        id = data.get('user')
        if id:
            return User.query.get(id)
        return None

Test these above methods in python shell

To test this static method, we can generate a rough token for the sample registered user and we can verify via terminal

To test this static method, we can generate a rough token for the sample registered user and we can verify via terminal

In [1]: t = "eyJhbGciOiJIUzI1NiIsImV4cCI6MTQwMzY4NDkyNywiaWF0IjoxNDAzNjgzMTI3fQ.eyJ1c2VyIjo3fQ.wTxkmuAvRG6bTbFWNXo97H3LbYvbcTPrMtp4hk_BDjs"
In [2]: from flask.ext.login import current_user,current_app
In [3]: from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
In [6]: with app.app_context():
    s = Serializer(current_app.config['SECRET_KEY'])
    data = s.loads(t)
    print data
   ...:     
{'user': 7}

success !! The verify_token function works as expected.

Now we have to build the layout for these entire process, if verification success, you have to give the platform to set new password by the user.

I did the layout design in the following way

if token and verified_result:
    is_verified_token = True
    password_submit_form = ResetPasswordSubmit(request.form)
    if password_submit_form.validate_on_submit():
        verified_result.password = generate_password_hash(password_submit_form.password.data)
        verified_result.is_active = True
        db.session.add(verified_result)
        db.session.commit()
        #return "password updated successfully"
        flash("password updated successfully")
        return redirect(url_for('users.login_view'))

This above lines are doing the following job:

  • Verifying the user entered token right or wrong?
  • if the token is valid then it askes the user to enter new password by generating ResetPasswordSubmit form
  • It submits the data to db.

Here you can contruct the code to send the url email once the email is submitted. like 127.0.0.1:5001/reset-password?token=123454ertrt

When the user visits the url you can verify the validity, then process next above lines.

I am here attached my screenshot.

reset_email1

Reset Email Message

new_password

New Password Form

Hope this article will be helpful and give you some idea.Finally, we would thank this guy  who contributes heavily for Python-Flask.

Thanks,

Navaneethan
 
Advertisements

About Navaneethan

mixed feelings...
This entry was posted in Flask, General_Tech, Problems, Python, Ubuntu and tagged , , . Bookmark the permalink.

2 Responses to How to implement forgot password feature in Flask?

  1. Atif Mumtaz says:

    Thanks for a good explanation. However, I am not clear about how verified_result is verified in your code.

  2. Navaneethan says:

    `verified_result = User.verify_token(token)`

    This is the way to verify the given token for the particular user. It returns boolean type.`verify_token` is available inside `User` models

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