@flink-app/generic-auth-plugin 0.11.16 → 0.11.18
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/.flink/generatedHandlers.ts +1 -1
- package/.flink/generatedJobs.ts +1 -1
- package/.flink/generatedRepos.ts +1 -1
- package/.flink/schemas/schemas.ts +1 -1
- package/.flink/start.ts +1 -1
- package/CLAUDE.md +32 -0
- package/dist/.flink/generatedHandlers.js +1 -1
- package/dist/.flink/generatedJobs.js +1 -1
- package/dist/.flink/generatedRepos.js +1 -1
- package/dist/.flink/start.js +1 -1
- package/dist/src/coreFunctions.d.ts +4 -2
- package/dist/src/coreFunctions.js +41 -16
- package/dist/src/genericAuthContext.d.ts +6 -3
- package/dist/src/genericAuthPluginOptions.d.ts +4 -0
- package/dist/src/handlers/UserCreate.js +1 -1
- package/dist/src/handlers/UserPasswordResetComplete.js +1 -1
- package/dist/src/handlers/UserPasswordResetStart.js +1 -1
- package/dist/src/schemas/User.d.ts +1 -0
- package/dist/src/schemas/UserPasswordResetSettings.d.ts +1 -0
- package/package.json +2 -2
- package/src/coreFunctions.ts +41 -14
- package/src/genericAuthContext.ts +64 -23
- package/src/genericAuthPluginOptions.ts +4 -0
- package/src/handlers/UserCreate.ts +2 -1
- package/src/handlers/UserPasswordResetComplete.ts +2 -1
- package/src/handlers/UserPasswordResetStart.ts +1 -1
- package/src/schemas/User.ts +1 -0
- package/src/schemas/UserPasswordResetSettings.ts +1 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Generated
|
|
1
|
+
// Generated Wed Mar 12 2025 11:44:41 GMT+0100 (Central European Standard Time)
|
|
2
2
|
import { autoRegisteredHandlers, HttpMethod } from "@flink-app/flink";
|
|
3
3
|
import * as UserCreate_0 from "../src/handlers/UserCreate";
|
|
4
4
|
import * as UserLogin_0 from "../src/handlers/UserLogin";
|
package/.flink/generatedJobs.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Generated
|
|
1
|
+
// Generated Wed Mar 12 2025 11:44:41 GMT+0100 (Central European Standard Time)
|
|
2
2
|
import { autoRegisteredJobs } from "@flink-app/flink";
|
|
3
3
|
export const jobs = [];
|
|
4
4
|
autoRegisteredJobs.push(...jobs);
|
package/.flink/generatedRepos.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Generated
|
|
1
|
+
// Generated Wed Mar 12 2025 11:44:41 GMT+0100 (Central European Standard Time)
|
|
2
2
|
import { autoRegisteredRepos } from "@flink-app/flink";
|
|
3
3
|
export const repos = [];
|
|
4
4
|
autoRegisteredRepos.push(...repos);
|
|
@@ -29,7 +29,7 @@ import { PutManagementUserRolesByUseridRes } from "../../src/schemas/Management/
|
|
|
29
29
|
import { PutManagementUserUsernameByUseridReq } from "../../src/schemas/Management/PutUserUsernameByUseridReq";
|
|
30
30
|
import { PutManagementUserUsernameByUseridRes } from "../../src/schemas/Management/PutUserUsernameByUseridRes";
|
|
31
31
|
|
|
32
|
-
// Generated
|
|
32
|
+
// Generated Wed Mar 12 2025 11:44:41 GMT+0100 (Central European Standard Time)
|
|
33
33
|
export interface UserCreate_7_ReqSchema extends UserCreateReq {}
|
|
34
34
|
|
|
35
35
|
export interface UserCreate_7_ResSchema extends UserCreateRes {}
|
package/.flink/start.ts
CHANGED
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# CLAUDE.md - Guidelines for Generic Auth Plugin
|
|
2
|
+
|
|
3
|
+
## Build Commands
|
|
4
|
+
- Build: `npm run build` (runs `flink build`)
|
|
5
|
+
- Watch mode: `npm run watch` (uses nodemon to watch files and trigger builds)
|
|
6
|
+
- Publish: `npm run prepublish` (runs build before publishing)
|
|
7
|
+
|
|
8
|
+
## Code Style
|
|
9
|
+
|
|
10
|
+
### TypeScript
|
|
11
|
+
- Target: ES5, CommonJS modules, strict typing enabled
|
|
12
|
+
- Keep type definitions in separate schema files in the `/schemas` directory
|
|
13
|
+
- Use explicit return types on all functions (eg: `Promise<UserLoginRes>`)
|
|
14
|
+
- Always handle optionality with `?` and `undefined`/`null` checks
|
|
15
|
+
|
|
16
|
+
### Naming Conventions
|
|
17
|
+
- camelCase for variables, functions, and methods
|
|
18
|
+
- PascalCase for interfaces and types
|
|
19
|
+
- Request types use `Req` suffix (UserLoginReq)
|
|
20
|
+
- Response types use `Res` suffix (UserLoginRes)
|
|
21
|
+
- Handler files match their function name (UserLogin.ts)
|
|
22
|
+
|
|
23
|
+
### Error Handling
|
|
24
|
+
- Use structured error responses with status fields and messages
|
|
25
|
+
- Return typed response objects rather than throwing exceptions
|
|
26
|
+
- Use try/catch blocks for operations that might fail (JWT verification)
|
|
27
|
+
|
|
28
|
+
### Code Structure
|
|
29
|
+
- Group imports: framework first, then local, then third-party
|
|
30
|
+
- Separate authentication methods and utility functions
|
|
31
|
+
- Use standard 4-space indentation with semicolons
|
|
32
|
+
- Maintain consistent function parameter ordering
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.handlers = void 0;
|
|
4
|
-
// Generated
|
|
4
|
+
// Generated Wed Mar 12 2025 11:44:41 GMT+0100 (Central European Standard Time)
|
|
5
5
|
var flink_1 = require("@flink-app/flink");
|
|
6
6
|
exports.handlers = [];
|
|
7
7
|
flink_1.autoRegisteredHandlers.push.apply(flink_1.autoRegisteredHandlers, exports.handlers);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.jobs = void 0;
|
|
4
|
-
// Generated
|
|
4
|
+
// Generated Wed Mar 12 2025 11:44:41 GMT+0100 (Central European Standard Time)
|
|
5
5
|
var flink_1 = require("@flink-app/flink");
|
|
6
6
|
exports.jobs = [];
|
|
7
7
|
flink_1.autoRegisteredJobs.push.apply(flink_1.autoRegisteredJobs, exports.jobs);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.repos = void 0;
|
|
4
|
-
// Generated
|
|
4
|
+
// Generated Wed Mar 12 2025 11:44:41 GMT+0100 (Central European Standard Time)
|
|
5
5
|
var flink_1 = require("@flink-app/flink");
|
|
6
6
|
exports.repos = [];
|
|
7
7
|
flink_1.autoRegisteredRepos.push.apply(flink_1.autoRegisteredRepos, exports.repos);
|
package/dist/.flink/start.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
// Generated
|
|
3
|
+
// Generated Wed Mar 12 2025 11:44:41 GMT+0100 (Central European Standard Time)
|
|
4
4
|
require("./generatedHandlers");
|
|
5
5
|
require("./generatedRepos");
|
|
6
6
|
require("./generatedJobs");
|
|
@@ -16,6 +16,8 @@ export declare function createUser(repo: FlinkRepo<any, User>, auth: JwtAuthPlug
|
|
|
16
16
|
hash: string;
|
|
17
17
|
salt: string;
|
|
18
18
|
} | null>;
|
|
19
|
+
}, onUserCreated?: {
|
|
20
|
+
(user: User): Promise<void>;
|
|
19
21
|
}): Promise<UserCreateRes>;
|
|
20
22
|
export declare function loginByToken(repo: FlinkRepo<any, User>, auth: JwtAuthPlugin, token: string, code: string, jwtSecret: string): Promise<UserLoginRes>;
|
|
21
23
|
export declare function loginUser(repo: FlinkRepo<any, User>, auth: JwtAuthPlugin, username: string, password: string | undefined, validatePasswordMethod?: {
|
|
@@ -29,10 +31,10 @@ export declare function changePassword(repo: FlinkRepo<any, User>, auth: JwtAuth
|
|
|
29
31
|
salt: string;
|
|
30
32
|
} | null>;
|
|
31
33
|
}): Promise<UserPasswordChangeRes>;
|
|
32
|
-
export declare function passwordResetStart(repo: FlinkRepo<any, User>, auth: JwtAuthPlugin, jwtSecret: string, username: string, numberOfDigits?: number, lifeTime?: string): Promise<UserPasswordResetStartRes>;
|
|
34
|
+
export declare function passwordResetStart(repo: FlinkRepo<any, User>, auth: JwtAuthPlugin, jwtSecret: string, username: string, numberOfDigits?: number, lifeTime?: string, passwordResetReusableTokens?: boolean): Promise<UserPasswordResetStartRes>;
|
|
33
35
|
export declare function passwordResetComplete(repo: FlinkRepo<any, User>, auth: JwtAuthPlugin, jwtSecret: string, passwordResetToken: string, code: string, newPassword: string, createPasswordHashAndSaltMethod?: {
|
|
34
36
|
(password: string): Promise<{
|
|
35
37
|
hash: string;
|
|
36
38
|
salt: string;
|
|
37
39
|
} | null>;
|
|
38
|
-
}): Promise<UserPasswordResetCompleteRes>;
|
|
40
|
+
}, passwordResetReusableTokens?: boolean): Promise<UserPasswordResetCompleteRes>;
|
|
@@ -70,7 +70,7 @@ function getJtwTokenPlugin(secret, rolePermissions, passwordPolicy, tokenTTL) {
|
|
|
70
70
|
});
|
|
71
71
|
}
|
|
72
72
|
exports.getJtwTokenPlugin = getJtwTokenPlugin;
|
|
73
|
-
function createUser(repo, auth, username, password, authentificationMethod, roles, profile, createPasswordHashAndSaltMethod) {
|
|
73
|
+
function createUser(repo, auth, username, password, authentificationMethod, roles, profile, createPasswordHashAndSaltMethod, onUserCreated) {
|
|
74
74
|
return __awaiter(this, void 0, void 0, function () {
|
|
75
75
|
var existingUser, userData, passwordAndSalt, user, token;
|
|
76
76
|
return __generator(this, function (_a) {
|
|
@@ -116,8 +116,13 @@ function createUser(repo, auth, username, password, authentificationMethod, role
|
|
|
116
116
|
case 6: return [4 /*yield*/, repo.create(userData)];
|
|
117
117
|
case 7:
|
|
118
118
|
user = _a.sent();
|
|
119
|
-
return [
|
|
119
|
+
if (!onUserCreated) return [3 /*break*/, 9];
|
|
120
|
+
return [4 /*yield*/, onUserCreated(user)];
|
|
120
121
|
case 8:
|
|
122
|
+
_a.sent();
|
|
123
|
+
_a.label = 9;
|
|
124
|
+
case 9: return [4 /*yield*/, auth.createToken({ username: username.toLowerCase(), _id: user._id }, roles)];
|
|
125
|
+
case 10:
|
|
121
126
|
token = _a.sent();
|
|
122
127
|
if (user.authentificationMethod == "sms") {
|
|
123
128
|
return [2 /*return*/, {
|
|
@@ -300,9 +305,10 @@ function changePassword(repo, auth, userId, newPassword, createPasswordHashAndSa
|
|
|
300
305
|
});
|
|
301
306
|
}
|
|
302
307
|
exports.changePassword = changePassword;
|
|
303
|
-
function passwordResetStart(repo, auth, jwtSecret, username, numberOfDigits, lifeTime) {
|
|
308
|
+
function passwordResetStart(repo, auth, jwtSecret, username, numberOfDigits, lifeTime, passwordResetReusableTokens) {
|
|
309
|
+
if (passwordResetReusableTokens === void 0) { passwordResetReusableTokens = true; }
|
|
304
310
|
return __awaiter(this, void 0, void 0, function () {
|
|
305
|
-
var user, fakepayload, fakeToken, payload, code, secret, options, token;
|
|
311
|
+
var user, fakepayload, fakeToken, payload, code, pwdResetStartedAt, secret, options, token;
|
|
306
312
|
return __generator(this, function (_a) {
|
|
307
313
|
switch (_a.label) {
|
|
308
314
|
case 0: return [4 /*yield*/, repo.getOne({ username: username.toLowerCase() })];
|
|
@@ -328,7 +334,17 @@ function passwordResetStart(repo, auth, jwtSecret, username, numberOfDigits, lif
|
|
|
328
334
|
username: username.toLocaleLowerCase(),
|
|
329
335
|
};
|
|
330
336
|
code = generate(numberOfDigits);
|
|
337
|
+
pwdResetStartedAt = new Date().toISOString();
|
|
338
|
+
if (!passwordResetReusableTokens) return [3 /*break*/, 2];
|
|
331
339
|
secret = jwtSecret + ":" + code;
|
|
340
|
+
return [3 /*break*/, 4];
|
|
341
|
+
case 2:
|
|
342
|
+
secret = jwtSecret + ":" + code + ":" + pwdResetStartedAt;
|
|
343
|
+
return [4 /*yield*/, repo.updateOne(user._id, { pwdResetStartedAt: pwdResetStartedAt })];
|
|
344
|
+
case 3:
|
|
345
|
+
_a.sent();
|
|
346
|
+
_a.label = 4;
|
|
347
|
+
case 4:
|
|
332
348
|
options = {
|
|
333
349
|
expiresIn: lifeTime,
|
|
334
350
|
};
|
|
@@ -344,28 +360,36 @@ function passwordResetStart(repo, auth, jwtSecret, username, numberOfDigits, lif
|
|
|
344
360
|
});
|
|
345
361
|
}
|
|
346
362
|
exports.passwordResetStart = passwordResetStart;
|
|
347
|
-
function passwordResetComplete(repo, auth, jwtSecret, passwordResetToken, code, newPassword, createPasswordHashAndSaltMethod) {
|
|
363
|
+
function passwordResetComplete(repo, auth, jwtSecret, passwordResetToken, code, newPassword, createPasswordHashAndSaltMethod, passwordResetReusableTokens) {
|
|
364
|
+
if (passwordResetReusableTokens === void 0) { passwordResetReusableTokens = true; }
|
|
348
365
|
return __awaiter(this, void 0, void 0, function () {
|
|
349
|
-
var payload,
|
|
366
|
+
var payload, user, secret, passwordAndSalt;
|
|
350
367
|
return __generator(this, function (_a) {
|
|
351
368
|
switch (_a.label) {
|
|
352
369
|
case 0:
|
|
353
|
-
payload =
|
|
354
|
-
|
|
355
|
-
secret = jwtSecret + ":" + code;
|
|
356
|
-
payload = jsonwebtoken_1.default.verify(passwordResetToken, secret);
|
|
357
|
-
}
|
|
358
|
-
catch (ex) {
|
|
370
|
+
payload = jsonwebtoken_1.default.decode(passwordResetToken);
|
|
371
|
+
if (!payload || !payload.username)
|
|
359
372
|
return [2 /*return*/, { status: "invalidCode" }];
|
|
360
|
-
}
|
|
361
373
|
return [4 /*yield*/, repo.getOne({ username: payload.username })];
|
|
362
374
|
case 1:
|
|
363
375
|
user = _a.sent();
|
|
364
|
-
if (user == null) {
|
|
376
|
+
if (!user || user == null || user.authentificationMethod != "password") {
|
|
365
377
|
return [2 /*return*/, { status: "userNotFound" }];
|
|
366
378
|
}
|
|
367
|
-
if (
|
|
368
|
-
|
|
379
|
+
if (passwordResetReusableTokens === true) {
|
|
380
|
+
secret = jwtSecret + ":" + code;
|
|
381
|
+
}
|
|
382
|
+
else {
|
|
383
|
+
if (!user.pwdResetStartedAt || user.pwdResetStartedAt === null) {
|
|
384
|
+
return [2 /*return*/, { status: "userNotFound" }];
|
|
385
|
+
}
|
|
386
|
+
secret = jwtSecret + ":" + code + ":" + user.pwdResetStartedAt;
|
|
387
|
+
}
|
|
388
|
+
try {
|
|
389
|
+
jsonwebtoken_1.default.verify(passwordResetToken, secret);
|
|
390
|
+
}
|
|
391
|
+
catch (ex) {
|
|
392
|
+
return [2 /*return*/, { status: "invalidCode" }];
|
|
369
393
|
}
|
|
370
394
|
passwordAndSalt = null;
|
|
371
395
|
if (!(createPasswordHashAndSaltMethod == null)) return [3 /*break*/, 3];
|
|
@@ -386,6 +410,7 @@ function passwordResetComplete(repo, auth, jwtSecret, passwordResetToken, code,
|
|
|
386
410
|
return [4 /*yield*/, repo.updateOne(user._id, {
|
|
387
411
|
password: passwordAndSalt.hash,
|
|
388
412
|
salt: passwordAndSalt.salt,
|
|
413
|
+
pwdResetStartedAt: null
|
|
389
414
|
})];
|
|
390
415
|
case 6:
|
|
391
416
|
_a.sent();
|
|
@@ -20,20 +20,20 @@ export interface genericAuthContext {
|
|
|
20
20
|
hash: string;
|
|
21
21
|
salt: string;
|
|
22
22
|
} | null>;
|
|
23
|
-
}): Promise<UserCreateRes>;
|
|
23
|
+
}, onUserCreated?: (user: User) => Promise<void>): Promise<UserCreateRes>;
|
|
24
24
|
changePassword(repo: FlinkRepo<any, User>, auth: JwtAuthPlugin, userId: string, newPassword: string, createPasswordHashAndSaltMethod?: {
|
|
25
25
|
(password: string): Promise<{
|
|
26
26
|
hash: string;
|
|
27
27
|
salt: string;
|
|
28
28
|
} | null>;
|
|
29
29
|
}): Promise<UserPasswordChangeRes>;
|
|
30
|
-
passwordResetStart(repo: FlinkRepo<any, User>, auth: JwtAuthPlugin, jwtSecret: string, username: string, numberOfDigits?: number, lifeTime?: string): Promise<UserPasswordResetStartRes>;
|
|
30
|
+
passwordResetStart(repo: FlinkRepo<any, User>, auth: JwtAuthPlugin, jwtSecret: string, username: string, numberOfDigits?: number, lifeTime?: string, passwordResetReusableTokens?: boolean): Promise<UserPasswordResetStartRes>;
|
|
31
31
|
passwordResetComplete(repo: FlinkRepo<any, User>, auth: JwtAuthPlugin, jwtSecret: string, passwordResetToken: string, code: string, newPassword: string, createPasswordHashAndSaltMethod?: {
|
|
32
32
|
(password: string): Promise<{
|
|
33
33
|
hash: string;
|
|
34
34
|
salt: string;
|
|
35
35
|
} | null>;
|
|
36
|
-
}): Promise<UserPasswordResetCompleteRes>;
|
|
36
|
+
}, passwordResetReusableTokens?: boolean): Promise<UserPasswordResetCompleteRes>;
|
|
37
37
|
repoName: string;
|
|
38
38
|
passwordResetSettings?: UserPasswordResetSettings;
|
|
39
39
|
createPasswordHashAndSaltMethod?: {
|
|
@@ -50,5 +50,8 @@ export interface genericAuthContext {
|
|
|
50
50
|
onSuccessfulLogin?: {
|
|
51
51
|
(user: User): Promise<void>;
|
|
52
52
|
};
|
|
53
|
+
onUserCreated?: {
|
|
54
|
+
(user: User): Promise<void>;
|
|
55
|
+
};
|
|
53
56
|
};
|
|
54
57
|
}
|
|
@@ -5,6 +5,7 @@ export interface GenericAuthPluginOptions {
|
|
|
5
5
|
repoName: string;
|
|
6
6
|
enableRoutes?: boolean;
|
|
7
7
|
enablePasswordReset?: boolean;
|
|
8
|
+
passwordResetReusableTokens?: boolean;
|
|
8
9
|
enablePushNotificationTokens?: boolean;
|
|
9
10
|
passwordResetSettings?: UserPasswordResetSettings;
|
|
10
11
|
enableUserCreation?: boolean;
|
|
@@ -27,6 +28,9 @@ export interface GenericAuthPluginOptions {
|
|
|
27
28
|
onSuccessfulLogin?: {
|
|
28
29
|
(user: User): Promise<void>;
|
|
29
30
|
};
|
|
31
|
+
onUserCreated?: {
|
|
32
|
+
(user: User): Promise<void>;
|
|
33
|
+
};
|
|
30
34
|
/**
|
|
31
35
|
* If true, when a new device is registered, all other devices identified by `deviceId`
|
|
32
36
|
* will be deregistered to avoid duplicate notifications.
|
|
@@ -64,7 +64,7 @@ var userCreateHandler = function (_a) {
|
|
|
64
64
|
if (!re.test(username)) {
|
|
65
65
|
return [2 /*return*/, flink_1.badRequest("Username does not meet requirements", "usernameError")];
|
|
66
66
|
}
|
|
67
|
-
return [4 /*yield*/, ctx.plugins.genericAuthPlugin.createUser(repo, ctx.auth, username.toLocaleLowerCase(), password, authentificationMethod, roles, profile, ctx.plugins.
|
|
67
|
+
return [4 /*yield*/, ctx.plugins.genericAuthPlugin.createUser(repo, ctx.auth, username.toLocaleLowerCase(), password, authentificationMethod, roles, profile, ctx.plugins[pluginName].createPasswordHashAndSaltMethod, ctx.plugins[pluginName].onSuccessfulLogin)];
|
|
68
68
|
case 1:
|
|
69
69
|
createUserResponse = _c.sent();
|
|
70
70
|
if (createUserResponse.status != "success") {
|
|
@@ -51,7 +51,7 @@ var postPasswordResetCompleteHandler = function (_a) {
|
|
|
51
51
|
return [2 /*return*/, flink_1.internalServerError("Password reset settings is needed to use password-reset")];
|
|
52
52
|
}
|
|
53
53
|
jwtSecret = ctx.plugins[pluginName].passwordResetSettings.code.jwtSecret;
|
|
54
|
-
return [4 /*yield*/, ctx.plugins.genericAuthPlugin.passwordResetComplete(repo, ctx.auth, jwtSecret, req.body.passwordResetToken, req.body.code, req.body.password, ctx.plugins.genericAuthPlugin.createPasswordHashAndSaltMethod)];
|
|
54
|
+
return [4 /*yield*/, ctx.plugins.genericAuthPlugin.passwordResetComplete(repo, ctx.auth, jwtSecret, req.body.passwordResetToken, req.body.code, req.body.password, ctx.plugins.genericAuthPlugin.createPasswordHashAndSaltMethod, ctx.plugins.genericAuthPlugin.passwordResetSettings.passwordResetReusableTokens)];
|
|
55
55
|
case 1:
|
|
56
56
|
resp = _b.sent();
|
|
57
57
|
switch (resp.status) {
|
|
@@ -60,7 +60,7 @@ var postPasswordResetStartHandler = function (_a) {
|
|
|
60
60
|
return [2 /*return*/, flink_1.internalServerError("Password reset settings is needed to use /password/reset")];
|
|
61
61
|
}
|
|
62
62
|
_b = genericAuthPlugin.passwordResetSettings.code, jwtSecret = _b.jwtSecret, numberOfDigits = _b.numberOfDigits, lifeTime = _b.lifeTime;
|
|
63
|
-
return [4 /*yield*/, genericAuthPlugin.passwordResetStart(repo, ctx.auth, jwtSecret, req.body.username, numberOfDigits, lifeTime)];
|
|
63
|
+
return [4 /*yield*/, genericAuthPlugin.passwordResetStart(repo, ctx.auth, jwtSecret, req.body.username, numberOfDigits, lifeTime, genericAuthPlugin.passwordResetSettings.passwordResetReusableTokens)];
|
|
64
64
|
case 1:
|
|
65
65
|
resp = _d.sent();
|
|
66
66
|
if (resp.status != "success") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flink-app/generic-auth-plugin",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.18",
|
|
4
4
|
"description": "Flink plugin that provides a generic user authentification solution.",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "echo \"Error: no test specified\"",
|
|
@@ -30,5 +30,5 @@
|
|
|
30
30
|
"ts-node": "^9.1.1",
|
|
31
31
|
"typescript": "^4.2.4"
|
|
32
32
|
},
|
|
33
|
-
"gitHead": "
|
|
33
|
+
"gitHead": "de631fa7613a2ae9830d22d954d36265d1ebf9ba"
|
|
34
34
|
}
|
package/src/coreFunctions.ts
CHANGED
|
@@ -50,6 +50,9 @@ export async function createUser(
|
|
|
50
50
|
profile: UserProfile,
|
|
51
51
|
createPasswordHashAndSaltMethod?: {
|
|
52
52
|
(password: string): Promise<{ hash: string; salt: string } | null>;
|
|
53
|
+
},
|
|
54
|
+
onUserCreated?: {
|
|
55
|
+
(user: User): Promise<void>;
|
|
53
56
|
}
|
|
54
57
|
): Promise<UserCreateRes> {
|
|
55
58
|
if (!roles.includes("user")) roles.push("user");
|
|
@@ -87,6 +90,10 @@ export async function createUser(
|
|
|
87
90
|
|
|
88
91
|
const user = await repo.create(userData);
|
|
89
92
|
|
|
93
|
+
if (onUserCreated) {
|
|
94
|
+
await onUserCreated(user);
|
|
95
|
+
}
|
|
96
|
+
|
|
90
97
|
const token = await auth.createToken({ username: username.toLowerCase(), _id: user._id }, roles);
|
|
91
98
|
|
|
92
99
|
if (user.authentificationMethod == "sms") {
|
|
@@ -285,7 +292,8 @@ export async function passwordResetStart(
|
|
|
285
292
|
jwtSecret: string,
|
|
286
293
|
username: string,
|
|
287
294
|
numberOfDigits?: number,
|
|
288
|
-
lifeTime?: string
|
|
295
|
+
lifeTime?: string,
|
|
296
|
+
passwordResetReusableTokens: boolean = true
|
|
289
297
|
): Promise<UserPasswordResetStartRes> {
|
|
290
298
|
const user = await repo.getOne({ username: username.toLowerCase() });
|
|
291
299
|
|
|
@@ -295,8 +303,6 @@ export async function passwordResetStart(
|
|
|
295
303
|
};
|
|
296
304
|
const fakeToken = jsonwebtoken.sign(fakepayload, "fake_payload", { expiresIn: lifeTime });
|
|
297
305
|
|
|
298
|
-
|
|
299
|
-
|
|
300
306
|
if (user == null) {
|
|
301
307
|
return { status: "userNotFound", passwordResetToken : fakeToken };
|
|
302
308
|
}
|
|
@@ -314,7 +320,14 @@ export async function passwordResetStart(
|
|
|
314
320
|
};
|
|
315
321
|
const code = generate(numberOfDigits);
|
|
316
322
|
|
|
317
|
-
const
|
|
323
|
+
const pwdResetStartedAt = new Date().toISOString();
|
|
324
|
+
let secret;
|
|
325
|
+
if(passwordResetReusableTokens) {
|
|
326
|
+
secret = jwtSecret + ":" + code;
|
|
327
|
+
} else {
|
|
328
|
+
secret = jwtSecret + ":" + code + ":" + pwdResetStartedAt;
|
|
329
|
+
await repo.updateOne(user._id, { pwdResetStartedAt });
|
|
330
|
+
}
|
|
318
331
|
|
|
319
332
|
const options: jsonwebtoken.SignOptions = {
|
|
320
333
|
expiresIn: lifeTime,
|
|
@@ -339,25 +352,38 @@ export async function passwordResetComplete(
|
|
|
339
352
|
newPassword: string,
|
|
340
353
|
createPasswordHashAndSaltMethod?: {
|
|
341
354
|
(password: string): Promise<{ hash: string; salt: string } | null>;
|
|
342
|
-
}
|
|
355
|
+
},
|
|
356
|
+
passwordResetReusableTokens: boolean = true
|
|
343
357
|
): Promise<UserPasswordResetCompleteRes> {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
} catch (ex) {
|
|
358
|
+
|
|
359
|
+
const payload = <{ username:string }>jsonwebtoken.decode(passwordResetToken);
|
|
360
|
+
|
|
361
|
+
if(!payload || !payload.username)
|
|
349
362
|
return { status: "invalidCode" };
|
|
350
|
-
}
|
|
351
363
|
|
|
352
364
|
const user = await repo.getOne({ username: payload.username });
|
|
353
|
-
|
|
365
|
+
|
|
366
|
+
if (!user || user == null || user.authentificationMethod != "password") {
|
|
354
367
|
return { status: "userNotFound" };
|
|
355
368
|
}
|
|
356
369
|
|
|
357
|
-
|
|
358
|
-
|
|
370
|
+
let secret;
|
|
371
|
+
if (passwordResetReusableTokens === true) {
|
|
372
|
+
secret = jwtSecret + ":" + code;
|
|
373
|
+
} else {
|
|
374
|
+
if (!user.pwdResetStartedAt || user.pwdResetStartedAt === null) {
|
|
375
|
+
return { status: "userNotFound" };
|
|
376
|
+
}
|
|
377
|
+
secret = jwtSecret + ":" + code + ":" + user.pwdResetStartedAt;
|
|
359
378
|
}
|
|
360
379
|
|
|
380
|
+
try {
|
|
381
|
+
jsonwebtoken.verify(passwordResetToken, secret);
|
|
382
|
+
} catch (ex) {
|
|
383
|
+
return { status: "invalidCode" };
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
|
|
361
387
|
let passwordAndSalt = null;
|
|
362
388
|
|
|
363
389
|
if (createPasswordHashAndSaltMethod == null) {
|
|
@@ -375,6 +401,7 @@ export async function passwordResetComplete(
|
|
|
375
401
|
await repo.updateOne(user._id, {
|
|
376
402
|
password: passwordAndSalt.hash,
|
|
377
403
|
salt: passwordAndSalt.salt,
|
|
404
|
+
pwdResetStartedAt: null
|
|
378
405
|
});
|
|
379
406
|
|
|
380
407
|
return { status: "success" };
|
|
@@ -1,32 +1,73 @@
|
|
|
1
|
-
import { FlinkRepo
|
|
1
|
+
import { FlinkRepo } from "@flink-app/flink";
|
|
2
2
|
import { JwtAuthPlugin } from "@flink-app/jwt-auth-plugin";
|
|
3
3
|
import { User } from "./schemas/User";
|
|
4
4
|
import { UserCreateRes } from "./schemas/UserCreateRes";
|
|
5
5
|
import { UserLoginRes } from "./schemas/UserLoginRes";
|
|
6
6
|
import { UserProfile } from "./schemas/UserProfile";
|
|
7
7
|
import { UserPasswordChangeRes } from "./schemas/UserPasswordChangeRes";
|
|
8
|
-
import { UserPasswordResetSettings} from "./schemas/UserPasswordResetSettings"
|
|
9
|
-
import { UserPasswordResetStartRes } from "./schemas/UserPasswordResetStartRes"
|
|
10
|
-
import { UserPasswordResetCompleteRes } from "./schemas/UserPasswordResetCompleteRes"
|
|
8
|
+
import { UserPasswordResetSettings } from "./schemas/UserPasswordResetSettings";
|
|
9
|
+
import { UserPasswordResetStartRes } from "./schemas/UserPasswordResetStartRes";
|
|
10
|
+
import { UserPasswordResetCompleteRes } from "./schemas/UserPasswordResetCompleteRes";
|
|
11
11
|
import { GenericAuthsmsOptions } from "./genericAuthPluginOptions";
|
|
12
12
|
|
|
13
|
-
export interface genericAuthContext{
|
|
14
|
-
genericAuthPlugin
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
13
|
+
export interface genericAuthContext {
|
|
14
|
+
genericAuthPlugin: {
|
|
15
|
+
loginUser(
|
|
16
|
+
repo: FlinkRepo<any, User>,
|
|
17
|
+
auth: JwtAuthPlugin,
|
|
18
|
+
username: string,
|
|
19
|
+
password?: string,
|
|
20
|
+
validatePasswordMethod?: { (password: string, hash: string, salt: string): Promise<boolean> },
|
|
21
|
+
smsOptions?: GenericAuthsmsOptions,
|
|
22
|
+
onSuccessfulLogin?: (user: User) => Promise<void>
|
|
23
|
+
): Promise<UserLoginRes>;
|
|
24
|
+
loginByToken(repo: FlinkRepo<any, User>, auth: JwtAuthPlugin, token: string, code: string, jwtSecret: string): Promise<UserLoginRes>;
|
|
25
|
+
createUser(
|
|
26
|
+
repo: FlinkRepo<any, User>,
|
|
27
|
+
auth: JwtAuthPlugin,
|
|
28
|
+
username: string,
|
|
29
|
+
password: string,
|
|
30
|
+
authentificationMethod: "password" | "sms",
|
|
31
|
+
roles: string[],
|
|
32
|
+
profile: UserProfile,
|
|
33
|
+
createPasswordHashAndSaltMethod?: {
|
|
34
|
+
(password: string): Promise<{ hash: string; salt: string } | null>;
|
|
35
|
+
},
|
|
36
|
+
onUserCreated?: (user: User) => Promise<void>
|
|
37
|
+
): Promise<UserCreateRes>;
|
|
38
|
+
changePassword(
|
|
39
|
+
repo: FlinkRepo<any, User>,
|
|
40
|
+
auth: JwtAuthPlugin,
|
|
41
|
+
userId: string,
|
|
42
|
+
newPassword: string,
|
|
43
|
+
createPasswordHashAndSaltMethod?: { (password: string): Promise<{ hash: string; salt: string } | null> }
|
|
44
|
+
): Promise<UserPasswordChangeRes>;
|
|
45
|
+
passwordResetStart(
|
|
46
|
+
repo: FlinkRepo<any, User>,
|
|
47
|
+
auth: JwtAuthPlugin,
|
|
48
|
+
jwtSecret: string,
|
|
49
|
+
username: string,
|
|
50
|
+
numberOfDigits?: number,
|
|
51
|
+
lifeTime?: string,
|
|
52
|
+
passwordResetReusableTokens?: boolean
|
|
53
|
+
): Promise<UserPasswordResetStartRes>;
|
|
54
|
+
passwordResetComplete(
|
|
55
|
+
repo: FlinkRepo<any, User>,
|
|
56
|
+
auth: JwtAuthPlugin,
|
|
57
|
+
jwtSecret: string,
|
|
58
|
+
passwordResetToken: string,
|
|
59
|
+
code: string,
|
|
60
|
+
newPassword: string,
|
|
61
|
+
createPasswordHashAndSaltMethod?: { (password: string): Promise<{ hash: string; salt: string } | null> },
|
|
62
|
+
passwordResetReusableTokens?: boolean
|
|
63
|
+
): Promise<UserPasswordResetCompleteRes>;
|
|
64
|
+
repoName: string;
|
|
65
|
+
passwordResetSettings?: UserPasswordResetSettings;
|
|
66
|
+
createPasswordHashAndSaltMethod?: { (password: string): Promise<{ hash: string; salt: string } | null> };
|
|
67
|
+
validatePasswordMethod?: { (password: string, hash: string, salt: string): Promise<boolean> };
|
|
68
|
+
usernameFormat: RegExp;
|
|
69
|
+
smsOptions?: GenericAuthsmsOptions;
|
|
70
|
+
onSuccessfulLogin?: { (user: User): Promise<void> };
|
|
71
|
+
onUserCreated?: { (user: User): Promise<void> };
|
|
72
|
+
};
|
|
32
73
|
}
|
|
@@ -5,6 +5,7 @@ export interface GenericAuthPluginOptions {
|
|
|
5
5
|
repoName: string;
|
|
6
6
|
enableRoutes?: boolean;
|
|
7
7
|
enablePasswordReset?: boolean;
|
|
8
|
+
passwordResetReusableTokens?: boolean;
|
|
8
9
|
enablePushNotificationTokens?: boolean;
|
|
9
10
|
passwordResetSettings?: UserPasswordResetSettings;
|
|
10
11
|
enableUserCreation?: boolean;
|
|
@@ -24,6 +25,9 @@ export interface GenericAuthPluginOptions {
|
|
|
24
25
|
onSuccessfulLogin?: {
|
|
25
26
|
(user: User): Promise<void>;
|
|
26
27
|
};
|
|
28
|
+
onUserCreated?: {
|
|
29
|
+
(user: User): Promise<void>;
|
|
30
|
+
};
|
|
27
31
|
/**
|
|
28
32
|
* If true, when a new device is registered, all other devices identified by `deviceId`
|
|
29
33
|
* will be deregistered to avoid duplicate notifications.
|
|
@@ -35,7 +35,8 @@ const userCreateHandler: Handler<FlinkContext<genericAuthContext>, UserCreateReq
|
|
|
35
35
|
authentificationMethod,
|
|
36
36
|
roles,
|
|
37
37
|
profile,
|
|
38
|
-
ctx.plugins.
|
|
38
|
+
(<any>ctx.plugins)[pluginName].createPasswordHashAndSaltMethod,
|
|
39
|
+
(<any>ctx.plugins)[pluginName].onSuccessfulLogin
|
|
39
40
|
);
|
|
40
41
|
if (createUserResponse.status != "success") {
|
|
41
42
|
switch (createUserResponse.status) {
|
|
@@ -34,7 +34,8 @@ const postPasswordResetCompleteHandler: Handler<
|
|
|
34
34
|
req.body.passwordResetToken,
|
|
35
35
|
req.body.code,
|
|
36
36
|
req.body.password,
|
|
37
|
-
ctx.plugins.genericAuthPlugin.createPasswordHashAndSaltMethod
|
|
37
|
+
ctx.plugins.genericAuthPlugin.createPasswordHashAndSaltMethod,
|
|
38
|
+
ctx.plugins.genericAuthPlugin.passwordResetSettings.passwordResetReusableTokens
|
|
38
39
|
);
|
|
39
40
|
|
|
40
41
|
switch (resp.status) {
|
|
@@ -27,7 +27,7 @@ const postPasswordResetStartHandler: Handler<
|
|
|
27
27
|
|
|
28
28
|
const { jwtSecret, numberOfDigits, lifeTime } = genericAuthPlugin.passwordResetSettings.code;
|
|
29
29
|
|
|
30
|
-
const resp = await genericAuthPlugin.passwordResetStart(repo, <JwtAuthPlugin>ctx.auth, jwtSecret, req.body.username, numberOfDigits, lifeTime);
|
|
30
|
+
const resp = await genericAuthPlugin.passwordResetStart(repo, <JwtAuthPlugin>ctx.auth, jwtSecret, req.body.username, numberOfDigits, lifeTime, genericAuthPlugin.passwordResetSettings.passwordResetReusableTokens);
|
|
31
31
|
|
|
32
32
|
if (resp.status != "success") {
|
|
33
33
|
return { data: { status: "success", passwordResetToken: resp.passwordResetToken } };
|
package/src/schemas/User.ts
CHANGED