This repository is an optional perimeter gate that sits outside the three-layer separation of concerns (authentication & token issuance / authorization decision / authorization enforcement) of the auth stack.
Reverse proxy that sits between clients and downstream services. Operates in one of two mutually exclusive modes selected at deploy time via auth.mode.
auth.mode is required — must be set explicitly to "validation" or "injection". Omission or a typo causes the proxy to fail to start. Set it via HOCON (auth.mode = "validation") or the AUTH_MODE environment variable.
Validates inbound Authorization: Bearer <token> headers against the provider's introspection endpoint. Requests without a Bearer header are forwarded unchanged (public endpoints remain reachable).
Flow:
- Detects
Authorization: Bearer <token>header (passes through if absent). - Checks in-memory cache keyed by SHA-256 of the token.
- On cache miss, calls provider's
POST /oauth/introspect. - Returns
401ifactive: false; forwards the request ifactive: true.
Benefits over JWT-only local validation:
- Detects revoked tokens immediately.
- Introspection results are cached (default 30s TTL), reducing provider load.
- Downstream services receive pre-validated requests without implementing auth logic.
Translates an inbound session cookie into an outbound Authorization: Bearer token. Realizes the OWASP OAuth 2.0 BCP Token Handler Pattern — browsers never hold access tokens.
Flow:
- Extracts the session cookie named in
auth.injection.sessionCookieName. - If absent, forwards the request unchanged (the service layer decides whether authentication is required).
- On cache hit, injects the cached Bearer and forwards.
- On cache miss, exchanges the cookie for an access token via the provider's
POST /oauth/tokenwithgrant_type=session. Concurrent misses on the same cookie coalesce into a single provider call (single-flight). - Injects
Authorization: Bearer <token>and forwards upstream.
- In-memory, per-instance. Restart and horizontal scale-out produce cold caches. Peak provider load during rollout ≈
instance count × active sessions. - TTL:
min(auth.injection.tokenCache.ttlSeconds, provider.expires_in) - safetyMarginSeconds. Default 60s minus 5s safety margin.
After logout at the provider, the proxy may continue returning cached tokens for up to tokenCache.ttlSeconds (minus the safety margin). To reduce revocation latency, lower ttlSeconds (e.g., 10). Tradeoff: higher provider load.
One proxy instance serves one OAuth scope domain. auth.injection.clientId and auth.injection.scope are fixed at deploy time. Serve multiple scope domains with multiple proxy instances.
The proxy is a transparent augmentation layer. It injects the Bearer but does NOT enforce CSRF. Combine with SameSite=Lax cookies, same-origin deployment, and CSRF protection in the upstream service.
Only the cookie named in auth.injection.sessionCookieName is forwarded to the provider on the session grant call. Other cookies (analytics, CSRF tokens, third-party) do not reach the provider.
Active access tokens reside in process memory. An attacker with read access to proxy process memory can extract all cached tokens. Standard host-security practices apply (container isolation, minimal image, no unnecessary ptrace capabilities).
pnpm install
pnpm run build
pnpm run startpnpm run debug # tsx watch modemake docker # Build runtime imageShared environment variables:
| Environment Variable | Description |
|---|---|
AUTH_MODE |
Required. "validation" or "injection". |
HTTP_PORT |
HTTP listen port (default: 80). |
HTTP_HOSTNAME |
HTTP listen hostname (default: 0.0.0.0). |
HTTP_PATH_PREFIX |
Path prefix for proxy routes (default: /). |
HTTP_BODY_LIMIT_SIZE |
Request body size limit (default: 10mb). |
UPSTREAM_BASEURL |
Upstream service base URL. |
CORS_ORIGIN_PATTERN |
CORS origin regex pattern (optional). |
Validation mode:
| Environment Variable | Description |
|---|---|
CLIENT_ID |
Client ID for introspection auth (optional; must be set together with CLIENT_SECRET). |
CLIENT_SECRET |
Client secret for introspection auth (optional; must be set together with CLIENT_ID). |
INTROSPECT_URL |
Introspection endpoint URL. |
INTROSPECT_CACHE_TTL_SEC |
Cache TTL in seconds (default: 30). |
INTROSPECT_CACHE_MAX_ENTRIES |
Cache max entries (default: 10000). |
INTROSPECT_TIMEOUT_MS |
Introspection HTTP timeout (default: 5000). |
Injection mode:
| Environment Variable | Description |
|---|---|
INJECTION_PROVIDER_ORIGIN |
Provider origin — scheme://host[:port], no path/query/fragment, no userinfo, http or https only (default: http://localhost:3000). |
INJECTION_CLIENT_ID |
Required. OAuth client_id. |
INJECTION_SCOPE |
Required. OAuth scope string (space-separated). |
INJECTION_SESSION_COOKIE_NAME |
Session cookie name (default: connect.sid). |
INJECTION_TOKEN_CACHE_TTL_SEC |
Token cache TTL in seconds (default: 60). |
INJECTION_TOKEN_CACHE_MAX_ENTRIES |
Token cache max entries (default: 10000). |
INJECTION_TOKEN_CACHE_SAFETY_MARGIN_SEC |
Clock-drift safety margin in seconds (default: 5). |
INJECTION_TIMEOUT_MS |
Provider HTTP timeout (default: 5000). |
- auth.provider — OAuth 2.0 token issuance.
- auth.policy-verifier — No-DSL ABAC policy verifier.
- auth — Architecture docs and cross-component E2E tests.
- protobuf.interceptors — protobuf-option-driven authorization interceptors for gRPC / ConnectRPC.
Apache License 2.0