@rebasepro/server-mongo 0.23.0 → 0.24.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.
@@ -4,15 +4,13 @@ export interface MongoDoc {
4
4
  _id?: string;
5
5
  [key: string]: any;
6
6
  }
7
- import { UserRepository, RoleRepository, TokenRepository, AuthRepository, UserData, CreateUserData, RoleData, CreateRoleData, RefreshTokenInfo, RefreshTokenSession, PasswordResetTokenInfo, MagicLinkTokenInfo, UserIdentityData, ListUsersOptions, PaginatedUsersResult, MfaFactor, MfaChallengeInfo } from "@rebasepro/server";
8
- export type Role = RoleData;
7
+ import { UserRepository, TokenRepository, AuthRepository, UserData, CreateUserData, RefreshTokenInfo, RefreshTokenSession, PasswordResetTokenInfo, MagicLinkTokenInfo, UserIdentityData, ListUsersOptions, PaginatedUsersResult, MfaFactor, MfaChallengeInfo } from "@rebasepro/server";
9
8
  export declare class MongoUserService implements UserRepository {
10
9
  private db;
11
10
  constructor(db: Db);
12
11
  private get collection();
13
12
  private get identitiesCollection();
14
13
  private get userRolesCollection();
15
- private get rolesCollection();
16
14
  createUser(data: CreateUserData): Promise<UserData>;
17
15
  getUserById(id: string): Promise<UserData | null>;
18
16
  getUserByEmail(email: string): Promise<UserData | null>;
@@ -35,25 +33,14 @@ export declare class MongoUserService implements UserRepository {
35
33
  setEmailVerified(id: string, verified: boolean): Promise<void>;
36
34
  setVerificationToken(id: string, token: string | null): Promise<void>;
37
35
  getUserByVerificationToken(token: string): Promise<UserData | null>;
38
- getUserRoles(uid: string): Promise<RoleData[]>;
39
36
  getUserRoleIds(uid: string): Promise<string[]>;
40
37
  setUserRoles(uid: string, roleIds: string[]): Promise<void>;
41
38
  assignDefaultRole(uid: string, roleId: string): Promise<void>;
42
39
  getUserWithRoles(uid: string): Promise<{
43
40
  user: UserData;
44
- roles: RoleData[];
41
+ roles: string[];
45
42
  } | null>;
46
43
  }
47
- export declare class MongoRoleService implements RoleRepository {
48
- private db;
49
- constructor(db: Db);
50
- private get collection();
51
- getRoleById(id: string): Promise<RoleData | null>;
52
- listRoles(): Promise<RoleData[]>;
53
- createRole(data: CreateRoleData): Promise<RoleData>;
54
- updateRole(id: string, data: Partial<Omit<RoleData, "id">>): Promise<RoleData | null>;
55
- deleteRole(id: string): Promise<void>;
56
- }
57
44
  export declare class MongoRefreshTokenService {
58
45
  private db;
59
46
  constructor(db: Db);
@@ -113,7 +100,6 @@ export declare class MongoTokenRepository implements TokenRepository {
113
100
  export declare class MongoAuthRepository implements AuthRepository {
114
101
  private db;
115
102
  private userService;
116
- private roleService;
117
103
  private tokenRepository;
118
104
  constructor(db: Db);
119
105
  createUser(data: CreateUserData): Promise<UserData>;
@@ -130,19 +116,13 @@ export declare class MongoAuthRepository implements AuthRepository {
130
116
  setEmailVerified(id: string, verified: boolean): Promise<void>;
131
117
  setVerificationToken(id: string, token: string | null): Promise<void>;
132
118
  getUserByVerificationToken(token: string): Promise<UserData | null>;
133
- getUserRoles(uid: string): Promise<RoleData[]>;
134
119
  getUserRoleIds(uid: string): Promise<string[]>;
135
120
  setUserRoles(uid: string, roleIds: string[]): Promise<void>;
136
121
  assignDefaultRole(uid: string, roleId: string): Promise<void>;
137
122
  getUserWithRoles(uid: string): Promise<{
138
123
  user: UserData;
139
- roles: RoleData[];
124
+ roles: string[];
140
125
  } | null>;
141
- getRoleById(id: string): Promise<RoleData | null>;
142
- listRoles(): Promise<RoleData[]>;
143
- createRole(data: CreateRoleData): Promise<RoleData>;
144
- updateRole(id: string, data: Partial<Omit<RoleData, "id">>): Promise<RoleData | null>;
145
- deleteRole(id: string): Promise<void>;
146
126
  createRefreshToken(uid: string, tokenHash: string, expiresAt: Date, userAgent?: string, ipAddress?: string, session?: RefreshTokenSession): Promise<void>;
147
127
  markRefreshTokenRotated(tokenHash: string): Promise<void>;
148
128
  revokeRefreshTokenSession(sessionId: string): Promise<void>;
@@ -1,50 +1,6 @@
1
1
  import { logger } from "@rebasepro/server";
2
2
  //#region src/auth/ensure-collections.ts
3
3
  /**
4
- * Default roles to seed on first run
5
- */
6
- var DEFAULT_ROLES = [
7
- {
8
- _id: "admin",
9
- id: "admin",
10
- name: "Admin",
11
- isAdmin: true,
12
- defaultPermissions: {
13
- read: true,
14
- create: true,
15
- edit: true,
16
- delete: true
17
- },
18
- createdAt: /* @__PURE__ */ new Date()
19
- },
20
- {
21
- _id: "editor",
22
- id: "editor",
23
- name: "Editor",
24
- isAdmin: false,
25
- defaultPermissions: {
26
- read: true,
27
- create: true,
28
- edit: true,
29
- delete: true
30
- },
31
- createdAt: /* @__PURE__ */ new Date()
32
- },
33
- {
34
- _id: "viewer",
35
- id: "viewer",
36
- name: "Viewer",
37
- isAdmin: false,
38
- defaultPermissions: {
39
- read: true,
40
- create: false,
41
- edit: false,
42
- delete: false
43
- },
44
- createdAt: /* @__PURE__ */ new Date()
45
- }
46
- ];
47
- /**
48
4
  * Indexes created by releases up to 0.13.0, on snake_case field names the auth
49
5
  * services never wrote.
50
6
  *
@@ -96,7 +52,6 @@ async function ensureAuthCollectionsExist(db) {
96
52
  const resetTokens = db.collection("rebase_password_reset_tokens");
97
53
  await resetTokens.createIndex({ tokenHash: 1 }, { unique: true });
98
54
  await resetTokens.createIndex({ uid: 1 });
99
- await seedDefaultRoles(db);
100
55
  logger.info("✅ MongoDB Auth collections ready");
101
56
  } catch (error) {
102
57
  logger.error("❌ Failed to set up MongoDB auth collections", { error });
@@ -113,18 +68,7 @@ async function dropLegacyIndexes(db) {
113
68
  } catch {}
114
69
  }
115
70
  }
116
- async function seedDefaultRoles(db) {
117
- const roles = db.collection("rebase_roles");
118
- const count = await roles.countDocuments();
119
- if (count > 0) {
120
- logger.info(`📋 Found ${count} existing roles`);
121
- return;
122
- }
123
- logger.info("🌱 Seeding default roles...");
124
- for (const role of DEFAULT_ROLES) await roles.updateOne({ id: role.id }, { $setOnInsert: role }, { upsert: true });
125
- logger.info("✅ Default roles created: admin, editor, viewer");
126
- }
127
71
  //#endregion
128
72
  export { ensureAuthCollectionsExist };
129
73
 
130
- //# sourceMappingURL=ensure-collections-Bl12bSwO.js.map
74
+ //# sourceMappingURL=ensure-collections-DHTVMlxf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ensure-collections-DHTVMlxf.js","names":[],"sources":["../src/auth/ensure-collections.ts"],"sourcesContent":["import { Db } from \"mongodb\";\nimport { logger } from \"@rebasepro/server\";\n\n/**\n * Indexes created by releases up to 0.13.0, on snake_case field names the auth\n * services never wrote.\n *\n * MongoDB indexes an absent field as `null`, so a unique index whose every key\n * component is always missing admits exactly **one** document per collection:\n * the second login on the whole deployment failed with `E11000`, as did the\n * second role assignment and the second outstanding password-reset token.\n * Dropping them is therefore part of the fix, not tidying — creating the\n * correct index alongside a broken one leaves the deployment just as broken.\n *\n * `{user_id, user_agent, ip_address}` has a second reason to go: it is the\n * reverted Postgres constraint. `MongoRefreshTokenService.createToken` explains\n * why evicting by that triple signs a second browser out; the unique index\n * expressing the same rule was left behind here.\n */\nconst LEGACY_INDEX_NAMES: Record<string, string[]> = {\n rebase_user_identities: [\"provider_1_provider_id_1\", \"user_id_1\"],\n rebase_user_roles: [\"user_id_1_role_id_1\", \"user_id_1\"],\n rebase_refresh_tokens: [\"token_hash_1\", \"user_id_1_user_agent_1_ip_address_1\", \"user_id_1\"],\n rebase_password_reset_tokens: [\"token_hash_1\", \"user_id_1\"]\n};\n\nexport async function ensureAuthCollectionsExist(db: Db): Promise<void> {\n logger.info(\"🔍 Checking MongoDB auth collections and indexes...\");\n\n try {\n await dropLegacyIndexes(db);\n\n // Every key below is a field `auth/services.ts` actually writes —\n // camelCase, matching the documents. Verified by\n // `test/MongoAuthServices.test.ts`, which now runs this function.\n\n // Users\n const users = db.collection(\"rebase_users\");\n await users.createIndex({ email: 1 }, { unique: true });\n await users.createIndex({ id: 1 });\n\n // User Identities\n const identities = db.collection(\"rebase_user_identities\");\n await identities.createIndex({ provider: 1,\nproviderId: 1 }, { unique: true });\n await identities.createIndex({ uid: 1 });\n\n // User Roles (junction collection)\n const userRoles = db.collection(\"rebase_user_roles\");\n await userRoles.createIndex({ uid: 1,\nroleId: 1 }, { unique: true });\n await userRoles.createIndex({ roleId: 1 });\n\n // Refresh Tokens. No unique index on (uid, userAgent, ipAddress):\n // tokens of one sign-in accumulate under a shared sessionId.\n const refreshTokens = db.collection(\"rebase_refresh_tokens\");\n await refreshTokens.createIndex({ tokenHash: 1 }, { unique: true });\n await refreshTokens.createIndex({ uid: 1 });\n await refreshTokens.createIndex({ sessionId: 1 });\n\n // Password Reset Tokens\n const resetTokens = db.collection(\"rebase_password_reset_tokens\");\n await resetTokens.createIndex({ tokenHash: 1 }, { unique: true });\n await resetTokens.createIndex({ uid: 1 });\n\n logger.info(\"✅ MongoDB Auth collections ready\");\n } catch (error) {\n // Not swallowed. Every index here is load-bearing for auth — a boot\n // that logs this and carries on is a deployment whose second login\n // fails, with a green boot log to say nothing is wrong.\n logger.error(\"❌ Failed to set up MongoDB auth collections\", { error: error });\n throw error;\n }\n}\n\n/** Drop the indexes of {@link LEGACY_INDEX_NAMES} that this database still has. */\nasync function dropLegacyIndexes(db: Db): Promise<void> {\n for (const [collectionName, indexNames] of Object.entries(LEGACY_INDEX_NAMES)) {\n const collection = db.collection(collectionName);\n for (const indexName of indexNames) {\n try {\n await collection.dropIndex(indexName);\n logger.info(`🧹 Dropped stale auth index ${collectionName}.${indexName}`);\n } catch {\n // NamespaceNotFound / IndexNotFound — nothing to drop here.\n }\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAmBA,IAAM,qBAA+C;CACjD,wBAAwB,CAAC,4BAA4B,WAAW;CAChE,mBAAmB,CAAC,uBAAuB,WAAW;CACtD,uBAAuB;EAAC;EAAgB;EAAuC;CAAW;CAC1F,8BAA8B,CAAC,gBAAgB,WAAW;AAC9D;AAEA,eAAsB,2BAA2B,IAAuB;CACpE,OAAO,KAAK,qDAAqD;CAEjE,IAAI;EACA,MAAM,kBAAkB,EAAE;EAO1B,MAAM,QAAQ,GAAG,WAAW,cAAc;EAC1C,MAAM,MAAM,YAAY,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,KAAK,CAAC;EACtD,MAAM,MAAM,YAAY,EAAE,IAAI,EAAE,CAAC;EAGjC,MAAM,aAAa,GAAG,WAAW,wBAAwB;EACzD,MAAM,WAAW,YAAY;GAAE,UAAU;GACjD,YAAY;EAAE,GAAG,EAAE,QAAQ,KAAK,CAAC;EACzB,MAAM,WAAW,YAAY,EAAE,KAAK,EAAE,CAAC;EAGvC,MAAM,YAAY,GAAG,WAAW,mBAAmB;EACnD,MAAM,UAAU,YAAY;GAAE,KAAK;GAC3C,QAAQ;EAAE,GAAG,EAAE,QAAQ,KAAK,CAAC;EACrB,MAAM,UAAU,YAAY,EAAE,QAAQ,EAAE,CAAC;EAIzC,MAAM,gBAAgB,GAAG,WAAW,uBAAuB;EAC3D,MAAM,cAAc,YAAY,EAAE,WAAW,EAAE,GAAG,EAAE,QAAQ,KAAK,CAAC;EAClE,MAAM,cAAc,YAAY,EAAE,KAAK,EAAE,CAAC;EAC1C,MAAM,cAAc,YAAY,EAAE,WAAW,EAAE,CAAC;EAGhD,MAAM,cAAc,GAAG,WAAW,8BAA8B;EAChE,MAAM,YAAY,YAAY,EAAE,WAAW,EAAE,GAAG,EAAE,QAAQ,KAAK,CAAC;EAChE,MAAM,YAAY,YAAY,EAAE,KAAK,EAAE,CAAC;EAExC,OAAO,KAAK,kCAAkC;CAClD,SAAS,OAAO;EAIZ,OAAO,MAAM,+CAA+C,EAAS,MAAM,CAAC;EAC5E,MAAM;CACV;AACJ;;AAGA,eAAe,kBAAkB,IAAuB;CACpD,KAAK,MAAM,CAAC,gBAAgB,eAAe,OAAO,QAAQ,kBAAkB,GAAG;EAC3E,MAAM,aAAa,GAAG,WAAW,cAAc;EAC/C,KAAK,MAAM,aAAa,YACpB,IAAI;GACA,MAAM,WAAW,UAAU,SAAS;GACpC,OAAO,KAAK,+BAA+B,eAAe,GAAG,WAAW;EAC5E,QAAQ,CAER;CAER;AACJ"}
package/dist/index.es.js CHANGED
@@ -2557,9 +2557,6 @@ var MongoUserService = class {
2557
2557
  get userRolesCollection() {
2558
2558
  return this.db.collection("rebase_user_roles");
2559
2559
  }
2560
- get rolesCollection() {
2561
- return this.db.collection("rebase_roles");
2562
- }
2563
2560
  async createUser(data) {
2564
2561
  const id = new ObjectId().toString();
2565
2562
  const now = /* @__PURE__ */ new Date();
@@ -2717,17 +2714,6 @@ var MongoUserService = class {
2717
2714
  const doc = await this.collection.findOne({ emailVerificationToken: token });
2718
2715
  return doc ? toUser(doc) : null;
2719
2716
  }
2720
- async getUserRoles(uid) {
2721
- const roleIds = (await this.userRolesCollection.find({ uid }).toArray()).map((ur) => ur.roleId);
2722
- if (roleIds.length === 0) return [];
2723
- return (await this.rolesCollection.find({ id: { $in: roleIds } }).toArray()).map((r) => ({
2724
- id: r.id,
2725
- name: r.name,
2726
- isAdmin: r.isAdmin ?? false,
2727
- defaultPermissions: r.defaultPermissions ?? null,
2728
- collectionPermissions: r.collectionPermissions ?? null
2729
- }));
2730
- }
2731
2717
  async getUserRoleIds(uid) {
2732
2718
  return (await this.userRolesCollection.find({ uid }).toArray()).map((ur) => ur.roleId);
2733
2719
  }
@@ -2757,59 +2743,10 @@ var MongoUserService = class {
2757
2743
  if (!user) return null;
2758
2744
  return {
2759
2745
  user,
2760
- roles: await this.getUserRoles(uid)
2746
+ roles: await this.getUserRoleIds(uid)
2761
2747
  };
2762
2748
  }
2763
2749
  };
2764
- var MongoRoleService = class {
2765
- db;
2766
- constructor(db) {
2767
- this.db = db;
2768
- }
2769
- get collection() {
2770
- return this.db.collection("rebase_roles");
2771
- }
2772
- async getRoleById(id) {
2773
- const doc = await this.collection.findOne({ id });
2774
- if (!doc) return null;
2775
- return {
2776
- id: doc.id,
2777
- name: doc.name,
2778
- isAdmin: doc.isAdmin ?? false,
2779
- defaultPermissions: doc.defaultPermissions ?? null,
2780
- collectionPermissions: doc.collectionPermissions ?? null
2781
- };
2782
- }
2783
- async listRoles() {
2784
- return (await this.collection.find().sort({ name: 1 }).toArray()).map((doc) => ({
2785
- id: doc.id,
2786
- name: doc.name,
2787
- isAdmin: doc.isAdmin ?? false,
2788
- defaultPermissions: doc.defaultPermissions ?? null,
2789
- collectionPermissions: doc.collectionPermissions ?? null
2790
- }));
2791
- }
2792
- async createRole(data) {
2793
- const doc = {
2794
- _id: data.id,
2795
- id: data.id,
2796
- name: data.name,
2797
- isAdmin: data.isAdmin ?? false,
2798
- defaultPermissions: data.defaultPermissions ?? null,
2799
- collectionPermissions: data.collectionPermissions ?? null
2800
- };
2801
- await this.collection.insertOne(doc);
2802
- return { ...doc };
2803
- }
2804
- async updateRole(id, data) {
2805
- await this.collection.updateOne({ id }, { $set: data });
2806
- return this.getRoleById(id);
2807
- }
2808
- async deleteRole(id) {
2809
- await this.collection.deleteOne({ id });
2810
- await this.db.collection("rebase_user_roles").deleteMany({ roleId: id });
2811
- }
2812
- };
2813
2750
  var MongoRefreshTokenService = class {
2814
2751
  db;
2815
2752
  constructor(db) {
@@ -3034,12 +2971,10 @@ var MongoTokenRepository = class {
3034
2971
  var MongoAuthRepository = class {
3035
2972
  db;
3036
2973
  userService;
3037
- roleService;
3038
2974
  tokenRepository;
3039
2975
  constructor(db) {
3040
2976
  this.db = db;
3041
2977
  this.userService = new MongoUserService(db);
3042
- this.roleService = new MongoRoleService(db);
3043
2978
  this.tokenRepository = new MongoTokenRepository(db);
3044
2979
  }
3045
2980
  async createUser(data) {
@@ -3084,9 +3019,6 @@ var MongoAuthRepository = class {
3084
3019
  async getUserByVerificationToken(token) {
3085
3020
  return this.userService.getUserByVerificationToken(token);
3086
3021
  }
3087
- async getUserRoles(uid) {
3088
- return this.userService.getUserRoles(uid);
3089
- }
3090
3022
  async getUserRoleIds(uid) {
3091
3023
  return this.userService.getUserRoleIds(uid);
3092
3024
  }
@@ -3099,21 +3031,6 @@ var MongoAuthRepository = class {
3099
3031
  async getUserWithRoles(uid) {
3100
3032
  return this.userService.getUserWithRoles(uid);
3101
3033
  }
3102
- async getRoleById(id) {
3103
- return this.roleService.getRoleById(id);
3104
- }
3105
- async listRoles() {
3106
- return this.roleService.listRoles();
3107
- }
3108
- async createRole(data) {
3109
- return this.roleService.createRole(data);
3110
- }
3111
- async updateRole(id, data) {
3112
- return this.roleService.updateRole(id, data);
3113
- }
3114
- async deleteRole(id) {
3115
- await this.roleService.deleteRole(id);
3116
- }
3117
3034
  async createRefreshToken(uid, tokenHash, expiresAt, userAgent, ipAddress, session) {
3118
3035
  await this.tokenRepository.createRefreshToken(uid, tokenHash, expiresAt, userAgent, ipAddress, session);
3119
3036
  }
@@ -3246,7 +3163,7 @@ function createMongoBootstrapper(mongoConfig) {
3246
3163
  },
3247
3164
  async initializeAuth(config, driverResult) {
3248
3165
  const db = driverResult.internals.db;
3249
- const { ensureAuthCollectionsExist } = await import("./ensure-collections-Bl12bSwO.js");
3166
+ const { ensureAuthCollectionsExist } = await import("./ensure-collections-DHTVMlxf.js");
3250
3167
  await ensureAuthCollectionsExist(db);
3251
3168
  const { createEmailService } = await import("@rebasepro/server");
3252
3169
  const authConfig = config;
@@ -3254,7 +3171,6 @@ function createMongoBootstrapper(mongoConfig) {
3254
3171
  if (authConfig?.email) emailService = createEmailService(authConfig.email);
3255
3172
  return {
3256
3173
  userService: new MongoUserService(db),
3257
- roleService: new MongoRoleService(db),
3258
3174
  authRepository: new MongoAuthRepository(db),
3259
3175
  emailService
3260
3176
  };
@@ -3319,7 +3235,7 @@ function createMongoBootstrapper(mongoConfig) {
3319
3235
  },
3320
3236
  mountRoutes() {},
3321
3237
  async initializeWebsockets(server, realtimeService, driver, config, authAdapter) {
3322
- const { createMongoWebSocket } = await import("./websocket-BtK4_9ZS.js");
3238
+ const { createMongoWebSocket } = await import("./websocket-CXSVz4oq.js");
3323
3239
  createMongoWebSocket(server, realtimeService, driver, config, cachedAdmin, authAdapter);
3324
3240
  }
3325
3241
  };