Common Pitfalls
Relevant source files
The following files were used as context for generating this wiki page:
This page identifies frequent errors and architectural misunderstandings when implementing @joindeed/prisma-auth. Proper implementation requires a declarative approach at the model level rather than an imperative approach at the resolver level.
The system relies on three primary middlewares that intercept field access, type relations, and list queries. Security vulnerabilities typically arise from missing query wrappers, incomplete role configurations, or insufficient data selection for role matchers.
Missing Query Constraints
The library generates security constraints but does not apply them automatically to every Prisma call. You must wrap every Prisma query with context.withAuth to overlay the generated where and select clauses.
Manual Application Requirement
If you omit withAuth in a resolver, Prisma executes the query without any authorization filters. This allows users to access records that should be restricted.
The diagram shows the necessity of the withAuth wrapper for securing the data flow.
Sources: README.md:76-82, src/makeListConstraintMiddleware.ts:18-22
Path and Type Mismatches
When a resolver returns a composite non-Prisma type, withAuth cannot automatically deduce the return entity. You must provide the exact path and type name to ensure the correct constraints apply.
Sources: README.md:86-95, src/makeListConstraintMiddleware.ts:46-52
Role Configuration Errors
The authorization engine throws runtime errors if it encounters roles in Prisma comments that lack definitions in the configuration object.
Missing Role Matchers
The system expects every role named in a /// @Auth comment to exist in either globalRoles or rolesPerType. If the role matcher is missing, the middleware fails to resolve the field or type.
Sources: src/makeFieldConstraintMiddleware.ts:50-53, src/makeTypeConstraintMiddleware.ts:60-63
Dependency Selection Omission
Role matchers often require specific fields from the record to evaluate access (e.g., an ownerId). If the client does not request these fields, the record object passed to the matcher will have undefined values, causing the matcher to return incorrect results.
Use matcherDependenciesSelect to force Prisma to select these fields regardless of the client's GraphQL query.
This sequence shows how dependency selection ensures matchers have the data they need.
Sources: src/getMatcherDependenciesSelect.ts:13-33, src/select.ts:200-208
Logic Short-Circuiting
The library uses specific "always-true" and "always-false" conditions to handle edge cases in query generation. Misunderstanding these can lead to unexpected empty results or over-exposure.
The Always-False Condition
If a model has @Auth roles defined but none of the roles provide a valid queryConstraint for the current context, the system defaults to an alwaysFalseCondition. This prevents accidental data leaks by ensuring the query returns zero results.
| Condition | Logic | Implementation |
|---|---|---|
| Always True | Matches everything except a dummy ID | id: { not: { equals: 'ffff...' } } |
| Always False | Matches only a dummy ID | id: { equals: 'ffff...' } |
Sources: src/getListWhereConstrains.ts:7-27, src/getListWhereConstrains.ts:61-68
Architectural Pitfalls
Whitelist vs Blacklist
The system is "whitelist by default" for fields. If a field lacks an @Auth annotation, the middleware does not apply constraints. However, if a model has a type-level annotation, the makeTypeConstraintMiddleware secures the entity as a whole.
Sources: src/makeFieldConstraintMiddleware.ts:44-46, src/makeTypeConstraintMiddleware.ts:57-59
Introspection Exposure
All middlewares explicitly skip logic for GraphQL introspection fields (fields starting with __). While this allows tools like Apollo Studio to function, it means authorization rules cannot hide the schema structure itself.
Sources: src/makeFieldConstraintMiddleware.ts:33-40, src/makeListConstraintMiddleware.ts:33-40, src/makeTypeConstraintMiddleware.ts:42-49
Relation Enforcement
Relation fields require careful configuration. A 1-to-1 relation field in Prisma (like Purchase.User) is secured by makeTypeConstraintMiddleware, while list relations (like User.Purchases) are secured by makeListConstraintMiddleware via injected where clauses.
Sources: src/makeTypeConstraintMiddleware.ts:25-34, src/select.ts:218-228
Summary of Configuration Options
| Option | Type | Description |
|---|---|---|
rolesPerType | RolesPerType | Model-specific role definitions. |
globalRoles | Record<string, Role> | Role definitions shared across all models. |
defaultFields | Record<string, Object> | Fields that are always selected for a specific model. |
dmmf | any[] | Prisma DMMF used for model lookups. |
Sources: src/index.ts:21-33, README.md:99-105
In summary, ensure every Prisma call uses withAuth, define all matchers declared in Prisma comments, and explicitly list all data requirements in matcherDependenciesSelect. Failure to follow these patterns results in either runtime errors or silent security vulnerabilities.