feat: Move mutation authorization from GraphQL-level to service-level#793
Conversation
🦋 Changeset detectedLatest commit: 715171f The changes in this PR will be included in the next version bump. This PR includes changesets to release 21 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Caution Review failedPull request was closed or merged during review 📝 WalkthroughWalkthroughMoves GraphQL mutation role declarations into service-method-level authorization (globalRoles, instanceRoles), updates schema, generators, compiler, UI, and example projects to generate and enforce service-level authorization instead of per-mutation GraphQL roles. Changes
Sequence Diagram(s)sequenceDiagram
participant UI as UI (Service Method Auth)
participant Project as Project Definition
participant Migrator as Migration (025)
participant Compiler as Project Builder Compiler
participant Generator as Fastify Generators
participant Service as Runtime Service
participant DB as Database (Prisma)
UI->>Project: user assigns global/instance roles (save)
Project->>Migrator: migration025 applied (roles -> service methods)
Migrator-->>Project: updated project-definition
Project->>Compiler: compile app config
Compiler->>Generator: request data-service code with role metadata
Generator->>Generator: generate authorize fragment (global+instance)
Generator-->>Compiler: emitted service code with authorize checks
Compiler-->>Service: deploy/run service code
Service->>DB: execute authorized operation (authorize enforced)
DB-->>Service: result
Service-->>UI: operation response
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (10)
examples/todo-with-better-auth/apps/backend/src/modules/accounts/users/services/user.crud.int.test.ts (1)
9-16: Prefer a least-privilege auth fixture for this test.Using
public + user + adminin one shared context can mask authorization regressions now that checks are in the service layer. Prefer the minimum role needed for this path (and separate fixtures/tests for allow/deny cases).♻️ Suggested tightening
const context = createTestServiceContext({ auth: createAuthContextFromSessionInfo({ type: 'user', id: 'test-session', userId: '00000000-0000-0000-0000-000000000001', - roles: ['public', 'user', 'admin'], + roles: ['admin'], }), });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/todo-with-better-auth/apps/backend/src/modules/accounts/users/services/user.crud.int.test.ts` around lines 9 - 16, The test uses an overly-privileged auth fixture which can hide authorization regressions; update the fixture created via createTestServiceContext and createAuthContextFromSessionInfo to use the least-privilege roles required by this test (e.g., remove 'admin' and/or 'user' if only 'public' is needed, or set just 'user' if that's the minimal allowed role), and add separate tests/fixtures that explicitly cover admin-only and denied cases so authorization checks in the service layer remain covered.examples/todo-with-better-auth/apps/backend/src/modules/accounts/users/services/user.data-service.ts (2)
105-132: Add JSDoc for touched exported service functions.
createUser,updateUser, anddeleteUserare exported but undocumented; please add full JSDoc (params + return) per repo standards. As per coding guidelines, "Add JSDoc comments to all exported functions, interfaces, and classes documenting the function/type, parameters, return value, and all fields".Also applies to: 136-166, 168-190
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/todo-with-better-auth/apps/backend/src/modules/accounts/users/services/user.data-service.ts` around lines 105 - 132, Add JSDoc comments for the exported service functions createUser, updateUser, and deleteUser (the functions using composeCreate and commitCreate) describing the function, each parameter (e.g., the destructured data/input, query, context and generic TQueryArgs), and the Promise return type (GetPayload<'user', TQueryArgs> or corresponding types) following the repo standard; place the JSDoc directly above each exported function, include `@param` tags for data/input, query, context and any generics, and an `@returns` describing the resolved payload and possible errors.
119-119: Extract repeated authorization literal to a shared constant.
['admin']is duplicated three times; centralizing it will reduce drift when policy names evolve.♻️ Proposed refactor
+const USER_ADMIN_AUTHORIZE = ['admin'] as const; + export async function createUser< TQueryArgs extends ModelQuery<'user'> = ModelQuery<'user'>, >({ @@ - authorize: ['admin'], + authorize: USER_ADMIN_AUTHORIZE, }); @@ export async function updateUser< TQueryArgs extends ModelQuery<'user'> = ModelQuery<'user'>, >({ @@ - authorize: ['admin'], + authorize: USER_ADMIN_AUTHORIZE, }); @@ export async function deleteUser< TQueryArgs extends ModelQuery<'user'> = ModelQuery<'user'>, >({ @@ - authorize: ['admin'], + authorize: USER_ADMIN_AUTHORIZE, }); }Also applies to: 152-152, 188-188
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/todo-with-better-auth/apps/backend/src/modules/accounts/users/services/user.data-service.ts` at line 119, Extract the repeated literal ['admin'] into a single exported constant (e.g., ADMIN_AUTH or ADMIN_ROLE) and replace each authorize: ['admin'] occurrence in user.data-service.ts (the authorize properties currently present on the relevant handlers/methods) with a reference to that constant; ensure the constant is exported from a shared location (or this module if intended local) so other files can reuse it and update the three occurrences mentioned to use the new constant.examples/todo-with-better-auth/apps/backend/src/modules/accounts/users/services/user.data-service.int.test.ts (1)
36-43: Use least-privilege roles in the default test context.Line 41 includes
adminin the shared context. That can make auth tests pass even if service checks are accidentally tightened to admin-only. Prefer a baseline of['public', 'user'], and add admin only in tests that explicitly need it.Suggested change
auth: createAuthContextFromSessionInfo({ type: 'user', id: 'test-session', userId: TEST_USER_ID, - roles: ['public', 'user', 'admin'], + roles: ['public', 'user'], }),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/todo-with-better-auth/apps/backend/src/modules/accounts/users/services/user.data-service.int.test.ts` around lines 36 - 43, The default test auth context is too privileged: in the createTestServiceContext call that uses createAuthContextFromSessionInfo (with id 'test-session' and userId TEST_USER_ID) remove 'admin' from the roles array so it is ['public','user'] instead of ['public','user','admin']; keep tests that require admin rights to explicitly add the 'admin' role in their own context setup rather than relying on the shared default.examples/todo-with-better-auth/apps/backend/src/modules/todos/services/todo-item.data-service.ts (1)
65-102: Add JSDoc for touched exported functions.Please add JSDoc for
createTodoItem,updateTodoItem, anddeleteTodoItem.As per coding guidelines "Add JSDoc comments to all exported functions, interfaces, and classes documenting the function/type, parameters, return value, and all fields".
Also applies to: 106-146, 148-170
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/todo-with-better-auth/apps/backend/src/modules/todos/services/todo-item.data-service.ts` around lines 65 - 102, Add JSDoc comments for the exported functions createTodoItem, updateTodoItem, and deleteTodoItem: for each function add a short description of its purpose, `@param` tags describing each parameter (data/input, query, context and any specific fields like assigneeId/todoListId), and an `@returns` tag describing the Promise return type (GetPayload<'todoItem', TQueryArgs> or appropriate generic). Place the JSDoc immediately above each exported function (createTodoItem, updateTodoItem, deleteTodoItem) and include notes about authorization behavior and any side effects (e.g., DB create/update/delete) and thrown errors if applicable.examples/todo-with-better-auth/apps/backend/src/modules/todos/services/todo-list-share.data-service.ts (1)
41-74: Add JSDoc to touched exported service functions.
createTodoListShare,updateTodoListShare, anddeleteTodoListShareare exported and currently undocumented.As per coding guidelines "Add JSDoc comments to all exported functions, interfaces, and classes documenting the function/type, parameters, return value, and all fields".
Also applies to: 80-116, 118-140
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/todo-with-better-auth/apps/backend/src/modules/todos/services/todo-list-share.data-service.ts` around lines 41 - 74, Add JSDoc comments to the exported service functions createTodoListShare, updateTodoListShare, and deleteTodoListShare: add a brief description of what each function does, annotate parameters (e.g., the DataCreateInput/DataUpdateInput/DataDeleteInput object and its properties like data/input, query, context), document the returned Promise type (GetPayload<'todoListShare', TQueryArgs> or appropriate generic), and note any thrown errors or side-effects (e.g., DB mutations via commitCreate/commitUpdate/commitDelete). Place the JSDoc immediately above each exported function declaration and ensure it covers parameter shapes and return value generics to satisfy the project's documentation guidelines.examples/todo-with-better-auth/apps/backend/src/modules/todos/services/todo-list.data-service.ts (1)
49-81: Add JSDoc for touched exported service functions.Please document
createTodoList,updateTodoList, anddeleteTodoListwith JSDoc.As per coding guidelines "Add JSDoc comments to all exported functions, interfaces, and classes documenting the function/type, parameters, return value, and all fields".
Also applies to: 85-120, 122-144
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/todo-with-better-auth/apps/backend/src/modules/todos/services/todo-list.data-service.ts` around lines 49 - 81, Add JSDoc comments to the exported service functions createTodoList, updateTodoList, and deleteTodoList: for each function provide a short description, document parameters (e.g., the destructured { data/input, query, context } or DataCreateInput<'todoList', typeof todoListInputFields, TQueryArgs>), describe the return type (e.g., Promise<GetPayload<'todoList', TQueryArgs>> or appropriate payload), and note any errors thrown or authorization behavior (authorize: ['user']). Ensure parameter names match the functions' signatures (input/data, query, context, TQueryArgs) and mention important fields like ownerId when relevant.examples/todo-with-better-auth/apps/backend/src/modules/todos/services/todo-item-service.int.test.ts (1)
12-31: Add one explicit unauthorized-user integration test in this suite.Given this PR’s auth migration scope, this file should also assert that a different authenticated user cannot mutate another owner’s todo item/list data.
Also applies to: 210-271
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/todo-with-better-auth/apps/backend/src/modules/todos/services/todo-item-service.int.test.ts` around lines 12 - 31, The suite needs an explicit integration test that verifies an authenticated but different user cannot mutate another owner’s todo data: add a test in this file that, after the existing beforeEach setup (which creates owner and sets context via createTestServiceContext and createAuthContextFromSessionInfo and sets ownerId), creates a second user via prisma.user.create, builds a new context for that user using createTestServiceContext/createAuthContextFromSessionInfo, then attempts a mutation on the owner’s todo list/item (use the same service methods exercised elsewhere in this test file, e.g., TodoItemService or the list mutation functions) and assert it fails with an authorization error (or returns unauthorized) rather than succeeding; ensure the test covers both list-level and item-level mutation paths if those are present in lines ~210-271.packages/project-builder-lib/src/migrations/migration-025-service-method-auth.unit.test.ts (1)
165-192: Add a regression test for preserving extra mutation entries.Current tests verify preserving
graphqlsiblings, but not unknown keys insidegraphql.mutationsitself (for example custom mutation configs).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/project-builder-lib/src/migrations/migration-025-service-method-auth.unit.test.ts` around lines 165 - 192, Add a regression test to migration-025-service-method-auth.unit.test.ts that ensures migration025ServiceMethodAuth.migrate preserves unknown keys inside graphql.mutations (e.g., a custom mutation entry) in addition to existing checks for siblings; create a model config with graphql.mutations containing both known keys (create/update/delete) and an extra custom key (like customX: { enabled: true, roles: [...] }) call migration025ServiceMethodAuth.migrate(config) and assert that result.models?.[0].graphql?.mutations still contains the custom mutation entry unchanged while existing mutation entries remain correct.examples/blog-with-auth/apps/backend/src/modules/blogs/authorizers/blog.authorizer.ts (1)
7-8: Add explicit return types to inline functions in this TypeScript file.
getModelByIdandownercurrently rely on inferred return types; this file path requires explicit function return types.As per coding guidelines, "Always specify explicit return types for functions".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/blog-with-auth/apps/backend/src/modules/blogs/authorizers/blog.authorizer.ts` around lines 7 - 8, Add explicit return types for the inline functions: annotate getModelById to return Promise<Blog | null> (or the appropriate Prisma Blog type) and annotate the owner role function to return boolean; update the getModelById signature (the getModelById property) and the owner function inside roles (roles.owner) accordingly and, if necessary, import the Blog type from `@prisma/client` or your Prisma types so the annotations compile.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@examples/blog-with-auth/apps/backend/src/modules/blogs/authorizers/blog.authorizer.ts`:
- Line 8: The owner role lambda in blog.authorizer.ts (roles: { owner: (ctx,
model) => model.userId === ctx.auth.userId }) can throw when ctx.auth is
missing; update the owner evaluator to first guard that ctx.auth and
ctx.auth.userId exist (e.g., check ctx.auth && ctx.auth.userId or
ctx.auth?.userId) and return false if missing, then compare model.userId to the
userId only when present so authorization safely denies instead of throwing.
In
`@examples/blog-with-auth/apps/backend/src/modules/blogs/schema/blog.mutations.ts`:
- Line 25: The schema-level authorize array for the updateBlog and deleteBlog
mutations is too permissive (includes 'public'); update the authorize entries in
blog.mutations.ts for the updateBlog and deleteBlog fields to match the
service-level enforcement by replacing ['public','user','system','admin'] with
the restrictive set used by the data service: ['admin',
blogAuthorizer.roles.owner] so schema and the updateBlog/deleteBlog resolve
paths align.
In
`@examples/blog-with-auth/apps/backend/src/modules/blogs/services/blog.data-service.ts`:
- Around line 31-40: Add JSDoc blocks for the exported service functions by
documenting parameters, return types, and behavior for updateBlog and
deleteBlog; locate the functions updateBlog and deleteBlog in
blog.data-service.ts and add standard JSDoc comments above each exported
function describing the function purpose, each parameter (where, data/input,
query, context) including types, and the Promise/return payload
(GetPayload<'blog', TQueryArgs>), ensuring the docs follow the project guideline
format for exported functions.
In
`@examples/todo-with-better-auth/apps/backend/src/modules/todos/services/todo-list-share.data-service.ts`:
- Line 57: The exported service functions createTodoListShare,
updateTodoListShare, and deleteTodoListShare currently only require
authenticated users (authorize: ['user']) but lack instance-level ownership
checks; add either a model authorizer (e.g., todoListShareAuthorizer similar to
userAuthorizer) or explicit service-level validation that the requesting user
owns the target todo list (verify ownerId on the TodoList before
creating/updating/deleting shares) and throw an authorization error if not
authorized. Also add JSDoc comments above each exported function
(createTodoListShare, updateTodoListShare, deleteTodoListShare) documenting
parameters, return values, and errors thrown. Ensure the authorization check
runs before any DB mutations and reuse existing user identity from the
request/context.
In
`@packages/project-builder-lib/src/migrations/migration-025-service-method-auth.ts`:
- Around line 88-92: The migration currently replaces graphql.mutations with
only create/update/delete and thus discards any other custom mutation entries;
update the migration logic in migration-025-service-method-auth.ts to merge into
the existing graphql.mutations object instead of overwriting it — e.g., read the
existing graphql.mutations (if any), preserve all its keys, and only
set/override the create/update/delete entries using mutations.create?.enabled ??
false etc., so non-CRUD mutation configurations remain intact.
In
`@packages/project-builder-web/src/routes/data/models/edit`.$key/-components/graphql/graph-ql-mutations-section.tsx:
- Around line 50-58: The current rendering branch for hasInstanceRoles in
graph-ql-mutations-section.tsx collapses mixed auth rules into a single
"Authenticated" badge and hides configured globalRoles; update the UI logic in
the component (the block guarded by hasInstanceRoles) to display both the
instance-level indicator and any globalRoles that exist (e.g., show the Badge
for Authenticated and also render the globalRoles list or badges when
globalRoles is present) so the full effective access rule set is visible; locate
and update the JSX around hasInstanceRoles, instanceRoles and globalRoles to
combine both displays instead of replacing globalRoles with a single
Authenticated label.
In
`@packages/project-builder-web/src/routes/data/models/edit`.$key/-components/graphql/graph-ql-queries-section.tsx:
- Around line 111-118: The Count Query SwitchFieldController
("graphql.queries.list.count.enabled") currently only respects isListEnabled and
can remain editable when the parent object type is disabled; update this
SwitchFieldController to use the same disabled condition as the parent query
toggles (i.e., mirror the object-type disabled flag they use) so it is disabled
whenever the object type is disabled — locate the parent query toggle usage to
copy the exact disabled expression and add disabled={...} to the
SwitchFieldController here.
---
Nitpick comments:
In
`@examples/blog-with-auth/apps/backend/src/modules/blogs/authorizers/blog.authorizer.ts`:
- Around line 7-8: Add explicit return types for the inline functions: annotate
getModelById to return Promise<Blog | null> (or the appropriate Prisma Blog
type) and annotate the owner role function to return boolean; update the
getModelById signature (the getModelById property) and the owner function inside
roles (roles.owner) accordingly and, if necessary, import the Blog type from
`@prisma/client` or your Prisma types so the annotations compile.
In
`@examples/todo-with-better-auth/apps/backend/src/modules/accounts/users/services/user.crud.int.test.ts`:
- Around line 9-16: The test uses an overly-privileged auth fixture which can
hide authorization regressions; update the fixture created via
createTestServiceContext and createAuthContextFromSessionInfo to use the
least-privilege roles required by this test (e.g., remove 'admin' and/or 'user'
if only 'public' is needed, or set just 'user' if that's the minimal allowed
role), and add separate tests/fixtures that explicitly cover admin-only and
denied cases so authorization checks in the service layer remain covered.
In
`@examples/todo-with-better-auth/apps/backend/src/modules/accounts/users/services/user.data-service.int.test.ts`:
- Around line 36-43: The default test auth context is too privileged: in the
createTestServiceContext call that uses createAuthContextFromSessionInfo (with
id 'test-session' and userId TEST_USER_ID) remove 'admin' from the roles array
so it is ['public','user'] instead of ['public','user','admin']; keep tests that
require admin rights to explicitly add the 'admin' role in their own context
setup rather than relying on the shared default.
In
`@examples/todo-with-better-auth/apps/backend/src/modules/accounts/users/services/user.data-service.ts`:
- Around line 105-132: Add JSDoc comments for the exported service functions
createUser, updateUser, and deleteUser (the functions using composeCreate and
commitCreate) describing the function, each parameter (e.g., the destructured
data/input, query, context and generic TQueryArgs), and the Promise return type
(GetPayload<'user', TQueryArgs> or corresponding types) following the repo
standard; place the JSDoc directly above each exported function, include `@param`
tags for data/input, query, context and any generics, and an `@returns` describing
the resolved payload and possible errors.
- Line 119: Extract the repeated literal ['admin'] into a single exported
constant (e.g., ADMIN_AUTH or ADMIN_ROLE) and replace each authorize: ['admin']
occurrence in user.data-service.ts (the authorize properties currently present
on the relevant handlers/methods) with a reference to that constant; ensure the
constant is exported from a shared location (or this module if intended local)
so other files can reuse it and update the three occurrences mentioned to use
the new constant.
In
`@examples/todo-with-better-auth/apps/backend/src/modules/todos/services/todo-item-service.int.test.ts`:
- Around line 12-31: The suite needs an explicit integration test that verifies
an authenticated but different user cannot mutate another owner’s todo data: add
a test in this file that, after the existing beforeEach setup (which creates
owner and sets context via createTestServiceContext and
createAuthContextFromSessionInfo and sets ownerId), creates a second user via
prisma.user.create, builds a new context for that user using
createTestServiceContext/createAuthContextFromSessionInfo, then attempts a
mutation on the owner’s todo list/item (use the same service methods exercised
elsewhere in this test file, e.g., TodoItemService or the list mutation
functions) and assert it fails with an authorization error (or returns
unauthorized) rather than succeeding; ensure the test covers both list-level and
item-level mutation paths if those are present in lines ~210-271.
In
`@examples/todo-with-better-auth/apps/backend/src/modules/todos/services/todo-item.data-service.ts`:
- Around line 65-102: Add JSDoc comments for the exported functions
createTodoItem, updateTodoItem, and deleteTodoItem: for each function add a
short description of its purpose, `@param` tags describing each parameter
(data/input, query, context and any specific fields like assigneeId/todoListId),
and an `@returns` tag describing the Promise return type (GetPayload<'todoItem',
TQueryArgs> or appropriate generic). Place the JSDoc immediately above each
exported function (createTodoItem, updateTodoItem, deleteTodoItem) and include
notes about authorization behavior and any side effects (e.g., DB
create/update/delete) and thrown errors if applicable.
In
`@examples/todo-with-better-auth/apps/backend/src/modules/todos/services/todo-list-share.data-service.ts`:
- Around line 41-74: Add JSDoc comments to the exported service functions
createTodoListShare, updateTodoListShare, and deleteTodoListShare: add a brief
description of what each function does, annotate parameters (e.g., the
DataCreateInput/DataUpdateInput/DataDeleteInput object and its properties like
data/input, query, context), document the returned Promise type
(GetPayload<'todoListShare', TQueryArgs> or appropriate generic), and note any
thrown errors or side-effects (e.g., DB mutations via
commitCreate/commitUpdate/commitDelete). Place the JSDoc immediately above each
exported function declaration and ensure it covers parameter shapes and return
value generics to satisfy the project's documentation guidelines.
In
`@examples/todo-with-better-auth/apps/backend/src/modules/todos/services/todo-list.data-service.ts`:
- Around line 49-81: Add JSDoc comments to the exported service functions
createTodoList, updateTodoList, and deleteTodoList: for each function provide a
short description, document parameters (e.g., the destructured { data/input,
query, context } or DataCreateInput<'todoList', typeof todoListInputFields,
TQueryArgs>), describe the return type (e.g., Promise<GetPayload<'todoList',
TQueryArgs>> or appropriate payload), and note any errors thrown or
authorization behavior (authorize: ['user']). Ensure parameter names match the
functions' signatures (input/data, query, context, TQueryArgs) and mention
important fields like ownerId when relevant.
In
`@packages/project-builder-lib/src/migrations/migration-025-service-method-auth.unit.test.ts`:
- Around line 165-192: Add a regression test to
migration-025-service-method-auth.unit.test.ts that ensures
migration025ServiceMethodAuth.migrate preserves unknown keys inside
graphql.mutations (e.g., a custom mutation entry) in addition to existing checks
for siblings; create a model config with graphql.mutations containing both known
keys (create/update/delete) and an extra custom key (like customX: { enabled:
true, roles: [...] }) call migration025ServiceMethodAuth.migrate(config) and
assert that result.models?.[0].graphql?.mutations still contains the custom
mutation entry unchanged while existing mutation entries remain correct.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 52b327bd-95fa-4da7-9473-e2cc928b4f62
⛔ Files ignored due to path filters (9)
examples/blog-with-auth/apps/backend/baseplate/generated/src/modules/accounts/services/user.data-service.tsis excluded by!**/generated/**,!**/generated/**examples/blog-with-auth/apps/backend/baseplate/generated/src/modules/blogs/authorizers/blog.authorizer.tsis excluded by!**/generated/**,!**/generated/**examples/blog-with-auth/apps/backend/baseplate/generated/src/modules/blogs/index.tsis excluded by!**/generated/**,!**/generated/**examples/blog-with-auth/apps/backend/baseplate/generated/src/modules/blogs/schema/blog.mutations.tsis excluded by!**/generated/**,!**/generated/**examples/blog-with-auth/apps/backend/baseplate/generated/src/modules/blogs/services/blog.data-service.tsis excluded by!**/generated/**,!**/generated/**examples/todo-with-better-auth/apps/backend/baseplate/generated/src/modules/accounts/users/services/user.data-service.tsis excluded by!**/generated/**,!**/generated/**examples/todo-with-better-auth/apps/backend/baseplate/generated/src/modules/todos/services/todo-item.data-service.tsis excluded by!**/generated/**,!**/generated/**examples/todo-with-better-auth/apps/backend/baseplate/generated/src/modules/todos/services/todo-list-share.data-service.tsis excluded by!**/generated/**,!**/generated/**examples/todo-with-better-auth/apps/backend/baseplate/generated/src/modules/todos/services/todo-list.data-service.tsis excluded by!**/generated/**,!**/generated/**
📒 Files selected for processing (35)
.changeset/service-method-auth.mdexamples/blog-with-auth/apps/backend/baseplate/file-id-map.jsonexamples/blog-with-auth/apps/backend/schema.graphqlexamples/blog-with-auth/apps/backend/src/modules/accounts/services/user.data-service.tsexamples/blog-with-auth/apps/backend/src/modules/blogs/authorizers/blog.authorizer.tsexamples/blog-with-auth/apps/backend/src/modules/blogs/index.tsexamples/blog-with-auth/apps/backend/src/modules/blogs/schema/blog.mutations.tsexamples/blog-with-auth/apps/backend/src/modules/blogs/services/blog.data-service.tsexamples/blog-with-auth/baseplate/project-definition.jsonexamples/todo-with-better-auth/apps/backend/src/modules/accounts/users/services/user.crud.int.test.tsexamples/todo-with-better-auth/apps/backend/src/modules/accounts/users/services/user.data-service.int.test.tsexamples/todo-with-better-auth/apps/backend/src/modules/accounts/users/services/user.data-service.tsexamples/todo-with-better-auth/apps/backend/src/modules/todos/services/todo-item-service.int.test.tsexamples/todo-with-better-auth/apps/backend/src/modules/todos/services/todo-item.data-service.tsexamples/todo-with-better-auth/apps/backend/src/modules/todos/services/todo-list-share.data-service.tsexamples/todo-with-better-auth/apps/backend/src/modules/todos/services/todo-list.data-service.tsexamples/todo-with-better-auth/baseplate/project-definition.jsonpackages/fastify-generators/src/generators/prisma/_shared/build-data-helpers/generate-authorize-fragment.tspackages/fastify-generators/src/generators/prisma/_shared/build-data-helpers/index.tspackages/fastify-generators/src/generators/prisma/prisma-data-create/prisma-data-create.generator.tspackages/fastify-generators/src/generators/prisma/prisma-data-delete/prisma-data-delete.generator.tspackages/fastify-generators/src/generators/prisma/prisma-data-update/prisma-data-update.generator.tspackages/project-builder-lib/src/migrations/index.tspackages/project-builder-lib/src/migrations/migration-025-service-method-auth.tspackages/project-builder-lib/src/migrations/migration-025-service-method-auth.unit.test.tspackages/project-builder-lib/src/schema/models/graphql.tspackages/project-builder-lib/src/schema/models/models.tspackages/project-builder-server/src/compiler/backend/graphql.tspackages/project-builder-server/src/compiler/backend/services.tspackages/project-builder-web/src/routes/data/models/edit.$key/-components/graphql/graph-ql-mutations-section.tsxpackages/project-builder-web/src/routes/data/models/edit.$key/-components/graphql/graph-ql-object-type-section.tsxpackages/project-builder-web/src/routes/data/models/edit.$key/-components/graphql/graph-ql-queries-section.tsxpackages/project-builder-web/src/routes/data/models/edit.$key/-components/service/service-method-auth-section.tsxpackages/project-builder-web/src/routes/data/models/edit.$key/service.tsxpackages/project-builder-web/src/routes/packages/apps.$key/web/admin.tsx
💤 Files with no reviewable changes (1)
- packages/project-builder-lib/src/schema/models/graphql.ts
| model: 'blog', | ||
| idField: 'id', | ||
| getModelById: (id) => prisma.blog.findUnique({ where: { id } }), | ||
| roles: { owner: (ctx, model) => model.userId === ctx.auth.userId }, |
There was a problem hiding this comment.
Guard owner-role evaluation against missing auth context.
Line 8 directly reads ctx.auth.userId; if auth context is absent, authorization can throw instead of denying access.
Proposed fix
- roles: { owner: (ctx, model) => model.userId === ctx.auth.userId },
+ roles: {
+ owner: (ctx, model): boolean =>
+ ctx.auth?.userId != null && model.userId === ctx.auth.userId,
+ },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@examples/blog-with-auth/apps/backend/src/modules/blogs/authorizers/blog.authorizer.ts`
at line 8, The owner role lambda in blog.authorizer.ts (roles: { owner: (ctx,
model) => model.userId === ctx.auth.userId }) can throw when ctx.auth is
missing; update the owner evaluator to first guard that ctx.auth and
ctx.auth.userId exist (e.g., check ctx.auth && ctx.auth.userId or
ctx.auth?.userId) and return false if missing, then compare model.userId to the
userId only when present so authorization safely denies instead of throwing.
| data: t.input.field({ required: true, type: updateBlogDataInputType }), | ||
| }, | ||
| payload: { blog: t.payload.field({ type: blogObjectType }) }, | ||
| authorize: ['public', 'user', 'system', 'admin'], |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n examples/blog-with-auth/apps/backend/src/modules/blogs/schema/blog.mutations.tsRepository: halfdomelabs/baseplate
Length of output: 2032
🏁 Script executed:
rg -n "authorize:" --type=ts examples/blog-with-auth/apps/backend -C 2 | head -80Repository: halfdomelabs/baseplate
Length of output: 7456
🏁 Script executed:
# Check if there are query/subscription files to compare authorization patterns
fd -e ts examples/blog-with-auth/apps/backend/src/modules --name "*.queries.ts" --o --name "*.subscriptions.ts" | xargs -I {} sh -c 'echo "=== {} ===" && rg -n "authorize:" {} -C 1'Repository: halfdomelabs/baseplate
Length of output: 316
Remove 'public' from the authorize array.
The mutations declare ['public', 'user', 'system', 'admin'], but the underlying service methods (updateBlog and deleteBlog in blog.data-service.ts) enforce ['admin', blogAuthorizer.roles.owner]. This inconsistency means the schema-level authorization is misleading. Change authorize to match the service layer: ['admin', blogAuthorizer.roles.owner] for both updateBlog and deleteBlog mutations (or use the restrictive authorization from the service methods that are actually called in the resolve functions).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@examples/blog-with-auth/apps/backend/src/modules/blogs/schema/blog.mutations.ts`
at line 25, The schema-level authorize array for the updateBlog and deleteBlog
mutations is too permissive (includes 'public'); update the authorize entries in
blog.mutations.ts for the updateBlog and deleteBlog fields to match the
service-level enforcement by replacing ['public','user','system','admin'] with
the restrictive set used by the data service: ['admin',
blogAuthorizer.roles.owner] so schema and the updateBlog/deleteBlog resolve
paths align.
| export async function updateBlog< | ||
| TQueryArgs extends ModelQuery<'blog'> = ModelQuery<'blog'>, | ||
| >({ | ||
| where, | ||
| data: input, | ||
| query, | ||
| context, | ||
| }: DataUpdateInput<'blog', typeof blogInputFields, TQueryArgs>): Promise< | ||
| GetPayload<'blog', TQueryArgs> | ||
| > { |
There was a problem hiding this comment.
Add JSDoc blocks for exported service functions.
updateBlog and deleteBlog are exported but currently undocumented.
📝 Suggested patch
+/**
+ * Updates a blog by unique filter and applies service-level authorization.
+ *
+ * `@param` where - Unique blog selector.
+ * `@param` data - Partial update payload validated by service input schema.
+ * `@param` query - Prisma selection/include query args.
+ * `@param` context - Request/service execution context.
+ * `@returns` Updated blog payload matching `TQueryArgs`.
+ */
export async function updateBlog<
TQueryArgs extends ModelQuery<'blog'> = ModelQuery<'blog'>,
>({
@@
+/**
+ * Deletes a blog by unique filter and applies service-level authorization.
+ *
+ * `@param` where - Unique blog selector.
+ * `@param` query - Prisma selection/include query args.
+ * `@param` context - Request/service execution context.
+ * `@returns` Deleted blog payload matching `TQueryArgs`.
+ */
export async function deleteBlog<
TQueryArgs extends ModelQuery<'blog'> = ModelQuery<'blog'>,
>({As per coding guidelines Add JSDocs to all exported functions, interfaces, and classes with documentation of parameters, return values, and all fields.
Also applies to: 63-71
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@examples/blog-with-auth/apps/backend/src/modules/blogs/services/blog.data-service.ts`
around lines 31 - 40, Add JSDoc blocks for the exported service functions by
documenting parameters, return types, and behavior for updateBlog and
deleteBlog; locate the functions updateBlog and deleteBlog in
blog.data-service.ts and add standard JSDoc comments above each exported
function describing the function purpose, each parameter (where, data/input,
query, context) including types, and the Promise/return payload
(GetPayload<'blog', TQueryArgs>), ensuring the docs follow the project guideline
format for exported functions.
| fields: todoListShareInputFields, | ||
| input, | ||
| context, | ||
| authorize: ['user'], |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Search for authorization patterns and tests around todo-list-share operations
fd "todo-list-share" examples/todo-with-better-authRepository: halfdomelabs/baseplate
Length of output: 939
🏁 Script executed:
# Check the file structure and exported functions
head -150 examples/todo-with-better-auth/apps/backend/src/modules/todos/services/todo-list-share.data-service.ts | tail -120Repository: halfdomelabs/baseplate
Length of output: 3195
🏁 Script executed:
# Search for authorizer configurations and instance-level checks
rg -n -C5 "todoListShare|Authorizer|instanceRoles|owner" examples/todo-with-better-auth/apps/backend/src --max-count=30Repository: halfdomelabs/baseplate
Length of output: 50380
🏁 Script executed:
# Look for tests that verify authorization enforcement
rg -n "todoListShare|forbidden|unauthorized|403" examples/todo-with-better-auth --glob "*test*" -C3Repository: halfdomelabs/baseplate
Length of output: 48
🏁 Script executed:
# Search for todoListShare authorizer configuration
rg -n "todoListShare.*Authorizer|createModelAuthorizer.*todoListShare" examples/todo-with-better-auth --max-count=20Repository: halfdomelabs/baseplate
Length of output: 48
🏁 Script executed:
# Check the composeCreate/composeUpdate/composeDelete implementations for authorization enforcement
rg -n "export.*function.*compose(Create|Update|Delete)" examples/todo-with-better-auth/apps/backend/src --max-count=10 -A 5Repository: halfdomelabs/baseplate
Length of output: 1664
🏁 Script executed:
# Search for JSDoc comments in the data-service file
head -75 examples/todo-with-better-auth/apps/backend/src/modules/todos/services/todo-list-share.data-service.tsRepository: halfdomelabs/baseplate
Length of output: 2024
🏁 Script executed:
# Check the composeCreate implementation to understand authorization enforcement
sed -n '27,70p' examples/todo-with-better-auth/apps/backend/src/utils/data-operations/compose-operations.tsRepository: halfdomelabs/baseplate
Length of output: 1416
🏁 Script executed:
# Search for any authorizer definitions in the entire codebase
fd "authorizer" examples/todo-with-better-auth/apps/backend/src --type fRepository: halfdomelabs/baseplate
Length of output: 236
🏁 Script executed:
# Look for model-specific authorizer setups
rg "createModelAuthorizer|ModelAuthorizer" examples/todo-with-better-auth/apps/backend/src/modules --max-count=20Repository: halfdomelabs/baseplate
Length of output: 390
🏁 Script executed:
# Check the user.authorizer to understand the pattern
cat examples/todo-with-better-auth/apps/backend/src/modules/accounts/users/authorizers/user.authorizer.tsRepository: halfdomelabs/baseplate
Length of output: 408
🏁 Script executed:
# Search specifically for todoListShare authorizer or instance checks
rg "todoListShare" examples/todo-with-better-auth/apps/backend/src/modules --max-count=50Repository: halfdomelabs/baseplate
Length of output: 8029
🏁 Script executed:
# Check if there's a separate authorization layer in GraphQL mutations
rg -B5 -A10 "createTodoListShare|updateTodoListShare|deleteTodoListShare" examples/todo-with-better-auth/apps/backend/src/modules/todos/schema/todo-list-share.mutations.ts | head -100Repository: halfdomelabs/baseplate
Length of output: 2908
Add row-level authorization checks and JSDoc documentation to exported functions.
The authorize: ['user'] setting enforces authenticated-user access only; it does not verify ownership of the todo list being shared. Without instance-level authorization checks (e.g., a todoListShareAuthorizer pattern similar to userAuthorizer), any authenticated user can mutate shares for lists they don't own. Implement instance-level authorization via model authorizer or service-level ownership validation.
Additionally, per coding guidelines, all three exported functions (createTodoListShare, updateTodoListShare, deleteTodoListShare) require JSDoc comments documenting the function, parameters, and return value.
Also applies to: 98-99, 138-138
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@examples/todo-with-better-auth/apps/backend/src/modules/todos/services/todo-list-share.data-service.ts`
at line 57, The exported service functions createTodoListShare,
updateTodoListShare, and deleteTodoListShare currently only require
authenticated users (authorize: ['user']) but lack instance-level ownership
checks; add either a model authorizer (e.g., todoListShareAuthorizer similar to
userAuthorizer) or explicit service-level validation that the requesting user
owns the target todo list (verify ownerId on the TodoList before
creating/updating/deleting shares) and throw an authorization error if not
authorized. Also add JSDoc comments above each exported function
(createTodoListShare, updateTodoListShare, deleteTodoListShare) documenting
parameters, return values, and errors thrown. Ensure the authorization check
runs before any DB mutations and reuse existing user identity from the
request/context.
| if (hasInstanceRoles) { | ||
| return ( | ||
| <div className="ml-10 flex items-center gap-2"> | ||
| <Badge variant="secondary">Authenticated</Badge> | ||
| <span className="text-xs text-muted-foreground"> | ||
| Service-level authorization applies | ||
| </span> | ||
| </div> | ||
| ); |
There was a problem hiding this comment.
Don’t collapse mixed auth rules into only Authenticated.
When instanceRoles exists, this branch hides any configured globalRoles (e.g. admin), so the UI no longer reflects the full effective access rule set.
💡 Suggested patch
if (hasInstanceRoles) {
return (
<div className="ml-10 flex items-center gap-2">
- <Badge variant="secondary">Authenticated</Badge>
+ {hasGlobalRoles &&
+ globalRoles.map((roleId) => (
+ <Badge key={roleId} variant="secondary">
+ {roleMap.get(roleId) ?? roleId}
+ </Badge>
+ ))}
+ <Badge variant="secondary">Instance-authorized</Badge>
<span className="text-xs text-muted-foreground">
Service-level authorization applies
</span>
</div>
);
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@packages/project-builder-web/src/routes/data/models/edit`.$key/-components/graphql/graph-ql-mutations-section.tsx
around lines 50 - 58, The current rendering branch for hasInstanceRoles in
graph-ql-mutations-section.tsx collapses mixed auth rules into a single
"Authenticated" badge and hides configured globalRoles; update the UI logic in
the component (the block guarded by hasInstanceRoles) to display both the
instance-level indicator and any globalRoles that exist (e.g., show the Badge
for Authenticated and also render the globalRoles list or badges when
globalRoles is present) so the full effective access rule set is visible; locate
and update the JSX around hasInstanceRoles, instanceRoles and globalRoles to
combine both displays instead of replacing globalRoles with a single
Authenticated label.
Summary by CodeRabbit
New Features
Bug Fixes
Refactor