Skip to main content

TokenManager

The TokenManager class handles JSON Web Token (JWT) creation and validation for user authentication in Framefox applications. It provides stateless authentication suitable for APIs and single-page applications. Source: framefox/core/security/token_manager.py:17

Constructor

TokenManager()
The TokenManager automatically loads configuration from Settings:
  • Algorithm: HS256 (HMAC with SHA-256)
  • Secret Key: From session_secret_key in settings
  • Expiration: 3600 seconds (1 hour) by default
The session_secret_key must be set in your configuration or environment variables, otherwise TokenManager will raise a ValueError.

Methods

create_token()

Creates a JWT token for an authenticated user.
def create_token(self, user, firewallname: str, roles: list = None) -> str
user
object
required
The authenticated user object. Must have id and email attributes.
firewallname
str
required
The name of the firewall that authenticated the user
roles
list
default:"[]"
List of role strings assigned to the user
return
str
The encoded JWT token string
Token Payload:
  • sub: User ID (as string)
  • email: User email address
  • roles: List of user roles
  • firewallname: Name of the firewall
  • iat: Issued at timestamp
  • exp: Expiration timestamp (iat + 3600 seconds)
Example:
from framefox.core.security.token_manager import TokenManager

token_manager = TokenManager()

# Create token for authenticated user
token = token_manager.create_token(
    user=current_user,
    firewallname="api",
    roles=["ROLE_USER", "ROLE_EDITOR"]
)

print(token)  # eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

decode_token()

Decodes and validates a JWT token.
def decode_token(self, token: str) -> dict | None
token
str
required
The JWT token string to decode
return
dict | None
The decoded token payload as a dictionary, or None if the token is invalid or expired
Returns None when:
  • Token signature is invalid
  • Token has expired
  • Token format is malformed
Example:
token_manager = TokenManager()

# Decode and validate token
payload = token_manager.decode_token(token)

if payload:
    user_id = payload["sub"]
    email = payload["email"]
    roles = payload["roles"]
    print(f"User: {email}, Roles: {roles}")
else:
    print("Invalid or expired token")

Virtual OAuth Users

The TokenManager supports creating tokens for virtual OAuth users (users authenticated via OAuth providers who don’t exist in your database):
# User object has is_virtual attribute
if hasattr(user, "is_virtual") and user.is_virtual:
    # Virtual user - ID is generated during OAuth flow
    token = token_manager.create_token(user, "oauth", roles=["ROLE_USER"])

Complete Example: Login Endpoint

from framefox.core.controller.abstract_controller import AbstractController
from framefox.core.routing.decorator.route import Route
from framefox.core.security.token_manager import TokenManager
from framefox.core.security.password.password_hasher import PasswordHasher
from pydantic import BaseModel

class LoginRequest(BaseModel):
    email: str
    password: str

class LoginResponse(BaseModel):
    token: str
    user: dict

class AuthController(AbstractController):
    def __init__(
        self,
        token_manager: TokenManager,
        password_hasher: PasswordHasher,
        user_repository
    ):
        super().__init__()
        self.token_manager = token_manager
        self.password_hasher = password_hasher
        self.user_repository = user_repository
    
    @Route("/api/login", "api.login", methods=["POST"], response_model=LoginResponse)
    async def login(self, credentials: LoginRequest):
        # Find user
        user = await self.user_repository.find_one_by(
            {"email": credentials.email}
        )
        
        if not user:
            return self.json({"error": "Invalid credentials"}, status=401)
        
        # Verify password
        if not self.password_hasher.verify(credentials.password, user.password):
            return self.json({"error": "Invalid credentials"}, status=401)
        
        # Create token
        token = self.token_manager.create_token(
            user=user,
            firewallname="api",
            roles=user.roles
        )
        
        return self.json({
            "token": token,
            "user": {
                "id": user.id,
                "email": user.email,
                "roles": user.roles
            }
        })

Token Verification in Middleware

from starlette.middleware.base import BaseHTTPMiddleware
from framefox.core.security.token_manager import TokenManager

class JWTMiddleware(BaseHTTPMiddleware):
    def __init__(self, app, token_manager: TokenManager):
        super().__init__(app)
        self.token_manager = token_manager
    
    async def dispatch(self, request, call_next):
        # Skip authentication for public routes
        if request.url.path in ["/login", "/register"]:
            return await call_next(request)
        
        # Get token from header
        auth_header = request.headers.get("Authorization")
        if not auth_header or not auth_header.startswith("Bearer "):
            return JSONResponse({"error": "Unauthorized"}, status_code=401)
        
        token = auth_header[7:]
        
        # Validate token
        payload = self.token_manager.decode_token(token)
        if not payload:
            return JSONResponse({"error": "Invalid token"}, status_code=401)
        
        # Attach user info to request state
        request.state.user_id = payload["sub"]
        request.state.email = payload["email"]
        request.state.roles = payload["roles"]
        
        return await call_next(request)

Token Refresh

Implement token refresh by checking the expiration time and issuing new tokens before they expire:
import time

@Route("/api/refresh", "api.refresh", methods=["POST"])
async def refresh_token(self, token_manager: TokenManager):
    # Get current token
    auth_header = request.headers.get("Authorization")
    token = auth_header[7:]
    
    # Decode token
    payload = token_manager.decode_token(token)
    if not payload:
        return self.json({"error": "Invalid token"}, status=401)
    
    # Check if token is close to expiration (within 5 minutes)
    time_until_expiry = payload["exp"] - int(time.time())
    if time_until_expiry > 300:  # More than 5 minutes left
        return self.json({"token": token})  # Return same token
    
    # Issue new token
    user = await self.user_repository.find(payload["sub"])
    new_token = token_manager.create_token(
        user=user,
        firewallname=payload["firewallname"],
        roles=payload["roles"]
    )
    
    return self.json({"token": new_token})

Security Best Practices

  • Never commit the secret key to version control
  • Use environment variables or secure configuration management
  • Rotate the secret key periodically
  • Use a strong, random secret key (at least 32 characters)
  • Use short expiration times (1 hour default)
  • Implement token refresh for better UX
  • Consider different expiration times for different use cases
  • Store tokens securely on the client (httpOnly cookies for web)
  • Never store tokens in localStorage for sensitive applications
  • Clear tokens on logout
  • Always use HTTPS in production
  • Tokens transmitted over HTTP can be intercepted

See Also

Authenticator

Authentication implementation

Authentication Guide

Complete authentication guide

Access Manager

Role-based access control

Security Overview

Security features overview

Build docs developers (and LLMs) love