Skip to content

05. token-based-authentication

Authentication is the common feature with almost all the modern applicaton. It provides a mechanism to authenticate a user and make the experience safe and secure.

There are various ways to authenticate a user. Token Based authentication is one the modern authentication mechanisms.

To implement token based authentication we will use flask-jwt-extendedflask-jwt-extended.readthedocs.io/en/stable/.

Token Based Authentication

Installation

pip install flask-jwt-extended

Configuration

Let's update the code in the config.py.

python filename=config.py

from datetime import timedelta # [tl! add]
ACCESS_EXPIRES = timedelta(hours=1) # [tl! add]


class LocalDevelopmentConfig:
    DEBUG = True
    SQLALCHEMY_DATABASE_URI = 'sqlite:///database.db'
    JWT_SECRET_KEY = '5#y2LF4Q8z\n\xec]/' # [tl! add]
    JWT_ACCESS_TOKEN_EXPIRES = ACCESS_EXPIRES # [tl! add]

Initialization and Setup

We will be using the two features of flask_jwt_extended - Automatic User Loading and Blocklist/Revoking.

python filename=main.py

# --- Imports Start here--- ###
...
from config import LocalDevelopmentConfig # [tl! add:start]
from flask_jwt_extended import (
    JWTManager,
    create_access_token,
    jwt_required,
    current_user,
    get_jwt_identity,
    verify_jwt_in_request,
    get_jwt
) # [tl! add:end]

# --- Imports End here--- ###

app = None # [tl! remove]
app, jwt = None, None # [tl! add]

# ---- Flask app factory ---- #


def create_app():
    app = Flask(__name__)
    db.init_app(app)
    app.config.from_objec(LocalDevelopmentConfig)# [tl! add:start]
    jwt = JWTManager(app)# [tl! add:end]
    app.app_context().push()
    db.create_all()
    return app


# [tl! add:start]
# Register a callback function that takes whatever object is passed in as the
# identity when creating JWTs and converts it to a JSON serializable format.
@jwt.user_identity_loader
def user_identity_lookup(user):
    return user.id


# Register a callback function that loads a user from your database whenever
# a protected route is accessed. This should return any python object on a
# successful lookup, or None if the lookup failed for any reason (for example
# if the user has been deleted from the database).
@jwt.user_lookup_loader
def user_lookup_callback(_jwt_header, jwt_data):
    identity = jwt_data["sub"]
    return User.query.filter_by(id=identity).one_or_none()


# Setup our redis connection for storing the blocklisted tokens. You will probably
# want your redis instance configured to persist data to disk, so that a restart
# does not cause your application to forget that a JWT was revoked.
jwt_redis_blocklist = redis.StrictRedis(
    host="localhost", port=6379, db=0, decode_responses=True
)


# Callback function to check if a JWT exists in the redis blocklist
@jwt.token_in_blocklist_loader
def check_if_token_is_revoked(jwt_header, jwt_payload: dict):
    jti = jwt_payload["jti"]
    token_in_redis = jwt_redis_blocklist.get(jti)
    return token_in_redis is not None
# [tl! add:end]


if __name__ == "__main__":
    app.run()

Note We will redis server to track the blocklisted tokens. You can read more for storing different ways in the flask-jwt-extendedflask-jwt-extended.readthedocs.io/en/stable/blocklist_and_token_revoking.html#.

All the above three decorators are basically the callbacks which will help us to implement the token based authentication. Now the entire system is setup you have the proper authentication in your backend to protect your resources.

Admin Credentials

We will setup to create admin credentials when the application and database will setup for the first time.

Add these lines of codes to implement the admin credentials creation.

python filename=main.py

...
# ------- Admin user through code ---# # [tl! add:start]
admin_exist = User.query.filter_by(email="sachin@gmail.com").first()
if admin_exist is None:
    user = User(email="sachin@gmail.com",
                password=generate_password_hash("sachin123"), name="sachin",
                role="admin", doj=datetime.now(), loginAt=datetime.now())
    db.session.add(user)
    db.session.commit() # [tl! add:end]
...

Testing it Out

Now let's test it out. Start the server by running these commands:

python3 main.py

Commit the changes with the following command:

git add .
git commit -m "Added token based authentication"

Continue to allow RBAC...