auth-yes/tasks/new/2026-0824.01.jul.feat.auth-api.traefik-ingress-control-2105.md

7.1 KiB

TASK METADATA

  • Target Files: server/db.ts, server/auth-session.ts, server/main.ts, server/main.test.ts, ui/components/AdminAppsPage.tsx, ui/mod.ts, infra/setup.ts, docs/TIER1_INGRESS_SPEC.md
  • Core Objective: Implement Tier 1 Universal Global Edge Ingress Protection on Traefik with dynamic Valkey-cached bypass matrix, self-exemption, CIDR allowlisting, unregistered app SSR fallback, and ephemeral guest sandboxes.
  • Dependencies: PostgreSQL apps schema, Valkey 8 cache, docs/FORWARDAUTH_REDIRECT_SPEC.md.
  • Additional Important Notes: Must guarantee <30\mu s edge lookups via Valkey L1/L2 caching and prevent infinite redirect deadlocks on auth-api and ACME challenge routers.

1. TASK METADATA

(Defined in the header block above)

2. Architectural Considerations & Risks

  • Risks & Vulnerabilities:

    • Recursive Deadlocks (Self-Exemption): Applying ForwardAuth globally at entryPoints.websecure.http.middlewares creates an infinite loop if auth-api intercepts its own login/verification endpoints. auth.atyg.org and /.well-known/acme-challenge/* must be explicitly exempt.
    • Edge Latency Regressions: Querying PostgreSQL on every incoming ForwardAuth request for dynamic bypass rules would bottleneck cluster throughput. All is_public, bypass_paths, and allowed_cidrs fields must be cached inside the Valkey auth:app_by_host:<host> key.
    • ReDoS in Path Matching: Evaluating wildcard bypasses using unconstrained regular expressions could allow malicious paths to trigger catastrophic backtracking. Path matching must use fast, deterministic prefix and glob matching.
    • Open Redirects on Unregistered Apps: When routing unregistered domains to SSR error views, destination parameters must be strictly validated against approved hosts to prevent phishing vectors.
  • Alternatives & Architecture Decisions:

    • Valkey App Caching over Traefik HTTP Dynamic Provider: Evaluating dynamic bypass paths inside auth-api via Valkey caches eliminates Traefik config file churn, provider polling overhead, and proxy reload downtime.
    • Defense-in-Depth Universal Bypasses:
      • Traefik Level: /.well-known/acme-challenge/* and global /healthz bypass ForwardAuth entirely at the edge router level to avoid certificate renewal failures during container restarts.
      • Auth-API Level: Application-specific paths (/api/public/*, raw/branch/main/sdk/*, /webhooks/*) and CIDR subnets are evaluated in auth-api with zero network overhead.
    • Dual-Response Protocol Compliance: Unregistered domains receive an HTTP 302 Redirect to https://auth.atyg.org/errors/unregistered?host=... for browsers (Accept: text/html) and a clean HTTP 403 Forbidden ({"error": "Application not registered"}) for APIs/cURL.
    • Ephemeral Guest Sandboxes (Use Case 11): Ephemeral sessions are held in Valkey with account_status: "guest" and promoted in-flight to full users upon passkey enrollment without losing UUID or state.

3. Proposed Implementation

Phase 1: Database Schema & Valkey Caching Upgrades

  1. Database Migration (server/db.ts):
    • Add idempotent migrations inside initDb():
      ALTER TABLE apps ADD COLUMN IF NOT EXISTS is_public BOOLEAN DEFAULT FALSE;
      ALTER TABLE apps ADD COLUMN IF NOT EXISTS bypass_paths TEXT[] DEFAULT '{}';
      ALTER TABLE apps ADD COLUMN IF NOT EXISTS allowed_cidrs TEXT[] DEFAULT '{}';
      
  2. Valkey App Cache Hydration (server/auth-session.ts):
    • Update getAppByHost(host) to select id, name, domain, is_public, bypass_paths, allowed_cidrs.
    • Cache the complete application metadata in auth:app_by_host:<host> with 1-hour TTL.

Phase 2: Dynamic Bypass Evaluator & ForwardAuth Hardening

  1. Path & CIDR Matcher Helper (server/auth-session.ts):
    • Implement isPathBypassed(requestPath: string, bypassPaths: string[]): boolean using deterministic prefix matching (path.startsWith(...) and /* wildcard support).
    • Implement isIpAllowed(clientIp: string, allowedCidrs: string[]): boolean for subnet validation.
  2. Update GET /api/forward-auth (server/main.ts):
    • Step 1: Unregistered App Check: If appRecord is null:
      • If Accept: text/html: Return 302 Redirect to https://${loginDomain}/errors/unregistered?host=${encodeURIComponent(host)}.
      • Else: Return 403 Forbidden ({"error": "Application not registered"}).
    • Step 2: Dynamic Bypass Check:
      • If appRecord.is_public === true, or isPathBypassed(uri, appRecord.bypass_paths), or isIpAllowed(clientIp, appRecord.allowed_cidrs):
        • Immediately return 200 OK (bypassing session checks).
    • Step 3: RFC 9421 & Session Checks: Proceed with existing HTTP Signatures and WebAuthn session validation.

Phase 3: Unregistered Application SSR Error Page & Admin UI

  1. Create SSR Error View (ui/components/UnregisteredAppPage.tsx):
    • Pure Hono SSR JSX page explaining the domain is not registered in the Auth-Yes catalog, with a direct 1-click button for Global Admins: "Register Application in IAM Console".
  2. Update Route in ui/mod.ts:
    • Mount GET /errors/unregistered rendering UnregisteredAppPage.
  3. Management Console Updates (ui/components/AdminAppsPage.tsx & server/main.ts):
    • Expose is_public, bypass_paths, and allowed_cidrs in POST /api/admin/apps and PUT /api/admin/apps/:id.
    • Add toggle switches and input fields to the Admin Web UI.

Phase 4: Ephemeral Guest Sandboxes (Use Case 11)

  1. Guest Endpoint (server/main.ts):
    • POST /api/guests/sandbox: Generates a guest sessionId, stores { uuid: guestUuid, username: "guest-<id>", account_status: "guest" } in Valkey with 2-hour TTL, and returns the session cookie.
  2. In-Flight Passkey Promotion (server/main.ts):
    • Update /api/register/verify to detect upgrade_session parameter, creating the PostgreSQL user with the existing guestUuid and changing status to active.

Phase 5: Documentation & Traefik Compose Guides

  1. Author docs/TIER1_INGRESS_SPEC.md:
    • Document static/dynamic Traefik configurations for entryPoints.websecure.http.middlewares=auth-forward@docker with router-level self-exemption overrides for auth.atyg.org.

4. Testing & Verification Matrix

  • Unit Tests (server/main.test.ts):
    • Verify GET /api/forward-auth returns 200 OK when appRecord.is_public === true.
    • Verify GET /api/forward-auth returns 200 OK for paths matching bypass_paths (e.g. /api/public/ping).
    • Verify GET /api/forward-auth returns 302 Redirect to /errors/unregistered for unauthenticated browser requests to unregistered domains.
    • Verify GET /api/forward-auth returns 403 Forbidden for API requests to unregistered domains.
    • Verify guest session generation and in-flight passkey promotion.
  • Quality Gates:
    • deno fmt, deno task lint, deno task check, deno task test (100% pass).