A simple JWT example with NodeJS and Python (part 1)

The following example is built using a Python Flask backend, a CURL-script to test it, and a simple React frontend that logs in, gets an authorization token and then continues to use it when accessing the backend.

We implement JWT using the Flask-JWT-Extended package.

pip install Flask-JWT-Extended

Backend in Python 3

import json
from flask import Flask, request, jsonify
from datetime import datetime, timedelta, timezone
from flask_jwt_extended import create_access_token,get_jwt,get_jwt_identity, \
unset_jwt_cookies, jwt_required, JWTManager
from flask_cors import CORS

api = Flask(__name__)
api.config["JWT_SECRET_KEY"] = "slemmig-torsk"
api.config["JWT_ACCESS_TOKEN_EXPIRES"] = timedelta(seconds=300) # 5 minutes
jwt = JWTManager(api)
CORS(api)

refresh_expiring_jwts is called after each request, and will refresh expiring tokens

@api.after_request
def refresh_expiring_jwts(response):
    try:
        exp_timestamp = get_jwt()["exp"]
        now = datetime.now(timezone.utc)
        target_timestamp = datetime.timestamp(now + timedelta(minutes=30))
        if target_timestamp > exp_timestamp:
            access_token = create_access_token(identity=get_jwt_identity())
            data = response.get_json()
            if type(data) is dict:
                data["access_token"] = access_token 
                response.data = json.dumps(data)
        return response
    except (RuntimeError, KeyError):
        # Case where there is not a valid JWT. Just return the original respone
        return response

This is the /login route which will respond with a token, or an error message

@api.route('/login', methods=["POST"])
@cross_origin()
def create_token():
    
    # Really simple authorization for this demo!
    email = request.json.get("email", None)
    password = request.json.get("password", None)

    if email != "test@test.se" or password != "123":
        return {"msg": "Wrong email or password"}, 401

    access_token = create_access_token(identity=email)
    response = {"access_token":access_token}
    return response

The logout route will just kill the token and make it unusable

@api.route("/logout", methods=["POST"])
@cross_origin()
def logout():
    response = jsonify({"msg": "logout successful"})
    unset_jwt_cookies(response)
    return response

This is an example pf a protected route, which will deny access without a working token

@api.route('/protected')
@jwt_required()
@cross_origin()
def my_profile():
    response_body = {
        "endpointname": "protected",
        "description" :"This endpoint is protected!"
    }

    return response_body

Run the application

if __name__ == "__main__":
  api.run(host="0.0.0.0")

You test the backend using this simple curl-script

curl -X POST http://[ip-address]:5000/login -H 'Content-Type: application/json' -d '{"email":"test@test.se","password":"123"}'

The response is something like this:

{"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTY1NDUxNDM3NiwianRpIjoiMmZjOTVlY2ItOGQyYS00MTYyLTk5OWMtZWRjOTIzMGI0YjczIiwidHlwZSI6ImFjY2VzcyIsInN1YiI6InRlc3RAdGVzdC5zZSIsIm5iZiI6MTY1NDUxNDM3NiwiZXhwIjoxNjU0NTE0Njc2fQ.7BtLHYKH6MgwfVP2wSUvFUAhNJwLDlMw14okHpYE31g"}

Copy the access_token and insert in the next statement:

curl http://192.168.1.67:5000/protected -X GET -H "Authorization: Bearer [access token here]"

 

Next article will show how to use this from a React application.

read part 2 of this series here: https://blogg.fsh.se/2022/06/08/a-simple-jwt-example-with-nodejs-and-python-part-2/

Github repository here: https://github.com/fshsweden/login-test-backend-python

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.