auth-yes/tasks/new/2026-0826.01.jul.story.events.phase-1-guest-ingress-1400.ph1.md

4.5 KiB

TASK METADATA

  • Target Files: server/routes/auth_forward.ts, ui/db_queries.ts, ui/mod.ts, server/routes/events.ts, server/tests/forward_auth.test.ts, server/tests/events.test.ts
  • Core Objective: Implement Phase 1 of the Event & Session Overhaul: Enable Traefik ForwardAuth guest ingress for scoped event attendees, bridge session custom scopes to the Launchpad UI, and wire audit logging on event seat claims.
  • Dependencies: None.
  • Additional Important Notes: Follow zero-trust default-deny principles for ForwardAuth. Guest session minting must correctly log to the Merkle audit ledger for SIEM visibility.

2. Architectural Considerations & Risks

  • Risks:

    • ForwardAuth Security: Modifying ForwardAuth to accept guest accounts risks inadvertently allowing guests to access applications they are not explicitly scoped for. Strict validation of auth.customScopes against the requested appRecord.name is critical.
    • Launchpad SQL Array Guard: If a user or guest has no app: custom scopes, passing an empty array into an IN () clause causes invalid SQL. The query must use if (appNames.length > 0) or WHERE name = ANY(${appNames}::text[]).
    • Audit Logging Integrity: Failure to capture the audit events properly during the join phase would break SIEM visibility. Using the non-blocking auditWrapper.auditLog with getClientIp(c) is required.
  • Alternatives:

    • We considered a bypass in ui/db_queries.ts where guests completely skip the grants JOIN. However, using a UNION is structurally superior as it seamlessly supports both pure guests and regular active users who might hold delegated scoped passes. It provides a unified data retrieval flow.

3. Proposed Implementation

Phase 1: ForwardAuth Guest Ingress (server/routes/auth_forward.ts)

  1. Permit Guest Accounts: Update the account status validation (around line 120) to allow user.account_status === 'guest' in addition to 'active'.
  2. Custom Scopes Validation: If the user is a guest (or relying on custom scopes), verify that auth.customScopes contains the explicit application grant (e.g., app:${appRecord.name} or wildcard *). If not, return a 403 Forbidden.
  3. Header Injection: For guest sessions, compute the scopes to inject into X-Forwarded-Scopes by taking the comma-joined list of auth.customScopes (auth.customScopes.filter(Boolean).join(",") || "viewer"). This propagates the app scope (e.g., app:ed-droid) and role (e.g., viewer) to downstream reverse proxies.

Phase 2: Launchpad App Query (ui/db_queries.ts & ui/mod.ts)

  1. Function Signature Update: Update getDashboardApps(userId: string, isAdmin: boolean, customScopes?: string[]) to accept the session's custom scopes.
  2. UNION Query with Array Guard:
    • Extract app names from customScopes (e.g. scopes starting with app:).
    • If appNames.length > 0, execute a UNION with SELECT id, name, description, domain, 'Guest (Viewer)' as role FROM apps WHERE domain IS NOT NULL AND name = ANY(${appNames}::text[]).
    • If appNames.length === 0, return only the primary grants query result.
  3. UI Integration: In ui/mod.ts under /dashboard, pass auth.customScopes from the resolved session into getDashboardApps.

Phase 3: Join Audit Logging (server/routes/events.ts)

  1. Import getClientIp: Import getClientIp from ../middleware.ts.
  2. Audit POST /api/join (Web): Immediately after minting the guest session, invoke: auditWrapper.auditLog(guestUuid, "event_seat_claimed", event.id, { slug: event.slug, name: event.name, seatNumber: event.seats_claimed, method: "web" }, getClientIp(c)).
  3. Audit GET /join/:slug (CLI): Similarly, wire: auditWrapper.auditLog(guestUuid, "event_seat_claimed", event.id, { slug: event.slug, name: event.name, seatNumber: event.seats_claimed, method: "cli" }, getClientIp(c)).

Phase 4: Unit Testing

  1. ForwardAuth Tests (server/tests/forward_auth.test.ts):
    • Add test: Tier 1 & 2: GET /api/forward-auth - Guest session with app scope allowed.
    • Add test: Tier 1 & 2: GET /api/forward-auth - Guest session without app scope rejected (403).
  2. Events Tests (server/tests/events.test.ts):
    • Ensure the POST /api/join and GET /join/:slug assertions verify the auditLog call payload (event_seat_claimed action, event.id as resource, and correct metadata structure).