Kind: Service
Source: atloria-monorepo/apps/api/src/auth/guards/project-org.guard.ts
ProjectOrgGuard — enforce that the authenticated user's organization OWNS the project named
by :projectId. OrganizationGuard only checks an explicit organizationId in the request,
so a project-scoped route (which carries only a projectId) sails past it — a user from org
A could act on org B's project. This guard closes that hole: it loads the project, 404s when
it does not exist, and 403s unless project.organizationId === user.organizationId.
Fail-closed: an unauthenticated request or a missing project context is rejected, never allowed through.
ProjectOrgGuard is a NestJS authorization guard for project-scoped routes. It loads the project identified by :projectId, returns 404 when the project does not exist, and rejects requests with 403 unless the authenticated user belongs to the project’s owning organization. It fails closed when authentication or project context is missing.
Methods
| Method | Signature | Returns |
|---|---|---|
canActivate | canActivate(context: ExecutionContext) | Promise<boolean> |
Dependencies
PrismaService
Where it refuses work
ProjectOrgGuardstops the work withForbiddenExceptionwhen!user— “User not authenticated”.ProjectOrgGuardstops the work withForbiddenExceptionwhen!projectId— “Project context required”.ProjectOrgGuardstops the work withNotFoundExceptionwhen!project.ProjectOrgGuardstops the work withForbiddenExceptionwhenproject.organizationId !== user.organizationId— “Access denied: this project belongs to another organization”.
Diagram
mermaidsequenceDiagram participant Client participant Guard as ProjectOrgGuard participant ProjectService participant Controller Client->>Guard: Request with :projectId and authenticated user Guard->>Guard: Read user.organizationId and params.projectId alt Missing user, organization, or projectId Guard-->>Client: Reject request else Request context is valid Guard->>ProjectService: Find project by projectId alt Project does not exist ProjectService-->>Guard: null Guard-->>Client: 404 Not Found else Project exists ProjectService-->>Guard: project.organizationId alt Organization IDs do not match Guard-->>Client: 403 Forbidden else Organization IDs match Guard->>Controller: Allow route handler Controller-->>Client: Response end end end
Usage
tsimport { Controller, Delete, Param, UseGuards } from '@nestjs/common';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
import { ProjectOrgGuard } from '../auth/guards/project-org.guard';
import { ProjectsService } from './projects.service';
@Controller('projects')
@UseGuards(JwtAuthGuard, ProjectOrgGuard)
export class ProjectsController {
constructor(private readonly projectsService: ProjectsService) {}
@Delete(':projectId')
async removeProject(@Param('projectId') projectId: string) {
return this.projectsService.remove(projectId);
}
}
ProjectOrgGuard expects the authentication guard to populate request.user, including the authenticated user's organizationId.
AI Coding Instructions
- Apply
ProjectOrgGuardto every route scoped by:projectId, especially mutation endpoints such as update, delete, member management, and project settings. - Ensure an authentication guard runs before this guard and attaches a user with a valid
organizationIdtorequest.user. - Preserve fail-closed behavior: missing authentication, missing organization context, or missing
projectIdmust reject the request rather than allow it. - Keep project lookup behavior consistent: nonexistent projects should produce
404 Not Found, while organization ownership mismatches should produce403 Forbidden. - Do not rely on
OrganizationGuardalone for project routes; project ownership must be resolved from the stored project record.
Relationships
- DEPENDS_ON →
PrismaService
Was this page helpful?