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

4.6 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 sessions minting needs to 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 Leakage: The getDashboardApps query modification must be robust. If the UNION query is not correctly structured, it could leak visibility of unregistered or unauthorized apps to guests.
    • Audit Logging Integrity: Failure to capture the audit events properly during the join phase would break SIEM visibility. Using the non-blocking auditWrapper.auditLog is required to avoid impacting user request latency while ensuring compliance.
  • 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 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}). 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.join(",")). 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: Update the non-admin SQL query to use a UNION.
    • The first part queries explicitly granted apps via the grants table.
    • The second part parses the provided customScopes array to extract allowed app names (e.g., stripping the app: prefix), querying the apps table for those matching names, and assigning them a pseudo-role such as "Guest (Viewer)" (or extracting the role from the scopes if possible).
  3. UI Integration: In ui/mod.ts, pass auth.customScopes from the resolved session into getDashboardApps.

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

  1. 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)).
  2. 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)) into this endpoint before returning the token.

Phase 4: Unit Testing

  1. ForwardAuth Tests (server/tests/forward_auth.test.ts):
    • Add tests simulating a guest session with an app:{name} scope to ensure a 200 OK response with correctly formatted X-Forwarded-Scopes.
    • Add tests simulating a guest session without the necessary app:{name} scope to ensure a 403 Forbidden response.
  2. Events Tests (server/tests/events.test.ts):
    • Ensure the POST /api/join and GET /join/:slug assertions capture the auditLog spy calls and verify the exact payload (event_seat_claimed action, event.id as resource, correct metadata structure).