Activity #46: Documentation of Python JWT

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

Step 1: Create Folder

2. Create a Virtual Environment

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:

1. Flask App Code:

  1.   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": "Romel",
  "password": "pass1425"
}

Response (if successful):

{
  "message": "User registered successfully"
}

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

URL: http://localhost:5000/login

Method: POST

Body (JSON format):

{
    "username": "Romel",
    "password": "pass1425"
}

Response (if successful):

{
    "message": "Login successful",
    "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJSb21lbCIsImV4cCI6MTczMTU0NTg0M30.DSyanTRicIwLj9gNGiN1QOnzngb26uOIetFnaQxnyvY"
}

3. 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": "Romel"
  }

4. 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"
        }