In a previous blog post, we talked about a Flask extension, Flask-JWT, which allows us to create JWTs (JSON Web Tokens) in our Flask apps. Flask-JWT is handy and provides a minimal set of features we would need for token based authentication. However, as our app grows more complex, we may find it a little bit restricting.

Read on for a more powerful alternative!

Introduction

In this post we introduce a new Flask extension: Flask-JWT-Extended. It has a more advanced set of features and enables us to design a more practical authentication workflow.

Flask-JWT-Extended has many advantages compared to Flask-JWT. For example, it supports token refreshing, which could result in a much more practical and user-friendly authentication workflow. It also has a much more active community that maintains and upgrades the project, so it is more likely to introduce new features and remain stable. We will be focusing on the authentication workflow in this post.

A recommended authentication workflow

Token based authentication

Very much like in Flask-JWT, we can perform a token-based authentication using Flask-JWT-Extended. The user gets authenticated and their info gets encrypted and returned as an access token (JWT).

Whenever the user wants to tell us who they are, they send the access token along with their request. The access token gives us some level of trust on the user's identity, and improved security since all the user's info is encrypted and unlikely to be compromised.

Token refreshing

For security purposes, each access token must have an expiration time. Normally this is set to between 5 and 15 minutes, after which the user must re-authenticate.

In Flask-JWT, the re-authentication would require the user to enter their username and password again. That can be very tedious for users!

Token refreshing serves exactly this purpose. When authenticating via credentials the first time, we not only return an access token that contains the user's account info—we also return a refresh token that only serves to refresh the access token.

When an access token has expired we provide the refresh token, and Flask-JWT-Extended verifies it and returns a new, valid access token. That way the user can keep using that access token for accessing the protected services.

This process repeats every time the original access token expires... So does this mean the user never has to enter their credentials again?

Token Freshness

Given the above description, one may ask what is the difference between using the token refreshing workflow and having an access token that never expires?

It is true that it does not make a difference if we simply allow token refreshing with no further restrictions. However, there is a solution to make our authentication workflow more robust.

Here's a common use case: once we've signed in, we are normally able to continue using an app without entering credentials. However, if we try to perform a "critical" operation—such deleting some data or completing a bank transaction—we are often asked for the credentials (or at least a password).

How does that work?

Enter, token freshness. As a rule of thumb, any access token acquired via credentials is marked as fresh, while access tokens acquired via the refresh mechanism are marked as non-fresh.

Going back to our previous authentication workflow, the first time a user logs in with his credentials, he would get a fresh access token and a refresh token. If he tries to log back in and finds out his current access token has expired, he uses his refresh token to get a new access token. This new access token would be non-fresh, since it's not acquired with credentials.

We still have some trust in the user when he presents the non-fresh access token. However, when he tries to perform a critical action, such as a transaction, the application would not accept this access token. Instead, he would need to enter his credentials again and get a new fresh access token to proceed with the critical action.

Let's have a look at some code.

Flask-JWT-Extended in action

Authentication

A code snippet for the user login endpoint is shown below using class-based views from Flask-RESTful. If you've taken our REST API course, this will look very familiar.

from models.user import UserModel
from flask_restful import Resource, reqparse
from flask_jwt_extended import (
    create_access_token,
    create_refresh_token
)
from werkzeug.security import safe_str_cmp

class UserLogin(Resource):
    # defining the request parser and expected arguments in the request
    parser = reqparse.RequestParser()
    parser.add_argument('username',
                        type=str,
                        required=True,
                        help="This field cannot be blank."
                        )
    parser.add_argument('password',
                        type=str,
                        required=True,
                        help="This field cannot be blank."
                        )

    def post(self):
        data = self.parser.parse_args()
        # read from database to find the user and then check the password
        user = UserModel.find_by_username(data['username'])

        if user and safe_str_cmp(user.password, data['password']):
            # when authenticated, return a fresh access token and a refresh token
            access_token = create_access_token(identity=user.id, fresh=True)
            refresh_token = create_refresh_token(user.id)
            return {
                'access_token': access_token,
                'refresh_token': refresh_token
            }, 200

        return {"message": "Invalid Credentials!"}, 401

Token refreshing

The following code snippet shows how the token refreshing endpoint works.

class TokenRefresh(Resource):
    @jwt_refresh_token_required
    def post(self):
        # retrive the user's identity from the refresh token using a Flask-JWT-Extended built-in method
        current_user = get_jwt_identity()
        # return a non-fresh token for the user
        new_token = create_access_token(identity=current_user, fresh=False)
        return {'access_token': new_token}, 200

Endpoints decorated with @jwt_refresh_token_required require that an Authorization: Bearer {refresh_token} header is included in the request.

Define different protection levels using token freshness

We can then protect our endpoints and define different protection levels like this:

# An endpoint that requires a valid access token (non-expired, either fresh or non-fresh)
@jwt_required
def get(self):
    pass

# An endpoint that requires a valid fresh access token (non-expired and fresh only)
@fresh_jwt_required
def post(self):
    pass

Acknowledgements

Flask-JWT-Extended is an open source python project maintained by an active and awesome community. You may refer to the original documentations as well as source code on their GitHub repo: https://github.com/vimalloc/flask-jwt-extended.

Want to learn much more about developing REST APIs with authentication, data storage, relational databases, and deployments? Check out our flagship course!