Securing Flask APIs with JWT

In the world of web development, securing APIs is of utmost importance. Flask, a lightweight web framework in Python, is widely used to build APIs. JSON Web Tokens (JWT) have emerged as a popular solution for securing these APIs. JWTs are a compact, URL-safe means of representing claims to be transferred between two parties. This blog post will delve into the details of securing Flask APIs with JWT, covering core concepts, typical usage scenarios, common pitfalls, and best practices.

Table of Contents

  1. Core Concepts
  2. Typical Usage Scenarios
  3. Setting Up a Flask API with JWT
  4. Common Pitfalls
  5. Best Practices
  6. Conclusion
  7. References

Core Concepts

What is JWT?

JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self - contained way for securely transmitting information between parties as a JSON object. A JWT consists of three parts:

  • Header: Contains the type of the token (JWT) and the signing algorithm being used, such as HMAC SHA - 256 or RSA.
  • Payload: Contains claims. Claims are statements about an entity (usually the user) and additional data. There are three types of claims: registered, public, and private claims.
  • Signature: To create the signature part, you have to take the encoded header, the encoded payload, a secret, the algorithm specified in the header, and sign that.

How JWT Works in API Security

When a user logs in to an application, the server verifies the user’s credentials. If the credentials are valid, the server generates a JWT and sends it back to the client. The client then includes this JWT in the Authorization header of subsequent requests to the API. The server validates the JWT on each request to ensure that the user is authenticated and authorized to access the requested resources.

Typical Usage Scenarios

Single - Page Applications (SPAs)

SPAs like React, Vue.js, or Angular often communicate with Flask APIs. JWTs are used to authenticate users and authorize access to protected endpoints. For example, a user logs in to a SPA, and the SPA sends the credentials to the Flask API. If the login is successful, the API returns a JWT, which the SPA stores and includes in subsequent requests.

Mobile Applications

Mobile apps also rely on Flask APIs for data. JWTs provide a secure way to authenticate users on mobile devices. When a user logs in to a mobile app, the app sends the credentials to the Flask API, receives a JWT, and uses it to access protected resources.

Microservices Architecture

In a microservices architecture, different services need to communicate with each other securely. JWTs can be used to authenticate and authorize requests between microservices. A user authenticates with a gateway service, which generates a JWT. This JWT is then passed along to other microservices for authorization.

Setting Up a Flask API with JWT

Prerequisites

  • Python installed on your system
  • Flask and Flask - JWT - Extended libraries installed. You can install them using pip install flask flask-jwt-extended

Code Example

from flask import Flask, jsonify, request
from flask_jwt_extended import JWTManager, create_access_token, jwt_required, get_jwt_identity

# Initialize Flask app
app = Flask(__name__)

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

# Mock user database
users = {
    'user1': 'password1'
}

# Login route
@app.route('/login', methods=['POST'])
def login():
    # Get username and password from the request
    data = request.get_json()
    username = data.get('username')
    password = data.get('password')

    # Check if the user exists and the password is correct
    if username in users and users[username] == password:
        # Create an access token
        access_token = create_access_token(identity=username)
        return jsonify(access_token=access_token), 200
    else:
        return jsonify({"msg": "Bad username or password"}), 401

# Protected route
@app.route('/protected', methods=['GET'])
@jwt_required()
def protected():
    # Get the identity of the current user
    current_user = get_jwt_identity()
    return jsonify(logged_in_as=current_user), 200

if __name__ == '__main__':
    app.run(debug=True)

Explanation

  • Initialization: We initialize a Flask app and set the secret key for signing JWTs. The JWTManager is used to manage JWTs in the application.
  • Login Route: The /login route checks the user’s credentials. If the credentials are valid, it creates an access token using create_access_token and returns it to the client.
  • Protected Route: The /protected route is protected by the @jwt_required() decorator. This decorator ensures that only requests with a valid JWT can access this route. The get_jwt_identity() function is used to get the identity of the current user from the JWT.

Common Pitfalls

Weak Secret Key

If the secret key used to sign JWTs is weak, an attacker can easily guess or brute - force it. This can lead to the attacker generating valid JWTs and accessing protected resources. Always use a strong, randomly generated secret key.

Not Verifying Token Expiration

JWTs can have an expiration time. If the server does not verify the expiration time of the JWT, an attacker can use an expired token to access protected resources. Make sure to set an appropriate expiration time for JWTs and verify it on the server side.

Storing JWTs in Local Storage

Storing JWTs in local storage of a browser can be a security risk. If an attacker manages to execute a cross - site scripting (XSS) attack, they can steal the JWT from local storage. Consider using HTTP - only cookies to store JWTs instead.

Best Practices

Use Strong Secret Keys

Generate a long, random secret key and store it securely. You can use tools like openssl rand -hex 32 to generate a strong secret key.

Set Appropriate Token Expiration

Set an appropriate expiration time for JWTs. Short - lived tokens reduce the risk of an attacker using a stolen token for an extended period. You can set the expiration time when creating the access token.

from flask_jwt_extended import create_access_token
import datetime

# Create an access token with an expiration time of 15 minutes
expires = datetime.timedelta(minutes=15)
access_token = create_access_token(identity='user1', expires_delta=expires)

Use HTTPS

Always use HTTPS to encrypt the communication between the client and the server. This prevents man - in - the - middle attacks where an attacker can intercept and modify the JWT.

Validate JWTs on the Server

Validate the JWT on the server for every request to a protected endpoint. Check the signature, expiration time, and other claims to ensure the token is valid.

Conclusion

Securing Flask APIs with JWT is a powerful and widely used technique. By understanding the core concepts, typical usage scenarios, common pitfalls, and best practices, you can build secure and reliable APIs. JWTs provide a simple and effective way to authenticate and authorize users in various application architectures, including SPAs, mobile apps, and microservices.

References