Lesson 4
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:

Python
1from flask import Flask, request, jsonify 2from flask_jwt_extended import JWTManager 3from marshmallow import Schema, fields, ValidationError 4from marshmallow.validate import Length 5 6# Initialize a Flask app instance 7app = Flask(__name__) 8 9# Mock database of users 10database = [ 11 {"id": 1, "username": "cosmo", "password": "space-corgi"} 12] 13 14# Define a schema for validating login data 15class LoginSchema(Schema): 16 username = fields.Str(required=True, validate=Length(min=1)) 17 password = fields.Str(required=True, validate=Length(min=1)) 18 19# Set the secret key for signing JWTs 20app.config['JWT_SECRET_KEY'] = 'super-secret' 21 22# Initialize the JWTManager with the Flask app 23jwt = 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:

Python
1from datetime import timedelta 2 3# Examples of durations 4seconds = timedelta(seconds=30) # 30 seconds 5minutes = timedelta(minutes=5) # 5 minutes 6hours = timedelta(hours=1) # 1 hour 7days = timedelta(days=1) # 1 day 8weeks = timedelta(weeks=1) # 1 week 9custom = 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:

Python
1from datetime import timedelta 2 3# Set the expiry time for access tokens (15 minutes) 4app.config['JWT_ACCESS_TOKEN_EXPIRES'] = timedelta(minutes=15) 5# Set the expiry time for refresh tokens (1 hour) 6app.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:

Python
1from flask_jwt_extended import create_access_token, create_refresh_token 2 3# Existing login route to include token generation 4@app.route('/login', methods=['POST']) 5def login(): 6 # -- previous input validation and user verification code goes here -- 7 8 # Check if the user exists and if the password matches 9 if user and user["password"] == password: 10 # Create a JWT access token 11 access_token = create_access_token(identity=username) 12 # Create a JWT refresh token 13 refresh_token = create_refresh_token(identity=username) 14 15 # Return the tokens as a JSON response 16 return jsonify(access_token=access_token, refresh_token=refresh_token), 200 17 else: 18 # Return an error if the user does not exist or the password is incorrect 19 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:

Python
1from flask_jwt_extended import jwt_required, get_jwt_identity 2 3# Define a refresh route that requires a valid refresh token to access 4@app.route('/refresh', methods=['POST']) 5@jwt_required(refresh=True) 6def refresh(): 7 # Get the identity of the current user from the JWT refresh token 8 current_user = get_jwt_identity() 9 # Create a new access token 10 new_access_token = create_access_token(identity=current_user) 11 # Return the new access token as a JSON response 12 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:

JSON
1{ 2 "msg": "Token has expired" 3}

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!

Enjoy this lesson? Now it's time to practice with Cosmo!
Practice is how you turn knowledge into actual skills.