104 lines
4.7 KiB
Markdown
104 lines
4.7 KiB
Markdown
# TASK METADATA
|
|
|
|
- **Target Files:** `server/auth-session.ts`, `server/routes/sessions.ts`,
|
|
`server/routes/events.ts`, `server/main.ts`, `ui/mod.ts`,
|
|
`server/main.test.ts`
|
|
- **Core Objective:** Implement strict Zero-Trust scope enforcement and
|
|
permission guards across all Auth-Yes internal API routes and SSR UI pages to
|
|
ensure delegated and read-only sessions cannot escalate privileges, perform
|
|
unauthorized mutations, or access admin interfaces.
|
|
- **Dependencies:** None
|
|
- **Additional Important Notes:** Delegated sessions (`is_agent = true`) must
|
|
hold strictly the intersection of user permissions and assigned
|
|
`custom_scopes`. Self-revocation of a delegated session is permitted, but
|
|
revoking other sessions is prohibited without `write:sessions` or `*`.
|
|
|
|
---
|
|
|
|
## Architectural Considerations & Risks
|
|
|
|
- **Risks:**
|
|
- **Backward-Compatibility with Cached Sessions:** Existing delegated sessions
|
|
in the Valkey cache might lack explicit scopes if they were created before
|
|
this strict enforcement. The middleware must default to a safe deny state
|
|
for unknown or missing scopes but handle legacy `is_agent = true` sessions
|
|
gracefully by denying mutating actions unless explicitly permitted.
|
|
- **Self-Revocation Edge Case:** The guard logic must correctly distinguish
|
|
between a session revoking itself (which is always allowed for clean
|
|
logouts) and a session attempting to revoke a sibling or parent session.
|
|
- **Global Admin Resolution:** The `isGlobalAdmin` check relies on database
|
|
queries or cache. Ensure that the new middleware does not introduce
|
|
significant latency by repeatedly querying PostgreSQL on every protected
|
|
route. Use Valkey caching where possible.
|
|
|
|
- **Alternatives:**
|
|
- Instead of injecting `customScopes` into the Valkey cache session payload,
|
|
we could perform a database lookup on every privileged API request to
|
|
determine exact grants. However, this violates the Auth-Yes architectural
|
|
principle of fast-path caching at the edge and would significantly degrade
|
|
performance. The chosen approach of checking scopes from the cached
|
|
`AuthenticatedUser` object is highly modular and performant.
|
|
|
|
## Proposed Implementation
|
|
|
|
### 1. Scope Guard Helpers (`server/auth-session.ts`)
|
|
|
|
Implement reusable composable functions to evaluate the current user's
|
|
capabilities.
|
|
|
|
- `hasScope(auth: AuthenticatedUser, requiredScope: string): boolean`: Evaluates
|
|
if `auth.customScopes` contains `*` or the `requiredScope`. If
|
|
`!auth.isAgent`, returns `true` (primary sessions have all capabilities).
|
|
- Create Hono middleware helpers:
|
|
- `requirePrimarySession()`: Denies access with `403 Forbidden` if
|
|
`auth.isAgent === true`.
|
|
- `requireScope(scope: string)`: Denies access if `hasScope(auth, scope)`
|
|
returns false.
|
|
- `requireAdmin(auth: AuthenticatedUser)`: Uses `isGlobalAdmin(auth.userId)`
|
|
and verifies `!auth.isAgent` OR `auth.customScopes.includes("*")`.
|
|
|
|
### 2. Guard Internal API Routes
|
|
|
|
Apply the newly created guards to critical endpoints.
|
|
|
|
- **`server/routes/sessions.ts`**
|
|
- `DELETE /api/sessions/:id`: Allow if target `id === auth.sessionId`.
|
|
Otherwise, require `write:sessions` scope.
|
|
- `PUT /api/sessions/:id/scopes`: Require `admin` or `*` scope, OR
|
|
`write:sessions`.
|
|
- `POST /api/sessions/:id/extend`: Require `write:sessions` or `*`.
|
|
- `POST /api/sessions/delegate`: Prevent privilege escalation. Require `admin`
|
|
or `*` scope to delegate an `admin` mode session. If creating a `read_only`
|
|
or `operator` session, require the delegator to have at least those
|
|
equivalent scopes.
|
|
|
|
- **`server/routes/events.ts`**
|
|
- `POST /api/events`: Require `write:events` or `*`.
|
|
- `POST /api/events/:id/end`: Require `write:events` or `*`.
|
|
- `POST /api/events/:id/extend`: Require `write:events` or `*`.
|
|
|
|
- **`server/main.ts`**
|
|
- Apply `requirePrimarySession()` or `requireAdmin()` to `/api/admin/*` and
|
|
`/api/passkeys/*` endpoints.
|
|
|
|
### 3. SSR UI Gates (`ui/mod.ts`)
|
|
|
|
Guard the user interfaces against unauthorized delegated sessions.
|
|
|
|
- `/admin/*`: Verify
|
|
`isAdmin && (!auth.isAgent || auth.customScopes.includes("*"))`. Redirect
|
|
unauthorized sessions to `/dashboard` or return a `403 Forbidden` page.
|
|
- `/dashboard/passkeys`: Verify `!auth.isAgent`. Delegated sessions cannot
|
|
manage passkeys.
|
|
|
|
### 4. Testing (`server/main.test.ts`)
|
|
|
|
- Write tests that create a delegated session with only `read_only` scopes.
|
|
- Assert that this session gets a `403 Forbidden` when attempting to call
|
|
`POST /api/sessions/delegate`, `DELETE /api/sessions/:other_id`, and
|
|
`POST /api/events`.
|
|
- Assert that the delegated session can successfully call
|
|
`DELETE /api/sessions/:own_id`.
|
|
- Write tests verifying that a delegated session attempting to access
|
|
`/admin/users` is redirected/blocked.
|