Flask-JWT-Extended Documentation

repository·main·Indexed 23 days ago

https://github.com/vimalloc/flask-jwt-extended

An extension for Flask providing robust JSON Web Token (JWT) support. Key features include custom claims, refresh tokens, token blocklisting, CSRF protection via cookies, and automatic user loading. It provides utilities for creating access and refresh tokens, protecting routes with @jwt_required(), and managing token revocation using the JWTManager.

Tokens
7.8K
Snippets
14
Records
49
Agent score
81%

What's inside Flask-JWT-Extended

  1. Overview of Flask-JWT-Extended features

    main

    Flask-JWT-Extended provides JSON Web Token (JWT) support for Flask applications to protect routes. It includes several optional features to simplify JWT management:

    • Custom Claims: Add and validate custom data within tokens.
    • Automatic User Loading: Access the authenticated user via current_user.
    • Refresh Tokens: Support for issuing and using refresh tokens.
    • Fresh Tokens: First-class support for identifying 'fresh' tokens, useful for authorizing sensitive operations.
    • Token Revocation: Mechanisms for blocklisting/revoking tokens.
    • Cookie Support: Ability to store tokens in cookies and provides built-in CSRF protection.
  2. Configure and override JWT token locations

    main

    You can control which methods your Flask application uses to accept JWTs using the JWT_TOKEN_LOCATION configuration option. Additionally, you can override this global setting for specific routes by passing the locations argument to the @jwt_required decorator.

    Common locations include:

    • Headers: Sending the token in the Authorization header.
    • Cookies: Using browser cookies (recommended for web browsers).
    • Query String: Sending the token as a URL parameter (not recommended due to security risks).
    • JSON Body: Including the token in the request body (only works for methods like POST, PUT, PATCH, or DELETE).
  3. Use the token freshness pattern

    main

    The token freshness pattern allows you to distinguish between tokens created via recent credentials (e.g., username/password) and tokens obtained via refreshing.

    • Fresh tokens: Can access all routes.
    • Non-fresh tokens: Can access standard routes but are blocked from critical or dangerous routes (e.g., changing an email address) that require a fresh token.

    This pattern works seamlessly with both implicit cookie refreshing and explicit refresh token strategies.

    Refer to the token_freshness.py example for implementation details.

  4. Implement Automatic User Loading

    main

    Flask-JWT-Extended provides a mechanism to automatically convert user objects to identities for JWTs and then reload those objects from the identity when a request is made. This is achieved using two callback functions registered with the JWTManager:

    1. user_identity_loader: A callback that takes a user object and returns a unique identifier (usually a string) to be stored in the JWT.
    2. user_lookup_loader: A callback that takes the identity from the JWT and performs a lookup (e.g., a database query) to return the original user object.

    Once configured, the loaded user object is accessible in protected routes via the current_user attribute.

  5. Revoke both Access and Refresh tokens during logout

    main

    When a user logs out, it is critical to revoke both the access token and the refresh token. If the refresh token is not revoked, it can be used to generate new access tokens.

    There are several ways to handle this:

    1. Two Requests: The frontend sends a request to revoke the access token and a separate request to revoke the refresh token.
    2. Single Endpoint (Multiple calls): Provide one /logout endpoint that the frontend calls twice (once for each token type).
    3. Single Request (Body): Send the access token in the header and the refresh token in the request body.
    4. Embedded JTI: Embed the refresh token's jti inside the access token payload. When the access token is revoked, the backend extracts the refresh jti and invalidates both.
    5. Token Tracking: Store every generated token's jti in a database with a boolean is_valid column. Revoking a token marks it (and potentially all other tokens for that user) as invalid.
    @app.route("/logout", methods=["DELETE"])
    @jwt_required(verify_type=False)
    def logout():
        token = get_jwt()
        jti = token["jti"]
        ttype = token["type"]
        # Logic to add jti to blocklist (Redis or DB)
        jwt_redis_blocklist.set(jti, "", ex=ACCESS_EXPIRES)
    
        return jsonify(msg=f"{ttype.capitalize()} token successfully revoked")
  6. Basic usage of Flask-JWT-Extended

    main

    The core workflow of Flask-JWT-Extended involves three main steps:

    1. Create a token: Use create_access_token to generate a JSON Web Token.
    2. Protect routes: Use the @jwt_required() decorator to ensure only requests with a valid JWT can access a specific view.
    3. Retrieve identity: Use get_jwt_identity() inside a protected route to access the identity (e.g., user ID or username) stored within the token.

    Security Warning: Always change the JWT secret key in your application to a secure value. The tokens are signed with this key; if it is compromised, attackers can forge arbitrary tokens.

  7. Partially protect routes using optional=True

    main

    If you want an endpoint to be accessible whether or not a JWT is present in the request, use the jwt_required decorator with the optional=True argument.

    Behavior when no JWT is provided:

    • get_jwt() and get_jwt_header() return an empty dictionary {}.
    • get_jwt_identity(), current_user, and get_current_user() return None.

    Behavior when an invalid JWT is provided:

    • If a JWT is present but is expired or not verifiable, the extension will still return an error as it would for a standard protected route. optional=True only bypasses the requirement for the presence of a token, not the validity of a token if one is sent.
  8. Handle JWT Claim Changes in v4.0.0

    main

    In version 4.0.0, the JWT_USER_CLAIMS configuration option has been removed. Additional claims are now placed at the top level of the JWT instead of inside a nested user_claims dictionary. This allows for overriding reserved claims like nbf.

    Warning: Existing JWTs using the old format will not work correctly.

    Migration Strategies:

    1. Change your secret key: This forces all users to receive new JWTs in the new format.
    2. Build a shim: If changing the secret key is not feasible, implement a shim to handle both the old format (claims inside user_claims) and the new format (claims at the top level) until existing tokens expire.
  9. Revoke tokens using a Database (SQLAlchemy)

    main

    Use a database if you need to maintain metadata about revoked tokens, such as when they were revoked, who revoked them, or if they can be un-revoked. This approach is useful for auditing and long-term tracking.

    class TokenBlocklist(db.Model):
        id = db.Column(db.Integer, primary_key=True)
        jti = db.Column(db.String(36), nullable=False, index=True)
        type = db.Column(db.String(16), nullable=False)
        user_id = db.Column(
            db.ForeignKey('person.id'),
            default=lambda: get_current_user().id,
            nullable=False,
        )
        created_at = db.Column(
            db.DateTime,
            server_default=func.now(),
            nullable=False,
        )
  10. Store additional data in JWTs using the `additional_claims` argument

    main

    You can include custom data in an access or refresh token by passing a dictionary to the additional_claims argument in create_access_token() or create_refresh_token(). These claims can then be retrieved in protected routes using get_jwt().

    Security Warning: JWTs are not encrypted and can be easily decoded. Never include sensitive information in these claims.