Create RLS context interceptor (fix SEC-API-4) #351

Closed
opened 2026-02-07 17:11:32 +00:00 by jason.woltje · 0 comments
Owner

Phase 1b - Security Foundations

Problem

RLS policies exist on 23 tables (and will be added to auth tables in #350), but the application NEVER sets the RLS session context. The utility functions in apps/api/src/lib/db-context.ts (withWorkspaceContext, setCurrentUser, etc.) are fully implemented but never called by any service. This is SEC-API-4 from the security review.

Without setting SET LOCAL app.current_user_id, the current_user_id() PostgreSQL function returns NULL, which means all RLS policies silently deny access (or allow it, depending on policy design).

Requirements

  1. Create an RlsContextInterceptor registered as APP_INTERCEPTOR
  2. After AuthGuard authenticates the user, the interceptor sets SET LOCAL app.current_user_id within a Prisma transaction
  3. After WorkspaceGuard resolves the workspace, set SET LOCAL app.current_workspace_id
  4. Use AsyncLocalStorage to propagate the transaction-scoped Prisma client to downstream services
  5. Services should transparently get the RLS-scoped client via a provider function

Implementation Notes

  • Register as APP_INTERCEPTOR in AppModule (after TelemetryInterceptor)
  • Use AsyncLocalStorage pattern: create rls-context.provider.ts with getRlsClient() and runWithRlsClient()
  • Services call getRlsClient() ?? this.prisma to get the appropriate client
  • Interceptor must handle unauthenticated routes gracefully (no RLS context set)
  • Reference: apps/api/src/lib/db-context.ts has the SET LOCAL patterns already implemented

Files

  • apps/api/src/common/interceptors/rls-context.interceptor.ts (new)
  • apps/api/src/common/interceptors/rls-context.interceptor.spec.ts (new)
  • apps/api/src/prisma/rls-context.provider.ts (new)
  • apps/api/src/prisma/rls-context.provider.spec.ts (new)
  • apps/api/src/app.module.ts (modify - register interceptor)

Acceptance Criteria

  • RLS context interceptor sets user and workspace session variables
  • AsyncLocalStorage propagates transaction client to services
  • Unauthenticated routes work without RLS context
  • Unit tests for interceptor behavior
  • Integration test: service queries are scoped by RLS when context is set

Dependencies

  • Blocks: #350 (RLS auth table policies depend on context being set)
  • Blocks: Account token encryption (needs working RLS context)

Refs #346

## Phase 1b - Security Foundations ### Problem RLS policies exist on 23 tables (and will be added to auth tables in #350), but the application NEVER sets the RLS session context. The utility functions in apps/api/src/lib/db-context.ts (withWorkspaceContext, setCurrentUser, etc.) are fully implemented but never called by any service. This is SEC-API-4 from the security review. Without setting SET LOCAL app.current_user_id, the current_user_id() PostgreSQL function returns NULL, which means all RLS policies silently deny access (or allow it, depending on policy design). ### Requirements 1. Create an RlsContextInterceptor registered as APP_INTERCEPTOR 2. After AuthGuard authenticates the user, the interceptor sets SET LOCAL app.current_user_id within a Prisma transaction 3. After WorkspaceGuard resolves the workspace, set SET LOCAL app.current_workspace_id 4. Use AsyncLocalStorage to propagate the transaction-scoped Prisma client to downstream services 5. Services should transparently get the RLS-scoped client via a provider function ### Implementation Notes - Register as APP_INTERCEPTOR in AppModule (after TelemetryInterceptor) - Use AsyncLocalStorage pattern: create rls-context.provider.ts with getRlsClient() and runWithRlsClient() - Services call getRlsClient() ?? this.prisma to get the appropriate client - Interceptor must handle unauthenticated routes gracefully (no RLS context set) - Reference: apps/api/src/lib/db-context.ts has the SET LOCAL patterns already implemented ### Files - apps/api/src/common/interceptors/rls-context.interceptor.ts (new) - apps/api/src/common/interceptors/rls-context.interceptor.spec.ts (new) - apps/api/src/prisma/rls-context.provider.ts (new) - apps/api/src/prisma/rls-context.provider.spec.ts (new) - apps/api/src/app.module.ts (modify - register interceptor) ### Acceptance Criteria - [ ] RLS context interceptor sets user and workspace session variables - [ ] AsyncLocalStorage propagates transaction client to services - [ ] Unauthenticated routes work without RLS context - [ ] Unit tests for interceptor behavior - [ ] Integration test: service queries are scoped by RLS when context is set ### Dependencies - Blocks: #350 (RLS auth table policies depend on context being set) - Blocks: Account token encryption (needs working RLS context) Refs #346
jason.woltje added this to the M9-CredentialSecurity (0.0.9) milestone 2026-02-07 17:11:32 +00:00
jason.woltje added the securityp0apiapi labels 2026-02-07 17:11:32 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mosaic/stack#351