Skip to content

Technical — OPA Auth

RAGA's forward-auth middleware service, built on Bun + Express 5. Called by the reverse proxy (Traefik) on every incoming request — it validates the JWT via Redis, then asks OPA (Open Policy Agent) for an authorization decision. If allowed, it forwards user information (role, name, grants, ID) to the downstream service via response headers.

Repository

KeyValue
Git Remotehttps://git.tlab.co.id/tarantula/tarantula-v2/opa-bun/opa-auth.git
Active Branchmain
bash
git clone https://git.tlab.co.id/tarantula/tarantula-v2/opa-bun/opa-auth.git
cd opa-auth

Tech Stack

LayerTechnology
RuntimeBun 1
FrameworkExpress 5 (TypeScript)
CacheRedis DB 1 (ioredis) — token whitelist validation
Policy EngineOPA (Open Policy Agent) — allow/deny decision
Secret ManagementInfisical SDK v5
LoggingMorgan (HTTP request log)
Buildbun build --compile → single binary

Environment Variables

There is no .env file — all variables are injected as container env vars. Only the Infisical bootstrap variables need to be set; the rest are pulled from Infisical at startup.

Bootstrap (container env vars)

VariableDefaultDescription
NODE_PORT3000Express server port
INFISICAL_SITE_URLhttps://app.infisical.comInfisical server URL
INFISICAL_CLIENT_IDUniversal Auth Client ID
INFISICAL_CLIENT_SECRETUniversal Auth Client Secret
INFISICAL_ENVIRONMENTTarget environment (dev / staging / prod)
INFISICAL_PROJECT_IDInfisical project ID
INFISICAL_PATHSecrets path in Infisical

Secrets via Infisical

VariableDescription
URL_OPAOPA server base URL; used as {URL_OPA}/v1/data/verify_access
REDIS_HOSTRedis/Dragonfly host for token whitelist validation
REDIS_PORTRedis port (default 6379)

Endpoints

MethodPathDescription
GET/Main forward-auth handler; called by Traefik on every request
GET/healthHealth check → { status: "UP" }

Authentication Flow

Logic Detail

  1. Header parsing — Traefik sends these headers:
    • x-forwarded-method → the original request's HTTP method
    • x-forwarded-uri → the original request URI (including query string)
    • x-forwarded-prefix → routing prefix, used to extract the service name (/api/tarantula/...tarantula)
    • Authorization → Bearer JWT token
  2. Path construction — The path sent to OPA is built as /{service}{cleanPath} (without the query string).
  3. Redis check — The token is checked against Redis DB 1 with key whitelist_access_token:{token}. This check is skipped for paths containing /notifications.
  4. OPA decision — OPA verifies the JWT, checks policy (see OPA Config Technical), and returns allow, user_roles, user_is_granted, user_cases, and token.payload (decoded JWT: sub, username).
  5. Response headers — If allowed, opa-auth sets headers that downstream services read to know the user's identity without decoding the JWT again.
  6. Login/logout/OPTIONS bypass — If cleanPath is exactly /auth/login or /auth/logout, or the request method is OPTIONS (CORS preflight), and OPA allows the request (allow === true), opa-auth replies with an empty 200 OK without setting the X-Auth-User-* headers — these endpoints don't need the user's identity forwarded downstream.
  7. Fallback error — Any exception not explicitly handled (e.g. OPA/Redis unreachable, timeout, unexpected response shape) is caught by the handler's catch block and always answered as 401 Your token is expired — not 500 — so infrastructure errors on opa-auth's side are indistinguishable from an expired token on the client side.

Response Headers Set

HeaderContentExample
X-Auth-User-RolesJSON array of roles["admin"]
X-Auth-User-NameUsername from the JWT payload"john_doe"
X-Auth-User-GrantArray of allowed resources["/workspace/read"]
X-Auth-User-CasesArray of special cases[]
X-Auth-User-IDUser UUID from the JWT sub"abc-123"

Build & Run

bash
# Install dependencies
bun install

# Run development
bun run index.ts

# Build a single binary (used in Docker)
bun build --compile --minify --sourcemap --target=bun-linux-x64 ./index.ts --outfile opa-auth

# Run the binary
./opa-auth

Docker

bash
docker build -t opa-auth .
docker run -p 3000:3000 \
  -e INFISICAL_SITE_URL=http://... \
  -e INFISICAL_CLIENT_ID=... \
  -e INFISICAL_CLIENT_SECRET=... \
  -e INFISICAL_ENVIRONMENT=dev \
  -e INFISICAL_PROJECT_ID=... \
  -e INFISICAL_PATH=... \
  opa-auth