List & Relation Authorization
Relevant source files
The following files were used as context for generating this wiki page:
List & Relation Authorization provides declarative resource protection for Prisma collections and model associations. It enforces security constraints by generating Prisma where clauses and select objects based on @Auth annotations defined in the Prisma schema. This system prevents data leakage in GraphQL environments where relations are automatically exposed by tools like Pal.js or nexus-prisma.
Sources: README.md:1-20, src/makeListConstraintMiddleware.ts:7-25
Architecture and Data Flow
The authorization system integrates with the GraphQL execution pipeline as a middleware. It intercepts resolver calls, parses authorization requirements from model descriptions, and injects specific query constraints into the Prisma execution context via a withAuth helper function.
The diagram shows the flow from a GraphQL request through the middleware to the final authorized Prisma query.
Sources: src/makeListConstraintMiddleware.ts:28-56, src/index.ts:39-43
List Constraint Middleware
The makeListConstraintMiddleware function creates a GraphQL middleware that manages authorization for collection queries and nested relations. It instantiates a PrismaSelect object to handle field selection and overlays security constraints.
Core Functions
- withAuth: A higher-order function injected into the context. It merges user-provided query arguments with security-mandated
whereandselectclauses. - resolve: The middleware calls the next resolver in the chain, passing an enhanced context containing the authorization utilities.
Sources: src/makeListConstraintMiddleware.ts:28-76, src/index.ts:34-36
Selection Logic
The middleware determines the correct authorization constraints based on the resolver's return type and provided pathing:
| Scenario | Logic Applied |
|---|---|
| Root List Query | Applies constraints for the root return type. |
| Nested Relation | Extracts select and where for a specific path (e.g., path.toUser). |
| Arbitrary Query (e.g., count) | Generates only where constraints for a specified entity type. |
Sources: src/makeListConstraintMiddleware.ts:46-62, README.md:92-120
Query Constraint Generation
The system generates Prisma-compatible filters using the getListWhereConstrains utility. It maps roles defined in @Auth tags to queryConstraint functions provided in the global or model-specific configuration.
The sequence shows how multiple role constraints are combined into a single Prisma AND/OR filter block.
Sources: src/getListWhereConstrains.ts:33-75, src/descriptionToRoles.ts:16-25
Logical Short-circuiting
To ensure safety, the system uses specific conditions for role matches that return boolean values:
- alwaysTrueCondition: Used when a role is granted without specific field filters. It uses an ID comparison that is logically always true:
{ id: { not: { equals: 'ffff...' } } }. - alwaysFalseCondition: Used when no roles are granted or a role explicitly denies access. It uses an ID comparison that is logically always false:
{ id: { equals: 'ffff...' } }.
Sources: src/getListWhereConstrains.ts:7-27, src/getListWhereConstrains.ts:71-73
Prisma Selection and Field Dependencies
The PrismaSelect class (forked and modified) handles the transformation of GraphQL info into Prisma select statements while injecting security requirements.
Matcher Dependencies
If a role's matcher function requires specific fields to be present in the record (e.g., userId to check ownership), these are defined in matcherDependenciesSelect. The PrismaSelect class merges these required fields into the final query to ensure the data is available for validation.
Sources: src/select.ts:241-260, src/getMatcherDependenciesSelect.ts:11-30
Security Constraints in Selection
The filterBy method in PrismaSelect performs the following security operations:
- Default Fields: Injects fields defined in the
defaultFieldsconfiguration for specific types. - Field-level Auth: Scans field-level
@Authannotations and adds necessary dependency fields. - Relation Filtering: Automatically adds
whereclauses to nested list relations to ensure child collections are also filtered.
Sources: src/select.ts:186-224, src/select.ts:229-238
Configuration Structure
Authorization behavior is controlled via a Configuration object passed to makeAuthorizationMiddlewares.
| Option | Type | Description |
|---|---|---|
globalRoles | Record<string, Role> | Roles applicable across all models. |
rolesPerType | RolesPerType | Model-specific role overrides. |
defaultFields | Record<string, object> | Fields that must always be selected for a type. |
Sources: src/index.ts:13-31, README.md:46-75
Conclusion
List & Relation Authorization secures Prisma-based GraphQL APIs by shifting authorization logic from imperative resolver checks to declarative model-level definitions. By wrapping queries in withAuth, the system ensures that every database fetch for a collection or relation is automatically restricted by the user's roles and permissions, effectively closing security gaps in exposed schemas.
Sources: README.md:7-15, src/makeListConstraintMiddleware.ts:10-25