High-Level Architecture
Relevant source files
The following files were used as context for generating this wiki page:
@joindeed/prisma-auth provides a declarative authorization layer for Prisma models. It moves authorization logic from the GraphQL resolver level to the Prisma model level by using triple-slash annotations (/// @Auth) in the Prisma schema. The system validates access during the GraphQL execution phase by intercepting requests and modifying Prisma queries.
Sources: README.md:3-9, src/index.ts:40-44
Core Components
The architecture consists of a central configuration, a specialized parser for authorization strings, and three primary GraphQL middlewares that enforce different levels of security constraints.
Middleware Suite
The makeAuthorizationMiddlewares function aggregates three specific middlewares to handle authorization at different granularities.
| Middleware | Scope | Purpose |
|---|---|---|
| Field Constraint | Individual Fields | Restricts access to specific fields based on role matchers. |
| Type Constraint | 1-to-1 Relations | Enforces authorization when resolving single-object relations. |
| List Constraint | Lists and Relations | Injects where and select clauses into Prisma queries for lists. |
Sources: src/index.ts:40-44, src/makeFieldConstraintMiddleware.ts:16-18, src/makeTypeConstraintMiddleware.ts:32-34, src/makeListConstraintMiddleware.ts:25-27
Configuration Structure
The Configuration interface defines how roles and global matchers are mapped to the authorization logic.
export interface Configuration {
rolesPerType?: RolesPerType
globalRoles?: {
[roleName: string]: Role<any, any, any>
}
defaultFields?: {
[key: string]: | { [key: string]: boolean } | ((select: any) => { [key: string]: boolean | undefined })
}
}
Sources: src/index.ts:18-31
Logic Flow and Data Transformation
The system operates by parsing Prisma model descriptions and applying role matchers defined in the configuration.
Authorization Parsing
The descriptionToRoles function utilizes a generated PEG.js parser to extract role names and arguments from the @Auth annotations found in the Prisma schema (DMMF).
List and Relation Filtering
The PrismaSelect class and makeListConstraintMiddleware work together to intercept queries. They inject where clauses generated by getListWhereConstrains to ensure users only see records they are authorized to access.
Field-Level Security
Field-level security happens post-resolution (for simple fields) or during query construction. If a role matcher returns false, the system uses patchResponse to return null, an empty string, or an empty array depending on the GraphQL return type.
Enforcement Logic
- The middleware identifies the roles required by the field description.
- It executes the
matcherfunction for each role. - If no role is granted, it replaces the field value with a safe default.
Sources: src/makeFieldConstraintMiddleware.ts:33-55, src/patchResponse.ts:1-10
Database Query Constraints
For list operations, the system generates mandatory AND conditions to wrap user-provided filters. This prevents users from bypassing security by crafting their own OR queries.
- alwaysTrueCondition: A condition that always evaluates to true (used for Admin access).
- alwaysFalseCondition: A condition that always evaluates to false (used to short-circuit unauthorized requests).
export const alwaysFalseCondition = {
id: {
equals: 'ffffffffffffffffffffffff',
},
}
Sources: src/getListWhereConstrains.ts:9-30
Summary
The high-level architecture of prisma-auth focuses on shifting authorization to the model level through declarative annotations. By using GraphQL middleware to intercept the resolution process, it ensures that select and where clauses are automatically augmented with security constraints before reaching the database, while field-level matchers provide a final layer of data masking.
Sources: README.md:11-20, src/index.ts:40-44