@checkstack/auth-backend 0.2.2 โ 0.4.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 +37 -0
- package/package.json +1 -1
- package/src/index.ts +43 -86
- package/src/router.test.ts +680 -30
- package/src/router.ts +406 -127
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,42 @@
|
|
|
1
1
|
# @checkstack/auth-backend
|
|
2
2
|
|
|
3
|
+
## 0.4.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- d94121b: Add group-to-role mapping for SAML and LDAP authentication
|
|
8
|
+
|
|
9
|
+
**Features:**
|
|
10
|
+
|
|
11
|
+
- SAML and LDAP users can now be automatically assigned Checkstack roles based on their directory group memberships
|
|
12
|
+
- Configure group mappings in the authentication strategy settings with dynamic role dropdowns
|
|
13
|
+
- Managed role sync: roles configured in mappings are fully synchronized (added when user gains group, removed when user leaves group)
|
|
14
|
+
- Unmanaged roles (manually assigned, not in any mapping) are preserved during sync
|
|
15
|
+
- Optional default role for all users from a directory
|
|
16
|
+
|
|
17
|
+
**Bug Fix:**
|
|
18
|
+
|
|
19
|
+
- Fixed `x-options-resolver` not working for fields inside arrays with `.default([])` in DynamicForm schemas
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- Updated dependencies [d94121b]
|
|
24
|
+
- @checkstack/backend-api@0.3.3
|
|
25
|
+
- @checkstack/auth-common@0.5.0
|
|
26
|
+
- @checkstack/command-backend@0.1.3
|
|
27
|
+
|
|
28
|
+
## 0.3.0
|
|
29
|
+
|
|
30
|
+
### Minor Changes
|
|
31
|
+
|
|
32
|
+
- 993d81a: Export role ID constants (USERS_ROLE_ID, ANONYMOUS_ROLE_ID, APPLICATIONS_ROLE_ID) for consistent usage across the codebase. Added protection to prevent deleting users with the admin role.
|
|
33
|
+
- df6ac7b: Added onboarding flow and user profile
|
|
34
|
+
|
|
35
|
+
### Patch Changes
|
|
36
|
+
|
|
37
|
+
- Updated dependencies [df6ac7b]
|
|
38
|
+
- @checkstack/auth-common@0.4.0
|
|
39
|
+
|
|
3
40
|
## 0.2.2
|
|
4
41
|
|
|
5
42
|
### Patch Changes
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -17,13 +17,13 @@ import {
|
|
|
17
17
|
} from "@checkstack/auth-common";
|
|
18
18
|
import { NotificationApi } from "@checkstack/notification-common";
|
|
19
19
|
import * as schema from "./schema";
|
|
20
|
-
import { eq, inArray
|
|
20
|
+
import { eq, inArray } from "drizzle-orm";
|
|
21
21
|
import { NodePgDatabase } from "drizzle-orm/node-postgres";
|
|
22
22
|
import { User } from "better-auth/types";
|
|
23
|
-
import {
|
|
23
|
+
import { verifyPassword } from "better-auth/crypto";
|
|
24
24
|
import { createExtensionPoint } from "@checkstack/backend-api";
|
|
25
25
|
import { enrichUser } from "./utils/user";
|
|
26
|
-
import { createAuthRouter } from "./router";
|
|
26
|
+
import { ADMIN_ROLE_ID, createAuthRouter } from "./router";
|
|
27
27
|
import { validateStrategySchema } from "./utils/validate-schema";
|
|
28
28
|
import {
|
|
29
29
|
strategyMetaConfigV1,
|
|
@@ -43,7 +43,7 @@ export interface BetterAuthExtensionPoint {
|
|
|
43
43
|
|
|
44
44
|
export const betterAuthExtensionPoint =
|
|
45
45
|
createExtensionPoint<BetterAuthExtensionPoint>(
|
|
46
|
-
"auth.betterAuthExtensionPoint"
|
|
46
|
+
"auth.betterAuthExtensionPoint",
|
|
47
47
|
);
|
|
48
48
|
|
|
49
49
|
/**
|
|
@@ -101,7 +101,7 @@ async function syncAccessRulesToDb({
|
|
|
101
101
|
|
|
102
102
|
for (const rule of accessRules) {
|
|
103
103
|
const hasAccess = adminRoleAccessRules.some(
|
|
104
|
-
(rp) => rp.accessRuleId === rule.id
|
|
104
|
+
(rp) => rp.accessRuleId === rule.id,
|
|
105
105
|
);
|
|
106
106
|
|
|
107
107
|
if (!hasAccess) {
|
|
@@ -126,12 +126,12 @@ async function syncAccessRulesToDb({
|
|
|
126
126
|
const registeredIds = new Set(accessRules.map((r) => r.id));
|
|
127
127
|
const allDbAccessRules = await database.select().from(schema.accessRule);
|
|
128
128
|
const orphanAccessRules = allDbAccessRules.filter(
|
|
129
|
-
(p) => !registeredIds.has(p.id)
|
|
129
|
+
(p) => !registeredIds.has(p.id),
|
|
130
130
|
);
|
|
131
131
|
|
|
132
132
|
if (orphanAccessRules.length > 0) {
|
|
133
133
|
logger.debug(
|
|
134
|
-
`๐งน Removing ${orphanAccessRules.length} orphan access rule(s)
|
|
134
|
+
`๐งน Removing ${orphanAccessRules.length} orphan access rule(s)...`,
|
|
135
135
|
);
|
|
136
136
|
for (const orphan of orphanAccessRules) {
|
|
137
137
|
// Delete role_access_rule entries first (FK doesn't cascade)
|
|
@@ -176,7 +176,7 @@ async function syncAuthenticatedDefaultAccessRulesToUsersRole({
|
|
|
176
176
|
}) {
|
|
177
177
|
// Debug: log all access rules with their isDefault status
|
|
178
178
|
logger.debug(
|
|
179
|
-
`[DEBUG] All access rules received (${accessRules.length} total)
|
|
179
|
+
`[DEBUG] All access rules received (${accessRules.length} total):`,
|
|
180
180
|
);
|
|
181
181
|
for (const r of accessRules) {
|
|
182
182
|
logger.debug(` -> ${r.id}: isDefault=${r.isDefault}`);
|
|
@@ -184,11 +184,11 @@ async function syncAuthenticatedDefaultAccessRulesToUsersRole({
|
|
|
184
184
|
|
|
185
185
|
const defaultRules = accessRules.filter((r) => r.isDefault);
|
|
186
186
|
logger.debug(
|
|
187
|
-
`๐ฅ Found ${defaultRules.length} authenticated default access rules to sync to users role
|
|
187
|
+
`๐ฅ Found ${defaultRules.length} authenticated default access rules to sync to users role`,
|
|
188
188
|
);
|
|
189
189
|
if (defaultRules.length === 0) {
|
|
190
190
|
logger.debug(
|
|
191
|
-
` -> No authenticated default access rules found, skipping sync
|
|
191
|
+
` -> No authenticated default access rules found, skipping sync`,
|
|
192
192
|
);
|
|
193
193
|
return;
|
|
194
194
|
}
|
|
@@ -213,7 +213,7 @@ async function syncAuthenticatedDefaultAccessRulesToUsersRole({
|
|
|
213
213
|
}
|
|
214
214
|
|
|
215
215
|
const hasAccess = usersRoleAccessRules.some(
|
|
216
|
-
(rp) => rp.accessRuleId === rule.id
|
|
216
|
+
(rp) => rp.accessRuleId === rule.id,
|
|
217
217
|
);
|
|
218
218
|
|
|
219
219
|
if (!hasAccess) {
|
|
@@ -222,7 +222,7 @@ async function syncAuthenticatedDefaultAccessRulesToUsersRole({
|
|
|
222
222
|
accessRuleId: rule.id,
|
|
223
223
|
});
|
|
224
224
|
logger.debug(
|
|
225
|
-
` -> Assigned authenticated default access rule ${rule.id} to users role
|
|
225
|
+
` -> Assigned authenticated default access rule ${rule.id} to users role`,
|
|
226
226
|
);
|
|
227
227
|
}
|
|
228
228
|
}
|
|
@@ -243,7 +243,7 @@ async function syncPublicDefaultAccessRulesToAnonymousRole({
|
|
|
243
243
|
}) {
|
|
244
244
|
const publicDefaults = accessRules.filter((r) => r.isPublic);
|
|
245
245
|
logger.debug(
|
|
246
|
-
`๐ Found ${publicDefaults.length} public default access rules to sync to anonymous role
|
|
246
|
+
`๐ Found ${publicDefaults.length} public default access rules to sync to anonymous role`,
|
|
247
247
|
);
|
|
248
248
|
if (publicDefaults.length === 0) {
|
|
249
249
|
logger.debug(` -> No public default access rules found, skipping sync`);
|
|
@@ -270,7 +270,7 @@ async function syncPublicDefaultAccessRulesToAnonymousRole({
|
|
|
270
270
|
}
|
|
271
271
|
|
|
272
272
|
const hasAccess = anonymousRoleAccessRules.some(
|
|
273
|
-
(rp) => rp.accessRuleId === rule.id
|
|
273
|
+
(rp) => rp.accessRuleId === rule.id,
|
|
274
274
|
);
|
|
275
275
|
|
|
276
276
|
if (!hasAccess) {
|
|
@@ -279,7 +279,7 @@ async function syncPublicDefaultAccessRulesToAnonymousRole({
|
|
|
279
279
|
accessRuleId: rule.id,
|
|
280
280
|
});
|
|
281
281
|
logger.debug(
|
|
282
|
-
` -> Assigned public default access rule ${rule.id} to anonymous role
|
|
282
|
+
` -> Assigned public default access rule ${rule.id} to anonymous role`,
|
|
283
283
|
);
|
|
284
284
|
}
|
|
285
285
|
}
|
|
@@ -317,7 +317,7 @@ export default createBackendPlugin({
|
|
|
317
317
|
const message =
|
|
318
318
|
error instanceof Error ? error.message : String(error);
|
|
319
319
|
throw new Error(
|
|
320
|
-
`Failed to register authentication strategy "${s.id}": ${message}
|
|
320
|
+
`Failed to register authentication strategy "${s.id}": ${message}`,
|
|
321
321
|
);
|
|
322
322
|
}
|
|
323
323
|
strategies.push(s);
|
|
@@ -385,7 +385,7 @@ export default createBackendPlugin({
|
|
|
385
385
|
.select({ roleId: schema.applicationRole.roleId })
|
|
386
386
|
.from(schema.applicationRole)
|
|
387
387
|
.where(
|
|
388
|
-
eq(schema.applicationRole.applicationId, applicationId)
|
|
388
|
+
eq(schema.applicationRole.applicationId, applicationId),
|
|
389
389
|
);
|
|
390
390
|
|
|
391
391
|
const roleIds = appRoles.map((r) => r.roleId);
|
|
@@ -410,7 +410,7 @@ export default createBackendPlugin({
|
|
|
410
410
|
.select({ teamId: schema.applicationTeam.teamId })
|
|
411
411
|
.from(schema.applicationTeam)
|
|
412
412
|
.where(
|
|
413
|
-
eq(schema.applicationTeam.applicationId, applicationId)
|
|
413
|
+
eq(schema.applicationTeam.applicationId, applicationId),
|
|
414
414
|
);
|
|
415
415
|
const teamIds = appTeams.map((t) => t.teamId);
|
|
416
416
|
|
|
@@ -470,12 +470,12 @@ export default createBackendPlugin({
|
|
|
470
470
|
const initializeBetterAuth = async () => {
|
|
471
471
|
const socialProviders: Record<string, unknown> = {};
|
|
472
472
|
logger.debug(
|
|
473
|
-
`[auth-backend] Processing ${strategies.length} strategies
|
|
473
|
+
`[auth-backend] Processing ${strategies.length} strategies...`,
|
|
474
474
|
);
|
|
475
475
|
|
|
476
476
|
for (const strategy of strategies) {
|
|
477
477
|
logger.debug(
|
|
478
|
-
`[auth-backend] -> Processing auth strategy: ${strategy.id}
|
|
478
|
+
`[auth-backend] -> Processing auth strategy: ${strategy.id}`,
|
|
479
479
|
);
|
|
480
480
|
|
|
481
481
|
// Skip credential strategy - it's built into better-auth
|
|
@@ -486,20 +486,20 @@ export default createBackendPlugin({
|
|
|
486
486
|
strategy.id,
|
|
487
487
|
strategy.configSchema,
|
|
488
488
|
strategy.configVersion,
|
|
489
|
-
strategy.migrations
|
|
489
|
+
strategy.migrations,
|
|
490
490
|
);
|
|
491
491
|
|
|
492
492
|
// Check if strategy is enabled from meta config
|
|
493
493
|
const metaConfig = await config.get(
|
|
494
494
|
`${strategy.id}.meta`,
|
|
495
495
|
strategyMetaConfigV1,
|
|
496
|
-
STRATEGY_META_CONFIG_VERSION
|
|
496
|
+
STRATEGY_META_CONFIG_VERSION,
|
|
497
497
|
);
|
|
498
498
|
const enabled = metaConfig?.enabled ?? false;
|
|
499
499
|
|
|
500
500
|
if (!enabled) {
|
|
501
501
|
logger.debug(
|
|
502
|
-
`[auth-backend] -> Strategy ${strategy.id} is disabled, skipping
|
|
502
|
+
`[auth-backend] -> Strategy ${strategy.id} is disabled, skipping`,
|
|
503
503
|
);
|
|
504
504
|
continue;
|
|
505
505
|
}
|
|
@@ -508,23 +508,23 @@ export default createBackendPlugin({
|
|
|
508
508
|
logger.debug(
|
|
509
509
|
`[auth-backend] -> Config keys for ${
|
|
510
510
|
strategy.id
|
|
511
|
-
}: ${Object.keys(strategyConfig || {}).join(", ")}
|
|
511
|
+
}: ${Object.keys(strategyConfig || {}).join(", ")}`,
|
|
512
512
|
);
|
|
513
513
|
socialProviders[strategy.id] = strategyConfig;
|
|
514
514
|
logger.debug(
|
|
515
|
-
`[auth-backend] -> โ
Added ${strategy.id} to socialProviders
|
|
515
|
+
`[auth-backend] -> โ
Added ${strategy.id} to socialProviders`,
|
|
516
516
|
);
|
|
517
517
|
}
|
|
518
518
|
|
|
519
519
|
// Check if credential strategy is enabled from meta config
|
|
520
520
|
const credentialStrategy = strategies.find(
|
|
521
|
-
(s) => s.id === "credential"
|
|
521
|
+
(s) => s.id === "credential",
|
|
522
522
|
);
|
|
523
523
|
const credentialMetaConfig = credentialStrategy
|
|
524
524
|
? await config.get(
|
|
525
525
|
"credential.meta",
|
|
526
526
|
strategyMetaConfigV1,
|
|
527
|
-
STRATEGY_META_CONFIG_VERSION
|
|
527
|
+
STRATEGY_META_CONFIG_VERSION,
|
|
528
528
|
)
|
|
529
529
|
: undefined;
|
|
530
530
|
// Default to true on fresh installs (no meta config)
|
|
@@ -534,7 +534,7 @@ export default createBackendPlugin({
|
|
|
534
534
|
const platformRegistrationConfig = await config.get(
|
|
535
535
|
PLATFORM_REGISTRATION_CONFIG_ID,
|
|
536
536
|
platformRegistrationConfigV1,
|
|
537
|
-
PLATFORM_REGISTRATION_CONFIG_VERSION
|
|
537
|
+
PLATFORM_REGISTRATION_CONFIG_VERSION,
|
|
538
538
|
);
|
|
539
539
|
const registrationAllowed =
|
|
540
540
|
platformRegistrationConfig?.allowRegistration ?? true;
|
|
@@ -542,7 +542,7 @@ export default createBackendPlugin({
|
|
|
542
542
|
logger.debug(
|
|
543
543
|
`[auth-backend] Initializing Better Auth with ${
|
|
544
544
|
Object.keys(socialProviders).length
|
|
545
|
-
} social providers: ${Object.keys(socialProviders).join(", ")}
|
|
545
|
+
} social providers: ${Object.keys(socialProviders).join(", ")}`,
|
|
546
546
|
);
|
|
547
547
|
|
|
548
548
|
return betterAuth({
|
|
@@ -579,7 +579,7 @@ export default createBackendPlugin({
|
|
|
579
579
|
});
|
|
580
580
|
|
|
581
581
|
logger.debug(
|
|
582
|
-
`[auth-backend] Password reset email sent to user: ${user.id}
|
|
582
|
+
`[auth-backend] Password reset email sent to user: ${user.id}`,
|
|
583
583
|
);
|
|
584
584
|
},
|
|
585
585
|
resetPasswordTokenExpiresIn: 60 * 60, // 1 hour
|
|
@@ -611,12 +611,12 @@ export default createBackendPlugin({
|
|
|
611
611
|
roleId: "users",
|
|
612
612
|
});
|
|
613
613
|
logger.debug(
|
|
614
|
-
`[auth-backend] Assigned 'users' role to new user: ${user.id}
|
|
614
|
+
`[auth-backend] Assigned 'users' role to new user: ${user.id}`,
|
|
615
615
|
);
|
|
616
616
|
} catch (error) {
|
|
617
617
|
// Role might not exist yet on first boot, that's okay
|
|
618
618
|
logger.debug(
|
|
619
|
-
`[auth-backend] Could not assign 'users' role to ${user.id}: ${error}
|
|
619
|
+
`[auth-backend] Could not assign 'users' role to ${user.id}: ${error}`,
|
|
620
620
|
);
|
|
621
621
|
}
|
|
622
622
|
},
|
|
@@ -632,7 +632,7 @@ export default createBackendPlugin({
|
|
|
632
632
|
// Reload function for dynamic auth config changes
|
|
633
633
|
const reloadAuth = async () => {
|
|
634
634
|
logger.info(
|
|
635
|
-
"[auth-backend] Reloading authentication configuration..."
|
|
635
|
+
"[auth-backend] Reloading authentication configuration...",
|
|
636
636
|
);
|
|
637
637
|
auth = await initializeBetterAuth();
|
|
638
638
|
logger.info("[auth-backend] โ
Authentication reloaded successfully");
|
|
@@ -643,10 +643,10 @@ export default createBackendPlugin({
|
|
|
643
643
|
const adminRole = await database
|
|
644
644
|
.select()
|
|
645
645
|
.from(schema.role)
|
|
646
|
-
.where(eq(schema.role.id,
|
|
646
|
+
.where(eq(schema.role.id, ADMIN_ROLE_ID));
|
|
647
647
|
if (adminRole.length === 0) {
|
|
648
648
|
await database.insert(schema.role).values({
|
|
649
|
-
id:
|
|
649
|
+
id: ADMIN_ROLE_ID,
|
|
650
650
|
name: "Administrators",
|
|
651
651
|
isSystem: true,
|
|
652
652
|
});
|
|
@@ -706,7 +706,7 @@ export default createBackendPlugin({
|
|
|
706
706
|
strategyRegistry,
|
|
707
707
|
reloadAuth,
|
|
708
708
|
config,
|
|
709
|
-
accessRuleRegistry
|
|
709
|
+
accessRuleRegistry,
|
|
710
710
|
);
|
|
711
711
|
rpc.registerRouter(authRouter, authContract);
|
|
712
712
|
|
|
@@ -714,50 +714,7 @@ export default createBackendPlugin({
|
|
|
714
714
|
rpc.registerHttpHandler((req: Request) => auth!.handler(req));
|
|
715
715
|
|
|
716
716
|
// All auth management endpoints are now via oRPC (see ./router.ts)
|
|
717
|
-
|
|
718
|
-
// 5. Idempotent Admin User Seeding (roles already seeded above)
|
|
719
|
-
const adminId = "initial-admin-id";
|
|
720
|
-
const existingAdmin = await database
|
|
721
|
-
.select()
|
|
722
|
-
.from(schema.user)
|
|
723
|
-
.where(
|
|
724
|
-
or(
|
|
725
|
-
eq(schema.user.email, "admin@checkstack.dev"),
|
|
726
|
-
eq(schema.user.id, adminId)
|
|
727
|
-
)
|
|
728
|
-
);
|
|
729
|
-
|
|
730
|
-
// Skip seeding if user exists by either email or ID
|
|
731
|
-
if (existingAdmin.length === 0) {
|
|
732
|
-
await database.insert(schema.user).values({
|
|
733
|
-
id: adminId,
|
|
734
|
-
name: "Admin",
|
|
735
|
-
email: "admin@checkstack.dev",
|
|
736
|
-
emailVerified: true,
|
|
737
|
-
createdAt: new Date(),
|
|
738
|
-
updatedAt: new Date(),
|
|
739
|
-
});
|
|
740
|
-
|
|
741
|
-
const hashedAdminPassword = await hashPassword("admin");
|
|
742
|
-
await database.insert(schema.account).values({
|
|
743
|
-
id: "initial-admin-account-id",
|
|
744
|
-
accountId: "admin@checkstack.dev",
|
|
745
|
-
providerId: "credential",
|
|
746
|
-
userId: adminId,
|
|
747
|
-
password: hashedAdminPassword,
|
|
748
|
-
createdAt: new Date(),
|
|
749
|
-
updatedAt: new Date(),
|
|
750
|
-
});
|
|
751
|
-
|
|
752
|
-
await database.insert(schema.userRole).values({
|
|
753
|
-
userId: adminId,
|
|
754
|
-
roleId: "admin",
|
|
755
|
-
});
|
|
756
|
-
|
|
757
|
-
logger.info(
|
|
758
|
-
" -> Created initial admin user (admin@checkstack.dev : admin)"
|
|
759
|
-
);
|
|
760
|
-
}
|
|
717
|
+
// Note: Admin user seeding removed - handled via onboarding flow
|
|
761
718
|
|
|
762
719
|
// Register command palette commands
|
|
763
720
|
registerSearchProvider({
|
|
@@ -810,7 +767,7 @@ export default createBackendPlugin({
|
|
|
810
767
|
// This is critical because during init, other plugins haven't registered yet
|
|
811
768
|
const allAccessRules = accessRuleRegistry.getAccessRules();
|
|
812
769
|
logger.debug(
|
|
813
|
-
`[auth-backend] afterPluginsReady: syncing ${allAccessRules.length} access rules from all plugins
|
|
770
|
+
`[auth-backend] afterPluginsReady: syncing ${allAccessRules.length} access rules from all plugins`,
|
|
814
771
|
);
|
|
815
772
|
await syncAccessRulesToDb({
|
|
816
773
|
database: database as NodePgDatabase<typeof schema>,
|
|
@@ -834,7 +791,7 @@ export default createBackendPlugin({
|
|
|
834
791
|
mode: "work-queue",
|
|
835
792
|
workerGroup: "access-rule-db-sync",
|
|
836
793
|
maxRetries: 5,
|
|
837
|
-
}
|
|
794
|
+
},
|
|
838
795
|
);
|
|
839
796
|
|
|
840
797
|
// Subscribe to plugin deregistered hook for access rule cleanup
|
|
@@ -843,7 +800,7 @@ export default createBackendPlugin({
|
|
|
843
800
|
coreHooks.pluginDeregistered,
|
|
844
801
|
async ({ pluginId }) => {
|
|
845
802
|
logger.debug(
|
|
846
|
-
`[auth-backend] Cleaning up access rules for deregistered plugin: ${pluginId}
|
|
803
|
+
`[auth-backend] Cleaning up access rules for deregistered plugin: ${pluginId}`,
|
|
847
804
|
);
|
|
848
805
|
|
|
849
806
|
// Delete all access rules with this plugin's prefix
|
|
@@ -851,7 +808,7 @@ export default createBackendPlugin({
|
|
|
851
808
|
.select()
|
|
852
809
|
.from(schema.accessRule);
|
|
853
810
|
const pluginAccessRules = allDbAccessRules.filter((p) =>
|
|
854
|
-
p.id.startsWith(`${pluginId}.`)
|
|
811
|
+
p.id.startsWith(`${pluginId}.`),
|
|
855
812
|
);
|
|
856
813
|
|
|
857
814
|
for (const perm of pluginAccessRules) {
|
|
@@ -867,14 +824,14 @@ export default createBackendPlugin({
|
|
|
867
824
|
}
|
|
868
825
|
|
|
869
826
|
logger.debug(
|
|
870
|
-
`[auth-backend] Cleaned up ${pluginAccessRules.length} access rules for ${pluginId}
|
|
827
|
+
`[auth-backend] Cleaned up ${pluginAccessRules.length} access rules for ${pluginId}`,
|
|
871
828
|
);
|
|
872
829
|
},
|
|
873
830
|
{
|
|
874
831
|
mode: "work-queue",
|
|
875
832
|
workerGroup: "access-rule-cleanup",
|
|
876
833
|
maxRetries: 3,
|
|
877
|
-
}
|
|
834
|
+
},
|
|
878
835
|
);
|
|
879
836
|
|
|
880
837
|
logger.debug("โ
Auth Backend afterPluginsReady complete.");
|