@flowerforce/flowerbase 1.2.1-beta.2 → 1.2.1-beta.20

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.
Files changed (97) hide show
  1. package/README.md +37 -6
  2. package/dist/auth/controller.d.ts.map +1 -1
  3. package/dist/auth/controller.js +55 -4
  4. package/dist/auth/plugins/jwt.d.ts.map +1 -1
  5. package/dist/auth/plugins/jwt.js +52 -6
  6. package/dist/auth/providers/anon-user/controller.d.ts +8 -0
  7. package/dist/auth/providers/anon-user/controller.d.ts.map +1 -0
  8. package/dist/auth/providers/anon-user/controller.js +90 -0
  9. package/dist/auth/providers/anon-user/dtos.d.ts +10 -0
  10. package/dist/auth/providers/anon-user/dtos.d.ts.map +1 -0
  11. package/dist/auth/providers/anon-user/dtos.js +2 -0
  12. package/dist/auth/providers/custom-function/controller.d.ts.map +1 -1
  13. package/dist/auth/providers/custom-function/controller.js +35 -25
  14. package/dist/auth/providers/custom-function/dtos.d.ts +4 -1
  15. package/dist/auth/providers/custom-function/dtos.d.ts.map +1 -1
  16. package/dist/auth/providers/local-userpass/controller.d.ts.map +1 -1
  17. package/dist/auth/providers/local-userpass/controller.js +159 -73
  18. package/dist/auth/providers/local-userpass/dtos.d.ts +17 -2
  19. package/dist/auth/providers/local-userpass/dtos.d.ts.map +1 -1
  20. package/dist/auth/utils.d.ts +76 -14
  21. package/dist/auth/utils.d.ts.map +1 -1
  22. package/dist/auth/utils.js +55 -61
  23. package/dist/constants.d.ts +12 -0
  24. package/dist/constants.d.ts.map +1 -1
  25. package/dist/constants.js +16 -4
  26. package/dist/features/functions/controller.d.ts.map +1 -1
  27. package/dist/features/functions/controller.js +31 -12
  28. package/dist/features/functions/dtos.d.ts +3 -0
  29. package/dist/features/functions/dtos.d.ts.map +1 -1
  30. package/dist/features/functions/interface.d.ts +3 -0
  31. package/dist/features/functions/interface.d.ts.map +1 -1
  32. package/dist/features/functions/utils.d.ts +3 -2
  33. package/dist/features/functions/utils.d.ts.map +1 -1
  34. package/dist/features/functions/utils.js +19 -7
  35. package/dist/features/triggers/index.d.ts.map +1 -1
  36. package/dist/features/triggers/index.js +49 -7
  37. package/dist/features/triggers/interface.d.ts +1 -0
  38. package/dist/features/triggers/interface.d.ts.map +1 -1
  39. package/dist/features/triggers/utils.d.ts.map +1 -1
  40. package/dist/features/triggers/utils.js +67 -26
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +48 -13
  43. package/dist/services/mongodb-atlas/index.d.ts.map +1 -1
  44. package/dist/services/mongodb-atlas/index.js +72 -2
  45. package/dist/services/mongodb-atlas/model.d.ts +3 -2
  46. package/dist/services/mongodb-atlas/model.d.ts.map +1 -1
  47. package/dist/shared/handleUserRegistration.d.ts.map +1 -1
  48. package/dist/shared/handleUserRegistration.js +66 -1
  49. package/dist/shared/models/handleUserRegistration.model.d.ts +2 -1
  50. package/dist/shared/models/handleUserRegistration.model.d.ts.map +1 -1
  51. package/dist/shared/models/handleUserRegistration.model.js +1 -0
  52. package/dist/utils/context/helpers.d.ts +6 -6
  53. package/dist/utils/context/helpers.d.ts.map +1 -1
  54. package/dist/utils/context/index.d.ts +1 -1
  55. package/dist/utils/context/index.d.ts.map +1 -1
  56. package/dist/utils/context/index.js +176 -9
  57. package/dist/utils/context/interface.d.ts +1 -1
  58. package/dist/utils/context/interface.d.ts.map +1 -1
  59. package/dist/utils/crypto/index.d.ts +1 -0
  60. package/dist/utils/crypto/index.d.ts.map +1 -1
  61. package/dist/utils/crypto/index.js +6 -2
  62. package/dist/utils/initializer/exposeRoutes.js +1 -1
  63. package/dist/utils/initializer/registerPlugins.d.ts.map +1 -1
  64. package/dist/utils/initializer/registerPlugins.js +12 -4
  65. package/dist/utils/roles/helpers.js +2 -1
  66. package/package.json +1 -2
  67. package/src/auth/controller.ts +71 -5
  68. package/src/auth/plugins/jwt.test.ts +93 -0
  69. package/src/auth/plugins/jwt.ts +67 -8
  70. package/src/auth/providers/anon-user/controller.ts +91 -0
  71. package/src/auth/providers/anon-user/dtos.ts +10 -0
  72. package/src/auth/providers/custom-function/controller.ts +40 -31
  73. package/src/auth/providers/custom-function/dtos.ts +5 -1
  74. package/src/auth/providers/local-userpass/controller.ts +211 -101
  75. package/src/auth/providers/local-userpass/dtos.ts +20 -2
  76. package/src/auth/utils.ts +66 -83
  77. package/src/constants.ts +14 -2
  78. package/src/features/functions/controller.ts +42 -12
  79. package/src/features/functions/dtos.ts +3 -0
  80. package/src/features/functions/interface.ts +3 -0
  81. package/src/features/functions/utils.ts +29 -8
  82. package/src/features/triggers/index.ts +44 -1
  83. package/src/features/triggers/interface.ts +1 -0
  84. package/src/features/triggers/utils.ts +89 -37
  85. package/src/index.ts +49 -13
  86. package/src/services/mongodb-atlas/__tests__/findOneAndUpdate.test.ts +95 -0
  87. package/src/services/mongodb-atlas/index.ts +100 -2
  88. package/src/services/mongodb-atlas/model.ts +16 -3
  89. package/src/shared/handleUserRegistration.ts +83 -2
  90. package/src/shared/models/handleUserRegistration.model.ts +2 -1
  91. package/src/utils/__tests__/registerPlugins.test.ts +5 -1
  92. package/src/utils/context/index.ts +238 -18
  93. package/src/utils/context/interface.ts +1 -1
  94. package/src/utils/crypto/index.ts +5 -1
  95. package/src/utils/initializer/exposeRoutes.ts +1 -1
  96. package/src/utils/initializer/registerPlugins.ts +8 -0
  97. package/src/utils/roles/helpers.ts +3 -2
package/README.md CHANGED
@@ -96,6 +96,13 @@ Ensure the following environment variables are set in your .env file or deployme
96
96
  | `APP_SECRET` | Secret used to sign and verify JWT tokens (choose a strong secret). | `supersecretkey123!` |
97
97
  | `HOST` | The host address the server binds to (usually `0.0.0.0` for public access). | `0.0.0.0` |
98
98
  | `HTTPS_SCHEMA` | The schema for your server requests (usually `https` or `http`). | `http` |
99
+ | `RESET_PASSWORD_TTL_SECONDS` | Time-to-live for password reset tokens (in seconds). | `3600` |
100
+ | `AUTH_RATE_LIMIT_WINDOW_MS` | Rate limit window for auth endpoints (in ms). | `900000` |
101
+ | `AUTH_LOGIN_MAX_ATTEMPTS` | Max login attempts per window. | `10` |
102
+ | `AUTH_RESET_MAX_ATTEMPTS` | Max reset requests per window. | `5` |
103
+ | `REFRESH_TOKEN_TTL_DAYS` | Refresh token time-to-live (in days). | `60` |
104
+ | `SWAGGER_UI_USER` | Basic Auth username for Swagger UI (optional). | `admin` |
105
+ | `SWAGGER_UI_PASSWORD` | Basic Auth password for Swagger UI (optional). | `change-me` |
99
106
 
100
107
 
101
108
  Example:
@@ -106,6 +113,13 @@ DB_CONNECTION_STRING=mongodb+srv://username:password@cluster.mongodb.net/dbname
106
113
  APP_SECRET=your-jwt-secret
107
114
  HOST=0.0.0.0
108
115
  HTTPS_SCHEMA=http
116
+ RESET_PASSWORD_TTL_SECONDS=3600
117
+ AUTH_RATE_LIMIT_WINDOW_MS=900000
118
+ AUTH_LOGIN_MAX_ATTEMPTS=10
119
+ AUTH_RESET_MAX_ATTEMPTS=5
120
+ REFRESH_TOKEN_TTL_DAYS=60
121
+ SWAGGER_UI_USER=admin
122
+ SWAGGER_UI_PASSWORD=change-me
109
123
  ```
110
124
 
111
125
  🛡️ Note: Never commit .env files to source control. Use a .gitignore file to exclude it.
@@ -215,11 +229,12 @@ However, all users will be required to reset their passwords since it is not pos
215
229
 
216
230
  ### ✅ Supported Auth Method
217
231
 
218
- The only authentication mode currently re-implemented in `@flowerforce/flowerbase` is:
232
+ The authentication modes currently re-implemented in `@flowerforce/flowerbase` are:
219
233
 
220
234
  - Local Email/Password (local-userpass)
235
+ - Anonymous (anon-user)
221
236
 
222
- > Other methods (OAuth, API key, anonymous, etc.) are not supported yet.
237
+ > Other methods (OAuth, API key, etc.) are not supported yet.
223
238
 
224
239
  #### Example user:
225
240
  ```js
@@ -256,13 +271,18 @@ Example
256
271
  "name": "local-userpass",
257
272
  "type": "local-userpass",
258
273
  "disabled": false,
259
- "auth_collection": "my-users-collection" //custom collection name
274
+ "auth_collection": "my-users-collection", //custom collection name
260
275
  "config": {
261
276
  "autoConfirm": true,
262
277
  "resetPasswordSubject": "reset",
263
278
  "resetPasswordUrl": "https://my.app.url/password-reset",
264
279
  "runConfirmationFunction": false
265
280
  }
281
+ },
282
+ "anon-user": {
283
+ "name": "anon-user",
284
+ "type": "anon-user",
285
+ "disabled": false
266
286
  }
267
287
  }
268
288
 
@@ -406,6 +426,13 @@ Ensure the following environment variables are set in your .env file or deployme
406
426
  | `APP_SECRET` | Secret used to sign and verify JWT tokens (choose a strong secret). | `supersecretkey123!` |
407
427
  | `HOST` | The host address the server binds to (usually `0.0.0.0` for public access). | `0.0.0.0` |
408
428
  | `HTTPS_SCHEMA` | The schema for your server requests (usually `https` or `http`). | `http` |
429
+ | `RESET_PASSWORD_TTL_SECONDS` | Time-to-live for password reset tokens (in seconds). | `3600` |
430
+ | `AUTH_RATE_LIMIT_WINDOW_MS` | Rate limit window for auth endpoints (in ms). | `900000` |
431
+ | `AUTH_LOGIN_MAX_ATTEMPTS` | Max login attempts per window. | `10` |
432
+ | `AUTH_RESET_MAX_ATTEMPTS` | Max reset requests per window. | `5` |
433
+ | `REFRESH_TOKEN_TTL_DAYS` | Refresh token time-to-live (in days). | `60` |
434
+ | `SWAGGER_UI_USER` | Basic Auth username for Swagger UI (optional). | `admin` |
435
+ | `SWAGGER_UI_PASSWORD` | Basic Auth password for Swagger UI (optional). | `change-me` |
409
436
 
410
437
 
411
438
  Example:
@@ -416,6 +443,13 @@ DB_CONNECTION_STRING=mongodb+srv://username:password@cluster.mongodb.net/dbname
416
443
  APP_SECRET=your-jwt-secret
417
444
  HOST=0.0.0.0
418
445
  HTTPS_SCHEMA=http
446
+ RESET_PASSWORD_TTL_SECONDS=3600
447
+ AUTH_RATE_LIMIT_WINDOW_MS=900000
448
+ AUTH_LOGIN_MAX_ATTEMPTS=10
449
+ AUTH_RESET_MAX_ATTEMPTS=5
450
+ REFRESH_TOKEN_TTL_DAYS=60
451
+ SWAGGER_UI_USER=admin
452
+ SWAGGER_UI_PASSWORD=change-me
419
453
  ```
420
454
 
421
455
  🛡️ Note: Never commit .env files to source control. Use a .gitignore file to exclude it.
@@ -472,6 +506,3 @@ export default app;
472
506
 
473
507
  >🔗 The baseUrl should point to the backend URL you deployed earlier using Flowerbase.
474
508
  This tells the frontend SDK where to send authentication and data requests.
475
-
476
-
477
-
@@ -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;AAOzC;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,eAAe,iBA8ExD"}
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,iBA+IxD"}
@@ -12,6 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.authController = authController;
13
13
  const bson_1 = require("bson");
14
14
  const constants_1 = require("../constants");
15
+ const crypto_1 = require("../utils/crypto");
15
16
  const utils_1 = require("./utils");
16
17
  const HANDLER_TYPE = 'preHandler';
17
18
  /**
@@ -21,8 +22,15 @@ const HANDLER_TYPE = 'preHandler';
21
22
  */
22
23
  function authController(app) {
23
24
  return __awaiter(this, void 0, void 0, function* () {
24
- const { authCollection, userCollection } = constants_1.AUTH_CONFIG;
25
+ const { authCollection, userCollection, refreshTokensCollection } = constants_1.AUTH_CONFIG;
25
26
  const db = app.mongo.client.db(constants_1.DB_NAME);
27
+ const refreshTokenTtlMs = constants_1.DEFAULT_CONFIG.REFRESH_TOKEN_TTL_DAYS * 24 * 60 * 60 * 1000;
28
+ try {
29
+ yield db.collection(refreshTokensCollection).createIndex({ expiresAt: 1 }, { expireAfterSeconds: 0 });
30
+ }
31
+ catch (error) {
32
+ console.error('Failed to ensure refresh token TTL index', error);
33
+ }
26
34
  app.addHook(HANDLER_TYPE, app.jwtAuthentication);
27
35
  /**
28
36
  * Endpoint to retrieve the authenticated user's profile.
@@ -64,6 +72,20 @@ function authController(app) {
64
72
  if (req.user.typ !== 'refresh') {
65
73
  throw new Error(utils_1.AUTH_ERRORS.INVALID_TOKEN);
66
74
  }
75
+ const authHeader = req.headers.authorization;
76
+ if (!(authHeader === null || authHeader === void 0 ? void 0 : authHeader.startsWith('Bearer '))) {
77
+ throw new Error(utils_1.AUTH_ERRORS.INVALID_TOKEN);
78
+ }
79
+ const refreshToken = authHeader.slice('Bearer '.length).trim();
80
+ const refreshTokenHash = (0, crypto_1.hashToken)(refreshToken);
81
+ const storedToken = yield db.collection(refreshTokensCollection).findOne({
82
+ tokenHash: refreshTokenHash,
83
+ revokedAt: null,
84
+ expiresAt: { $gt: new Date() }
85
+ });
86
+ if (!storedToken) {
87
+ throw new Error(utils_1.AUTH_ERRORS.INVALID_TOKEN);
88
+ }
67
89
  const auth_user = yield (db === null || db === void 0 ? void 0 : db.collection(authCollection).findOne({ _id: new this.mongo.ObjectId(req.user.sub) }));
68
90
  if (!auth_user) {
69
91
  throw new Error(`User with ID ${req.user.sub} not found`);
@@ -73,16 +95,45 @@ function authController(app) {
73
95
  : {};
74
96
  res.status(201);
75
97
  return {
76
- access_token: this.createAccessToken(Object.assign(Object.assign({}, auth_user), { user_data: user }))
98
+ access_token: this.createAccessToken(Object.assign(Object.assign({}, auth_user), { user_data: Object.assign(Object.assign({}, user), { id: req.user.sub }) }))
77
99
  };
78
100
  });
79
101
  });
80
102
  /**
81
103
  * Endpoint to destroy the existing session.
82
104
  */
83
- app.delete(utils_1.AUTH_ENDPOINTS.SESSION, function () {
105
+ app.delete(utils_1.AUTH_ENDPOINTS.SESSION, function (req, res) {
84
106
  return __awaiter(this, void 0, void 0, function* () {
85
- return { status: "ok" };
107
+ var _a, _b;
108
+ const authHeader = req.headers.authorization;
109
+ if (!(authHeader === null || authHeader === void 0 ? void 0 : authHeader.startsWith('Bearer '))) {
110
+ res.status(204);
111
+ return;
112
+ }
113
+ const refreshToken = authHeader.slice('Bearer '.length).trim();
114
+ const refreshTokenHash = (0, crypto_1.hashToken)(refreshToken);
115
+ const now = new Date();
116
+ const expiresAt = new Date(Date.now() + refreshTokenTtlMs);
117
+ const updateResult = yield db.collection(refreshTokensCollection).findOneAndUpdate({ tokenHash: refreshTokenHash }, {
118
+ $set: {
119
+ revokedAt: now,
120
+ expiresAt
121
+ }
122
+ }, { returnDocument: 'after' });
123
+ const fromToken = (_a = req.user) === null || _a === void 0 ? void 0 : _a.sub;
124
+ let userId = (_b = updateResult === null || updateResult === void 0 ? void 0 : updateResult.value) === null || _b === void 0 ? void 0 : _b.userId;
125
+ if (!userId && fromToken) {
126
+ try {
127
+ userId = new bson_1.ObjectId(fromToken);
128
+ }
129
+ catch (_c) {
130
+ userId = fromToken;
131
+ }
132
+ }
133
+ if (userId && authCollection) {
134
+ yield db.collection(authCollection).updateOne({ _id: userId }, { $set: { lastLogoutAt: now } });
135
+ }
136
+ return { status: 'ok' };
86
137
  });
87
138
  });
88
139
  });
@@ -1 +1 @@
1
- {"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../../../src/auth/plugins/jwt.ts"],"names":[],"mappings":"AAIA,KAAK,OAAO,GAAG;IACb,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAED;;;;;;;GAOG;iUAC8C,OAAO;AAAxD,wBAwDE"}
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;AAQD;;;;;;;GAOG;iUAC8C,OAAO;AAAxD,wBA4GE"}
@@ -15,6 +15,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
15
15
  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
+ const constants_1 = require("../../constants");
18
19
  /**
19
20
  * This module is a Fastify plugin that sets up JWT-based authentication and token creation.
20
21
  * It registers JWT authentication, and provides methods to create access and refresh tokens.
@@ -31,19 +32,64 @@ exports.default = (0, fastify_plugin_1.default)(function (fastify, opts) {
31
32
  });
32
33
  fastify.decorate('jwtAuthentication', function (request, reply) {
33
34
  return __awaiter(this, void 0, void 0, function* () {
35
+ var _a, _b, _c;
34
36
  try {
35
37
  yield request.jwtVerify();
36
38
  }
37
39
  catch (err) {
38
- // TODO: handle error
39
- reply.send(err);
40
+ fastify.log.warn({ err }, 'JWT authentication failed');
41
+ reply.code(401).send({ message: 'Unauthorized' });
42
+ return;
43
+ }
44
+ if (((_a = request.user) === null || _a === void 0 ? void 0 : _a.typ) !== 'access') {
45
+ return;
46
+ }
47
+ const db = (_c = (_b = fastify.mongo) === null || _b === void 0 ? void 0 : _b.client) === null || _c === void 0 ? void 0 : _c.db(constants_1.DB_NAME);
48
+ if (!db) {
49
+ fastify.log.warn('Mongo client unavailable while checking logout state');
50
+ return;
51
+ }
52
+ if (!request.user.sub) {
53
+ reply.code(401).send({ message: 'Unauthorized' });
54
+ return;
55
+ }
56
+ let authUser;
57
+ try {
58
+ authUser = yield db
59
+ .collection(constants_1.AUTH_CONFIG.authCollection)
60
+ .findOne({ _id: new mongodb_1.ObjectId(request.user.sub) });
61
+ }
62
+ catch (err) {
63
+ fastify.log.warn({ err }, 'Failed to lookup user during JWT authentication');
64
+ reply.code(401).send({ message: 'Unauthorized' });
65
+ return;
66
+ }
67
+ if (!authUser) {
68
+ reply.code(401).send({ message: 'Unauthorized' });
69
+ return;
70
+ }
71
+ const lastLogoutAt = authUser.lastLogoutAt ? new Date(authUser.lastLogoutAt) : null;
72
+ const accessUser = request.user;
73
+ const rawIssuedAt = accessUser.iat;
74
+ const issuedAt = typeof rawIssuedAt === 'number'
75
+ ? rawIssuedAt
76
+ : typeof rawIssuedAt === 'string'
77
+ ? Number(rawIssuedAt)
78
+ : undefined;
79
+ if (lastLogoutAt &&
80
+ !Number.isNaN(lastLogoutAt.getTime()) &&
81
+ typeof issuedAt === 'number' &&
82
+ !Number.isNaN(issuedAt) &&
83
+ lastLogoutAt.getTime() >= issuedAt * 1000) {
84
+ reply.code(401).send({ message: 'Unauthorized' });
85
+ return;
40
86
  }
41
87
  });
42
88
  });
43
89
  fastify.decorate('createAccessToken', function (user) {
44
90
  const id = user._id.toString();
45
- const userDataId = user.user_data._id.toString();
46
- const user_data = Object.assign({ _id: userDataId, id: userDataId, email: user.email }, user.user_data);
91
+ // const userDataId = user.user_data._id.toString()
92
+ const user_data = Object.assign(Object.assign({}, user.user_data), { _id: id, id: id, email: user.email });
47
93
  return this.jwt.sign({
48
94
  typ: 'access',
49
95
  id,
@@ -53,7 +99,7 @@ exports.default = (0, fastify_plugin_1.default)(function (fastify, opts) {
53
99
  }, {
54
100
  iss: BAAS_ID,
55
101
  jti: BAAS_ID,
56
- sub: user._id.toJSON(),
102
+ sub: id,
57
103
  expiresIn: '300m'
58
104
  });
59
105
  });
@@ -63,7 +109,7 @@ exports.default = (0, fastify_plugin_1.default)(function (fastify, opts) {
63
109
  baas_id: BAAS_ID
64
110
  }, {
65
111
  sub: user._id.toJSON(),
66
- expiresIn: '60d'
112
+ expiresIn: `${constants_1.DEFAULT_CONFIG.REFRESH_TOKEN_TTL_DAYS}d`
67
113
  });
68
114
  });
69
115
  });
@@ -0,0 +1,8 @@
1
+ import { FastifyInstance } from 'fastify';
2
+ /**
3
+ * Controller for handling anonymous user login.
4
+ * @testable
5
+ * @param {FastifyInstance} app - The Fastify instance.
6
+ */
7
+ export declare function anonUserController(app: FastifyInstance): Promise<void>;
8
+ //# sourceMappingURL=controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../../../src/auth/providers/anon-user/controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAOzC;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,eAAe,iBA8E5D"}
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.anonUserController = anonUserController;
13
+ const constants_1 = require("../../../constants");
14
+ const crypto_1 = require("../../../utils/crypto");
15
+ const handleUserRegistration_model_1 = require("../../../shared/models/handleUserRegistration.model");
16
+ const utils_1 = require("../../utils");
17
+ /**
18
+ * Controller for handling anonymous user login.
19
+ * @testable
20
+ * @param {FastifyInstance} app - The Fastify instance.
21
+ */
22
+ function anonUserController(app) {
23
+ return __awaiter(this, void 0, void 0, function* () {
24
+ const db = app.mongo.client.db(constants_1.DB_NAME);
25
+ const { authCollection, refreshTokensCollection, providers } = constants_1.AUTH_CONFIG;
26
+ const refreshTokenTtlMs = constants_1.DEFAULT_CONFIG.REFRESH_TOKEN_TTL_DAYS * 24 * 60 * 60 * 1000;
27
+ const anonUserTtlSeconds = constants_1.DEFAULT_CONFIG.ANON_USER_TTL_SECONDS;
28
+ try {
29
+ yield db.collection(authCollection).createIndex({ createdAt: 1 }, {
30
+ expireAfterSeconds: anonUserTtlSeconds,
31
+ partialFilterExpression: { 'identities.provider_type': handleUserRegistration_model_1.PROVIDER.ANON_USER }
32
+ });
33
+ }
34
+ catch (error) {
35
+ console.error('Failed to ensure anonymous user TTL index', error);
36
+ }
37
+ app.post(utils_1.AUTH_ENDPOINTS.LOGIN, function () {
38
+ return __awaiter(this, void 0, void 0, function* () {
39
+ const anonProvider = providers === null || providers === void 0 ? void 0 : providers['anon-user'];
40
+ if (anonProvider === null || anonProvider === void 0 ? void 0 : anonProvider.disabled) {
41
+ throw new Error('Anonymous authentication disabled');
42
+ }
43
+ const now = new Date();
44
+ const insertResult = yield db.collection(authCollection).insertOne({
45
+ status: 'confirmed',
46
+ createdAt: now,
47
+ custom_data: {},
48
+ identities: [
49
+ {
50
+ provider_type: handleUserRegistration_model_1.PROVIDER.ANON_USER,
51
+ provider_data: {}
52
+ }
53
+ ]
54
+ });
55
+ const userId = insertResult.insertedId;
56
+ yield db.collection(authCollection).updateOne({ _id: userId }, {
57
+ $set: {
58
+ identities: [
59
+ {
60
+ id: userId.toString(),
61
+ provider_id: userId.toString(),
62
+ provider_type: handleUserRegistration_model_1.PROVIDER.ANON_USER,
63
+ provider_data: {}
64
+ }
65
+ ]
66
+ }
67
+ });
68
+ const currentUserData = {
69
+ _id: userId,
70
+ user_data: {}
71
+ };
72
+ const refreshToken = this.createRefreshToken(currentUserData);
73
+ const refreshTokenHash = (0, crypto_1.hashToken)(refreshToken);
74
+ yield db.collection(refreshTokensCollection).insertOne({
75
+ userId,
76
+ tokenHash: refreshTokenHash,
77
+ createdAt: now,
78
+ expiresAt: new Date(Date.now() + refreshTokenTtlMs),
79
+ revokedAt: null
80
+ });
81
+ return {
82
+ access_token: this.createAccessToken(currentUserData),
83
+ refresh_token: refreshToken,
84
+ device_id: '',
85
+ user_id: userId.toString()
86
+ };
87
+ });
88
+ });
89
+ });
90
+ }
@@ -0,0 +1,10 @@
1
+ export type LoginSuccessDto = {
2
+ access_token: string;
3
+ device_id: string;
4
+ refresh_token: string;
5
+ user_id: string;
6
+ };
7
+ export interface LoginDto {
8
+ Reply: LoginSuccessDto;
9
+ }
10
+ //# sourceMappingURL=dtos.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dtos.d.ts","sourceRoot":"","sources":["../../../../src/auth/providers/anon-user/dtos.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GAAG;IAC5B,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,eAAe,CAAA;CACvB"}
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -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;AAezC;;;;GAIG;AACH,wBAAsB,wBAAwB,CAAC,GAAG,EAAE,eAAe,iBAiFlE"}
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,iBAgGlE"}
@@ -8,16 +8,12 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
- var __importDefault = (this && this.__importDefault) || function (mod) {
12
- return (mod && mod.__esModule) ? mod : { "default": mod };
13
- };
14
11
  Object.defineProperty(exports, "__esModule", { value: true });
15
12
  exports.customFunctionController = customFunctionController;
16
13
  const constants_1 = require("../../../constants");
17
- const handleUserRegistration_1 = __importDefault(require("../../../shared/handleUserRegistration"));
18
- const handleUserRegistration_model_1 = require("../../../shared/models/handleUserRegistration.model");
19
14
  const state_1 = require("../../../state");
20
15
  const context_1 = require("../../../utils/context");
16
+ const crypto_1 = require("../../../utils/crypto");
21
17
  const utils_1 = require("../../utils");
22
18
  const schema_1 = require("./schema");
23
19
  /**
@@ -29,6 +25,9 @@ function customFunctionController(app) {
29
25
  return __awaiter(this, void 0, void 0, function* () {
30
26
  const functionsList = state_1.StateManager.select('functions');
31
27
  const services = state_1.StateManager.select('services');
28
+ const db = app.mongo.client.db(constants_1.DB_NAME);
29
+ const { authCollection, refreshTokensCollection } = constants_1.AUTH_CONFIG;
30
+ const refreshTokenTtlMs = constants_1.DEFAULT_CONFIG.REFRESH_TOKEN_TTL_DAYS * 24 * 60 * 60 * 1000;
32
31
  /**
33
32
  * Endpoint for user login.
34
33
  *
@@ -38,7 +37,7 @@ function customFunctionController(app) {
38
37
  */
39
38
  app.post(utils_1.AUTH_ENDPOINTS.LOGIN, {
40
39
  schema: schema_1.LOGIN_SCHEMA
41
- }, function (req) {
40
+ }, function (req, reply) {
42
41
  return __awaiter(this, void 0, void 0, function* () {
43
42
  const { providers } = constants_1.AUTH_CONFIG;
44
43
  const authFunctionName = providers["custom-function"].authFunctionName;
@@ -46,7 +45,7 @@ function customFunctionController(app) {
46
45
  throw new Error("Missing Auth Function");
47
46
  }
48
47
  const { ips, host, hostname, url, method, ip, id } = req;
49
- const res = yield (0, context_1.GenerateContext)({
48
+ const authResult = yield (0, context_1.GenerateContext)({
50
49
  args: [
51
50
  req.body
52
51
  ],
@@ -66,25 +65,36 @@ function customFunctionController(app) {
66
65
  id
67
66
  }
68
67
  });
69
- if (res.id) {
70
- const user = yield (0, handleUserRegistration_1.default)(app, { run_as_system: true, skipUserCheck: true, provider: handleUserRegistration_model_1.PROVIDER.CUSTOM_FUNCTION })({ email: res.id, password: (0, utils_1.generatePassword)() });
71
- if (!(user === null || user === void 0 ? void 0 : user.insertedId)) {
72
- throw new Error('Failed to register custom user');
73
- }
74
- const currentUserData = {
75
- _id: user.insertedId,
76
- user_data: {
77
- _id: user.insertedId
78
- }
79
- };
80
- return {
81
- access_token: this.createAccessToken(currentUserData),
82
- refresh_token: this.createRefreshToken(currentUserData),
83
- device_id: '',
84
- user_id: user.insertedId.toString()
85
- };
68
+ if (!authResult.id) {
69
+ reply.code(401).send({ message: 'Unauthorized' });
70
+ return;
71
+ }
72
+ const authUser = yield db.collection(authCollection).findOne({ email: authResult.id });
73
+ if (!authUser) {
74
+ reply.code(401).send({ message: 'Unauthorized' });
75
+ return;
86
76
  }
87
- throw new Error("Authentication Failed");
77
+ const currentUserData = {
78
+ _id: authUser._id,
79
+ user_data: {
80
+ _id: authUser._id
81
+ }
82
+ };
83
+ const refreshToken = this.createRefreshToken(currentUserData);
84
+ const refreshTokenHash = (0, crypto_1.hashToken)(refreshToken);
85
+ yield db.collection(refreshTokensCollection).insertOne({
86
+ userId: authUser._id,
87
+ tokenHash: refreshTokenHash,
88
+ createdAt: new Date(),
89
+ expiresAt: new Date(Date.now() + refreshTokenTtlMs),
90
+ revokedAt: null
91
+ });
92
+ return {
93
+ access_token: this.createAccessToken(currentUserData),
94
+ refresh_token: refreshToken,
95
+ device_id: '',
96
+ user_id: authUser._id.toString()
97
+ };
88
98
  });
89
99
  });
90
100
  });
@@ -8,8 +8,11 @@ export type LoginSuccessDto = {
8
8
  refresh_token: string;
9
9
  user_id: string;
10
10
  };
11
+ export type LoginErrorDto = {
12
+ message: string;
13
+ };
11
14
  export interface LoginDto {
12
15
  Body: LoginUserDto;
13
- Reply: LoginSuccessDto;
16
+ Reply: LoginSuccessDto | LoginErrorDto;
14
17
  }
15
18
  //# sourceMappingURL=dtos.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dtos.d.ts","sourceRoot":"","sources":["../../../../src/auth/providers/custom-function/dtos.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,YAAY,CAAA;IAClB,KAAK,EAAE,eAAe,CAAA;CACvB"}
1
+ {"version":3,"file":"dtos.d.ts","sourceRoot":"","sources":["../../../../src/auth/providers/custom-function/dtos.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,YAAY,CAAA;IAClB,KAAK,EAAE,eAAe,GAAG,aAAa,CAAA;CACvC"}
@@ -1 +1 @@
1
- {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../../../src/auth/providers/local-userpass/controller.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAuBzC;;;;GAIG;AACH,wBAAsB,uBAAuB,CAAC,GAAG,EAAE,eAAe,iBAqOjE"}
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,iBAsUjE"}