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
/authendpoint. - 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
- Authenticate
SendPOST /authwith JSON{ "username": "...", "password": "..." }. - Store Token
On success, receive{ "access_token": "<JWT>" }. - Call Protected Endpoint
Include headerAuthorization: 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"
}
}
}