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

90 lines
4.5 KiB
Markdown

# 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).