Authentication in Flask

Authentication is one of most critical and important aspect of a web application. It prevents unauthorized people to stay out of protected areas of a website. If you have a good understanding of cookies and know how to properly hash passwords you can roll out your own authentication system. This could be an interesting small project to test your skills.

As you might have already guessed there already exists an extension to make your life easier. Flask-Login is an extension which allows you to integrate authentication system into your Flask application easily. Install Flask-Login and its dependencies using the following command:

Creating User Model

Right now, we are not storing any data about the users who will be administrator/publisher of our site. So our first task would be to create a User model to store the user data. Open main2.py file and add User model below the Employee model as follows:

flask_app/main2.py

To update our database, we need to create a new migration. In the terminal enter the following command to create the migration script:

Run the migration using the upgrade command as follows:

This will create the users table in the database.

Hashing Passwords

You must never store user passwords as plain text in the database. In case, a malicious user breaks into your database, he would be able to read all the passwords and email associated with it. It is a well-known fact that most people use the same password across multiple websites, that means an attacker may gain access to user’s other online accounts as well.

Instead of storing password directly in the database, we will store password hash. A hash is simply a random looking, long string that looks like this:

A hash is created using one-way hash function. A one-way hash function takes a variable length input and returns a fixed length output which we call hash. What makes it secure is that once we have a hash, we can’t get back the original string that generated it (hence one way). For the same input, the one-way hash function will always return the same result.

Here is the workflow involved when working with password hash:

When the user gives you their password (in the sign-up phase), hash it and then save the hash to the database. When the user logs in, create the hash from the entered password and then compare it with the hash stored in the database. If they match, log in the user. Otherwise, display an error message.

Flask ships with a package called Werkzeug which provides the following two helper functions for password hashing.

Method Description
generate_password_hash(password) It accepts a password and returns a hash. By default, it uses pbkdf2 one-way function to generate the hash.
check_password_hash(password_hash, password) It accepts password hash and password in plain text, then compares the hash of password with the password_hash. If both are same, it returns True, otherwise False.

The following shell session shows how to work with these functions:

Notice that when check_password_hash() is called with the correct password ( "secret password" ), it returns True and when called with wrong password ( "pass" ) it returns False.

Next, update User model to implement password hashing as follows (changes are highlighted):

flask_app/main2.py

Let’s create some users and put password hashing to test.

As the output shows, everything is working as expected and we now have two users in our database.

Integrating Flask-Login

To initialize Flask-Login import LoginManager class from flask_login package and create a new instance of LoginManager as follows (changes are highlighted):

To authenticate users, Flask-Login requires you implement a handful special methods in the User class. The following table lists the required methods:

Method Description
is_authenticated() returns True if user is authenticated (i.e logged in). Otherwise False.
is_active() returns True if account is not suspended. Otherwise False.
is_anonymous() returns True for anonymous users (i.e users who are not logged in). Otherwise False.
get_id() returns a unique identifier for the User object.

Flask-Login also provides default implementation of these methods via UserMixin class. So, instead of defining all these methods manually, we could just inherit them from the UserMixin class. Open main2.py and modify the User model header as follows:

flask_app/main2.py

The only thing remaining is to add a user_loader callback. Just above the User model add the following method.

flask_app/main2.py

The function decorated with user_loader decorator will be called everytime a request comes to the server. It loads the user from the user id stored in the session cookie. Flask-Login makes the loaded user accessible via current_user proxy. To use current_user import it from flask_login package. It acts like a global variable and is available in view functions and in templates. At any point, current_user either references a logged in user or an anonymous user. We can differentiate between the two using is_authenticated attribute of the current_user. For anonymous users is_authenticated attribute returns False, otherwise True.

Restricting Access to Views

As things stand, we don’t have any admin area in our site. For this lesson, admin area will be represented by a dummy page. To prevent unauthorized users from accessing protected pages Flask-Login provides a decorator named login_required. In main2.py, add the following code just below the updating_session() view function:

flask_app/main2.py

The login_required decorator ensures that the admin() view function is called only when a user is logged in. By default, when an anonymous user (a user who is not logged in) attempts to visit a protected view, he will be displayed HTTP 401 Unauthorized page.

Start the server, if not already running and visit http://localhost:5000/admin/. You will be displayed a page like this:

Rather than showing 401 unauthorized error, a better approach would be to redirect the user to the login page. To make this happen set the login_view attribute of the LoginManager instance to the login() view function as follows (changes are highlighted):

flask_app/main2.py

Currently, the login() function is defined as follows (we are soon going to change it):

flask_app/main2.py

Now visit http://localhost:5000/admin/ and you will be redirected to the login page:

Flask-Login also sets flash message when a user is redirected to the login page, but we didn’t see any message because the login template (template/login.html) is not displaying any flash message. Open login.html and add the following code just before the <form> tag as follows (changes are highlighted):

flask_app/templates/login.html

Visit http://localhost:5000/admin/ again. This time you will see the flash message on the login page as follows:

To change the flash message simply assign a new message to the login_message attribute of the LoginManager instance.

While we are at it, let’s create the template used by admin() view function. Create a new template name admin.html with the following code:

flask_app/templates/admin.html

Here we are using current_user variable to print the details of the logged in user.

Creating Login Form

Before we log in, we need a login form. The login form will have three fields: username, password, and remember me. Open forms.py and add LoginForm class just below the ContactForm class as follows (changes are highlighted):

flask_app/forms.py

Logging In Users

To log in a user Flask-Login provides login_user() function. It accepts user object to log in. On success, it returns True and establishes a session. Otherwise, it returns False. By default, the session established by login_user() expires when the browser is closed. To let the users remain logged in for a long period of time pass remember=True to login_user() function while logging in a user. Open main2.py and modify login() view function as follows (changes are highlighted):

flask_app/main2.py

Next, We need to update login.html to use LoginForm() class. Open login.html and modify it as follows (changes are highlighted):

flask_app/templates/login.html

We are now ready to login. Visit http://localhost:5000/admin and you will be redirected to the login page.

Enter correct username and password and hit submit. You will be redirected to the admin page which should look like this:

In case you didn’t check “Remember Me” checkbox while logging in you will be logged out as soon as the browser is closed. Otherwise, you will remain logged in.

On entering invalid username or password, you will be redirected to login page along with a flash message which looks like this:

Logging Out Users

The logout_user() function of Flask-Login logouts a user by removing the user id stored in the session. In the main2.py file, add the following code below the login() view function:

flask_app/main2.py

Next, update the admin.html template to include a link to logout route as follows (changes are highlighted):

flask_app/templates/admin.html

If you now visit http://localhost:5000/admin/ (assuming you are logged in), you will see a logout link at the bottom of the page.

To logout click the link and you will be redirected to the login page.

The Final Touch

There is one small problem with the login page. Right now, if a logged in user visits http://localhost:5000/login/, he will again see the login page. There is no point in displaying the login form to a user who is already logged. To solve this problem make the following changes in the login() view function.

flask_app/main2.py

After these changes, if a logged in user visits the login page, he will be redirected to the admin page.

Leave a Comment