3.3 KiB
| title | description | connects | created | updated | |||
|---|---|---|---|---|---|---|---|
| Connection: FastAPI + Azure AD + Docker — The Oliver Trinity | These three always appear together in Oliver internal tools — how they wire up and where each touches the other |
|
2026-04-27 | 2026-04-27 |
Connection: FastAPI + Azure AD + Docker — The Oliver Trinity
The Connection
FastAPI, Azure AD MSAL, and Docker Compose appear together in almost every Oliver internal tool. They're designed independently but have specific integration points that only become clear across multiple projects.
Key Insight
The auth middleware must run inside the container, but credentials must come from outside it. This creates a specific pattern: Azure AD env vars flow in through Docker Compose env_file, the MSAL JWKS validation runs in FastAPI middleware, and the Docker healthcheck must not hit authenticated endpoints (it has no token).
The three systems interact at exactly three points:
- JWT validation: FastAPI reads
AZURE_TENANT_ID+AZURE_CLIENT_IDfrom env → fetches JWKS from Azure → validates tokens from MSAL.js frontend - CORS: FastAPI CORS origins must include the exact frontend origin (no trailing slash) — when running in Docker, the origin is the host's port/domain, not the container's internal address
- Local dev bypass:
DISABLE_AUTH=trueskips Azure AD entirely in dev — this env var must be in the Docker service's env_file, not just the host shell
The Wiring
Browser (MSAL.js PKCE)
↓ acquireTokenSilent() → Azure AD → access_token (JWT)
↓ Authorization: Bearer {token}
Apache/nginx
↓ passes all headers intact (do NOT strip Authorization)
FastAPI middleware (HTTPBearer)
↓ fetches JWKS from https://login.microsoftonline.com/{tenant}/.well-known/openid-configuration
↓ validates token signature + audience + expiry
↓ injects user claims into request.state.user
Route handlers
↓ read request.state.user.preferred_username / roles
Environment Variable Pattern
# docker-compose.yml — the right way
services:
api:
env_file: ./backend/.env # contains AZURE_* + DISABLE_AUTH
frontend:
environment:
- VITE_AZURE_CLIENT_ID=${AZURE_CLIENT_ID}
- VITE_AZURE_TENANT_ID=${AZURE_TENANT_ID}
# backend/.env
AZURE_TENANT_ID=xxx
AZURE_CLIENT_ID=xxx
AZURE_CLIENT_SECRET=xxx # only if app-only calls needed
DISABLE_AUTH=true # remove in production
When DISABLE_AUTH Breaks
Teams sometimes forget to remove DISABLE_AUTH=true on the server. Symptoms: authenticated routes return 200 to anyone with no token. Add a startup check:
import os
if os.getenv("DISABLE_AUTH", "false").lower() == "true":
import logging
logging.warning("⚠️ AUTH IS DISABLED — do not use in production")
Projects Where This Trinity Appears
GMAL, Mod Comms, Video Accessibility, Semblance, Enterprise Nexus, Barclays Banner Builder, BAIC Dashboard.