Integration with Deed Services
Relevant source files
The following files were used as context for generating this wiki page:
Integration with Deed services relies on the @joindeed/prisma-auth middleware to enforce declarative authorization at the Prisma model level. This system secures the data layer across Deed's ecosystem (including Marketplace, Hive, and Honey) by ensuring that GraphQL resolvers do not inadvertently expose unauthorized records through relations. The middleware operates by parsing @Auth annotations within Prisma schema comments and applying logic to filter lists and validate individual record access.
The integration provides a standardized way for Deed developers to define roles like Owner, Admin, or Organizer and map them to specific data constraints. This shifts authorization logic away from imperative code in resolvers to a centralized, declarative configuration that matches Deed's multi-tenant and role-based access control requirements.
Sources: README.md:1-20, src/index.ts:46-50
Architecture and Middleware Flow
The authorization system integrates into a GraphQL server using three primary middlewares that intercept requests and modify Prisma queries. These middlewares handle field-level access, type-level record validation, and list-level query constraints.
Middleware Execution Order
The makeAuthorizationMiddlewares function initializes the security stack in a specific sequence to ensure comprehensive coverage.
The diagram shows the sequential application of authorization checks before data reaches the requester.
- Field Constraint Middleware: Validates if the requesting user has permission to view specific columns or fields within a model.
- Type Constraint Middleware: Enforces authorization on 1-to-1 relations by checking the specific record returned.
- List Constraint Middleware: Generates
whereclauses to filter list results and handles thewithAuthhelper injection.
Sources: src/index.ts:46-50, src/makeFieldConstraintMiddleware.ts:16-20, src/makeTypeConstraintMiddleware.ts:32-35
Core Integration Components
Authorization Roles and Matchers
Deed services define roles using the Role interface. Each role requires a matcher for individual record validation and a queryConstraint for database-level filtering.
| Component | Description | Logic Type |
|---|---|---|
matcher | Returns a boolean indicating if a user can access a specific record. | Imperative |
queryConstraint | Returns a Prisma where object to filter results at the database level. | Declarative |
matcherDependenciesSelect | Defines fields required from the DB to execute the matcher logic. | Configuration |
Sources: src/index.ts:8-12, README.md:52-65
The withAuth Integration Pattern
To integrate authorization into a resolver, you must wrap Prisma calls with the context.withAuth method. This method merges the user's manual query with automatically generated security constraints.
This flow illustrates how withAuth acts as a gateway between the business logic and the database.
Sources: src/makeListConstraintMiddleware.ts:37-45, README.md:83-89
Data Filtering Logic
List Constraints
For list operations, the system generates a where clause using an AND wrapper containing an OR of all applicable role constraints. If no roles match or are defined, it defaults to an alwaysFalseCondition to prevent data leakage.
- Always True:
id: { not: { equals: 'ffffffff...' } } - Always False:
id: { equals: 'ffffffff...' }
Sources: src/getListWhereConstrains.ts:11-30, src/getListWhereConstrains.ts:60-70
Field-Level Security
When a field is annotated with @Auth, the makeFieldConstraintMiddleware checks the user's roles against the readRoles extracted from the Prisma schema.
- Extraction: Parses
@Auth(read: [...])from the field description. - Validation: Iterates through roles; if
somerole matcher returns true, access is granted. - Result: If unauthorized, it calls
patchResponseto return a safe default (e.g.,null,[], or"").
Sources: src/makeFieldConstraintMiddleware.ts:37-55, src/descriptionToRoles.ts:18-25
Configuration and Setup
Integration requires a Configuration object passed to the middleware factory. This includes globalRoles (applied across all models) and rolesPerType (model-specific overrides).
const config: Configuration = {
globalRoles: {
Admin: {
matcher: (ctx) => ctx.currentUser?.isAdmin,
queryConstraint: (ctx) => ctx.currentUser?.isAdmin || false,
},
},
defaultFields: {
User: { id: true, email: true }
}
};
Sources: src/index.ts:20-33, README.md:46-55
Conclusion
Integration with Deed services via prisma-auth ensures that security is a first-class citizen of the data model. By utilizing the withAuth helper and declarative schema annotations, developers maintain a robust boundary against unauthorized data access while keeping resolvers clean and focused on business logic.
Sources: README.md:12-18, src/makeListConstraintMiddleware.ts:10-25