@flink-app/generic-auth-plugin 0.11.15 → 0.11.17
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/UserPasswordResetForm.d.ts +1 -0
- package/dist/src/handlers/UserPasswordResetForm.js +13 -2
- package/dist/src/handlers/UserPasswordResetStart.js +1 -1
- package/dist/src/init.js +5 -2
- 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/UserPasswordResetForm.ts +38 -32
- package/src/handlers/UserPasswordResetStart.ts +1 -1
- package/src/init.ts +13 -2
- 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:24:56 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:24:56 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:24:56 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:24:57 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:24:56 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:24:56 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:24:56 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:24:56 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.genericAuthPlugin.createPasswordHashAndSaltMethod)];
|
|
67
|
+
return [4 /*yield*/, ctx.plugins.genericAuthPlugin.createUser(repo, ctx.auth, username.toLocaleLowerCase(), password, authentificationMethod, roles, profile, ctx.plugins.genericAuthPlugin.createPasswordHashAndSaltMethod, ctx.plugins.genericAuthPlugin.onUserCreated)];
|
|
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) {
|
|
@@ -6,4 +6,5 @@ export declare function handleUserPasswordResetForm(_req: ExpressRequest, res: E
|
|
|
6
6
|
export declare function resetPasswordFormScript(req: ExpressRequest, res: ExpressResponse, { completeUrl }: {
|
|
7
7
|
completeUrl: string;
|
|
8
8
|
}): Promise<void>;
|
|
9
|
+
export declare function resetPasswordFormCss(res: ExpressResponse): Promise<void>;
|
|
9
10
|
export declare const __assumedHttpMethod = "", __file = "UserPasswordResetForm.ts", __query: never[], __params: never[];
|
|
@@ -39,12 +39,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
39
39
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
40
40
|
};
|
|
41
41
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
-
exports.__params = exports.__query = exports.__file = exports.__assumedHttpMethod = exports.resetPasswordFormScript = exports.handleUserPasswordResetForm = void 0;
|
|
42
|
+
exports.__params = exports.__query = exports.__file = exports.__assumedHttpMethod = exports.resetPasswordFormCss = exports.resetPasswordFormScript = exports.handleUserPasswordResetForm = void 0;
|
|
43
43
|
var promises_1 = __importDefault(require("fs/promises"));
|
|
44
44
|
var flink_1 = require("@flink-app/flink");
|
|
45
45
|
var handlebars_1 = __importDefault(require("handlebars"));
|
|
46
|
-
var defaultTemplate = "<html>\n<head>\n <title>Password reset</title>\n <
|
|
46
|
+
var defaultTemplate = "<html>\n<head>\n <title>Password reset</title>\n <link rel=\"stylesheet\" href=\"form/style.css\" />\n <script src=\"form/script.js\" type=\"text/javascript\"></script>\n</head>\n<body>\n <form id=\"form\">\n <p>Please enter new password</p>\n <input type=\"password\" name=\"password\" placeholder=\"Enter new password\" />\n <input\n type=\"password\"\n name=\"confirmPassword\"\n placeholder=\"Confirm new password\"\n />\n <button id=\"submit-btn\">Submit</button>\n </form>\n <div id=\"success\">Password has been updated, please proceed to login.</div>\n</body>\n</html>\n";
|
|
47
47
|
var script = "\n window.onload = () => {\n const urlSearchParams = new URLSearchParams(window.location.search);\n const params = Object.fromEntries(urlSearchParams.entries());\n const { token, code } = params;\n\n if (!token) {\n alert(\"Missing token\");\n } else if (!code) {\n alert(\"Missing code\");\n }\n\n const submitBtnEl = document.getElementById(\"submit-btn\");\n const [passwordInputEl, confirmPasswordEl] =\n document.getElementsByTagName(\"input\");\n\n submitBtnEl.onclick = async (e) => {\n e.preventDefault();\n e.stopPropagation();\n\n if (!passwordInputEl.value) {\n return alert(\"Enter a new password\");\n } else if (passwordInputEl.value !== confirmPasswordEl.value) {\n return alert(\n \"Passwords does not match, make sure that new and confirmed passwords are the same\"\n );\n }\n\n const res = await window.fetch(\"{{completeUrl}}\", {\n method: \"POST\",\n headers: {\n Accept: \"application/json\",\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({\n passwordResetToken: token,\n code: code,\n password: passwordInputEl.value,\n }),\n });\n\n if (res.status > 399) {\n alert(\"Failed to set new password\");\n } else {\n document.getElementById(\"form\").style.display = \"none\";\n document.getElementById(\"success\").style.display = \"block\";\n }\n };\n };\n";
|
|
48
|
+
var style = " * {\n box-sizing: border-box;\n font-family: Arial, Helvetica, sans-serif;\n }\n\n p {\n margin: 0.5rem 0;\n }\n\n body {\n padding: 1rem;\n }\n\n form {\n display: block;\n max-width: 320px;\n }\n\n input {\n width: 100%;\n display: block;\n margin: 0.5rem 0;\n }\n\n #success {\n display: none;\n font-size: 1.2rem;\n color: rgb(0, 177, 115);\n max-width: 350px;\n }\n";
|
|
48
49
|
function handleUserPasswordResetForm(_req, res, _a) {
|
|
49
50
|
var templateFile = _a.templateFile, completeUrl = _a.completeUrl;
|
|
50
51
|
return __awaiter(this, void 0, void 0, function () {
|
|
@@ -77,6 +78,16 @@ function resetPasswordFormScript(req, res, _a) {
|
|
|
77
78
|
});
|
|
78
79
|
}
|
|
79
80
|
exports.resetPasswordFormScript = resetPasswordFormScript;
|
|
81
|
+
function resetPasswordFormCss(res) {
|
|
82
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
83
|
+
return __generator(this, function (_a) {
|
|
84
|
+
res.header("Content-Type", "text/css");
|
|
85
|
+
res.send(style);
|
|
86
|
+
return [2 /*return*/];
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
exports.resetPasswordFormCss = resetPasswordFormCss;
|
|
80
91
|
var cachedTemplate = "";
|
|
81
92
|
function readTemplate(templateFilename) {
|
|
82
93
|
return __awaiter(this, void 0, void 0, function () {
|
|
@@ -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/dist/src/init.js
CHANGED
|
@@ -34,7 +34,7 @@ var postUserRemoveTokenHandler = __importStar(require("./handlers/UserPushRemove
|
|
|
34
34
|
var getUserTokenHandler = __importStar(require("./handlers/UserToken"));
|
|
35
35
|
var UserPasswordResetForm_1 = require("./handlers/UserPasswordResetForm");
|
|
36
36
|
function init(app, options) {
|
|
37
|
-
var _a, _b, _c;
|
|
37
|
+
var _a, _b, _c, _d;
|
|
38
38
|
if (options.enableUserCreation == null)
|
|
39
39
|
options.enableUserCreation = true;
|
|
40
40
|
if (options.enableProfileUpdate == null)
|
|
@@ -124,7 +124,10 @@ function init(app, options) {
|
|
|
124
124
|
});
|
|
125
125
|
});
|
|
126
126
|
(_c = app.expressApp) === null || _c === void 0 ? void 0 : _c.get(options.baseUrl + "/password/reset/form/script.js", function (req, res) {
|
|
127
|
-
UserPasswordResetForm_1.resetPasswordFormScript(req, res, { completeUrl: options.baseUrl + "/password/reset/complete"
|
|
127
|
+
UserPasswordResetForm_1.resetPasswordFormScript(req, res, { completeUrl: options.baseUrl + "/password/reset/complete" });
|
|
128
|
+
});
|
|
129
|
+
(_d = app.expressApp) === null || _d === void 0 ? void 0 : _d.get(options.baseUrl + "/password/reset/form/style.css", function (req, res) {
|
|
130
|
+
UserPasswordResetForm_1.resetPasswordFormCss(res);
|
|
128
131
|
});
|
|
129
132
|
}
|
|
130
133
|
}
|
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.17",
|
|
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": "bd864fc2a0c9d7c266ee868041b1b00b7c339243"
|
|
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.genericAuthPlugin.createPasswordHashAndSaltMethod
|
|
38
|
+
ctx.plugins.genericAuthPlugin.createPasswordHashAndSaltMethod,
|
|
39
|
+
ctx.plugins.genericAuthPlugin.onUserCreated
|
|
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) {
|
|
@@ -6,38 +6,7 @@ import Handlebars from "handlebars";
|
|
|
6
6
|
const defaultTemplate = `<html>
|
|
7
7
|
<head>
|
|
8
8
|
<title>Password reset</title>
|
|
9
|
-
<style
|
|
10
|
-
* {
|
|
11
|
-
box-sizing: border-box;
|
|
12
|
-
font-family: Arial, Helvetica, sans-serif;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
p {
|
|
16
|
-
margin: 0.5rem 0;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
body {
|
|
20
|
-
padding: 1rem;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
form {
|
|
24
|
-
display: block;
|
|
25
|
-
max-width: 320px;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
input {
|
|
29
|
-
width: 100%;
|
|
30
|
-
display: block;
|
|
31
|
-
margin: 0.5rem 0;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
#success {
|
|
35
|
-
display: none;
|
|
36
|
-
font-size: 1.2rem;
|
|
37
|
-
color: rgb(0, 177, 115);
|
|
38
|
-
max-width: 350px;
|
|
39
|
-
}
|
|
40
|
-
</style>
|
|
9
|
+
<link rel="stylesheet" href="form/style.css" />
|
|
41
10
|
<script src="form/script.js" type="text/javascript"></script>
|
|
42
11
|
</head>
|
|
43
12
|
<body>
|
|
@@ -107,6 +76,38 @@ const script = `
|
|
|
107
76
|
};
|
|
108
77
|
`;
|
|
109
78
|
|
|
79
|
+
const style =` * {
|
|
80
|
+
box-sizing: border-box;
|
|
81
|
+
font-family: Arial, Helvetica, sans-serif;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
p {
|
|
85
|
+
margin: 0.5rem 0;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
body {
|
|
89
|
+
padding: 1rem;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
form {
|
|
93
|
+
display: block;
|
|
94
|
+
max-width: 320px;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
input {
|
|
98
|
+
width: 100%;
|
|
99
|
+
display: block;
|
|
100
|
+
margin: 0.5rem 0;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
#success {
|
|
104
|
+
display: none;
|
|
105
|
+
font-size: 1.2rem;
|
|
106
|
+
color: rgb(0, 177, 115);
|
|
107
|
+
max-width: 350px;
|
|
108
|
+
}
|
|
109
|
+
`;
|
|
110
|
+
|
|
110
111
|
export async function handleUserPasswordResetForm(
|
|
111
112
|
_req: ExpressRequest,
|
|
112
113
|
res: ExpressResponse,
|
|
@@ -127,6 +128,11 @@ export async function resetPasswordFormScript(req: ExpressRequest, res: ExpressR
|
|
|
127
128
|
res.send(js);
|
|
128
129
|
}
|
|
129
130
|
|
|
131
|
+
export async function resetPasswordFormCss(res: ExpressResponse) {
|
|
132
|
+
res.header("Content-Type", "text/css");
|
|
133
|
+
res.send(style);
|
|
134
|
+
}
|
|
135
|
+
|
|
130
136
|
let cachedTemplate = "";
|
|
131
137
|
|
|
132
138
|
async function readTemplate(templateFilename?: string) {
|
|
@@ -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/init.ts
CHANGED
|
@@ -11,7 +11,11 @@ import { GenericAuthPluginOptions } from "./genericAuthPluginOptions";
|
|
|
11
11
|
import * as postUserPushRegisterTokenHandler from "./handlers/UserPushRegisterToken";
|
|
12
12
|
import * as postUserRemoveTokenHandler from "./handlers/UserPushRemoveToken";
|
|
13
13
|
import * as getUserTokenHandler from "./handlers/UserToken";
|
|
14
|
-
import {
|
|
14
|
+
import {
|
|
15
|
+
handleUserPasswordResetForm,
|
|
16
|
+
resetPasswordFormCss,
|
|
17
|
+
resetPasswordFormScript,
|
|
18
|
+
} from "./handlers/UserPasswordResetForm";
|
|
15
19
|
|
|
16
20
|
export function init(app: FlinkApp<any>, options: GenericAuthPluginOptions) {
|
|
17
21
|
if (options.enableUserCreation == null) options.enableUserCreation = true;
|
|
@@ -110,7 +114,14 @@ export function init(app: FlinkApp<any>, options: GenericAuthPluginOptions) {
|
|
|
110
114
|
app.expressApp?.get(
|
|
111
115
|
options.baseUrl + "/password/reset/form/script.js",
|
|
112
116
|
(req, res) => {
|
|
113
|
-
resetPasswordFormScript(req, res, { completeUrl: options.baseUrl + "/password/reset/complete"
|
|
117
|
+
resetPasswordFormScript(req, res, { completeUrl: options.baseUrl + "/password/reset/complete"});
|
|
118
|
+
}
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
app.expressApp?.get(
|
|
122
|
+
options.baseUrl + "/password/reset/form/style.css",
|
|
123
|
+
(req, res) => {
|
|
124
|
+
resetPasswordFormCss(res);
|
|
114
125
|
}
|
|
115
126
|
);
|
|
116
127
|
}
|
package/src/schemas/User.ts
CHANGED