Introduction & Motivation
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 within GraphQL applications. It shifts authorization logic from imperative resolver-level checks to the Prisma model level by using schema annotations.
The system secures data access by intercepting GraphQL requests and applying constraints based on roles defined in the Prisma schema. It specifically addresses security risks in modern GraphQL platforms that automatically expose schema relations.
Sources: README.md:1-5, package.json:3
The Motivation for Declarative Authorization
Imperative authorization rules present security risks, especially when using tools like Pal.js or nexus-prisma that automatically expose entire database schemas. Writing security logic inside every resolver is repetitive and error-prone. A single overlooked relation, such as Purchase.User.Organization.Users, could expose sensitive data across the entire platform.
By moving authorization to the Prisma model level, developers define access once per type or field. This ensures that every entry point to a specific record type adheres to the same security constraints.
Sources: README.md:7-19
System Architecture
The library operates as a set of GraphQL middlewares that analyze the schema's @Auth annotations. These annotations direct how the system should restrict read operations for users.
Core Middlewares
The system uses three primary middlewares to enforce constraints at different granularities:
| Middleware | Target | Description |
|---|---|---|
| Field Constraint | Individual Fields | Restricts access to specific fields within a model based on user roles. |
| Type Constraint | 1-to-1 Relations | Enforces authorization when accessing single related records. |
| List Constraint | List Queries | Generates Prisma where clauses to filter results in list fields and relations. |
Sources: src/index.ts:38-42, src/makeFieldConstraintMiddleware.ts:18, src/makeTypeConstraintMiddleware.ts:31, src/makeListConstraintMiddleware.ts:25
Request Flow
The following diagram illustrates how the system processes a GraphQL request to secure data.
The system parses @Auth comments, evaluates role matchers, and applies constraints via the withAuth context helper.
Sources: src/makeListConstraintMiddleware.ts:37-56, src/descriptionToRoles.ts:18-24
Declarative Annotations
Authorization rules are defined using triple-slash comments (///) in the schema.prisma file. This syntax supports both model-level and field-level definitions.
Annotation Syntax
Rules are defined for the read operation and can include multiple roles. Roles may also accept arguments passed to the matcher.
/// @Auth(read: [Owner(privileges: [x,y]), Admin])
model Purchase {
id String @id
/// @Auth(read:[Owner])
email String
}
Sources: README.md:23-35, src/generated/parser.ts:327-375
Role Configuration
The system requires a configuration object that defines how roles behave. Roles include matchers for individual records and query constraints for list operations.
Configuration Structure
| Property | Type | Description |
|---|---|---|
globalRoles | Record<string, Role> | Roles applicable across all Prisma models. |
rolesPerType | RolesPerType | Overrides or specific roles for a particular Prisma model. |
defaultFields | Record<string, object> | Defines fields that must be selected whenever a specific field is requested. |
Sources: src/index.ts:16-30, README.md:41-68
Matcher and Constraint Logic
A Role definition consists of three main parts:
- matcher: A function returning a
booleanto restrict access to a single record. - queryConstraint: A function returning a Prisma
whereobject to filter list results. - matcherDependenciesSelect: An object or function defining the database fields required to execute the matcher (e.g., ensuring
userIdis available to check ownership).
Sources: src/index.ts:8-12, src/getMatcherDependenciesSelect.ts:11-30
Implementation via withAuth
To enforce the declarative rules, developers wrap Prisma queries with the context.withAuth method. This method merges the user's manual query parameters with the security constraints generated by the List Constraint Middleware.
The PrismaSelect class (forked from prisma-tools) handles the conversion of GraphQL info into Prisma select and where objects, ensuring that all required security fields are included in the final database call.
Sources: src/select.ts:1-40, src/makeListConstraintMiddleware.ts:40-62
Summary
@joindeed/prisma-auth secures GraphQL applications by treating the Prisma schema as the source of truth for authorization. By replacing imperative checks with declarative annotations and automated middlewares, it reduces the risk of data leaks through complex GraphQL relations and simplifies the maintenance of security policies.
Sources: README.md:1-15, src/index.ts:38-42