@flowerforce/flowerbase 1.7.3-beta.3 → 1.7.3

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,3 +1,12 @@
1
+ ## 1.7.3 (2026-02-17)
2
+
3
+
4
+ ### 🩹 Fixes
5
+
6
+ - response EJSON in function ([fa70c11](https://github.com/flowerforce/flowerbase/commit/fa70c11))
7
+
8
+ - issues #5 #6 #7 #8 #10 #11 #12 #13 #14 ([#5](https://github.com/flowerforce/flowerbase/issues/5), [#6](https://github.com/flowerforce/flowerbase/issues/6), [#7](https://github.com/flowerforce/flowerbase/issues/7), [#8](https://github.com/flowerforce/flowerbase/issues/8), [#10](https://github.com/flowerforce/flowerbase/issues/10), [#11](https://github.com/flowerforce/flowerbase/issues/11), [#12](https://github.com/flowerforce/flowerbase/issues/12), [#13](https://github.com/flowerforce/flowerbase/issues/13), [#14](https://github.com/flowerforce/flowerbase/issues/14))
9
+
1
10
  ## 1.7.2 (2026-02-12)
2
11
 
3
12
 
@@ -1 +1 @@
1
- {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../src/auth/controller.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAQzC;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,eAAe,iBAwKxD"}
1
+ {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../src/auth/controller.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAQzC;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,eAAe,iBA2KxD"}
@@ -111,7 +111,7 @@ function authController(app) {
111
111
  : {};
112
112
  res.status(201);
113
113
  return {
114
- access_token: this.createAccessToken(Object.assign(Object.assign({}, auth_user), { user_data: Object.assign(Object.assign({}, user), { id: req.user.sub }) }))
114
+ access_token: this.createAccessToken(Object.assign(Object.assign({}, auth_user), { user_data: Object.assign(Object.assign({}, user), { id: req.user.sub }), custom_data: Object.assign({}, user) }))
115
115
  };
116
116
  });
117
117
  });
@@ -1 +1 @@
1
- {"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../../../src/auth/plugins/jwt.ts"],"names":[],"mappings":"AAKA,KAAK,OAAO,GAAG;IACb,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAeD;;;;;;;GAOG;iUAC8C,OAAO;AAAxD,wBA4GE"}
1
+ {"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../../../src/auth/plugins/jwt.ts"],"names":[],"mappings":"AAKA,KAAK,OAAO,GAAG;IACb,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAkBD;;;;;;;GAOG;iUAC8C,OAAO;AAAxD,wBA+GE"}
@@ -16,6 +16,7 @@ const jwt_1 = __importDefault(require("@fastify/jwt"));
16
16
  const fastify_plugin_1 = __importDefault(require("fastify-plugin"));
17
17
  const mongodb_1 = require("mongodb");
18
18
  const constants_1 = require("../../constants");
19
+ const isRecord = (value) => !!value && typeof value === 'object' && !Array.isArray(value);
19
20
  const unauthorizedSessionError = {
20
21
  message: 'Unauthorized',
21
22
  error: 'unauthorized',
@@ -94,14 +95,16 @@ exports.default = (0, fastify_plugin_1.default)(function (fastify, opts) {
94
95
  });
95
96
  fastify.decorate('createAccessToken', function (user) {
96
97
  const id = user._id.toString();
97
- // const userDataId = user.user_data._id.toString()
98
- const user_data = Object.assign(Object.assign({}, user.user_data), { _id: id, id: id, email: user.email });
98
+ const userData = isRecord(user.user_data) ? Object.assign({}, user.user_data) : {};
99
+ const customData = isRecord(user.custom_data)
100
+ ? Object.assign({}, user.custom_data) : Object.assign({}, userData);
101
+ const mergedUserData = Object.assign(Object.assign(Object.assign({}, customData), userData), { _id: id, id, email: typeof user.email === 'string' ? user.email : userData.email });
99
102
  return this.jwt.sign({
100
103
  typ: 'access',
101
104
  id,
102
- data: user_data,
103
- user_data: user_data,
104
- custom_data: user_data
105
+ data: mergedUserData,
106
+ user_data: mergedUserData,
107
+ custom_data: customData
105
108
  }, {
106
109
  iss: BAAS_ID,
107
110
  jti: BAAS_ID,
@@ -1 +1 @@
1
- {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../../../src/auth/providers/custom-function/controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AASzC;;;;GAIG;AACH,wBAAsB,wBAAwB,CAAC,GAAG,EAAE,eAAe,iBAqGlE"}
1
+ {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../../../src/auth/providers/custom-function/controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AASzC;;;;GAIG;AACH,wBAAsB,wBAAwB,CAAC,GAAG,EAAE,eAAe,iBAiHlE"}
@@ -26,7 +26,7 @@ function customFunctionController(app) {
26
26
  const functionsList = state_1.StateManager.select('functions');
27
27
  const services = state_1.StateManager.select('services');
28
28
  const db = app.mongo.client.db(constants_1.DB_NAME);
29
- const { authCollection, refreshTokensCollection } = constants_1.AUTH_CONFIG;
29
+ const { authCollection, refreshTokensCollection, userCollection, user_id_field } = constants_1.AUTH_CONFIG;
30
30
  const refreshTokenTtlMs = constants_1.DEFAULT_CONFIG.REFRESH_TOKEN_TTL_DAYS * 24 * 60 * 60 * 1000;
31
31
  /**
32
32
  * Endpoint for user login.
@@ -80,11 +80,15 @@ function customFunctionController(app) {
80
80
  reply.code(401).send({ message: 'Unauthorized' });
81
81
  return;
82
82
  }
83
+ const user = user_id_field && userCollection
84
+ ? yield db
85
+ .collection(userCollection)
86
+ .findOne({ [user_id_field]: authUser._id.toString() })
87
+ : {};
83
88
  const currentUserData = {
84
89
  _id: authUser._id,
85
- user_data: {
86
- _id: authUser._id
87
- }
90
+ user_data: Object.assign(Object.assign({}, (user || {})), { id: authUser._id.toString(), email: authUser.email }),
91
+ custom_data: Object.assign({}, (user || {}))
88
92
  };
89
93
  const refreshToken = this.createRefreshToken(currentUserData);
90
94
  const refreshTokenHash = (0, crypto_1.hashToken)(refreshToken);
@@ -1 +1 @@
1
- {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../../../src/auth/providers/local-userpass/controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAqCzC;;;;GAIG;AACH,wBAAsB,uBAAuB,CAAC,GAAG,EAAE,eAAe,iBA+WjE"}
1
+ {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../../../src/auth/providers/local-userpass/controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAqCzC;;;;GAIG;AACH,wBAAsB,uBAAuB,CAAC,GAAG,EAAE,eAAe,iBAoXjE"}
@@ -209,7 +209,7 @@ function localUserPassController(app) {
209
209
  .findOne({ [user_id_field]: authUser._id.toString() })
210
210
  : {};
211
211
  authUser === null || authUser === void 0 ? true : delete authUser.password;
212
- const userWithCustomData = Object.assign(Object.assign({}, authUser), { user_data: Object.assign(Object.assign({}, (user || {})), { _id: authUser._id }), data: { email: authUser.email }, id: authUser._id.toString() });
212
+ const userWithCustomData = Object.assign(Object.assign({}, authUser), { user_data: Object.assign(Object.assign({}, (user || {})), { id: authUser._id.toString(), email: authUser.email }), custom_data: Object.assign({}, (user || {})), data: { email: authUser.email }, id: authUser._id.toString() });
213
213
  if (authUser && authUser.status !== 'confirmed') {
214
214
  throw new Error(utils_1.AUTH_ERRORS.USER_NOT_CONFIRMED);
215
215
  }
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/monitoring/utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAG9D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAA;AAIxD,MAAM,MAAM,YAAY,GAAG;IACzB,EAAE,EAAE,MAAM,CAAA;IACV,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,OAAO,CAAA;CACf,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,OAAO,EAAE,CAAA;IACf,WAAW,EAAE,OAAO,CAAA;IACpB,IAAI,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACtC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG;IAClC,EAAE,EAAE,MAAM,CAAA;IACV,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,OAAO,GAAG,WAAW,CAAA;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9B,WAAW,EAAE,OAAO,CAAA;IACpB,IAAI,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACtC,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,CAAC,CAAC,EAAE,MAAM,CAAA;IACV,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,GAAG,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAA;IAClC,IAAI,EAAE,CAAC,KAAK,CAAC,EAAE,UAAU,KAAK,YAAY,EAAE,CAAA;IAC5C,KAAK,EAAE,MAAM,IAAI,CAAA;CAClB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG;IACtB,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,KAAK,CAAA;IACb,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,eAAO,MAAM,MAAM,QAAsB,CAAA;AACzC,eAAO,MAAM,SAAS,KAAK,CAAA;AAC3B,eAAO,MAAM,SAAS,KAAK,CAAA;AAC3B,eAAO,MAAM,UAAU,MAAM,CAAA;AAC7B,eAAO,MAAM,oBAAoB,KAAK,CAAA;AAEtC,eAAO,MAAM,SAAS,eACuD,CAAA;AAE7E,eAAO,MAAM,aAAa,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,OAAO,CAAC,OAAO,CAC2B,CAAA;AAElG,eAAO,MAAM,aAAa,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CACf,CAAA;AAE/D,eAAO,MAAM,aAAa,GAAI,OAAO,OAAO,WAc3C,CAAA;AAeD,eAAO,MAAM,YAAY,GAAI,OAAO,MAAM,WAWzC,CAAA;AAED,eAAO,MAAM,oBAAoB,GAAI,OAAO,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,4BAOlE,CAAA;AAED,eAAO,MAAM,WAAW,GAAI,OAAO,OAAO,KAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAS3E,CAAA;AAED,eAAO,MAAM,iBAAiB,GAAI,OAAO,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,MAAM,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAuCvG,CAAA;AAED,eAAO,MAAM,QAAQ,GAAI,OAAO,OAAO,EAAE,cAAS,KAAG,OAqCpD,CAAA;AAED,eAAO,MAAM,eAAe,GAAI,OAAO,OAAO;;;;;;CAc7C,CAAA;AAED,eAAO,MAAM,WAAW,GAAI,SAAS,cAAc,CAAC,SAAS,CAAC,YAQ7D,CAAA;AAED,eAAO,MAAM,gBAAgB,GAAI,UAAU,MAAM,EAAE,WAAW,MAAM,KAAG,UAqCtE,CAAA;AAED,eAAO,MAAM,eAAe,GAAI,KAAK,MAAM,uEAS1C,CAAA;AAED,eAAO,MAAM,aAAa,cAC8C,CAAA;AAExE,eAAO,MAAM,cAAc,GAAI,OAAO,SAAS;;;;;;;;;;;;;;;;aA2B9C,CAAA;AAED,eAAO,MAAM,4BAA4B,GACvC,OAAO,KAAK,GAAG,SAAS,EACxB,YAAY,MAAM,EAClB,OAAO,OAAO,EACd,cAAc,OAAO,6DAItB,CAAA;AAED,eAAO,MAAM,sBAAsB,GAAI,UAAU,MAAM,EAAE,QAAQ,MAAM,aAiBtE,CAAA;AAED,eAAO,MAAM,SAAS,GAAI,UAAU,MAAM,EAAE,QAAQ,MAAM,WAYzD,CAAA;AAED,eAAO,MAAM,kBAAkB,GAC7B,KAAK,eAAe,EACpB,SAAS,MAAM,EACf,cAAc,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,iDAuDtC,CAAA;AAED,eAAO,MAAM,WAAW,GAAI,eAAe,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;;aAajE,CAAA"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/monitoring/utils.ts"],"names":[],"mappings":"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"}
@@ -354,14 +354,15 @@ const resolveUserContext = (app, userId, userPayload) => __awaiter(void 0, void
354
354
  : (customUser && typeof customUser[userIdField] !== 'undefined'
355
355
  ? String(customUser[userIdField])
356
356
  : normalizedUserId);
357
- const user_data = Object.assign(Object.assign({}, (customUser ? (0, exports.stripSensitiveFields)(customUser) : {})), { id, _id: id, email: authUser && typeof authUser.email === 'string'
357
+ const custom_data = customUser ? (0, exports.stripSensitiveFields)(customUser) : {};
358
+ const user_data = Object.assign(Object.assign({}, custom_data), { id, _id: id, email: authUser && typeof authUser.email === 'string'
358
359
  ? authUser.email
359
360
  : undefined });
360
361
  const user = {
361
362
  id,
362
363
  user_data,
363
364
  data: user_data,
364
- custom_data: user_data
365
+ custom_data
365
366
  };
366
367
  if (isObjectId) {
367
368
  user._id = new mongodb_1.ObjectId(id);
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/mongodb-atlas/index.ts"],"names":[],"mappings":"AAuBA,OAAO,EAGL,oBAAoB,EAErB,MAAM,SAAS,CAAA;AA0tChB,QAAA,MAAM,YAAY,EAAE,oBA6BlB,CAAA;AAEF,eAAe,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/mongodb-atlas/index.ts"],"names":[],"mappings":"AAuBA,OAAO,EAGL,oBAAoB,EAErB,MAAM,SAAS,CAAA;AA6tChB,QAAA,MAAM,YAAY,EAAE,oBA6BlB,CAAA;AAEF,eAAe,YAAY,CAAA"}
@@ -112,6 +112,7 @@ const normalizeFindOneAndUpdateOptions = (options) => {
112
112
  }
113
113
  return Object.assign(Object.assign({}, rest), { returnDocument: returnNewDocument ? 'after' : 'before' });
114
114
  };
115
+ const buildAndQuery = (clauses) => clauses.length ? { $and: clauses } : {};
115
116
  const hasAtomicOperators = (data) => Object.keys(data).some((key) => key.startsWith('$'));
116
117
  const normalizeUpdatePayload = (data) => hasAtomicOperators(data) ? data : { $set: data };
117
118
  const hasOperatorExpressions = (value) => isPlainObject(value) && Object.keys(value).some((key) => key.startsWith('$'));
@@ -356,7 +357,7 @@ const getOperators = (collection, { rules, collName, user, run_as_system, monito
356
357
  logService('findOne query', { collName, formattedQuery });
357
358
  const safeQuery = (0, utils_3.normalizeQuery)(formattedQuery);
358
359
  logService('findOne normalizedQuery', { collName, safeQuery });
359
- const result = yield collection.findOne({ $and: safeQuery }, resolvedOptions);
360
+ const result = yield collection.findOne(buildAndQuery(safeQuery), resolvedOptions);
360
361
  logDebug('findOne result', {
361
362
  collection: collName,
362
363
  result
@@ -417,7 +418,7 @@ const getOperators = (collection, { rules, collName, user, run_as_system, monito
417
418
  // Apply access control filters
418
419
  const formattedQuery = (0, utils_3.getFormattedQuery)(filters, query, user);
419
420
  // Retrieve the document to check permissions before deleting
420
- const result = yield collection.findOne({ $and: formattedQuery });
421
+ const result = yield collection.findOne(buildAndQuery(formattedQuery));
421
422
  const winningRole = (0, utils_2.getWinningRole)(result, user, roles);
422
423
  logDebug('delete winningRole', {
423
424
  collection: collName,
@@ -435,7 +436,7 @@ const getOperators = (collection, { rules, collName, user, run_as_system, monito
435
436
  if (!status) {
436
437
  throw new Error('Delete not permitted');
437
438
  }
438
- const res = yield collection.deleteOne({ $and: formattedQuery }, options);
439
+ const res = yield collection.deleteOne(buildAndQuery(formattedQuery), options);
439
440
  emitMongoEvent('deleteOne');
440
441
  return res;
441
442
  }
@@ -536,10 +537,10 @@ const getOperators = (collection, { rules, collName, user, run_as_system, monito
536
537
  const safeQuery = Array.isArray(formattedQuery)
537
538
  ? (0, utils_3.normalizeQuery)(formattedQuery)
538
539
  : formattedQuery;
539
- const result = yield collection.findOne({ $and: safeQuery });
540
+ const result = yield collection.findOne(buildAndQuery(safeQuery));
540
541
  if (!result) {
541
542
  if (options === null || options === void 0 ? void 0 : options.upsert) {
542
- const upsertResult = yield collection.updateOne({ $and: safeQuery }, normalizedData, options);
543
+ const upsertResult = yield collection.updateOne(buildAndQuery(safeQuery), normalizedData, options);
543
544
  emitMongoEvent('updateOne');
544
545
  return upsertResult;
545
546
  }
@@ -563,7 +564,7 @@ const getOperators = (collection, { rules, collName, user, run_as_system, monito
563
564
  if (!status || !areDocumentsEqual) {
564
565
  throw new Error('Update not permitted');
565
566
  }
566
- const res = yield collection.updateOne({ $and: safeQuery }, normalizedData, options);
567
+ const res = yield collection.updateOne(buildAndQuery(safeQuery), normalizedData, options);
567
568
  emitMongoEvent('updateOne');
568
569
  return res;
569
570
  }
@@ -596,7 +597,7 @@ const getOperators = (collection, { rules, collName, user, run_as_system, monito
596
597
  const safeQuery = Array.isArray(formattedQuery)
597
598
  ? (0, utils_3.normalizeQuery)(formattedQuery)
598
599
  : formattedQuery;
599
- const result = yield collection.findOne({ $and: safeQuery });
600
+ const result = yield collection.findOne(buildAndQuery(safeQuery));
600
601
  if (!result) {
601
602
  throw new Error('Update not permitted');
602
603
  }
@@ -609,7 +610,7 @@ const getOperators = (collection, { rules, collName, user, run_as_system, monito
609
610
  : getUpdatedPaths(normalizedData);
610
611
  const [docToCheck] = Array.isArray(normalizedData)
611
612
  ? yield collection.aggregate([
612
- { $match: { $and: safeQuery } },
613
+ { $match: buildAndQuery(safeQuery) },
613
614
  { $limit: 1 },
614
615
  ...normalizedData
615
616
  ]).toArray()
@@ -627,8 +628,8 @@ const getOperators = (collection, { rules, collName, user, run_as_system, monito
627
628
  throw new Error('Update not permitted');
628
629
  }
629
630
  const updateResult = normalizedOptions
630
- ? yield collection.findOneAndUpdate({ $and: safeQuery }, normalizedData, normalizedOptions)
631
- : yield collection.findOneAndUpdate({ $and: safeQuery }, normalizedData);
631
+ ? yield collection.findOneAndUpdate(buildAndQuery(safeQuery), normalizedData, normalizedOptions)
632
+ : yield collection.findOneAndUpdate(buildAndQuery(safeQuery), normalizedData);
632
633
  if (!updateResult) {
633
634
  emitMongoEvent('findOneAndUpdate');
634
635
  return updateResult;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowerforce/flowerbase",
3
- "version": "1.7.3-beta.3",
3
+ "version": "1.7.3",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -124,6 +124,9 @@ export async function authController(app: FastifyInstance) {
124
124
  user_data: {
125
125
  ...user,
126
126
  id: req.user.sub
127
+ },
128
+ custom_data: {
129
+ ...user
127
130
  }
128
131
  })
129
132
  }
@@ -13,9 +13,10 @@ jest.mock('node:diagnostics_channel', () => {
13
13
  }
14
14
  })
15
15
 
16
+ import { ObjectId } from 'bson'
16
17
  import fastify, { FastifyInstance, FastifyReply } from 'fastify'
18
+ import * as jwt from 'jsonwebtoken'
17
19
  import jwtAuthPlugin from './jwt'
18
- import { ObjectId } from 'bson'
19
20
 
20
21
  const SECRET = 'test-secret'
21
22
 
@@ -52,7 +53,7 @@ describe('jwtAuthentication', () => {
52
53
  const collectionMock = { findOne: findOneMock }
53
54
  const dbMock = { collection: jest.fn().mockReturnValue(collectionMock) }
54
55
  const mongoMock = { client: { db: jest.fn().mockReturnValue(dbMock) } }
55
- ;(app as any).mongo = mongoMock
56
+ ; (app as any).mongo = mongoMock
56
57
  }
57
58
 
58
59
  const createReply = () => {
@@ -111,4 +112,36 @@ describe('jwtAuthentication', () => {
111
112
  expect(reply.code).toHaveBeenCalledWith(401)
112
113
  expect(reply.send).toHaveBeenCalledWith(unauthorizedSessionError)
113
114
  })
115
+
116
+ it('preserves linked custom_data _id in access token payload', async () => {
117
+ const authId = new ObjectId()
118
+ const linkedId = new ObjectId()
119
+ const token = (app as any).createAccessToken({
120
+ _id: authId,
121
+ email: 'owner@example.com',
122
+ user_data: {
123
+ _id: linkedId.toHexString(),
124
+ role: 'owner'
125
+ },
126
+ custom_data: {
127
+ _id: linkedId.toHexString(),
128
+ role: 'owner'
129
+ }
130
+ })
131
+
132
+ const decoded = jwt.decode(token) as {
133
+ id: string
134
+ data: { _id: string; id: string }
135
+ user_data: { _id: string; id: string }
136
+ custom_data: { _id: string; role: string }
137
+ sub: string
138
+ }
139
+
140
+ expect(decoded.id).toBe(authId.toHexString())
141
+ expect(decoded.sub).toBe(authId.toHexString())
142
+ expect(decoded.data._id).toBe(authId.toHexString())
143
+ expect(decoded.user_data._id).toBe(authId.toHexString())
144
+ expect(decoded.custom_data._id).toBe(linkedId.toHexString())
145
+ expect(decoded.custom_data.role).toBe('owner')
146
+ })
114
147
  })
@@ -13,6 +13,9 @@ type JwtAccessWithTimestamp = {
13
13
  iat?: number
14
14
  }
15
15
 
16
+ const isRecord = (value: unknown): value is Record<string, unknown> =>
17
+ !!value && typeof value === 'object' && !Array.isArray(value)
18
+
16
19
  const unauthorizedSessionError = {
17
20
  message: 'Unauthorized',
18
21
  error: 'unauthorized',
@@ -98,22 +101,25 @@ export default fp(async function (fastify, opts: Options) {
98
101
 
99
102
  fastify.decorate('createAccessToken', function (user: WithId<Document>) {
100
103
  const id = user._id.toString()
101
- // const userDataId = user.user_data._id.toString()
102
-
103
- const user_data = {
104
- ...user.user_data,
104
+ const userData = isRecord(user.user_data) ? { ...user.user_data } : {}
105
+ const customData = isRecord(user.custom_data)
106
+ ? { ...user.custom_data }
107
+ : { ...userData }
108
+ const mergedUserData = {
109
+ ...customData,
110
+ ...userData,
105
111
  _id: id,
106
- id: id,
107
- email: user.email,
112
+ id,
113
+ email: typeof user.email === 'string' ? user.email : userData.email
108
114
  }
109
115
 
110
116
  return this.jwt.sign(
111
117
  {
112
118
  typ: 'access',
113
119
  id,
114
- data: user_data,
115
- user_data: user_data,
116
- custom_data: user_data
120
+ data: mergedUserData,
121
+ user_data: mergedUserData,
122
+ custom_data: customData
117
123
  },
118
124
  {
119
125
  iss: BAAS_ID,
@@ -17,7 +17,7 @@ export async function customFunctionController(app: FastifyInstance) {
17
17
  const functionsList = StateManager.select('functions')
18
18
  const services = StateManager.select('services')
19
19
  const db = app.mongo.client.db(DB_NAME)
20
- const { authCollection, refreshTokensCollection } = AUTH_CONFIG
20
+ const { authCollection, refreshTokensCollection, userCollection, user_id_field } = AUTH_CONFIG
21
21
  const refreshTokenTtlMs = DEFAULT_CONFIG.REFRESH_TOKEN_TTL_DAYS * 24 * 60 * 60 * 1000
22
22
 
23
23
  /**
@@ -89,10 +89,22 @@ export async function customFunctionController(app: FastifyInstance) {
89
89
  return
90
90
  }
91
91
 
92
+ const user =
93
+ user_id_field && userCollection
94
+ ? await db
95
+ .collection(userCollection)
96
+ .findOne({ [user_id_field]: authUser._id.toString() })
97
+ : {}
98
+
92
99
  const currentUserData = {
93
100
  _id: authUser._id,
94
101
  user_data: {
95
- _id: authUser._id
102
+ ...(user || {}),
103
+ id: authUser._id.toString(),
104
+ email: authUser.email
105
+ },
106
+ custom_data: {
107
+ ...(user || {})
96
108
  }
97
109
  }
98
110
  const refreshToken = this.createRefreshToken(currentUserData)
@@ -268,7 +268,12 @@ export async function localUserPassController(app: FastifyInstance) {
268
268
 
269
269
  const userWithCustomData = {
270
270
  ...authUser,
271
- user_data: { ...(user || {}), _id: authUser._id },
271
+ user_data: {
272
+ ...(user || {}),
273
+ id: authUser._id.toString(),
274
+ email: authUser.email
275
+ },
276
+ custom_data: { ...(user || {}) },
272
277
  data: { email: authUser.email },
273
278
  id: authUser._id.toString()
274
279
  }
@@ -404,8 +404,9 @@ export const resolveUserContext = async (
404
404
  ? String(customUser[userIdField])
405
405
  : normalizedUserId)
406
406
 
407
+ const custom_data = customUser ? stripSensitiveFields(customUser) : {}
407
408
  const user_data = {
408
- ...(customUser ? stripSensitiveFields(customUser) : {}),
409
+ ...custom_data,
409
410
  id,
410
411
  _id: id,
411
412
  email: authUser && typeof (authUser as { email?: unknown }).email === 'string'
@@ -417,7 +418,7 @@ export const resolveUserContext = async (
417
418
  id,
418
419
  user_data,
419
420
  data: user_data,
420
- custom_data: user_data
421
+ custom_data
421
422
  }
422
423
 
423
424
  if (isObjectId) {
@@ -250,6 +250,24 @@ describe('mongodb-atlas Realm compatibility', () => {
250
250
  expect(find).toHaveBeenCalledWith({ active: true }, { session })
251
251
  })
252
252
 
253
+ it('allows findOne({}) without emitting an empty $and query', async () => {
254
+ const doc = { _id: new ObjectId(), label: 'first' }
255
+ const findOne = jest.fn().mockResolvedValue(doc)
256
+ const collection = {
257
+ collectionName: 'todos',
258
+ findOne
259
+ }
260
+ const operators = MongoDbAtlas(createAppWithCollection(collection) as any, {
261
+ rules: createRules(),
262
+ user: { id: 'user-1' }
263
+ }).db('db').collection('todos')
264
+
265
+ const result = await operators.findOne({})
266
+
267
+ expect(result).toEqual(doc)
268
+ expect(findOne).toHaveBeenCalledWith({}, undefined)
269
+ })
270
+
253
271
  it('returns insertMany insertedIds as an array', async () => {
254
272
  const id0 = new ObjectId()
255
273
  const id1 = new ObjectId()
@@ -136,6 +136,9 @@ const normalizeFindOneAndUpdateOptions = (
136
136
  }
137
137
  }
138
138
 
139
+ const buildAndQuery = (clauses: MongoFilter<Document>[]): MongoFilter<Document> =>
140
+ clauses.length ? { $and: clauses } : {}
141
+
139
142
  const hasAtomicOperators = (data: Document) => Object.keys(data).some((key) => key.startsWith('$'))
140
143
 
141
144
  const normalizeUpdatePayload = (data: Document) =>
@@ -410,7 +413,7 @@ const getOperators: GetOperatorsFunction = (
410
413
  logService('findOne query', { collName, formattedQuery })
411
414
  const safeQuery = normalizeQuery(formattedQuery)
412
415
  logService('findOne normalizedQuery', { collName, safeQuery })
413
- const result = await collection.findOne({ $and: safeQuery }, resolvedOptions)
416
+ const result = await collection.findOne(buildAndQuery(safeQuery), resolvedOptions)
414
417
  logDebug('findOne result', {
415
418
  collection: collName,
416
419
  result
@@ -477,7 +480,7 @@ const getOperators: GetOperatorsFunction = (
477
480
  const formattedQuery = getFormattedQuery(filters, query, user)
478
481
 
479
482
  // Retrieve the document to check permissions before deleting
480
- const result = await collection.findOne({ $and: formattedQuery })
483
+ const result = await collection.findOne(buildAndQuery(formattedQuery))
481
484
  const winningRole = getWinningRole(result, user, roles)
482
485
 
483
486
  logDebug('delete winningRole', {
@@ -502,7 +505,7 @@ const getOperators: GetOperatorsFunction = (
502
505
  throw new Error('Delete not permitted')
503
506
  }
504
507
 
505
- const res = await collection.deleteOne({ $and: formattedQuery }, options)
508
+ const res = await collection.deleteOne(buildAndQuery(formattedQuery), options)
506
509
  emitMongoEvent('deleteOne')
507
510
  return res
508
511
  }
@@ -610,12 +613,12 @@ const getOperators: GetOperatorsFunction = (
610
613
  ? normalizeQuery(formattedQuery)
611
614
  : formattedQuery
612
615
 
613
- const result = await collection.findOne({ $and: safeQuery })
616
+ const result = await collection.findOne(buildAndQuery(safeQuery))
614
617
 
615
618
  if (!result) {
616
619
  if (options?.upsert) {
617
620
  const upsertResult = await collection.updateOne(
618
- { $and: safeQuery },
621
+ buildAndQuery(safeQuery),
619
622
  normalizedData,
620
623
  options
621
624
  )
@@ -649,7 +652,7 @@ const getOperators: GetOperatorsFunction = (
649
652
  if (!status || !areDocumentsEqual) {
650
653
  throw new Error('Update not permitted')
651
654
  }
652
- const res = await collection.updateOne({ $and: safeQuery }, normalizedData, options)
655
+ const res = await collection.updateOne(buildAndQuery(safeQuery), normalizedData, options)
653
656
  emitMongoEvent('updateOne')
654
657
  return res
655
658
  }
@@ -685,7 +688,7 @@ const getOperators: GetOperatorsFunction = (
685
688
  ? normalizeQuery(formattedQuery)
686
689
  : formattedQuery
687
690
 
688
- const result = await collection.findOne({ $and: safeQuery })
691
+ const result = await collection.findOne(buildAndQuery(safeQuery))
689
692
 
690
693
  if (!result) {
691
694
  throw new Error('Update not permitted')
@@ -700,7 +703,7 @@ const getOperators: GetOperatorsFunction = (
700
703
  : getUpdatedPaths(normalizedData as Document)
701
704
  const [docToCheck] = Array.isArray(normalizedData)
702
705
  ? await collection.aggregate([
703
- { $match: { $and: safeQuery } },
706
+ { $match: buildAndQuery(safeQuery) },
704
707
  { $limit: 1 },
705
708
  ...normalizedData
706
709
  ]).toArray()
@@ -725,8 +728,8 @@ const getOperators: GetOperatorsFunction = (
725
728
  }
726
729
 
727
730
  const updateResult = normalizedOptions
728
- ? await collection.findOneAndUpdate({ $and: safeQuery }, normalizedData, normalizedOptions)
729
- : await collection.findOneAndUpdate({ $and: safeQuery }, normalizedData)
731
+ ? await collection.findOneAndUpdate(buildAndQuery(safeQuery), normalizedData, normalizedOptions)
732
+ : await collection.findOneAndUpdate(buildAndQuery(safeQuery), normalizedData)
730
733
  if (!updateResult) {
731
734
  emitMongoEvent('findOneAndUpdate')
732
735
  return updateResult