Application Architecture: Authentication and Security

This section explains how authentication and security are implemented using Flask-JWT and the security.py module. You’ll see how user credentials are verified, how JWTs are issued, and how protected endpoints enforce access control.

security.py Module

The security.py module defines two core functions that integrate with Flask-JWT:

from werkzeug.security import safe_str_cmp
from models.user import UserModel

def authenticate(username, password):
    user = UserModel.find_by_username(username)
    if user and safe_str_cmp(user.password, password):
        return user

def identity(payload):
    user_id = payload['identity']
    return UserModel.find_by_id(user_id)
  • authenticate: Verifies a user’s credentials.
  • identity: Retrieves a user instance from the JWT payload.

Key Authentication Functions

Below is a summary of the two functions and their responsibilities:

Function Purpose Inputs Returns
authenticate Verify username & password match username: strpassword: str UserModel instance or None
identity Extract user from JWT payload payload: dict UserModel instance

Integration with Flask-JWT

In app.py, the authentication handlers are wired into Flask-JWT:

from flask_jwt import JWT
from security import authenticate, identity

app = Flask(__name__)
# ... other config ...
jwt = JWT(app, authenticate, identity)  # Registers /auth endpoint
  • Initializes /auth endpoint.
  • Uses app.secret_key to sign tokens.

Authentication Flow 🔒

When a client logs in or accesses a protected route, the following sequence occurs:

sequenceDiagram
    participant Client
    participant Server
    participant SecModule
    participant Database
    Client->>Server: POST /auth {username,password}
    Server->>SecModule: authenticate(username,password)
    SecModule->>Database: UserModel.find_by_username()
    Database-->>SecModule: user record
    SecModule-->>Server: user object or None
    Server-->>Client: { access_token }
    Client->>Server: GET /book/title (Authorization: JWT <token>)
    Server->>Server: identity(payload)
    Server->>Database: UserModel.find_by_id()
    Database-->>Server: user instance
    Server-->>Client: Protected resource or 401

Protecting Endpoints

Endpoints declare their need for a valid JWT via the @jwt_required() decorator:

from flask_jwt import jwt_required

class Book(Resource):
    @jwt_required()
    def get(self, title):
        # Only authenticated users reach here
        book = BookModel.find_by_title(title)
        ...

This decorator checks the Authorization: JWT <token> header and invokes identity.

Using the Token at Runtime

  1. Authenticate
    Send POST /auth with JSON { "username": "...", "password": "..." }.
  2. Store Token
    On success, receive { "access_token": "<JWT>" }.
  3. Call Protected Endpoint
    Include header Authorization: JWT <token>.

Best Practices and Notes

{
    "title": "Security Note",
    "content": "Passwords are stored in plaintext for demo only; use hashing in production."
}
{
    "title": "Key Management",
    "content": "Store `secret_key` in environment variables, not in code."
}

Authentication Endpoint

POST /auth

{
    "title": "Authenticate User",
    "description": "Generates a JWT token for valid user credentials.",
    "method": "POST",
    "baseUrl": "http://localhost:5000",
    "endpoint": "/auth",
    "headers": [
        {
            "key": "Content-Type",
            "value": "application/json",
            "required": true
        }
    ],
    "bodyType": "json",
    "requestBody": "{\n  \"username\": \"user1\",\n  \"password\": \"pass123\"\n}",
    "responses": {
        "200": {
            "description": "Token issued",
            "body": "{\n  \"access_token\": \"<JWT>\"\n}"
        },
        "401": {
            "description": "Invalid credentials"
        }
    }
}