@checkstack/auth-common 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,138 @@
1
1
  # @checkstack/auth-common
2
2
 
3
+ ## 0.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 7a23261: ## TanStack Query Integration
8
+
9
+ Migrated all frontend components to use `usePluginClient` hook with TanStack Query integration, replacing the legacy `forPlugin()` pattern.
10
+
11
+ ### New Features
12
+
13
+ - **`usePluginClient` hook**: Provides type-safe access to plugin APIs with `.useQuery()` and `.useMutation()` methods
14
+ - **Automatic request deduplication**: Multiple components requesting the same data share a single network request
15
+ - **Built-in caching**: Configurable stale time and cache duration per query
16
+ - **Loading/error states**: TanStack Query provides `isLoading`, `error`, `isRefetching` states automatically
17
+ - **Background refetching**: Stale data is automatically refreshed when components mount
18
+
19
+ ### Contract Changes
20
+
21
+ All RPC contracts now require `operationType: "query"` or `operationType: "mutation"` metadata:
22
+
23
+ ```typescript
24
+ const getItems = proc()
25
+ .meta({ operationType: "query", access: [access.read] })
26
+ .output(z.array(itemSchema))
27
+ .query();
28
+
29
+ const createItem = proc()
30
+ .meta({ operationType: "mutation", access: [access.manage] })
31
+ .input(createItemSchema)
32
+ .output(itemSchema)
33
+ .mutation();
34
+ ```
35
+
36
+ ### Migration
37
+
38
+ ```typescript
39
+ // Before (forPlugin pattern)
40
+ const api = useApi(myPluginApiRef);
41
+ const [items, setItems] = useState<Item[]>([]);
42
+ useEffect(() => {
43
+ api.getItems().then(setItems);
44
+ }, [api]);
45
+
46
+ // After (usePluginClient pattern)
47
+ const client = usePluginClient(MyPluginApi);
48
+ const { data: items, isLoading } = client.getItems.useQuery({});
49
+ ```
50
+
51
+ ### Bug Fixes
52
+
53
+ - Fixed `rpc.test.ts` test setup for middleware type inference
54
+ - Fixed `SearchDialog` to use `setQuery` instead of deprecated `search` method
55
+ - Fixed null→undefined warnings in notification and queue frontends
56
+
57
+ ### Patch Changes
58
+
59
+ - Updated dependencies [7a23261]
60
+ - @checkstack/common@0.3.0
61
+
62
+ ## 0.2.0
63
+
64
+ ### Minor Changes
65
+
66
+ - 9faec1f: # Unified AccessRule Terminology Refactoring
67
+
68
+ This release completes a comprehensive terminology refactoring from "permission" to "accessRule" across the entire codebase, establishing a consistent and modern access control vocabulary.
69
+
70
+ ## Changes
71
+
72
+ ### Core Infrastructure (`@checkstack/common`)
73
+
74
+ - Introduced `AccessRule` interface as the primary access control type
75
+ - Added `accessPair()` helper for creating read/manage access rule pairs
76
+ - Added `access()` builder for individual access rules
77
+ - Replaced `Permission` type with `AccessRule` throughout
78
+
79
+ ### API Changes
80
+
81
+ - `env.registerPermissions()` → `env.registerAccessRules()`
82
+ - `meta.permissions` → `meta.access` in RPC contracts
83
+ - `usePermission()` → `useAccess()` in frontend hooks
84
+ - Route `permission:` field → `accessRule:` field
85
+
86
+ ### UI Changes
87
+
88
+ - "Roles & Permissions" tab → "Roles & Access Rules"
89
+ - "You don't have permission..." → "You don't have access..."
90
+ - All permission-related UI text updated
91
+
92
+ ### Documentation & Templates
93
+
94
+ - Updated 18 documentation files with AccessRule terminology
95
+ - Updated 7 scaffolding templates with `accessPair()` pattern
96
+ - All code examples use new AccessRule API
97
+
98
+ ## Migration Guide
99
+
100
+ ### Backend Plugins
101
+
102
+ ```diff
103
+ - import { permissionList } from "./permissions";
104
+ - env.registerPermissions(permissionList);
105
+ + import { accessRules } from "./access";
106
+ + env.registerAccessRules(accessRules);
107
+ ```
108
+
109
+ ### RPC Contracts
110
+
111
+ ```diff
112
+ - .meta({ userType: "user", permissions: [permissions.read.id] })
113
+ + .meta({ userType: "user", access: [access.read] })
114
+ ```
115
+
116
+ ### Frontend Hooks
117
+
118
+ ```diff
119
+ - const canRead = accessApi.usePermission(permissions.read.id);
120
+ + const canRead = accessApi.useAccess(access.read);
121
+ ```
122
+
123
+ ### Routes
124
+
125
+ ```diff
126
+ - permission: permissions.entityRead.id,
127
+ + accessRule: access.read,
128
+ ```
129
+
130
+ ### Patch Changes
131
+
132
+ - Updated dependencies [9faec1f]
133
+ - Updated dependencies [f533141]
134
+ - @checkstack/common@0.2.0
135
+
3
136
  ## 0.1.0
4
137
 
5
138
  ### Minor Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@checkstack/auth-common",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
package/src/access.ts ADDED
@@ -0,0 +1,91 @@
1
+ import { access, accessPair, type AccessRule } from "@checkstack/common";
2
+
3
+ /**
4
+ * Access rules for the Auth plugin.
5
+ *
6
+ * Auth has fine-grained access rules for different operations
7
+ * on users, roles, teams, strategies, etc.
8
+ */
9
+ export const authAccess = {
10
+ /**
11
+ * User management access rules.
12
+ */
13
+ users: {
14
+ read: access("users", "read", "List all users"),
15
+ create: access(
16
+ "users.create",
17
+ "manage",
18
+ "Create new users (credential strategy)"
19
+ ),
20
+ manage: access("users", "manage", "Delete users"),
21
+ },
22
+
23
+ /**
24
+ * Role management access rules.
25
+ */
26
+ roles: {
27
+ read: access("roles", "read", "Read and list roles"),
28
+ create: access("roles.create", "manage", "Create new roles"),
29
+ update: access(
30
+ "roles.update",
31
+ "manage",
32
+ "Update role names and access rules"
33
+ ),
34
+ delete: access("roles.delete", "manage", "Delete roles"),
35
+ manage: access("roles", "manage", "Assign roles to users"),
36
+ },
37
+
38
+ /**
39
+ * Authentication strategy management.
40
+ */
41
+ strategies: access(
42
+ "strategies",
43
+ "manage",
44
+ "Manage authentication strategies and settings"
45
+ ),
46
+
47
+ /**
48
+ * Registration settings management.
49
+ */
50
+ registration: access(
51
+ "registration",
52
+ "manage",
53
+ "Manage user registration settings"
54
+ ),
55
+
56
+ /**
57
+ * External application management.
58
+ */
59
+ applications: access(
60
+ "applications",
61
+ "manage",
62
+ "Create, update, delete, and view external applications"
63
+ ),
64
+
65
+ /**
66
+ * Team management access rules.
67
+ */
68
+ teams: accessPair("teams", {
69
+ read: "View teams and team memberships",
70
+ manage: "Create, delete, and manage all teams and resource access",
71
+ }),
72
+ };
73
+
74
+ /**
75
+ * All access rules for registration with the plugin system.
76
+ */
77
+ export const authAccessRules: AccessRule[] = [
78
+ authAccess.users.read,
79
+ authAccess.users.create,
80
+ authAccess.users.manage,
81
+ authAccess.roles.read,
82
+ authAccess.roles.create,
83
+ authAccess.roles.update,
84
+ authAccess.roles.delete,
85
+ authAccess.roles.manage,
86
+ authAccess.strategies,
87
+ authAccess.registration,
88
+ authAccess.applications,
89
+ authAccess.teams.read,
90
+ authAccess.teams.manage,
91
+ ];
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- export * from "./permissions";
1
+ export * from "./access";
2
2
  export * from "./rpc-contract";
3
3
  export * from "./plugin-metadata";
4
4
  export * from "./schemas";
@@ -1,16 +1,12 @@
1
- import { oc } from "@orpc/contract";
2
1
  import {
3
2
  createClientDefinition,
4
- type ProcedureMetadata,
3
+ proc,
5
4
  lucideIconSchema,
6
5
  } from "@checkstack/common";
7
6
  import { z } from "zod";
8
- import { permissions } from "./permissions";
7
+ import { authAccess } from "./access";
9
8
  import { pluginMetadata } from "./plugin-metadata";
10
9
 
11
- // Base builder with full metadata support
12
- const _base = oc.$meta<ProcedureMetadata>({});
13
-
14
10
  // Zod schemas for return types
15
11
  const UserDtoSchema = z.object({
16
12
  id: z.string(),
@@ -23,12 +19,12 @@ const RoleDtoSchema = z.object({
23
19
  id: z.string(),
24
20
  name: z.string(),
25
21
  description: z.string().optional().nullable(),
26
- permissions: z.array(z.string()),
22
+ accessRules: z.array(z.string()),
27
23
  isSystem: z.boolean().optional(),
28
- isAssignable: z.boolean().optional(), // False for anonymous role
24
+ isAssignable: z.boolean().optional(),
29
25
  });
30
26
 
31
- const PermissionDtoSchema = z.object({
27
+ const AccessRuleDtoSchema = z.object({
32
28
  id: z.string(),
33
29
  description: z.string().optional(),
34
30
  });
@@ -40,9 +36,9 @@ const StrategyDtoSchema = z.object({
40
36
  icon: lucideIconSchema.optional(),
41
37
  enabled: z.boolean(),
42
38
  configVersion: z.number(),
43
- configSchema: z.record(z.string(), z.unknown()), // JSON Schema representation
44
- config: z.record(z.string(), z.unknown()).optional(), // VersionedConfig.data (secrets redacted)
45
- adminInstructions: z.string().optional(), // Markdown instructions for admins
39
+ configSchema: z.record(z.string(), z.unknown()),
40
+ config: z.record(z.string(), z.unknown()).optional(),
41
+ adminInstructions: z.string().optional(),
46
42
  });
47
43
 
48
44
  const EnabledStrategyDtoSchema = z.object({
@@ -58,32 +54,25 @@ const RegistrationStatusSchema = z.object({
58
54
  allowRegistration: z.boolean(),
59
55
  });
60
56
 
61
- // ==========================================================================
62
- // SERVICE-TO-SERVICE SCHEMAS (for auth provider plugins like LDAP)
63
- // ==========================================================================
64
-
57
+ // Service-to-service schemas
65
58
  const FindUserByEmailInputSchema = z.object({
66
59
  email: z.string().email(),
67
60
  });
68
61
 
69
- const FindUserByEmailOutputSchema = z
70
- .object({
71
- id: z.string(),
72
- })
73
- .optional();
62
+ const FindUserByEmailOutputSchema = z.object({ id: z.string() }).optional();
74
63
 
75
64
  const UpsertExternalUserInputSchema = z.object({
76
65
  email: z.string().email(),
77
66
  name: z.string(),
78
- providerId: z.string(), // e.g., "ldap"
79
- accountId: z.string(), // Provider-specific account ID (e.g., LDAP username)
80
- password: z.string(), // Hashed password
81
- autoUpdateUser: z.boolean().optional(), // Update existing user's name
67
+ providerId: z.string(),
68
+ accountId: z.string(),
69
+ password: z.string(),
70
+ autoUpdateUser: z.boolean().optional(),
82
71
  });
83
72
 
84
73
  const UpsertExternalUserOutputSchema = z.object({
85
74
  userId: z.string(),
86
- created: z.boolean(), // true if new user was created, false if existing
75
+ created: z.boolean(),
87
76
  });
88
77
 
89
78
  const CreateSessionInputSchema = z.object({
@@ -95,7 +84,7 @@ const CreateSessionInputSchema = z.object({
95
84
  const CreateCredentialUserInputSchema = z.object({
96
85
  email: z.string().email(),
97
86
  name: z.string().min(1),
98
- password: z.string(), // Validated against passwordSchema on backend
87
+ password: z.string(),
99
88
  });
100
89
 
101
90
  const CreateCredentialUserOutputSchema = z.object({
@@ -106,103 +95,132 @@ const CreateCredentialUserOutputSchema = z.object({
106
95
  export const authContract = {
107
96
  // ==========================================================================
108
97
  // ANONYMOUS ENDPOINTS (userType: "anonymous")
109
- // These can be called without authentication (login/registration pages)
110
98
  // ==========================================================================
111
99
 
112
- getEnabledStrategies: _base
113
- .meta({ userType: "anonymous" })
114
- .output(z.array(EnabledStrategyDtoSchema)),
100
+ getEnabledStrategies: proc({
101
+ operationType: "query",
102
+ userType: "anonymous",
103
+ access: [],
104
+ }).output(z.array(EnabledStrategyDtoSchema)),
115
105
 
116
- getRegistrationStatus: _base
117
- .meta({ userType: "anonymous" })
118
- .output(RegistrationStatusSchema),
106
+ getRegistrationStatus: proc({
107
+ operationType: "query",
108
+ userType: "anonymous",
109
+ access: [],
110
+ }).output(RegistrationStatusSchema),
119
111
 
120
112
  // ==========================================================================
121
- // AUTHENTICATED ENDPOINTS (userType: "authenticated" - no specific permission)
113
+ // AUTHENTICATED ENDPOINTS (userType: "authenticated")
122
114
  // ==========================================================================
123
115
 
124
- permissions: _base
125
- .meta({ userType: "authenticated" }) // Any authenticated user can check their own permissions
126
- .output(z.object({ permissions: z.array(z.string()) })),
116
+ accessRules: proc({
117
+ operationType: "query",
118
+ userType: "authenticated",
119
+ access: [],
120
+ }).output(z.object({ accessRules: z.array(z.string()) })),
127
121
 
128
122
  // ==========================================================================
129
- // USER MANAGEMENT (userType: "user" with permissions)
123
+ // USER MANAGEMENT (userType: "user" with access)
130
124
  // ==========================================================================
131
125
 
132
- getUsers: _base
133
- .meta({ userType: "user", permissions: [permissions.usersRead.id] })
134
- .output(z.array(UserDtoSchema)),
126
+ getUsers: proc({
127
+ operationType: "query",
128
+ userType: "user",
129
+ access: [authAccess.users.read],
130
+ }).output(z.array(UserDtoSchema)),
135
131
 
136
- deleteUser: _base
137
- .meta({ userType: "user", permissions: [permissions.usersManage.id] })
132
+ deleteUser: proc({
133
+ operationType: "mutation",
134
+ userType: "user",
135
+ access: [authAccess.users.manage],
136
+ })
138
137
  .input(z.string())
139
138
  .output(z.void()),
140
139
 
141
- createCredentialUser: _base
142
- .meta({ userType: "user", permissions: [permissions.usersCreate.id] })
140
+ createCredentialUser: proc({
141
+ operationType: "mutation",
142
+ userType: "user",
143
+ access: [authAccess.users.create],
144
+ })
143
145
  .input(CreateCredentialUserInputSchema)
144
146
  .output(CreateCredentialUserOutputSchema),
145
147
 
146
- updateUserRoles: _base
147
- .meta({ userType: "user", permissions: [permissions.usersManage.id] })
148
- .input(
149
- z.object({
150
- userId: z.string(),
151
- roles: z.array(z.string()),
152
- })
153
- )
148
+ updateUserRoles: proc({
149
+ operationType: "mutation",
150
+ userType: "user",
151
+ access: [authAccess.users.manage],
152
+ })
153
+ .input(z.object({ userId: z.string(), roles: z.array(z.string()) }))
154
154
  .output(z.void()),
155
155
 
156
156
  // ==========================================================================
157
- // ROLE MANAGEMENT (userType: "user" with permissions)
157
+ // ROLE MANAGEMENT (userType: "user" with access)
158
158
  // ==========================================================================
159
159
 
160
- getRoles: _base
161
- .meta({ userType: "user", permissions: [permissions.rolesRead.id] })
162
- .output(z.array(RoleDtoSchema)),
163
-
164
- getPermissions: _base
165
- .meta({ userType: "user", permissions: [permissions.rolesRead.id] })
166
- .output(z.array(PermissionDtoSchema)),
167
-
168
- createRole: _base
169
- .meta({ userType: "user", permissions: [permissions.rolesCreate.id] })
160
+ getRoles: proc({
161
+ operationType: "query",
162
+ userType: "user",
163
+ access: [authAccess.roles.read],
164
+ }).output(z.array(RoleDtoSchema)),
165
+
166
+ getAccessRules: proc({
167
+ operationType: "query",
168
+ userType: "user",
169
+ access: [authAccess.roles.read],
170
+ }).output(z.array(AccessRuleDtoSchema)),
171
+
172
+ createRole: proc({
173
+ operationType: "mutation",
174
+ userType: "user",
175
+ access: [authAccess.roles.create],
176
+ })
170
177
  .input(
171
178
  z.object({
172
179
  name: z.string(),
173
180
  description: z.string().optional(),
174
- permissions: z.array(z.string()),
181
+ accessRules: z.array(z.string()),
175
182
  })
176
183
  )
177
184
  .output(z.void()),
178
185
 
179
- updateRole: _base
180
- .meta({ userType: "user", permissions: [permissions.rolesUpdate.id] })
186
+ updateRole: proc({
187
+ operationType: "mutation",
188
+ userType: "user",
189
+ access: [authAccess.roles.update],
190
+ })
181
191
  .input(
182
192
  z.object({
183
193
  id: z.string(),
184
194
  name: z.string().optional(),
185
195
  description: z.string().optional(),
186
- permissions: z.array(z.string()),
196
+ accessRules: z.array(z.string()),
187
197
  })
188
198
  )
189
199
  .output(z.void()),
190
200
 
191
- deleteRole: _base
192
- .meta({ userType: "user", permissions: [permissions.rolesDelete.id] })
201
+ deleteRole: proc({
202
+ operationType: "mutation",
203
+ userType: "user",
204
+ access: [authAccess.roles.delete],
205
+ })
193
206
  .input(z.string())
194
207
  .output(z.void()),
195
208
 
196
209
  // ==========================================================================
197
- // STRATEGY MANAGEMENT (userType: "user" with permissions)
210
+ // STRATEGY MANAGEMENT (userType: "user" with access)
198
211
  // ==========================================================================
199
212
 
200
- getStrategies: _base
201
- .meta({ userType: "user", permissions: [permissions.strategiesManage.id] })
202
- .output(z.array(StrategyDtoSchema)),
213
+ getStrategies: proc({
214
+ operationType: "query",
215
+ userType: "user",
216
+ access: [authAccess.strategies],
217
+ }).output(z.array(StrategyDtoSchema)),
203
218
 
204
- updateStrategy: _base
205
- .meta({ userType: "user", permissions: [permissions.strategiesManage.id] })
219
+ updateStrategy: proc({
220
+ operationType: "mutation",
221
+ userType: "user",
222
+ access: [authAccess.strategies],
223
+ })
206
224
  .input(
207
225
  z.object({
208
226
  id: z.string(),
@@ -212,26 +230,27 @@ export const authContract = {
212
230
  )
213
231
  .output(z.object({ success: z.boolean() })),
214
232
 
215
- reloadAuth: _base
216
- .meta({ userType: "user", permissions: [permissions.strategiesManage.id] })
217
- .output(z.object({ success: z.boolean() })),
233
+ reloadAuth: proc({
234
+ operationType: "mutation",
235
+ userType: "user",
236
+ access: [authAccess.strategies],
237
+ }).output(z.object({ success: z.boolean() })),
218
238
 
219
239
  // ==========================================================================
220
- // REGISTRATION MANAGEMENT (userType: "user" with permissions)
240
+ // REGISTRATION MANAGEMENT (userType: "user" with access)
221
241
  // ==========================================================================
222
242
 
223
- getRegistrationSchema: _base
224
- .meta({
225
- userType: "user",
226
- permissions: [permissions.registrationManage.id],
227
- })
228
- .output(z.record(z.string(), z.unknown())),
229
-
230
- setRegistrationStatus: _base
231
- .meta({
232
- userType: "user",
233
- permissions: [permissions.registrationManage.id],
234
- })
243
+ getRegistrationSchema: proc({
244
+ operationType: "query",
245
+ userType: "user",
246
+ access: [authAccess.registration],
247
+ }).output(z.record(z.string(), z.unknown())),
248
+
249
+ setRegistrationStatus: proc({
250
+ operationType: "mutation",
251
+ userType: "user",
252
+ access: [authAccess.registration],
253
+ })
235
254
  .input(RegistrationStatusSchema)
236
255
  .output(z.object({ success: z.boolean() })),
237
256
 
@@ -239,48 +258,41 @@ export const authContract = {
239
258
  // INTERNAL SERVICE ENDPOINTS (userType: "service")
240
259
  // ==========================================================================
241
260
 
242
- /**
243
- * Get permissions assigned to the anonymous role.
244
- * Used by core AuthService for permission checks on public endpoints.
245
- */
246
- getAnonymousPermissions: _base
247
- .meta({ userType: "service" })
248
- .output(z.array(z.string())),
261
+ getAnonymousAccessRules: proc({
262
+ operationType: "query",
263
+ userType: "service",
264
+ access: [],
265
+ }).output(z.array(z.string())),
249
266
 
250
- /**
251
- * Find a user by email address.
252
- * Used by external auth providers (e.g., LDAP) to check if a user exists.
253
- */
254
- findUserByEmail: _base
255
- .meta({ userType: "service" })
267
+ findUserByEmail: proc({
268
+ operationType: "query",
269
+ userType: "service",
270
+ access: [],
271
+ })
256
272
  .input(FindUserByEmailInputSchema)
257
273
  .output(FindUserByEmailOutputSchema),
258
274
 
259
- /**
260
- * Upsert a user from an external auth provider.
261
- * Creates user + account if new, or updates user if autoUpdateUser is true.
262
- * Used by external auth providers (e.g., LDAP) to sync users.
263
- */
264
- upsertExternalUser: _base
265
- .meta({ userType: "service" })
275
+ upsertExternalUser: proc({
276
+ operationType: "mutation",
277
+ userType: "service",
278
+ access: [],
279
+ })
266
280
  .input(UpsertExternalUserInputSchema)
267
281
  .output(UpsertExternalUserOutputSchema),
268
282
 
269
- /**
270
- * Create a session for a user.
271
- * Used by external auth providers (e.g., LDAP) after successful authentication.
272
- */
273
- createSession: _base
274
- .meta({ userType: "service" })
283
+ createSession: proc({
284
+ operationType: "mutation",
285
+ userType: "service",
286
+ access: [],
287
+ })
275
288
  .input(CreateSessionInputSchema)
276
289
  .output(z.object({ sessionId: z.string() })),
277
290
 
278
- /**
279
- * Get a user by their ID.
280
- * Used by notification backend to fetch user email for contact resolution.
281
- */
282
- getUserById: _base
283
- .meta({ userType: "service" })
291
+ getUserById: proc({
292
+ operationType: "query",
293
+ userType: "service",
294
+ access: [],
295
+ })
284
296
  .input(z.object({ userId: z.string() }))
285
297
  .output(
286
298
  z
@@ -292,49 +304,46 @@ export const authContract = {
292
304
  .optional()
293
305
  ),
294
306
 
295
- /**
296
- * Filter a list of user IDs to only those who have a specific permission.
297
- * Used by SignalService to send signals only to authorized users.
298
- */
299
- filterUsersByPermission: _base
300
- .meta({ userType: "service" })
307
+ filterUsersByAccessRule: proc({
308
+ operationType: "query",
309
+ userType: "service",
310
+ access: [],
311
+ })
301
312
  .input(
302
313
  z.object({
303
314
  userIds: z.array(z.string()),
304
- permission: z.string(), // Fully-qualified permission ID
315
+ accessRule: z.string(),
305
316
  })
306
317
  )
307
- .output(z.array(z.string())), // Returns filtered user IDs
318
+ .output(z.array(z.string())),
308
319
 
309
320
  // ==========================================================================
310
- // APPLICATION MANAGEMENT (userType: "user" with permissions)
311
- // External API applications (API keys) with RBAC integration
321
+ // APPLICATION MANAGEMENT (userType: "user" with access)
312
322
  // ==========================================================================
313
323
 
314
- getApplications: _base
315
- .meta({
316
- userType: "user",
317
- permissions: [permissions.applicationsManage.id],
318
- })
319
- .output(
320
- z.array(
321
- z.object({
322
- id: z.string(),
323
- name: z.string(),
324
- description: z.string().optional().nullable(),
325
- roles: z.array(z.string()),
326
- createdById: z.string(),
327
- createdAt: z.coerce.date(),
328
- lastUsedAt: z.coerce.date().optional().nullable(),
329
- })
330
- )
331
- ),
324
+ getApplications: proc({
325
+ operationType: "query",
326
+ userType: "user",
327
+ access: [authAccess.applications],
328
+ }).output(
329
+ z.array(
330
+ z.object({
331
+ id: z.string(),
332
+ name: z.string(),
333
+ description: z.string().optional().nullable(),
334
+ roles: z.array(z.string()),
335
+ createdById: z.string(),
336
+ createdAt: z.coerce.date(),
337
+ lastUsedAt: z.coerce.date().optional().nullable(),
338
+ })
339
+ )
340
+ ),
332
341
 
333
- createApplication: _base
334
- .meta({
335
- userType: "user",
336
- permissions: [permissions.applicationsManage.id],
337
- })
342
+ createApplication: proc({
343
+ operationType: "mutation",
344
+ userType: "user",
345
+ access: [authAccess.applications],
346
+ })
338
347
  .input(
339
348
  z.object({
340
349
  name: z.string().min(1).max(100),
@@ -351,15 +360,15 @@ export const authContract = {
351
360
  createdById: z.string(),
352
361
  createdAt: z.coerce.date(),
353
362
  }),
354
- secret: z.string(), // Full secret - ONLY shown once!
363
+ secret: z.string(),
355
364
  })
356
365
  ),
357
366
 
358
- updateApplication: _base
359
- .meta({
360
- userType: "user",
361
- permissions: [permissions.applicationsManage.id],
362
- })
367
+ updateApplication: proc({
368
+ operationType: "mutation",
369
+ userType: "user",
370
+ access: [authAccess.applications],
371
+ })
363
372
  .input(
364
373
  z.object({
365
374
  id: z.string(),
@@ -370,50 +379,47 @@ export const authContract = {
370
379
  )
371
380
  .output(z.void()),
372
381
 
373
- deleteApplication: _base
374
- .meta({
375
- userType: "user",
376
- permissions: [permissions.applicationsManage.id],
377
- })
382
+ deleteApplication: proc({
383
+ operationType: "mutation",
384
+ userType: "user",
385
+ access: [authAccess.applications],
386
+ })
378
387
  .input(z.string())
379
388
  .output(z.void()),
380
389
 
381
- regenerateApplicationSecret: _base
382
- .meta({
383
- userType: "user",
384
- permissions: [permissions.applicationsManage.id],
385
- })
390
+ regenerateApplicationSecret: proc({
391
+ operationType: "mutation",
392
+ userType: "user",
393
+ access: [authAccess.applications],
394
+ })
386
395
  .input(z.string())
387
- .output(z.object({ secret: z.string() })), // New secret - shown once
396
+ .output(z.object({ secret: z.string() })),
388
397
 
389
398
  // ==========================================================================
390
- // TEAM MANAGEMENT (userType: "authenticated" with permissions)
391
- // Resource-level access control via teams
392
- // Both users and applications can manage teams with proper permissions
399
+ // TEAM MANAGEMENT (userType: "authenticated" with access)
393
400
  // ==========================================================================
394
401
 
395
- getTeams: _base
396
- .meta({
397
- userType: "authenticated",
398
- permissions: [permissions.teamsRead.id],
399
- })
400
- .output(
401
- z.array(
402
- z.object({
403
- id: z.string(),
404
- name: z.string(),
405
- description: z.string().optional().nullable(),
406
- memberCount: z.number(),
407
- isManager: z.boolean(),
408
- })
409
- )
410
- ),
402
+ getTeams: proc({
403
+ operationType: "query",
404
+ userType: "authenticated",
405
+ access: [authAccess.teams.read],
406
+ }).output(
407
+ z.array(
408
+ z.object({
409
+ id: z.string(),
410
+ name: z.string(),
411
+ description: z.string().optional().nullable(),
412
+ memberCount: z.number(),
413
+ isManager: z.boolean(),
414
+ })
415
+ )
416
+ ),
411
417
 
412
- getTeam: _base
413
- .meta({
414
- userType: "authenticated",
415
- permissions: [permissions.teamsRead.id],
416
- })
418
+ getTeam: proc({
419
+ operationType: "query",
420
+ userType: "authenticated",
421
+ access: [authAccess.teams.read],
422
+ })
417
423
  .input(z.object({ teamId: z.string() }))
418
424
  .output(
419
425
  z
@@ -431,11 +437,11 @@ export const authContract = {
431
437
  .optional()
432
438
  ),
433
439
 
434
- createTeam: _base
435
- .meta({
436
- userType: "authenticated",
437
- permissions: [permissions.teamsManage.id],
438
- })
440
+ createTeam: proc({
441
+ operationType: "mutation",
442
+ userType: "authenticated",
443
+ access: [authAccess.teams.manage],
444
+ })
439
445
  .input(
440
446
  z.object({
441
447
  name: z.string().min(1).max(100),
@@ -444,11 +450,11 @@ export const authContract = {
444
450
  )
445
451
  .output(z.object({ id: z.string() })),
446
452
 
447
- updateTeam: _base
448
- .meta({
449
- userType: "authenticated",
450
- permissions: [permissions.teamsRead.id],
451
- })
453
+ updateTeam: proc({
454
+ operationType: "mutation",
455
+ userType: "authenticated",
456
+ access: [authAccess.teams.read],
457
+ })
452
458
  .input(
453
459
  z.object({
454
460
  id: z.string(),
@@ -458,51 +464,51 @@ export const authContract = {
458
464
  )
459
465
  .output(z.void()),
460
466
 
461
- deleteTeam: _base
462
- .meta({
463
- userType: "authenticated",
464
- permissions: [permissions.teamsManage.id],
465
- })
467
+ deleteTeam: proc({
468
+ operationType: "mutation",
469
+ userType: "authenticated",
470
+ access: [authAccess.teams.manage],
471
+ })
466
472
  .input(z.string())
467
473
  .output(z.void()),
468
474
 
469
- addUserToTeam: _base
470
- .meta({
471
- userType: "authenticated",
472
- permissions: [permissions.teamsRead.id],
473
- })
475
+ addUserToTeam: proc({
476
+ operationType: "mutation",
477
+ userType: "authenticated",
478
+ access: [authAccess.teams.read],
479
+ })
474
480
  .input(z.object({ teamId: z.string(), userId: z.string() }))
475
481
  .output(z.void()),
476
482
 
477
- removeUserFromTeam: _base
478
- .meta({
479
- userType: "authenticated",
480
- permissions: [permissions.teamsRead.id],
481
- })
483
+ removeUserFromTeam: proc({
484
+ operationType: "mutation",
485
+ userType: "authenticated",
486
+ access: [authAccess.teams.read],
487
+ })
482
488
  .input(z.object({ teamId: z.string(), userId: z.string() }))
483
489
  .output(z.void()),
484
490
 
485
- addTeamManager: _base
486
- .meta({
487
- userType: "authenticated",
488
- permissions: [permissions.teamsManage.id],
489
- })
491
+ addTeamManager: proc({
492
+ operationType: "mutation",
493
+ userType: "authenticated",
494
+ access: [authAccess.teams.manage],
495
+ })
490
496
  .input(z.object({ teamId: z.string(), userId: z.string() }))
491
497
  .output(z.void()),
492
498
 
493
- removeTeamManager: _base
494
- .meta({
495
- userType: "authenticated",
496
- permissions: [permissions.teamsManage.id],
497
- })
499
+ removeTeamManager: proc({
500
+ operationType: "mutation",
501
+ userType: "authenticated",
502
+ access: [authAccess.teams.manage],
503
+ })
498
504
  .input(z.object({ teamId: z.string(), userId: z.string() }))
499
505
  .output(z.void()),
500
506
 
501
- getResourceTeamAccess: _base
502
- .meta({
503
- userType: "authenticated",
504
- permissions: [permissions.teamsRead.id],
505
- })
507
+ getResourceTeamAccess: proc({
508
+ operationType: "query",
509
+ userType: "authenticated",
510
+ access: [authAccess.teams.read],
511
+ })
506
512
  .input(z.object({ resourceType: z.string(), resourceId: z.string() }))
507
513
  .output(
508
514
  z.array(
@@ -515,11 +521,11 @@ export const authContract = {
515
521
  )
516
522
  ),
517
523
 
518
- setResourceTeamAccess: _base
519
- .meta({
520
- userType: "authenticated",
521
- permissions: [permissions.teamsManage.id],
522
- })
524
+ setResourceTeamAccess: proc({
525
+ operationType: "mutation",
526
+ userType: "authenticated",
527
+ access: [authAccess.teams.manage],
528
+ })
523
529
  .input(
524
530
  z.object({
525
531
  resourceType: z.string(),
@@ -531,11 +537,11 @@ export const authContract = {
531
537
  )
532
538
  .output(z.void()),
533
539
 
534
- removeResourceTeamAccess: _base
535
- .meta({
536
- userType: "authenticated",
537
- permissions: [permissions.teamsManage.id],
538
- })
540
+ removeResourceTeamAccess: proc({
541
+ operationType: "mutation",
542
+ userType: "authenticated",
543
+ access: [authAccess.teams.manage],
544
+ })
539
545
  .input(
540
546
  z.object({
541
547
  resourceType: z.string(),
@@ -545,20 +551,19 @@ export const authContract = {
545
551
  )
546
552
  .output(z.void()),
547
553
 
548
- // Resource-level access settings (teamOnly flag)
549
- getResourceAccessSettings: _base
550
- .meta({
551
- userType: "authenticated",
552
- permissions: [permissions.teamsRead.id],
553
- })
554
+ getResourceAccessSettings: proc({
555
+ operationType: "query",
556
+ userType: "authenticated",
557
+ access: [authAccess.teams.read],
558
+ })
554
559
  .input(z.object({ resourceType: z.string(), resourceId: z.string() }))
555
560
  .output(z.object({ teamOnly: z.boolean() })),
556
561
 
557
- setResourceAccessSettings: _base
558
- .meta({
559
- userType: "authenticated",
560
- permissions: [permissions.teamsManage.id],
561
- })
562
+ setResourceAccessSettings: proc({
563
+ operationType: "mutation",
564
+ userType: "authenticated",
565
+ access: [authAccess.teams.manage],
566
+ })
562
567
  .input(
563
568
  z.object({
564
569
  resourceType: z.string(),
@@ -572,8 +577,11 @@ export const authContract = {
572
577
  // S2S ENDPOINTS FOR TEAM ACCESS (userType: "service")
573
578
  // ==========================================================================
574
579
 
575
- checkResourceTeamAccess: _base
576
- .meta({ userType: "service" })
580
+ checkResourceTeamAccess: proc({
581
+ operationType: "query",
582
+ userType: "service",
583
+ access: [],
584
+ })
577
585
  .input(
578
586
  z.object({
579
587
  userId: z.string(),
@@ -581,13 +589,16 @@ export const authContract = {
581
589
  resourceType: z.string(),
582
590
  resourceId: z.string(),
583
591
  action: z.enum(["read", "manage"]),
584
- hasGlobalPermission: z.boolean(),
592
+ hasGlobalAccess: z.boolean(),
585
593
  })
586
594
  )
587
595
  .output(z.object({ hasAccess: z.boolean() })),
588
596
 
589
- getAccessibleResourceIds: _base
590
- .meta({ userType: "service" })
597
+ getAccessibleResourceIds: proc({
598
+ operationType: "query",
599
+ userType: "service",
600
+ access: [],
601
+ })
591
602
  .input(
592
603
  z.object({
593
604
  userId: z.string(),
@@ -595,13 +606,16 @@ export const authContract = {
595
606
  resourceType: z.string(),
596
607
  resourceIds: z.array(z.string()),
597
608
  action: z.enum(["read", "manage"]),
598
- hasGlobalPermission: z.boolean(),
609
+ hasGlobalAccess: z.boolean(),
599
610
  })
600
611
  )
601
612
  .output(z.array(z.string())),
602
613
 
603
- deleteResourceGrants: _base
604
- .meta({ userType: "service" })
614
+ deleteResourceGrants: proc({
615
+ operationType: "mutation",
616
+ userType: "service",
617
+ access: [],
618
+ })
605
619
  .input(z.object({ resourceType: z.string(), resourceId: z.string() }))
606
620
  .output(z.void()),
607
621
  };
@@ -1,58 +0,0 @@
1
- import type { Permission } from "@checkstack/common";
2
-
3
- export const permissions = {
4
- usersRead: {
5
- id: "users.read",
6
- description: "List all users",
7
- },
8
- usersCreate: {
9
- id: "users.create",
10
- description: "Create new users (credential strategy)",
11
- },
12
- usersManage: {
13
- id: "users.manage",
14
- description: "Delete users",
15
- },
16
- rolesRead: {
17
- id: "roles.read",
18
- description: "Read and list roles",
19
- },
20
- rolesCreate: {
21
- id: "roles.create",
22
- description: "Create new roles",
23
- },
24
- rolesUpdate: {
25
- id: "roles.update",
26
- description: "Update role names and permissions",
27
- },
28
- rolesDelete: {
29
- id: "roles.delete",
30
- description: "Delete roles",
31
- },
32
- rolesManage: {
33
- id: "roles.manage",
34
- description: "Assign roles to users",
35
- },
36
- strategiesManage: {
37
- id: "strategies.manage",
38
- description: "Manage authentication strategies and settings",
39
- },
40
- registrationManage: {
41
- id: "registration.manage",
42
- description: "Manage user registration settings",
43
- },
44
- applicationsManage: {
45
- id: "applications.manage",
46
- description: "Create, update, delete, and view external applications",
47
- },
48
- teamsRead: {
49
- id: "teams.read",
50
- description: "View teams and team memberships",
51
- },
52
- teamsManage: {
53
- id: "teams.manage",
54
- description: "Create, delete, and manage all teams and resource access",
55
- },
56
- } satisfies Record<string, Permission>;
57
-
58
- export const permissionList = Object.values(permissions);