python LogoJWT Flask Integration with flask-jwt-extended

JSON Web Tokens (JWTs) are a compact, URL-safe means of representing claims to be transferred between two parties. In the context of web applications, they are commonly used for authorization. A server generates a JWT upon successful user authentication and sends it to the client. The client then includes this JWT in subsequent requests to access protected resources. The server can verify the token's authenticity and validity without needing to query a database every time.

`flask-jwt-extended` is a Flask extension that provides a full suite of utilities for working with JWTs. It simplifies the process of adding JWT-based authentication to your Flask applications. Key features include:

1. Token Creation: Easily generate `access_token` and `refresh_token` for authenticated users.
2. Token Verification: Decorators like `@jwt_required` automatically verify incoming tokens.
3. Identity Management: Retrieve the identity of the current user from the token payload.
4. Token Refreshing: Allows clients to obtain new access tokens using a refresh token without re-authenticating.
5. Token Blacklisting/Revocation: Provides mechanisms to invalidate JWTs, important for security events like logout or password changes.
6. Flexible Storage: Supports sending tokens via headers, query parameters, or cookies.

How it works:

- Login: When a user logs in successfully, the application creates an `access_token` and often a `refresh_token`. The `access_token` is short-lived and used for protected API calls. The `refresh_token` is long-lived and used to obtain new `access_token`s after the old one expires.
- Protected Routes: Routes requiring authentication are decorated with `@jwt_required`. When a request hits such a route, `flask-jwt-extended` extracts the JWT from the request (typically from the `Authorization` header), verifies its signature, expiration, and other claims. If valid, the request proceeds; otherwise, an unauthorized error is returned.
- Token Refresh: When an `access_token` expires, the client sends the `refresh_token` to a designated refresh endpoint. If the `refresh_token` is valid, a new `access_token` is issued.
- Logout/Revocation: To invalidate a token before its natural expiration, `flask-jwt-extended` provides hooks for blacklisting tokens. This typically involves storing invalidated token IDs in a database or cache.

Example Code

from flask import Flask, jsonify, request
from flask_jwt_extended import (
    JWTManager,
    create_access_token,
    jwt_required,
    get_jwt_identity,
    create_refresh_token,
    jwt_refresh_token_required,
    get_raw_jwt,
    set_access_cookies,
    set_refresh_cookies,
    unset_jwt_cookies
)

 --- Configuration --- 
app = Flask(__name__)

 Setup the Flask-JWT-Extended extension
app.config["JWT_SECRET_KEY"] = "super-secret-jwt-key"   Change this in production!
app.config["JWT_TOKEN_LOCATION"] = ["cookies"]  Where to look for JWTs
app.config["JWT_COOKIE_SECURE"] = False  Set to True in production (HTTPS only)
app.config["JWT_CSRF_PROTECTION"] = True  Enable CSRF protection for cookies
app.config["JWT_ACCESS_TOKEN_EXPIRES"] = 60 - 5  Access token valid for 5 minutes
app.config["JWT_REFRESH_TOKEN_EXPIRES"] = 60 - 60 - 24 - 7  Refresh token valid for 7 days

jwt = JWTManager(app)

 --- In-memory store for revoked tokens (for demonstration) --- 
 In a real application, use a persistent store like Redis or a database.
BLACKLIST = set()

 Callback function to check if a JWT has been revoked
@jwt.token_loader
def check_if_token_in_blacklist(decrypted_token):
    jti = decrypted_token['jti']
    return jti in BLACKLIST

 --- Dummy User Data (for demonstration) --- 
users = {
    "testuser": {"password": "testpass", "roles": ["user"]},
    "admin": {"password": "adminpass", "roles": ["admin", "user"]}
}

 --- Routes --- 

@app.route('/login', methods=['POST'])
def login():
    username = request.json.get('username', None)
    password = request.json.get('password', None)

    user_data = users.get(username)

    if not user_data or not user_data['password'] == password:
        return jsonify({"msg": "Bad username or password"}), 401

     Create the tokens. We can add additional claims here if needed.
    access_token = create_access_token(identity=username, expires_delta=app.config["JWT_ACCESS_TOKEN_EXPIRES"])
    refresh_token = create_refresh_token(identity=username, expires_delta=app.config["JWT_REFRESH_TOKEN_EXPIRES"])

     Set the cookies in the response
    resp = jsonify({"login": True})
    set_access_cookies(resp, access_token)
    set_refresh_cookies(resp, refresh_token)
    return resp, 200


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


@app.route('/refresh', methods=['POST'])
@jwt_refresh_token_required
def refresh():
     Create a new access token, using the identity from the refresh token.
    current_user = get_jwt_identity()
    new_access_token = create_access_token(identity=current_user, expires_delta=app.config["JWT_ACCESS_TOKEN_EXPIRES"])

     Set the new access token in the response cookies
    resp = jsonify({"refresh": True})
    set_access_cookies(resp, new_access_token)
    return resp, 200


@app.route('/logout', methods=['POST'])
def logout():
     When logging out, we need to revoke the access token.
     In a real app, you might also revoke the refresh token, or just rely on its expiration.
     For simplicity, this example just clears the cookies.

    resp = jsonify({"logout": True})
    unset_jwt_cookies(resp)  Removes the access and refresh token cookies

     Optionally, you could blacklist the current access token's JTI
     to immediately invalidate it, even if the cookies are cleared.
     jti = get_raw_jwt()['jti']  Needs @jwt_required to get token details
     BLACKLIST.add(jti)

    return resp, 200


 --- Main --- 
if __name__ == '__main__':
    app.run(debug=True, port=5000)

 To test this application:
 1. Run the Flask app.
 2. Use a tool like Postman or curl to send requests.

 Example Curl Commands:

 Login:
 curl -X POST -H "Content-Type: application/json" -d '{"username": "testuser", "password": "testpass"}' http://127.0.0.1:5000/login -c cookie-jar.txt

 Access Protected Resource (after login):
 curl -X GET http://127.0.0.1:5000/protected -b cookie-jar.txt

 Refresh Token (after access token expires or just to get a new one):
 curl -X POST http://127.0.0.1:5000/refresh -b cookie-jar.txt -c cookie-jar.txt

 Logout:
 curl -X POST http://127.00.1:5000/logout -b cookie-jar.txt -c cookie-jar.txt