Setting Token Expiry and Refreshing Tokens

Setting Token Expiry and Refreshing Tokens

Welcome back! In the previous lessons, you've learned how to set up a basic /login endpoint, generate JSON Web Tokens (JWT) upon successful login, and secure an endpoint using JWT in your Flask application. These are essential steps in building a secure API.

In this lesson, we'll go a step further by discussing the importance of setting token expiration times and introducing the concepts of access and refresh tokens. Using these tokens effectively is crucial for maintaining the security and usability of your application.

Recap of Existing Setup

Before diving into the new content, let's quickly recap our existing Flask and JWT setup to ensure we're all on the same page. Here's the fundamental configuration for our Flask app, including the mock database, JWT setup, and schema validation:

from flask import Flask, request, jsonify
from flask_jwt_extended import JWTManager
from marshmallow import Schema, fields, ValidationError
from marshmallow.validate import Length

# Initialize a Flask app instance
app = Flask(__name__)

# Mock database of users
database = [
    {"id": 1, "username": "cosmo", "password": "space-corgi"}
]

# Define a schema for validating login data
class LoginSchema(Schema):
    username = fields.Str(required=True, validate=Length(min=1))
    password = fields.Str(required=True, validate=Length(min=1))

# Set the secret key for signing JWTs
app.config['JWT_SECRET_KEY'] = 'super-secret'

# Initialize the JWTManager with the Flask app
jwt = JWTManager(app)

This setup initializes a Flask application, configures the JWT secret key, initializes the JWTManager, and sets up a mock database and a login schema for validation.

Access Tokens vs Refresh Tokens

In previous lessons, we used access tokens to secure our endpoints when users logged in. Now, we'll introduce another type of token: the refresh token.

  • Access Tokens are short-lived tokens used to authorize access to protected resources. They are included in the headers of API requests. Typically, access tokens are used on endpoints that require user authentication, such as /profile, /dashboard, or any other user-specific routes.
  • Refresh Tokens are longer-lived and are used to get new access tokens without needing the user to log in again. They are used specifically on the token refresh route, such as /refresh, to obtain a new access token when the old one expires. Refresh tokens are not used directly to access resources but rather to acquire new access tokens.

By using refresh tokens, we can make our application more secure and user-friendly. Instead of forcing users to log in frequently, we can allow them to stay logged in by obtaining new access tokens automatically.

Setting Duration Times

Setting token expiration times is critical for security. Tokens that never expire can be a significant security risk. By configuring these times, you reduce the window for potential misuse.

The timedelta class from Python's datetime module allows us to specify these durations. It accepts parameters like seconds, minutes, hours, days and weeks.

Here are some examples of how to create different durations:

from datetime import timedelta

# Examples of durations
seconds = timedelta(seconds=30)  # 30 seconds
minutes = timedelta(minutes=5)  # 5 minutes
hours = timedelta(hours=1)  # 1 hour
days = timedelta(days=1)  # 1 day
weeks = timedelta(weeks=1)  # 1 week
custom = timedelta(days=2, hours=3, minutes=15)  # 2 days, 3 hours, and 15 minutes

Configuring Token Expiration Times

Now that we understand how to specify durations with timedelta, let's apply this to our token expiration configuration and ensure that tokens have a limited lifespan, enhancing security.

Here's how to configure the expiration times for access and refresh tokens:

from datetime import timedelta

# Set the expiry time for access tokens (15 minutes)
app.config['JWT_ACCESS_TOKEN_EXPIRES'] = timedelta(minutes=15)
# Set the expiry time for refresh tokens (1 hour)
app.config['JWT_REFRESH_TOKEN_EXPIRES'] = timedelta(hours=1)

In this configuration:

  • Access Tokens expire after 15 minutes.
  • Refresh Tokens expire after 1 hour.

By setting these expiration times, you enforce periodic re-authentication and token renewal, which helps maintain the security of your application.

Generating Access Tokens and Refresh Tokens

With our expiration times now set, let's extend our existing /login route to generate both tokens upon a successful login:

from flask_jwt_extended import create_access_token, create_refresh_token

# Existing login route to include token generation
@app.route('/login', methods=['POST'])
def login():
    # -- previous input validation and user verification code goes here --
    
    # Check if the user exists and if the password matches
    if user and user["password"] == password:
        # Create a JWT access token
        access_token = create_access_token(identity=username)
        # Create a JWT refresh token
        refresh_token = create_refresh_token(identity=username)

        # Return the tokens as a JSON response
        return jsonify(access_token=access_token, refresh_token=refresh_token), 200
    else:
        # Return an error if the user does not exist or the password is incorrect
        return jsonify(error="Bad username or password"), 401

In this route, upon successful login:

  • We generate an access token using create_access_token(identity=username).
  • We generate a refresh token using create_refresh_token(identity=username).
  • Both tokens are then returned in the response.

This setup ensures that the user receives both tokens needed for future access and token refresh actions.

Creating a Token Refresh Route

Once the access token expires, users will need a way to get a new one without having to re-authenticate. This is where the refresh token comes into play.

Below is the implementation of a /refresh route that uses the refresh token to provide a new access token:

from flask_jwt_extended import jwt_required, get_jwt_identity

# Define a refresh route that requires a valid refresh token to access
@app.route('/refresh', methods=['POST'])
@jwt_required(refresh=True)
def refresh():
    # Get the identity of the current user from the JWT refresh token
    current_user = get_jwt_identity()
    # Create a new access token
    new_access_token = create_access_token(identity=current_user)
    # Return the new access token as a JSON response
    return jsonify(access_token=new_access_token), 200

In this route:

  • The @jwt_required(refresh=True) decorator ensures that the request includes a valid refresh token.
  • get_jwt_identity() retrieves the identity (user) from the current refresh token.
  • A new access token is generated using create_access_token(identity=current_user).
  • The new access token is returned in the response.

This process allows users to stay authenticated without having to provide their credentials every time their access token expires.

Handling Expired Tokens

When an access token expires, the server responds with a 401 Unauthorized status and a message like:

{
    "msg": "Token has expired"
}

This indicates that the token is invalid due to expiration. The client should use the refresh token to get a new access token or prompt the user to re-authenticate, ensuring ongoing security.

Summary and Next Steps

In this lesson, we covered:

  • The importance of setting token expiration times for both access and refresh tokens.
  • How to configure token expiration times in Flask.
  • The differences between access tokens and refresh tokens.
  • Generating both types of tokens upon login.
  • Creating a route to refresh access tokens using a valid refresh token.

Congratulations on reaching the final stage of our course! Your dedication has brought you to a point where you can confidently secure Flask applications using JWT authentication. Up next, you have a few final tasks to complete that will reinforce these concepts.

You're almost there, so keep going strong!

Sign up

Join the 1M+ learners on CodeSignal

Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal