Activity #46: Documentation of Python JWT

Documentation of Activity#45

Step-by-Step Guide for Implementing JWT in Python Flask

Step 1: Create Folder

It's a good practice to use a virtual environment to isolate your dependencies. You can create and activate a virtual environment using the following commands in your CMD:

python -m venv venv
venv\Scripts\activate

After activation, your terminal prompt should change to indicate the virtual environment is active.

3. Install Required Libraries

Install Flask, PyJWT, and Werkzeug (for password hashing). These libraries are essential for the Flask API and JWT implementation.

pip install Flask PyJWT werkzeug
  • Flask: The micro web framework.

  • PyJWT: A library for working with JSON Web Tokens.

  • Werkzeug: A utility library, which will be used to hash and check passwords securely.

Step 4: Create the app.py File

In the project folder, create a new file named app.py where we’ll add the Flask routes and JWT logic.

  1. Import Libraries: We’ll need Flask, request, jsonify from Flask, and jwt for generating and verifying tokens.

  2. Create the Flask App:

    Flask App Code:

     from flask import Flask, jsonify, request
     import jwt
     import datetime
     from werkzeug.security import generate_password_hash, check_password_hash
    
     app = Flask(__name__)
    
     # Secret key for encoding and decoding JWT
     SECRET_KEY = 'your_secret_key'
    
     # In-memory "database" for users (for demonstration purposes)
     users_db = {}
    
     # Helper function to create a JWT
     def create_jwt(user_id):
         expiration_time = datetime.datetime.utcnow() + datetime.timedelta(hours=1)
         payload = {
             'sub': user_id,  # Subject claim (username)
             'exp': expiration_time
         }
         token = jwt.encode(payload, SECRET_KEY, algorithm='HS256')
         return token
    
     # Route to get a JWT (For demonstration, will just return a pre-generated token)
     @app.route('/get-jwt', methods=['GET'])
     def get_jwt():
         token = request.headers.get('Authorization')  # Expecting JWT in the Authorization header
         if token:
             try:
                 # Decode JWT to verify its validity
                 decoded_token = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
                 return jsonify({"message": "Token is valid", "user": decoded_token['sub']}), 200
             except jwt.ExpiredSignatureError:
                 return jsonify({"error": "Token has expired"}), 401
             except jwt.InvalidTokenError:
                 return jsonify({"error": "Invalid token"}), 401
         return jsonify({"error": "Token is missing"}), 400
    
     # Route to set a JWT (Login or create JWT by providing username and password)
     @app.route('/set-jwt', methods=['POST'])
     def set_jwt():
         data = request.get_json()
         username = data.get('username')
         password = data.get('password')
    
         if username not in users_db:
             return jsonify({"error": "User not found"}), 404
    
         user = users_db[username]
    
         if not check_password_hash(user['password'], password):
             return jsonify({"error": "Invalid password"}), 401
    
         # Create JWT token
         token = create_jwt(username)
         return jsonify({"message": "JWT created", "token": token}), 200
    
     # Route to handle user login
     @app.route('/login', methods=['POST'])
     def login():
         data = request.get_json()
         username = data.get('username')
         password = data.get('password')
    
         if username not in users_db:
             return jsonify({"error": "User not found"}), 404
    
         user = users_db[username]
    
         if not check_password_hash(user['password'], password):
             return jsonify({"error": "Invalid password"}), 401
    
         # Create JWT token on successful login
         token = create_jwt(username)
         return jsonify({"message": "Login successful", "token": token}), 200
    
     # Route to handle user registration
     @app.route('/register', methods=['POST'])
     def register():
         data = request.get_json()
         username = data.get('username')
         password = data.get('password')
    
         if username in users_db:
             return jsonify({"error": "Username already exists"}), 400
    
         # Hash the password before storing
         hashed_password = generate_password_hash(password)
         users_db[username] = {'password': hashed_password}
    
         return jsonify({"message": "User registered successfully"}), 201
    
     if __name__ == '__main__':
         app.run(debug=True)
    

Running the Flask App:

To start the Flask app, run the following command in your terminal:

python app.py

Make sure your Flask app is running at http://localhost:5000. You can then test the endpoints using Postman as described above.

Explanation of Routes:

  1. GET /get-jwt:

    • This route checks if the provided JWT in the Authorization header is valid. If valid, it returns a success message along with the decoded user info (username). If not, it returns an error.
  2. POST /set-jwt:

    • This route receives a username and password and returns a JWT if the credentials are correct.
  3. POST /login:

    • This route handles login. If the username and password are correct, it returns a JWT.
  4. POST /register:

    • This route allows new users to register by providing a username and password. The password is hashed and stored in the users_db.

How to Test Using Postman

  1. Register a New User (POST /register):

{
  "username": "Lucy",
  "password": "mypassword"
}

Response (if successful):

{
  "message": "User registered successfully"
}

  1. Login and Get JWT (POST /login):

    • URL: http://localhost:5000/login

    • Method: POST

    • Body (JSON format):

        {
          "username": "Lucy",
          "password": "mypassword"
        }
      
    • Response (if successful):

        {
            "message": "Login successful",
            "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJMdWN5IiwiZXhwIjoxNzMxNDg2NDk1fQ.Ee3Gmb7n3veTz5y6ApaQv-MP_rLnjJvpfY4oTvPZl6s"
        }
      

  1. Access Protected Route with JWT (GET /get-jwt):

    • URL: http://localhost:5000/get-jwt

    • Method: GET

    • In Postman:

      • Go to the Headers tab.

      • Add a key Authorization with the value Bearer your_jwt_token_here.

    • Response (if the token is valid):

        {
          "message": "Token is valid",
          "user": "Lucy"
        }
      

  1. Access Protected Route Without JWT or Invalid JWT:

    • If you don't provide the JWT or use an expired/invalid JWT, you will receive an error message:

        {
          "error": "Token is missing"
        }
      

      or:

        {
          "error": "Invalid token"
        }