Skip to content
thesarfo

Note

User Registration & Login in Flask

Hashing passwords with flask-bcrypt, validating uniqueness on registration, and wiring up flask-login for sessions, logout, and route restrictions.

views 0

Adding Users to the Database

Before we create our users we will need a way to hash our passwords. It’s not advisable to use plain text passwords in your database because of security reasons so we need to hash them. One good hashing algorithm we can use is called bcrypt and there is a flask extension that helps us do that. The flask extension is called flask-bcrypt. Install it with pip using “pip install flask-bcrypt”

Bcrypt has a method called bcrypt.generate_password_hash('testing') that is used to hash passwords, and the argument it takes is the password string you want to hash. Everytime you run this method it returns a different hashed version of the password. In order to check if the hashed password is actually the original password text you can use the method bcrypt.check_password_hash() which takes two arguments, the first argument is the variable that contains the hashed password and the second one is the plain password text. It returns true or false based on whether the two arguments are the same or not. see below

hashed_pw = bcrypt.generate_password_hash('testing').decode('utf-8') # the decode method just hashes the password into an utf-8 string instead of the default bytes
bcrypt.check_password_hash(hashed_pw, 'helloworld') --> returns false
bcrypt.check_password_hash(hashed_pw, 'testing') --> returns true

you need to import the Bcrypt class from the extension with “from flask-bcrypt import Bcrypt” in your app initialization. Then you should create an instance of the class with “bcrypt = Bcrypt(app)”

Please note that I have changed the application structure where i have created a module folder that contains my templates, static folders. Also I have created different files for the routes and models inside this new module folder. And the main app configuration is done in a file called __init__.py which is also in this new folder. Outside this new module folder is a file called run.py which contains the code for running the flask app. “if name == main, app.run(debug=True)”.

In this case you should import the bcrypt extension in the __init__.py file.

Check below for how we used what we’ve learnt so far to allow a new user to register to our database.

@app.route('/register', methods=['GET', 'POST'])
def register():
"""Route for user registration."""
form = RegistrationForm()
if form.validate_on_submit():
hashed_password = bcrypt.generate_password_hash('form.password.data').decode('utf-8')
user = User(username=form.username.data, email=form.email.data, password=hashed_password)
db.session.add(user)
db.session.commit()
flash('Your account has been created. You can Login In', 'success')
return redirect(url_for('login'))
return render_template('register.html', title='Register', form=form)

However, note that even though we have added a user to our database. There is still one problem that exists in the sense that we are not checking if the username or email already exists. Remember that we want our username and email to be unique to one person which means we dont want another user trying to register with the same email or username. To put this validation check in place we can write a custom validation function in our forms.py file under the “RegistrationForm” class, where we basically check if the username or email exists before. If the username or email already exists we can raise a validation error to the user with an error message of choice. See below:

class RegistrationForm(FlaskForm):
username = StringField('Username', validators=[DataRequired(), Length(min=2, max=20)])
email = StringField('Email', validators=[DataRequired(), Email()])
password = PasswordField('Password', validators=[DataRequired()])
confirm_password = PasswordField('Confirm Password', validators=[DataRequired(), EqualTo('password')])
submit = SubmitField('Sign Up')
def validate_username(self, username):
user = User.query.filter_by(username=username.data).first()
if user:
raise ValidationError('User Already Exists, Try Again')
def validate_email(self, email):
user = User.query.filter_by(email=email.data).first()
if user:
raise ValidationError('Email Already Exists, Try Again')

Note that you will have to import the ValidationError from the WTForm.


Logging In

Now that we have a pretty good registration system, we need to create a login system so users with accounts can login and logout. This can be done with an extension called “flask-login” install it with pip. Then you add it to your app initialization that is the __init__.py file you need to import the LoginManager class from the extension with “from flask_login import LoginManager” in your app initialization. Then you should create an instance of the class with “login_manager = LoginManager(app)”

The way this works is that we will add some functionality to our database models, and then it will handle all of the sessions in the background for us.

Ps: this process was too long for me to document. To check the login functionality, open the routes.py and the models.py file to see how i set it up. Also you can use chatgpt to generate a list of steps you can follow to make that happen.


Logging Out

This is fairly simple. Create a logout route, call the logout_user() method inside the logout function and redirect to the homepage. Inside the blog application though, i used a condition in my layout template to check if user is logged in, if yes we displayed the logout link at the top right. Else we just display the regular login/register links


Restrictions

This is basically how to put a restriction on certain routes, so you can only visit those routes when you are logged in. for instance, you have to be logged in to twitter in order to edit your profile, so they’ve put a restriction on the edit profile route where it only allowes logged in users.

First lets create a route for the users’ account that they can access after they’ve logged in

@app.route('/account')
def account():
return render_template('account.html', title="Account")

Then we’ll follow standard procedure to create an “account.html” file. But now inside our layout, we’ll now display a link to the account route when the user is logged in. We can do that by using an if statement to check if the user is is_authenticated.

Also, we want to put a check in place that makes sure that a user is logged in before they can access the account page. To do that we can use the “login_required” decorator from the flask-login extension. After importing the login_required, you can add it to the account route. See below

@app.route('/account')
@login_required
def account():
return render_template('account.html', title="Account")

After doing that, we also need to tell our extension where our login route is located. We can do that by opening our __init__.py file, and right under where you created the instance of the login_manager, you can set the login route by typing the “login_manager.login_view = ‘login’”. The view that we pass in here is the function name that you pass to your route, so its the same thing that you pass to the url_for function.