Merge pull request #27 from mrteye/zero-trust-scope-guards-task-6161145555757642969
sec(tasks): draft task for zero-trust scope guards
This commit is contained in:
commit
e4bc07513f
@ -0,0 +1,56 @@
|
|||||||
|
# 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.
|
||||||
Loading…
x
Reference in New Issue
Block a user