Flask JWT Authentication Tutorial

In the Flask JWT Authentication tutorial, we will build a demo application together; learn about the Flask framework, REST APIs, and Auth Token Authentication. If this is your first time implementing token authentication in Flask, don’t worry! After going through this tutorial, your doubts would be solved and you won’t be a beginner anymore. For simplifying the tutorial, I have classified it into various sections.

What are JSON Web Tokens?

JSON Web Tokens (JWT) is a secure and compact way to transmit data between two parties with the help of JSON objects.

JSON web token consists of three parts-

  • Payload
  • Header
  • Signature

JSON uses two different structure types for transmitting data.

  • Serialized- This type is used when you’re transferring information to the network via every request and response. It contains a payload, header, and signature.
  • Deserialized- This type is used when you’re reading/writing information to the token. It contains a payload and header.
Flask JWT Authentication

What is Flask Framework?

Flask is a python based micro-framework used to build rest API. A “micro-framework” neither implies that your entire web app has to fit into a single Python code file nor Flask lacks functionality. The core idea of the Flask framework is to keep things simple but extensible. It allows developers to add custom extensions for database integration, authentication, session management, and all the other backend systems based on preferences.

Goal of Flask JWT Authentication Tutorial

Before knowing how to implement Flask JWT Authentication, let’s see the video and know what we will build.

Step-by-step Tutorial to Implement Flask JWT Authentication

Let’s start the implementation of the Flask JWT Authentication. Here’s my system setup and Flask JWT example for better understanding:

  • Ubuntu 20.04 OS
  • Postman
  • Python 3.8+

Virtual environment Set Up using virtualenv

A virtual environment ensures that none of the packages used in the project conflict with system packages. It is also a good practice to avoid polluting your OS by installing every package directly onto the OS.

We will use the virtualenv command for setting up a new virtual environment in our project.

We will need pip command to proceed further. If you don’t have pip installed on your system, use the below command for installing pip on your system.

sudo apt-get install python3-pip

Once you have the pip command installed on your system, run the following command to install virtualenv.

pip install virtualenv

Now, run the mkdir command to create a new folder/directory for storing the virtual environment.

mkdir myflaskproject

Change the current working directory to myflaskproject:

cd myflaskproject

Inside the myflaskproject directory, create a new virtual environment with the help of the virtualenv tool:

virtualenv venv

After you have successfully created a virtual environment using the irtualenv tool, activate the virtual environment using the following command:

Install packages using pip

Now, it’s time to install the packages we need for this project to build Python REST API authentication token and other necessary packages for this API project such as-

  • flask
  • pyjwt
  • flask-sqlalchemy
  • datetime
  • uuid

An efficient way of doing this is by creating a requirements.txt file and listing all the packages into it. You can also declare the versions of the packages wherever necessary.

flask==1.1.2
pyjwt==2.0.0
datetime
uuid
Flask-SQLAlchemy

Now, use this file to install all the listed packages with pip.

pip install -r requirements.txt

Set up a database

To keep this simple, we will use SQLite for this project. Use the following code to install SQLite.

sudo apt-get update
sudo apt-get install sqlite3

Create a database named “bookstore” consisting of two tables-

  • Users
  • Books

Users table will store registered users. We will also keep a check, allowing only the registered users to access the Books table.

Books table will store the details and information about books, such as the book’s name, author of the book, publication of the book, and submitted by the registered users.

Create the database:

sqlite3 bookstore.db

Run the below command for checking if you have successfully created the database or not:

.databases

Create a new file named “app.py” in the myflaskproject directory or run this command in your terminal:

touch app.py

NOTE- while executing commands in the terminal, make sure you do it inside the virtual environment named “venv” we created earlier.

Now, paste the following code inside the python file named app.py:

app.py

from flask import Flask, jsonify, make_response, request
from werkzeug.security import generate_password_hash,check_password_hash
from flask_sqlalchemy import SQLAlchemy
from functools import wraps
import uuid
import jwt
import datetime

Let’s see the purpose of importing the packages mentioned above.

Packages from Flask framework

⦿ Packages from Flask framework

  • request – For keeping track of the associated data at the request level during a request.
  • jsonify – We will need jsonify to receive the output responses in JSON format and request

⦿ Package from SQLAlchemy

  • flask_sqlalchemy-This package will help us to integrate SQLAlchemy features into the Flask framework. SQLAlchemy is the Object Relational Mapper & Python SQL toolkit that provides full power and flexibility of SQL to developers.

⦿ Package from werkzeug.security

  • check_password_hash- For checking the user’s password. It compares the password provided by the user with the one stored in the database.

⦿ datetime– The package datetime will help us manipulate date and time as date objects. We need this module because python does not have any data type to support dates.

⦿ uuid– Universal Unique Identifiers create random ID numbers for users. The uuid is a very useful package, especially for such database engines that do not support incremental primary key features. Also, it is better to use multi-character alpha-numeric values as IDs instead of using linearly incremental numeric IDs.

Now it’s time to configure settings for the Bookstore API inside the app.py file using the below code.

app.py

app = Flask(__name__)
 
app.config['SECRET_KEY']='004f2af45d3a4e161a7dd2d17fdae47f'
app.config['SQLALCHEMY_DATABASE_URI']='sqlite://///home/manthantrivedi/Documents/Bacancy/bacancy_blogs/flask_auth/myflaskproject/bookstore.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
 
db = SQLAlchemy(app)

Here, the value assigned to the config variable ‘SECRET KEY’ can be auto-generated using a python library named ‘secrets.’ We can simply run the following code in your terminal to generate this value, as shown below.

configure settings for the Bookstore API

Now, we will create two models for the Books and Users table.

app.py

class Users(db.Model):
   id = db.Column(db.Integer, primary_key=True)
   public_id = db.Column(db.Integer)
   name = db.Column(db.String(50))
   password = db.Column(db.String(50))
   admin = db.Column(db.Boolean)
class Books(db.Model):
   id = db.Column(db.Integer, primary_key=True)
   user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
   name = db.Column(db.String(50), unique=True, nullable=False)
   Author = db.Column(db.String(50), unique=True, nullable=False)
   Publisher = db.Column(db.String(50), nullable=False)
   book_prize = db.Column(db.Integer)

Generate Users and Books Tables

Moving ahead with Flask-JWT Authentication Tutorial. Use the below code for creating tables for both tables:

from app import db
db.create_all()

Now, go to the app.py file and create the other functions required.

The “login_user” function will generate tokens to allow only registered users to access and manipulate a set of API operations against the Books table.

Simply paste the following code after the database model for both tables.

def token_required(f):
   @wraps(f)
   def decorator(*args, **kwargs):
       token = None
       if 'x-access-tokens' in request.headers:
           token = request.headers['x-access-tokens']
 
       if not token:
           return jsonify({'message': 'a valid token is missing'})
       try:
           data = jwt.decode(token, app.config['SECRET_KEY'], algorithms=["HS256"])
           current_user = Users.query.filter_by(public_id=data['public_id']).first()
       except:
           return jsonify({'message': 'token is invalid'})
 
       return f(current_user, *args, **kwargs)
   return decorator

This code is actually a special function. This function will create a custom decorator with the code required to create and validate tokens. Python provides a very amazing feature named function decorators. These function decorators allow very neat features for web development. In Flask, each view is considered as a function, and decorators are used for injecting additional functionality to one or more functions. In this case, the functionality handled by this custom decorator will be to create and validate tokens.

Creating routes for Users tables

In this step, we will generate a route for allowing users to register for the Books API using their name and password. With this route, we will create a view to encrypt the user’s password, store the new user’s details into the database, and return a success message.
Again, inside the app.py file, paste the following code after token_required(f) function:

@app.route('/register', methods=['POST'])
def signup_user(): 
   data = request.get_json() 
   hashed_password = generate_password_hash(data['password'], method='sha256')
 
   new_user = Users(public_id=str(uuid.uuid4()), name=data['name'], password=hashed_password, admin=False)
   db.session.add(new_user) 
   db.session.commit()   
   return jsonify({'message': 'registered successfully'})

Now, generate another route that will allow all the registered users to log in. With the login route, we will create a view to handle the user login feature. When a user logs in, the entered password is matched with the user’s stored password. If the password matches successfully, a random token is generated to access the Bookstore API. For instance, we will keep the expiration time for this random token to be 45 minutes.

You can simply update your file with the below-mentioned code beneath the registered route we created in the previous step:

@app.route('/login', methods=['POST']) 
def login_user():
   auth = request.authorization  
   if not auth or not auth.username or not auth.password: 
       return make_response('could not verify', 401, {'Authentication': 'login required"'})   
 
   user = Users.query.filter_by(name=auth.username).first()  
   if check_password_hash(user.password, auth.password):
       token = jwt.encode({'public_id' : user.public_id, 'exp' : datetime.datetime.utcnow() + datetime.timedelta(minutes=45)}, app.config['SECRET_KEY'], "HS256")
 
       return jsonify({'token' : token})
 
   return make_response('could not verify',  401, {'Authentication': '"login required"'})

Create another route in the app.py file to get all the registered users. This route verifies the registered users in the Users table and provides the output in JSON format. Use the below code after the login route.

@app.route('/users', methods=['GET'])
def get_all_users(): 
 
   users = Users.query.all()
   result = []  
   for user in users:  
       user_data = {}  
       user_data['public_id'] = user.public_id 
       user_data['name'] = user.name
       user_data['password'] = user.password
       user_data['admin'] = user.admin
     
       result.append(user_data)  
   return jsonify({'users': result})

Creating routes for Books tables

Let’s create routes for the Books table. These routes will allow users to retrieve all the Books in the database and delete them. We will also implement a mandatory check to verify the users having valid tokens can only perform any API requests.

Define a route for all the registered users to create a new book. The following code creates a route to meet this requirement:

@app.route('/book', methods=['POST'])
@token_required
def create_book(current_user):
 
   data = request.get_json()
 
   new_books = Books(name=data['name'], Author=data['Author'], Publisher=data['Publisher'], book_prize=data['book_prize'], user_id=current_user.id) 
   db.session.add(new_books)  
   db.session.commit() 
   return jsonify({'message' : 'new books created'})

Now, create a route to allow a logged in user with valid token to get all the books in the Books table as shown below:

@app.route('/books', methods=['GET'])
@token_required
def get_books(current_user):
 
   books = Books.query.filter_by(user_id=current_user.id).all()
   output = []
   for book in books:
       book_data = {}
       book_data['id'] = book.id
       book_data['name'] = book.name
       book_data['Author'] = book.Author
       book_data['Publisher'] = book.Publisher
       book_data['book_prize'] = book.book_prize
       output.append(book_data)
 
   return jsonify({'list_of_books' : output})

Finally, we will create the last route to delete a specific book. We will create a view responsible for handling requests made to delete an existing record in the Books table. It will verify and delete the given record from the DB, if exists. The below-mentioned code can be implemented after the route allows the user to retrieve a list of books.

@app.route('/books/<book_id>', methods=['DELETE'])
@token_required
def delete_book(current_user, book_id): 
 
   book = Books.query.filter_by(id=book_id, user_id=current_user.id).first()  
   if not book:  
       return jsonify({'message': 'book does not exist'})  
 
   db.session.delete(book) 
   db.session.commit()  
   return jsonify({'message': 'Book deleted'})
 
if  __name__ == '__main__': 
    app.run(debug=True)

Now run the app.py file by using the following command inside the virtual environment in the appropriate directory.

python app.py

If the above command does not work, here’s an alternative command.

python3 app.py

You can find the entire source code here – Flask JWT Authentication Example.

Conclusion

So, this was about how to implement Flask JWT Authentication. I hope the purpose of landing on this tutorial has been served the way you expected. If you are interested in learning more about Python, please visit Python Tutorials and play around with the code. If you are looking for assistance for token-based authentication with Flask, then connect with us today to hire Python developers from us to secure a Flask REST API with JSON web token.

Frequently Asked Questions (FAQs)

What are the advantages of using Flask?

Some of the advantages of using Flask are-

– Provides an inbuilt development server
– Higher compatibility with modern technologies
– Routing URL is simple
– Minimal and powerful framework
– Smaller codebase size

How to install Flask on the Linux system?

Use Python’s package manager, pip, to install flask on your Linux system-

pip install Flask

Why is Flask said to be a Microframework?

Flask is defined as a Microframework because it only offers the primary features like – request, blueprints, and routing. We can use Flask-Extension for using features like ORM, Caching, forms, and so on.

X

Get Our Newsletter

Be The First To Get The Latest Updates And Tutorials.

Request A Free Consultation