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
| Key | Value |
|---|---|
| Git Remote | https://git.tlab.co.id/tarantula/tarantula-v2/opa-bun/opa-auth.git |
| Active Branch | main |
bash
git clone https://git.tlab.co.id/tarantula/tarantula-v2/opa-bun/opa-auth.git
cd opa-authTech Stack
| Layer | Technology |
|---|---|
| Runtime | Bun 1 |
| Framework | Express 5 (TypeScript) |
| Cache | Redis DB 1 (ioredis) — token whitelist validation |
| Policy Engine | OPA (Open Policy Agent) — allow/deny decision |
| Secret Management | Infisical SDK v5 |
| Logging | Morgan (HTTP request log) |
| Build | bun 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)
| Variable | Default | Description |
|---|---|---|
NODE_PORT | 3000 | Express server port |
INFISICAL_SITE_URL | https://app.infisical.com | Infisical server URL |
INFISICAL_CLIENT_ID | — | Universal Auth Client ID |
INFISICAL_CLIENT_SECRET | — | Universal Auth Client Secret |
INFISICAL_ENVIRONMENT | — | Target environment (dev / staging / prod) |
INFISICAL_PROJECT_ID | — | Infisical project ID |
INFISICAL_PATH | — | Secrets path in Infisical |
Secrets via Infisical
| Variable | Description |
|---|---|
URL_OPA | OPA server base URL; used as {URL_OPA}/v1/data/verify_access |
REDIS_HOST | Redis/Dragonfly host for token whitelist validation |
REDIS_PORT | Redis port (default 6379) |
Endpoints
| Method | Path | Description |
|---|---|---|
GET | / | Main forward-auth handler; called by Traefik on every request |
GET | /health | Health check → { status: "UP" } |
Authentication Flow
Logic Detail
- Header parsing — Traefik sends these headers:
x-forwarded-method→ the original request's HTTP methodx-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
- Path construction — The path sent to OPA is built as
/{service}{cleanPath}(without the query string). - Redis check — The token is checked against Redis DB 1 with key
whitelist_access_token:{token}. This check is skipped for paths containing/notifications. - OPA decision — OPA verifies the JWT, checks policy (see OPA Config Technical), and returns
allow,user_roles,user_is_granted,user_cases, andtoken.payload(decoded JWT:sub,username). - Response headers — If allowed, opa-auth sets headers that downstream services read to know the user's identity without decoding the JWT again.
- Login/logout/OPTIONS bypass — If
cleanPathis exactly/auth/loginor/auth/logout, or the request method isOPTIONS(CORS preflight), and OPA allows the request (allow === true), opa-auth replies with an empty200 OKwithout setting theX-Auth-User-*headers — these endpoints don't need the user's identity forwarded downstream. - Fallback error — Any exception not explicitly handled (e.g. OPA/Redis unreachable, timeout, unexpected response shape) is caught by the handler's
catchblock and always answered as401 Your token is expired— not500— so infrastructure errors on opa-auth's side are indistinguishable from an expired token on the client side.
Response Headers Set
| Header | Content | Example |
|---|---|---|
X-Auth-User-Roles | JSON array of roles | ["admin"] |
X-Auth-User-Name | Username from the JWT payload | "john_doe" |
X-Auth-User-Grant | Array of allowed resources | ["/workspace/read"] |
X-Auth-User-Cases | Array of special cases | [] |
X-Auth-User-ID | User 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-authDocker
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