@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.
- package/README.md +37 -6
- package/dist/auth/controller.d.ts.map +1 -1
- package/dist/auth/controller.js +55 -4
- package/dist/auth/plugins/jwt.d.ts.map +1 -1
- package/dist/auth/plugins/jwt.js +52 -6
- package/dist/auth/providers/anon-user/controller.d.ts +8 -0
- package/dist/auth/providers/anon-user/controller.d.ts.map +1 -0
- package/dist/auth/providers/anon-user/controller.js +90 -0
- package/dist/auth/providers/anon-user/dtos.d.ts +10 -0
- package/dist/auth/providers/anon-user/dtos.d.ts.map +1 -0
- package/dist/auth/providers/anon-user/dtos.js +2 -0
- package/dist/auth/providers/custom-function/controller.d.ts.map +1 -1
- package/dist/auth/providers/custom-function/controller.js +35 -25
- package/dist/auth/providers/custom-function/dtos.d.ts +4 -1
- package/dist/auth/providers/custom-function/dtos.d.ts.map +1 -1
- package/dist/auth/providers/local-userpass/controller.d.ts.map +1 -1
- package/dist/auth/providers/local-userpass/controller.js +159 -73
- package/dist/auth/providers/local-userpass/dtos.d.ts +17 -2
- package/dist/auth/providers/local-userpass/dtos.d.ts.map +1 -1
- package/dist/auth/utils.d.ts +76 -14
- package/dist/auth/utils.d.ts.map +1 -1
- package/dist/auth/utils.js +55 -61
- package/dist/constants.d.ts +12 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +16 -4
- package/dist/features/functions/controller.d.ts.map +1 -1
- package/dist/features/functions/controller.js +31 -12
- package/dist/features/functions/dtos.d.ts +3 -0
- package/dist/features/functions/dtos.d.ts.map +1 -1
- package/dist/features/functions/interface.d.ts +3 -0
- package/dist/features/functions/interface.d.ts.map +1 -1
- package/dist/features/functions/utils.d.ts +3 -2
- package/dist/features/functions/utils.d.ts.map +1 -1
- package/dist/features/functions/utils.js +19 -7
- package/dist/features/triggers/index.d.ts.map +1 -1
- package/dist/features/triggers/index.js +49 -7
- package/dist/features/triggers/interface.d.ts +1 -0
- package/dist/features/triggers/interface.d.ts.map +1 -1
- package/dist/features/triggers/utils.d.ts.map +1 -1
- package/dist/features/triggers/utils.js +67 -26
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +48 -13
- package/dist/services/mongodb-atlas/index.d.ts.map +1 -1
- package/dist/services/mongodb-atlas/index.js +72 -2
- package/dist/services/mongodb-atlas/model.d.ts +3 -2
- package/dist/services/mongodb-atlas/model.d.ts.map +1 -1
- package/dist/shared/handleUserRegistration.d.ts.map +1 -1
- package/dist/shared/handleUserRegistration.js +66 -1
- package/dist/shared/models/handleUserRegistration.model.d.ts +2 -1
- package/dist/shared/models/handleUserRegistration.model.d.ts.map +1 -1
- package/dist/shared/models/handleUserRegistration.model.js +1 -0
- package/dist/utils/context/helpers.d.ts +6 -6
- package/dist/utils/context/helpers.d.ts.map +1 -1
- package/dist/utils/context/index.d.ts +1 -1
- package/dist/utils/context/index.d.ts.map +1 -1
- package/dist/utils/context/index.js +176 -9
- package/dist/utils/context/interface.d.ts +1 -1
- package/dist/utils/context/interface.d.ts.map +1 -1
- package/dist/utils/crypto/index.d.ts +1 -0
- package/dist/utils/crypto/index.d.ts.map +1 -1
- package/dist/utils/crypto/index.js +6 -2
- package/dist/utils/initializer/exposeRoutes.js +1 -1
- package/dist/utils/initializer/registerPlugins.d.ts.map +1 -1
- package/dist/utils/initializer/registerPlugins.js +12 -4
- package/dist/utils/roles/helpers.js +2 -1
- package/package.json +1 -2
- package/src/auth/controller.ts +71 -5
- package/src/auth/plugins/jwt.test.ts +93 -0
- package/src/auth/plugins/jwt.ts +67 -8
- package/src/auth/providers/anon-user/controller.ts +91 -0
- package/src/auth/providers/anon-user/dtos.ts +10 -0
- package/src/auth/providers/custom-function/controller.ts +40 -31
- package/src/auth/providers/custom-function/dtos.ts +5 -1
- package/src/auth/providers/local-userpass/controller.ts +211 -101
- package/src/auth/providers/local-userpass/dtos.ts +20 -2
- package/src/auth/utils.ts +66 -83
- package/src/constants.ts +14 -2
- package/src/features/functions/controller.ts +42 -12
- package/src/features/functions/dtos.ts +3 -0
- package/src/features/functions/interface.ts +3 -0
- package/src/features/functions/utils.ts +29 -8
- package/src/features/triggers/index.ts +44 -1
- package/src/features/triggers/interface.ts +1 -0
- package/src/features/triggers/utils.ts +89 -37
- package/src/index.ts +49 -13
- package/src/services/mongodb-atlas/__tests__/findOneAndUpdate.test.ts +95 -0
- package/src/services/mongodb-atlas/index.ts +100 -2
- package/src/services/mongodb-atlas/model.ts +16 -3
- package/src/shared/handleUserRegistration.ts +83 -2
- package/src/shared/models/handleUserRegistration.model.ts +2 -1
- package/src/utils/__tests__/registerPlugins.test.ts +5 -1
- package/src/utils/context/index.ts +238 -18
- package/src/utils/context/interface.ts +1 -1
- package/src/utils/crypto/index.ts +5 -1
- package/src/utils/initializer/exposeRoutes.ts +1 -1
- package/src/utils/initializer/registerPlugins.ts +8 -0
- 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
|
|
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,
|
|
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;
|
|
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"}
|
package/dist/auth/controller.js
CHANGED
|
@@ -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
|
-
|
|
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":"
|
|
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"}
|
package/dist/auth/plugins/jwt.js
CHANGED
|
@@ -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
|
-
|
|
39
|
-
reply.send(
|
|
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:
|
|
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:
|
|
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:
|
|
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 @@
|
|
|
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"}
|
|
@@ -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;
|
|
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
|
|
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 (
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
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;
|
|
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":"
|
|
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"}
|