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