auth-yes/tasks/complete/2026-0824.03.jul.feat.auth-api.ingress-grant-injection-1200.md
google-labs-jules[bot] 8ff5090ffa feat: Implement Ingress Grant Vector Injection for ForwardAuth
- Add `domain` column to `apps` table.
- Create Valkey caching layers for app resolution by host (`auth:app_by_host:<host>`) and user grants (`auth:grants:<userId>:<appId>`) with PostgreSQL fallback in `server/auth-session.ts`.
- Update `/api/forward-auth` endpoint to resolve `X-Forwarded-Host`, enforce Default-Deny, check RBAC grants, and inject `X-Forwarded-*` scopes.
- Update relevant unit tests to cover missing and invalid scenarios with correct Mock stubs.

Co-authored-by: mrteye <1945243+mrteye@users.noreply.github.com>
2026-08-24 04:23:04 +00:00

3.7 KiB

TASK METADATA

  • Target Files: server/main.ts, server/db.ts, server/auth-session.ts
  • Core Objective: Implement Ingress Grant Vector Injection in /api/forward-auth to resolve X-Forwarded-Host against registered apps, evaluate RBAC grants via Valkey caching, and inject flattened user grant headers into downstream requests.
  • Dependencies: Valkey caching infrastructure, database migration for domain column on apps table.
  • Additional Important Notes: Latency is critical; ForwardAuth must resolve in <30μs using Valkey and must not hit PostgreSQL on every request. Global Admins bypass app-specific grant checks.

Architectural Considerations & Risks

Risks

  • Cache Invalidation: Stale data in Valkey could allow revoked users to retain access or prevent newly granted users from accessing apps. We must ensure that grants and app updates properly invalidate or update their respective Valkey keys.
  • Header Spoofing / Parsing Issues: Ensure the X-Forwarded-* headers are safely injected and override any existing headers from the client to prevent privilege escalation (handled by Traefik, but good to be mindful of).
  • Performance Bottlenecks: While caching resolves database latency, improper cache interactions (e.g., waiting for multiple sequential round-trips to Valkey instead of using pipelining or MGET where appropriate) could still add overhead.

Alternatives Evaluated

  • Direct PostgreSQL queries: Rejected due to the high volume of requests hitting the edge proxy /api/forward-auth.
  • JWT Injection: Instead of flattened headers, injecting a signed JWT was considered. However, flattened headers (X-Forwarded-User-Id, X-Forwarded-Scopes) are native to Traefik ForwardAuth and simpler for downstream SSR hydration without requiring public key distribution to every downstream app.

Proposed Implementation

1. Database Schema Update (server/db.ts)

  • Modify initDb() to add the domain column to the apps table: ALTER TABLE apps ADD COLUMN IF NOT EXISTS domain TEXT;.
  • Ensure application seeding or registry logic accounts for the new domain field if necessary.

2. Caching Strategy Implementation

  • Implement helper functions to fetch from Valkey or fallback to DB and populate Valkey.
  • App Resolution Cache (auth:app_by_host:<host>):
    • Match X-Forwarded-Host against apps.domain.
    • Fallback: Match apps.name against the first subdomain segment.
    • Cache the resulting app_id and name in Valkey as JSON.
  • Grants Cache (auth:grants:<userId>:<appId>):
    • Cache the user's roles for the specific app.

3. ForwardAuth Endpoint Logic (server/main.ts)

  • Update GET /api/forward-auth to:
    1. Extract X-Forwarded-Host from the request.
    2. Resolve the target application via the App Resolution Cache (Valkey). If no app matches, decide whether to allow pass-through without scopes or strictly deny (typically 403 for protected routes without an app, or allow pass-through if unmapped). Assuming strict mapping per requirements, return 403 or 404 if not found.
    3. Validate the user session (existing logic).
    4. Check if the user is a Global Admin (isGlobalAdmin). If yes, authorize automatically and set X-Forwarded-Scopes: admin.
    5. For regular users, resolve their scopes for the target app via the Grants Cache (Valkey).
    6. Enforce Default-Deny: If no scopes are found, return 403 Forbidden.
    7. On success, inject headers:
      • X-Forwarded-User-Id: <user.id>
      • X-Forwarded-User-Name: <user.username>
      • X-Forwarded-Scopes: <comma-separated list of roles>
      • X-Forwarded-App-Id: <app_id>
      • Return HTTP 200 OK.