Flask JWT Authentication Tutorial

Page 1

Flask JWT Authentication Tutorial www.bacancytechnology.com


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 ib 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.


CONTENTS 1. What are JSON Web Tokens? 2. What is Flask Framework? 3. Goal of Flask JWT Authentication Tutorial 4. Step-by-step Tutorial to Implement Flask JWT Authentication 5. Conclusion


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 partsPayload 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.


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.

Watch Video


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 tablesUsers 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']='004f2af45d3a4e161a7d d2d17fdae47f' app.config['SQLALCHEMY_DATABASE_URI']='sql ite://///home/manthantrivedi/Documents/Bacan cy/bacancy_blogs/flask_auth/myflaskproject/bo okstore.db' app.config['SQLALCHEMY_TRACK_MODIFICATI ONS'] = 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.


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) : 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_i d']).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 belowmentioned 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).fir st() 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}


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.


Thank You

www.bacancytechnology.com


Turn static files into dynamic content formats.

Create a flipbook
Issuu converts static files into: digital portfolios, online yearbooks, online catalogs, digital photo albums and more. Sign up and create your flipbook.