Python Flask JWT Authentication and Authorization: A Complete Beginner’s Guide

Here's a breakdown of each section:

1. User Registration

  • Step 1: The user sends a POST request to the /auth/register endpoint with their username and password.
  • Step 2: The Flask App checks the database to see if the user already exists.
  • Step 3: If the user doesn't exist, the app saves the new user with a hashed password to the database.
  • Step 4: A success message is returned to the user.

2. User Login

  • Step 1: The user sends a POST request to /auth/login with their credentials.
  • Step 2: The Flask App validates the credentials against the database.
  • Step 3: If valid, the app generates a JWT access token.
  • Step 4: The token is sent back to the user for future authentication.

3. Accessing a Protected Route

  • Step 1: The user sends a GET request to /auth/protected with the JWT token in the Authorization header.
  • Step 2: The app validates the token using the JWT service.
  • Step 3: If the token is valid, the app retrieves the user's identity from the database.
  • Step 4: A success response is returned to the user with a personalized message.

4. Invalid Access Attempt

  • Step 1: If the user tries to access the protected route without a token or with an invalid token, the app validates the token.
  • Step 2: The validation fails, and the app responds with a 401 Unauthorized error message.

This guide provides a step-by-step tutorial on implementing JWT (JSON Web Token) Authentication and Authorization in a Python Flask application from scratch. You'll learn how to build a secure authentication system, allowing users to register, log in, and access protected resources based on their roles. The guide covers essential components like setting up a Flask project, integrating JWT, handling user roles, and securing routes. By the end, you'll have a robust foundation for building scalable, secure APIs for your web or mobile applications.


Step-by-Step Guide

1. Setting Up Your Flask Project

Install the required dependencies:

pip install flask flask-jwt-extended flask-sqlalchemy werkzeug

2. Create Project Structure

Organize your files as follows:

/flask_jwt_auth ├── app.py ├── models.py ├── routes.py ├── database.py ├── config.py └── requirements.txt

3. Configuring the Application

In config.py, define the secret key for JWT:

class Config:
    SECRET_KEY = "your_secret_key"
    JWT_SECRET_KEY = "your_jwt_secret_key"  # For JWT token signing
    SQLALCHEMY_DATABASE_URI = "sqlite:///users.db"  # Database URL
    SQLALCHEMY_TRACK_MODIFICATIONS = False

4. Database Setup

In database.py, initialize SQLAlchemy:

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

def init_db(app):
    db.init_app(app)
    with app.app_context():
        db.create_all()

5. Create User Model

In models.py, define the User model:

from werkzeug.security import generate_password_hash, check_password_hash
from database import db

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    password_hash = db.Column(db.String(200), nullable=False)

    def set_password(self, password):
        self.password_hash = generate_password_hash(password)

    def check_password(self, password):
        return check_password_hash(self.password_hash, password)

6. Implement Routes

In routes.py, define authentication and authorization logic:

from flask import Blueprint, request, jsonify
from flask_jwt_extended import create_access_token, jwt_required, get_jwt_identity
from models import User
from database import db

auth_bp = Blueprint("auth", __name__)

@auth_bp.route('/register', methods=['POST'])
def register():
    data = request.get_json()
    username = data.get('username')
    password = data.get('password')

    if not username or not password:
        return jsonify({"message": "Username and password are required"}), 400

    if User.query.filter_by(username=username).first():
        return jsonify({"message": "User already exists"}), 400

    user = User(username=username)
    user.set_password(password)
    db.session.add(user)
    db.session.commit()

    return jsonify({"message": "User registered successfully"}), 201

@auth_bp.route('/login', methods=['POST'])
def login():
    data = request.get_json()
    username = data.get('username')
    password = data.get('password')

    user = User.query.filter_by(username=username).first()

    if not user or not user.check_password(password):
        return jsonify({"message": "Invalid username or password"}), 401

    access_token = create_access_token(identity=user.username)
    return jsonify({"access_token": access_token}), 200

@auth_bp.route('/protected', methods=['GET'])
@jwt_required()
def protected():
    current_user = get_jwt_identity()
    return jsonify({"message": f"Hello, {current_user}!"}), 200

7. Integrating Components

In app.py, integrate everything:

from flask import Flask
from flask_jwt_extended import JWTManager
from config import Config
from routes import auth_bp
from database import init_db

app = Flask(__name__)
app.config.from_object(Config)

jwt = JWTManager(app)
init_db(app)

app.register_blueprint(auth_bp, url_prefix="/auth")

if __name__ == "__main__":
    app.run(debug=True)

8. Testing the Application

Register a User

Endpoint: POST /auth/register
Request Body:

{
  "username": "testuser",
  "password": "testpassword"
}

Login a User

Endpoint: POST /auth/login
Request Body:

{
  "username": "testuser",
  "password": "testpassword"
}

Response:

{
  "access_token": "JWT_TOKEN_HERE"
}

Access Protected Route

Endpoint: GET /auth/protected
Headers:

Authorization: Bearer JWT_TOKEN_HERE

Response:

{
  "message": "Hello, testuser!"
}

9. Secure Routes with Roles (Optional)

Extend the User model to include roles and add role-based authorization checks. For example:

class User(db.Model):
    ...
    role = db.Column(db.String(50), default="user")

@auth_bp.route('/admin', methods=['GET'])
@jwt_required()
def admin():
    current_user = get_jwt_identity()
    user = User.query.filter_by(username=current_user).first()

    if user.role != "admin":
        return jsonify({"message": "Access denied"}), 403

    return jsonify({"message": "Welcome, Admin!"}), 200

10. Secure the Application

  • Use HTTPS in production.
  • Rotate JWT_SECRET_KEY periodically.
  • Implement token expiration and refresh.

Popular posts from this blog

Learn Java 8 streams with an example - print odd/even numbers from Array and List

Java Stream API - How to convert List of objects to another List of objects using Java streams?

Registration and Login with Spring Boot + Spring Security + Thymeleaf

Java, Spring Boot Mini Project - Library Management System - Download

ReactJS, Spring Boot JWT Authentication Example

Top 5 Java ORM tools - 2024

Java - Blowfish Encryption and decryption Example

Spring boot video streaming example-HTML5

Google Cloud Storage + Spring Boot - File Upload, Download, and Delete