️ Middlewares

Middleware functions are used to intercept requests before they reach the main logic of the application. In Lila, core middlewares are defined in the core/middleware.py file. Middlewares can be used for tasks such as authentication, logging, security, and error handling.

Lila uses a unified Middleware manager that provides both class-based HTTP middlewares and route decorators.

Default Route Middlewares

Lila includes several built-in middlewares that can be used as decorators on your routes:

Example usage in routes

from lila.core.middleware import login_required, session_active, validate_token

# Require authentication for this route
@router.get('/dashboard')
@login_required
async def dashboard(request: Request):
    return render(request=request, template='dashboard')

# Redirect authenticated users away from login page
@router.get('/login')
@session_active
async def login_page(request: Request):
    return render(request=request, template='login')

# Validate JWT for API routes
@router.get('/api/data')
@validate_token
async def api_data(request: Request):
    return JSONResponse({"data": "secure"})
                    

️ Creating Custom Middlewares

You can easily create your own route middlewares using the create_decorator helper. This tool automatically handles the boilerplate of Python decorators.

core/middleware.py (Custom Middleware)

from lila.core.middleware import create_decorator

@create_decorator
async def check_admin(request: Request, role="admin"):
    # If the logic returns a response, it breaks the chain and returns it
    # If it returns True or None, it continues to the route
    user = request.state.user
    if user.get("role") != role:
        return JSONResponse({"error": "Unauthorized"}, status_code=403)
    return True

# Usage in routes:
# @check_admin -> uses default role="admin"
# @check_admin(role="superadmin") -> uses custom role
                    

️ Router-Level Middlewares

Instead of decorating every route manually, you can assign one or more middlewares to a Router during initialization. Every route registered on that router will inherit them and run them in left-to-right order.

Router-level middleware inheritance

from lila.core.routing import Router
from lila.core.middleware import login_required, validate_token

# Both middlewares will automatically run for every route in this router
auth_router = Router(prefix="v1/api", middlewares=[login_required, validate_token])

@auth_router.get("/user")
async def user_details(request: Request):
    return JSONResponse({"user": request.state.user})
                    

CSRF Protection Middleware

Lila includes a built-in @csrf decorator that protects routes against Cross-Site Request Forgery attacks. It verifies the CSRF token on all unsafe HTTP methods (POST, PUT, PATCH, DELETE). Safe methods (GET, HEAD, OPTIONS) are always allowed through.

The token is read from the X-CSRF-Token header first, then from the csrf field in the request body, and compared against the signed _csrf cookie set by render(csrf=True).

CSRF usage example

from lila.core.templates import render
from lila.core.middleware import csrf
from lila.core.responses import JSONResponse
from lila.core.request import Request

# GET: render the form with CSRF token injected automatically
@router.get('/contact')
async def contact_page(request: Request):
    return render(request, 'contact', csrf=True)

# POST: verify token before processing the form
@router.post('/contact')
@csrf
async def contact_submit(request: Request):
    return JSONResponse({"success": True})
                    

In your Jinja2 template, use the csrf_input global helper to render the hidden field:

Template (Jinja2)

<form method="POST" action="/contact">
  {{ csrf_input | safe }}
  <input type="text" name="name" />
  <button type="submit">Send</button>
</form>
                    

The Http() function in public/js/utils.js automatically detects document.getElementById('csrf') and sends the X-CSRF-Token header on every fetch request. No additional client-side code is required.

Global Security Middlewares

Lila includes a set of global HTTP middlewares for application-wide protection. These are configured in main.py using the Middleware class.

main.py configuration

from lila.core.middleware import (
    Middleware,
    LoggingMiddleware, 
    SecurityHeadersMiddleware,
    SecurityShieldMiddleware,
    RateLimitMiddleware,
    ErrorHandlerMiddleware,
)

middlewares = [
    Middleware(LoggingMiddleware),
    Middleware(SecurityHeadersMiddleware),
    Middleware(SecurityShieldMiddleware),
    Middleware(RateLimitMiddleware),
    Middleware(ErrorHandlerMiddleware)
]

app = App(..., middleware=middlewares)