Quick Summary

Flask JWT Authentication enables secure user authentication through tokens in Flask applications. This blog provides a step-by-step guide to implementing JWT Authentication in Flask. It covers essential topics, including the Flask framework, REST APIs, and Auth Token Authentication. Designed for beginners, the tutorial simplifies complex concepts and breaks them into clear, manageable sections for easy understanding.

Table of Contents

What are JSON Web Tokens?

JSON Web Tokens (JWTs) are a compact and secure way to transmit information as a JSON object, commonly used for authentication and secure data exchange. A JWT has three components:

  • Header: Specifies the token type (JWT) and signing algorithm.
  • Payload: Contains user-specific claims, such as user ID or roles.
  • Signature: Validates the token’s integrity using the header, payload, and a secret or private key.

JWTs are particularly useful in stateless authentication, where the server doesn’t store session data. Instead, the client sends the JWT with each request, enabling the server to authenticate and authorize efficiently. They are lightweight, self-contained, and ideal for securing REST APIs.

What is Flask Framework?

Flask is a lightweight, flexible, and easy-to-use web framework for Python. It is designed to help developers build web applications quickly and with minimal overhead. Flask is often referred to as a “microframework” because it provides the essential tools for web development without enforcing a specific project structure or including unnecessary features. The comprehensive features of Flask include:

  • Lightweight and Modular: Flask focuses on simplicity and allows developers to integrate only the necessary tools and libraries.
  • Built-in Development Server: Provides a debugger and server for testing and development.
  • Flexible: Developers have complete control over application structure and extensions.
  • RESTful Support: Ideal for creating REST APIs with clean routing and request handling.
  • Rich Ecosystem: Compatible with numerous extensions for features like database integration, authentication, and more.

Flask is widely used for small to medium-sized projects and serves as a solid foundation for more complex applications with the addition of extensions. Its simplicity and flexibility make it a popular choice among developers.

Step-by-step Tutorial to Implement Flask JWT Authentication

This tutorial provides a step-by-step guide to implementing JWT (JSON Web Token) authentication in a Flask application. Here, you will explore how to secure API endpoints, authenticate users, and manage authorization efficiently. You’ll have a solid foundation for building a secure authentication system by the end.

Let’s start with the implementation of the Flask JWT Authentication.

Here’s my system setup and JWT Flask Authentication 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.

To proceed, we will need a pip command. If you don’t have pip installed on your system, use the below command to install it.

Copy Text
sudo apt-get install python3-pip

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

Copy Text
pip install virtualenv

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

Copy Text
mkdir myflaskproject

To change the current working directory to myflaskproject run the following command:

Copy Text
cd myflaskproject

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

Copy Text
virtualenv venv

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

Copy Text
source venv/bin/activate

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 to do this is to create a requirements.txt file and list all the packages in it. You can also declare the packages’ versions wherever necessary.

For example, create one ‘requirement.txt’ file and paste the following packages into requirements.txt :

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

Now, use the following command to install all the listed packages in the ‘requirements.txt’ file with pip :

Copy Text
pip install -r requirements.txt

Set up a database

After completing the project’s installation and setup, we are now going to set up the database for our application.

To keep this simple, we will use SQLite for this project.

SQLite is a small, simple database that stores all its data in one file. It doesn’t require a server to run, making it easy to use in apps and small projects.

Use the following code to install SQLite :

Copy Text
sudo apt-get update
sudo apt-get install sqlite3

After installing sqlite3 to your system, we will now create a database named “bookstore” consisting of two tables :

  • Users table allows us to store registered users. We can also keep a check, allowing only the registered users to access the Books table.
  • Books table allows us to 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.

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:

To create the database, run the following command :

Copy Text
sqlite3 bookstore.db

For checking if you have successfully created the database or not run the below command:

Copy Text
.databases

If the database is created successfully, we will go ahead and create a new file named “app.py” in the myflaskproject directory or run this command in your terminal:

Copy Text
touch app.py

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

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

Copy Text
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 what is the purpose of importing the packages mentioned above :

⦿ 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

  • Generate_password_hash: It is a function in werkzeug.security used to securely hash passwords. It creates a hashed password version that can be stored in a database.
  • 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 database engines that do not support incremental primary key features. Also, using multi-character alpha-numeric values as IDs is better than 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.

Copy Text
app = Flask(__name__)
 
app.config['SECRET_KEY']='004f2af45d3a4e161a7dd2d17fdae47f'
app.config['SQLALCHEMY_DATABASE_URI']='sqlite:///PATH_TO_YOUR_SQLITE3_DB_FILE'
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.’ As shown below, we can simply run the following code in your terminal to generate this value.

configure settings for the Bookstore API

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

Copy the code below to your app.py file or create your own models.

Copy Text
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)
Copy Text
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.Now we are going to create the tables from the models we have created, to do so add the below code to your app.py file for creating tables for both models:

Copy Text
from app import db
db.create_all()

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

We create a ‘token_required(f)’ function that 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.

Copy Text
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 a special function. This function will create a custom decorator with the code required to create and validate tokens. Python provides an amazing feature called function decorators, which allows very neat features for web development.

In Flask, each view is considered a function, and decorators are used to inject additional functionality into one or more functions. In this case, this custom decorator will handle creating and validating 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 in the database, and return a success message.

Again, inside the app.py file, paste the following code after the token_required(f) function:

Copy Text
@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'})

Here,

  • @app.route(‘/register’, methods=[‘POST’]): Defines the route ‘/register’ to handle POST requests for user registration.
  • def signup_user(): Defines the function that handles user registration.
    data = request.get_json(): Retrieves the incoming JSON data from the request.
  • generate_password_hash(data[‘password’], method=’sha256′): Hashes the password using the SHA-256 algorithm for secure storage.
  • Users(public_id=str(uuid.uuid4()), name=data[‘name’], password=hashed_password, admin=False): Creates a new Users object with a unique ID, user name, hashed password, and default admin status.
  • db.session.add(new_user): Adds the new user object to the database session for insertion.
  • db.session.commit(): Commits the transaction, saving the new user to the database.
  • return jsonify({‘message’: ‘registered successfully’}): Returns a JSON response indicating successful registration.

Now, we will generate another route allowing all registered users to log in.

We will create a view to handle the user login feature with the login route. When a user logs in, the entered password matches the user’s stored password. A random token is generated to access the Bookstore API if the password matches successfully. For instance, we will keep the expiration time for this random token to 45 minutes.

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

Copy Text
@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"'})

Here,

  • @app.route(‘/login’, methods=[‘POST’]): Maps the /login endpoint to the login_user function for POST requests.
  • auth = request.authorization: Retrieves the Authorization header containing the username and password.
  • Users.query.filter_by(name=auth.username).first(): Queries the database for a user with the provided username.
  • jwt.encode(…): Generates a JWT token with the user’s public ID and an expiration time of 45 minutes.
  • return jsonify({‘token’: token}): Returns the generated token as a JSON response.
  • return make_response(‘could not verify’, 401, {‘Authentication’: ‘login required’}): Returns a 401 error if authentication fails.

After authorization routes, let’s 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.

Copy Text
@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

After creating all the routes for the users, let’s create routes for the Books table. These routes will allow users to retrieve and delete all the books in the database. We will also implement a mandatory check to verify that users with valid tokens can only perform 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:

Copy Text
@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'})

Here,

  • @token_required: A decorator that ensures the user is authenticated by requiring a valid token.
  • data = request.get_json(): Retrieves the incoming JSON data with book details (name, author, publisher, price).
  • new_books = Books(…): Creates a new Books object with the provided data and associates it with the current_user’s ID.
  • db.session.add(new_books): This function adds the new book object to the database session for insertion.
  • db.session.commit(): Commits the transaction, saving the new book to the database.
  • return jsonify({‘message’ : ‘new books created’}): Returns a JSON response indicating the book was successfully created.

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

Copy Text
@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})

Here,

  • @token_required: A decorator that ensures the user is authenticated by requiring a valid token.
  • Books.query.filter_by(user_id=current_user.id).all(): Queries the Books table for all books associated with the current_user’s ID.
  • output = []: Initializes an empty list to store the book data to be returned in the response.
  • book_data[…]: Creates a dictionary for each book containing its ID, name, author, publisher, and price.
  • output.append(book_data): Adds the dictionary of book details to the output list.
  • return jsonify({‘list_of_books’ : output}): Returns a JSON response containing the list of books.

Finally, we will create the last route to delete a specific book. We will make a view responsible for handling requests to delete an existing record in the Books table. If it exists, it will verify and delete the given record from the DB.

The code mentioned below can be implemented after the route allows the user to retrieve a list of books.

Copy Text
@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.

Copy Text
python app.py

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

Copy Text
python3 app.py

The entire source code is here – Python Flask JWT Authentication Example.

Conclusion

In conclusion, this guide has walked you through the steps to implement Flask JWT Authentication, helping you easily secure your application. We hope this tutorial has fulfilled your expectations and provided valuable insights. If you have any questions or need expert assistance, we’re here to help. Get in touch today and let us help you create a secure, scalable, and feature-rich application. Hire Dedicated Flask Developer to take your project to the next level and ensure top-notch results. Don’t hesitate to reach out with your queries, suggestions, or feedback—we’d love to hear from you!

Frequently Asked Questions (FAQs)

Flask is a minimal yet powerful framework with several advantages, including an inbuilt development server, simple URL routing, and high compatibility with modern technologies. Its smaller codebase ensures better performance and maintainability, making it an efficient choice for web development.

To install Flask on Linux, install Python and pip with sudo apt update && sudo apt install python3 python3-pip. Create a virtual environment using python3 -m venv flask_env, activate it with source flask_env/bin/activate, and install Flask using pip install Flask. Verify with python3 -m flask –version.

Flask is a microframework because it is lightweight and minimal, offering only the essentials for web development. It allows developers to add features as needed, making it flexible and ideal for small and complex projects.

Python Tutorials

Explore Advanced knowledge of Programming

LEARN NOW

Build Your Agile Team

Hire Skilled Developer From Us

solutions@bacancy.com

Your Success Is Guaranteed !

We accelerate the release of digital product and guaranteed their success

We Use Slack, Jira & GitHub for Accurate Deployment and Effective Communication.

How Can We Help You?