4.7 KiB
4.7 KiB
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 assignedcustom_scopes. Self-revocation of a delegated session is permitted, but revoking other sessions is prohibited withoutwrite:sessionsor*.
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 = truesessions 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
isGlobalAdmincheck 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.
- 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
-
Alternatives:
- Instead of injecting
customScopesinto 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 cachedAuthenticatedUserobject is highly modular and performant.
- Instead of injecting
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 ifauth.customScopescontains*or therequiredScope. If!auth.isAgent, returnstrue(primary sessions have all capabilities).- Create Hono middleware helpers:
requirePrimarySession(): Denies access with403 Forbiddenifauth.isAgent === true.requireScope(scope: string): Denies access ifhasScope(auth, scope)returns false.requireAdmin(auth: AuthenticatedUser): UsesisGlobalAdmin(auth.userId)and verifies!auth.isAgentORauth.customScopes.includes("*").
2. Guard Internal API Routes
Apply the newly created guards to critical endpoints.
-
server/routes/sessions.tsDELETE /api/sessions/:id: Allow if targetid === auth.sessionId. Otherwise, requirewrite:sessionsscope.PUT /api/sessions/:id/scopes: Requireadminor*scope, ORwrite:sessions.POST /api/sessions/:id/extend: Requirewrite:sessionsor*.POST /api/sessions/delegate: Prevent privilege escalation. Requireadminor*scope to delegate anadminmode session. If creating aread_onlyoroperatorsession, require the delegator to have at least those equivalent scopes.
-
server/routes/events.tsPOST /api/events: Requirewrite:eventsor*.POST /api/events/:id/end: Requirewrite:eventsor*.POST /api/events/:id/extend: Requirewrite:eventsor*.
-
server/main.ts- Apply
requirePrimarySession()orrequireAdmin()to/api/admin/*and/api/passkeys/*endpoints.
- Apply
3. SSR UI Gates (ui/mod.ts)
Guard the user interfaces against unauthorized delegated sessions.
/admin/*: VerifyisAdmin && (!auth.isAgent || auth.customScopes.includes("*")). Redirect unauthorized sessions to/dashboardor return a403 Forbiddenpage./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_onlyscopes. - Assert that this session gets a
403 Forbiddenwhen attempting to callPOST /api/sessions/delegate,DELETE /api/sessions/:other_id, andPOST /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/usersis redirected/blocked.