@adminforth/two-factors-auth 2.9.20 → 2.10.0
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/.woodpecker/buildSlackNotify.sh +3 -1
- package/.woodpecker/release.yml +24 -7
- package/build.log +1 -1
- package/dist/index.js +90 -7
- package/index.ts +90 -5
- package/package.json +1 -1
- package/types.ts +6 -0
|
@@ -4,8 +4,10 @@ set -x
|
|
|
4
4
|
|
|
5
5
|
COMMIT_SHORT_SHA=$(echo $CI_COMMIT_SHA | cut -c1-8)
|
|
6
6
|
|
|
7
|
+
STATUS=${1}
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
|
|
10
|
+
if [ "$STATUS" = "success" ]; then
|
|
9
11
|
MESSAGE="Did a build without issues on \`$CI_REPO_NAME/$CI_COMMIT_BRANCH\`. Commit: _${CI_COMMIT_MESSAGE}_ (<$CI_COMMIT_URL|$COMMIT_SHORT_SHA>)"
|
|
10
12
|
|
|
11
13
|
curl -s -X POST -H "Content-Type: application/json" -d '{
|
package/.woodpecker/release.yml
CHANGED
|
@@ -16,25 +16,42 @@ steps:
|
|
|
16
16
|
commands:
|
|
17
17
|
- infisical export --domain https://vault.devforth.io/api --format=dotenv-export --env="prod" > /woodpecker/deploy.vault.env
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
build:
|
|
20
20
|
image: node:20
|
|
21
21
|
when:
|
|
22
22
|
- event: push
|
|
23
23
|
commands:
|
|
24
24
|
- apt update && apt install -y rsync
|
|
25
|
-
-
|
|
25
|
+
- . /woodpecker/deploy.vault.env
|
|
26
26
|
- npm clean-install
|
|
27
27
|
- /bin/bash ./.woodpecker/buildRelease.sh
|
|
28
28
|
- npm audit signatures
|
|
29
|
+
|
|
30
|
+
release:
|
|
31
|
+
image: node:20
|
|
32
|
+
when:
|
|
33
|
+
- event:
|
|
34
|
+
- push
|
|
35
|
+
branch:
|
|
36
|
+
- main
|
|
37
|
+
commands:
|
|
38
|
+
- . /woodpecker/deploy.vault.env
|
|
29
39
|
- npx semantic-release
|
|
30
40
|
|
|
31
41
|
slack-on-failure:
|
|
42
|
+
image: curlimages/curl
|
|
32
43
|
when:
|
|
33
44
|
- event: push
|
|
34
|
-
status: [failure
|
|
35
|
-
|
|
45
|
+
status: [failure]
|
|
46
|
+
commands:
|
|
47
|
+
- . /woodpecker/deploy.vault.env
|
|
48
|
+
- /bin/sh ./.woodpecker/buildSlackNotify.sh failure
|
|
49
|
+
|
|
50
|
+
slack-on-success:
|
|
36
51
|
image: curlimages/curl
|
|
52
|
+
when:
|
|
53
|
+
- event: push
|
|
54
|
+
status: [success]
|
|
37
55
|
commands:
|
|
38
|
-
-
|
|
39
|
-
- /bin/sh ./.woodpecker/buildSlackNotify.sh
|
|
40
|
-
|
|
56
|
+
- . /woodpecker/deploy.vault.env
|
|
57
|
+
- /bin/sh ./.woodpecker/buildSlackNotify.sh success
|
package/build.log
CHANGED
package/dist/index.js
CHANGED
|
@@ -12,6 +12,7 @@ import twofactor from 'node-2fa';
|
|
|
12
12
|
import { generateRegistrationOptions, verifyRegistrationResponse, generateAuthenticationOptions, verifyAuthenticationResponse } from '@simplewebauthn/server';
|
|
13
13
|
import { isoUint8Array, isoBase64URL } from '@simplewebauthn/server/helpers';
|
|
14
14
|
import aaguids from './custom/aaguid.json';
|
|
15
|
+
import crypto from 'crypto';
|
|
15
16
|
export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
16
17
|
constructor(options) {
|
|
17
18
|
super(options, import.meta.url);
|
|
@@ -45,9 +46,63 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
45
46
|
return { skipAllowed: false };
|
|
46
47
|
});
|
|
47
48
|
}
|
|
49
|
+
generateHashForStepUpMfaGraceCookie(headers) {
|
|
50
|
+
const ip = this.adminforth.auth.getClientIp(headers);
|
|
51
|
+
const userAgent = headers['user-agent'] || '';
|
|
52
|
+
const acceptLanguage = headers['accept-language'] || '';
|
|
53
|
+
if (!ip || !userAgent || !acceptLanguage) {
|
|
54
|
+
console.error("❗️❗️❗️ Cannot set step-up MFA grace cookie: missing required request headers to identify client ❗️❗️❗️");
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
const hmac = crypto.createHmac('sha256', process.env.ADMINFORTH_SECRET)
|
|
59
|
+
.update(`${acceptLanguage}_${userAgent}_${ip}`)
|
|
60
|
+
.digest('hex');
|
|
61
|
+
return hmac;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
issueTempSkip2FAGraceJWT(opts) {
|
|
65
|
+
if (opts.response) {
|
|
66
|
+
if (opts.extra.headers) {
|
|
67
|
+
const hash = this.generateHashForStepUpMfaGraceCookie(opts.extra.headers);
|
|
68
|
+
if (!hash) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const jwt = this.adminforth.auth.issueJWT({ hash: hash }, 'MfaGrace', `${this.options.stepUpMfaGracePeriodSeconds}s`);
|
|
72
|
+
//TODO: fix ts-ignore after releasing new version of adminforth with updated types
|
|
73
|
+
//@ts-ignore
|
|
74
|
+
this.adminforth.auth.setCustomCookie({ response: opts.response, payload: { name: "TempSkip2FA_Modal_JWT", value: jwt, expirySeconds: this.options.stepUpMfaGracePeriodSeconds, httpOnly: true } });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
console.error("❗️❗️❗️ Cannot set step-up MFA grace cookie: response object is missing. You probably called verify() method without response parameter ❗️❗️❗️");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
isTempSkip2FAGraceValid(headers_1, cookies_1) {
|
|
82
|
+
return __awaiter(this, arguments, void 0, function* (headers, cookies, checkIfJWTAboutToExpire = false) {
|
|
83
|
+
const hash = this.generateHashForStepUpMfaGraceCookie(headers);
|
|
84
|
+
if (!hash) {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
const jwt = this.adminforth.auth.getCustomCookie({ cookies: cookies, name: "TempSkip2FA_Modal_JWT" });
|
|
88
|
+
const jwtVerificationResult = yield this.adminforth.auth.verify(jwt, 'MfaGrace', false);
|
|
89
|
+
if (!jwtVerificationResult) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
const jwtHash = jwtVerificationResult['hash'];
|
|
93
|
+
if (checkIfJWTAboutToExpire && (jwtVerificationResult["exp"] - (Date.now() / 1000)) < 30) {
|
|
94
|
+
console.error("❗️❗️❗️ Cannot validate step-up MFA grace cookie: token is expired or about to expire ❗️❗️❗️");
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
if (hash === jwtHash) {
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
return false;
|
|
101
|
+
});
|
|
102
|
+
}
|
|
48
103
|
verify(confirmationResult, opts) {
|
|
49
104
|
return __awaiter(this, void 0, void 0, function* () {
|
|
50
|
-
var _a, _b, _c, _d;
|
|
105
|
+
var _a, _b, _c, _d, _e;
|
|
51
106
|
if (!confirmationResult)
|
|
52
107
|
return { error: "Confirmation result is required" };
|
|
53
108
|
if (this.options.usersFilterToApply) {
|
|
@@ -62,6 +117,12 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
62
117
|
return { ok: true };
|
|
63
118
|
}
|
|
64
119
|
}
|
|
120
|
+
if (this.options.stepUpMfaGracePeriodSeconds && ((_a = opts.extra) === null || _a === void 0 ? void 0 : _a.headers) && !confirmationResult.mode) {
|
|
121
|
+
const verificationResult = yield this.isTempSkip2FAGraceValid(opts.extra.headers, opts.cookies);
|
|
122
|
+
if (verificationResult === true) {
|
|
123
|
+
return { ok: true };
|
|
124
|
+
}
|
|
125
|
+
}
|
|
65
126
|
if (confirmationResult.mode === "totp") {
|
|
66
127
|
const code = confirmationResult.result;
|
|
67
128
|
const authRes = this.adminforth.config.resources
|
|
@@ -69,10 +130,10 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
69
130
|
if (!authRes)
|
|
70
131
|
return { error: "Auth resource not found" };
|
|
71
132
|
const connector = this.adminforth.connectors[authRes.dataSource];
|
|
72
|
-
const pkName = (
|
|
133
|
+
const pkName = (_b = authRes.columns.find(c => c.primaryKey)) === null || _b === void 0 ? void 0 : _b.name;
|
|
73
134
|
if (!pkName)
|
|
74
135
|
return { error: "Primary key not found on auth resource" };
|
|
75
|
-
const pk = (
|
|
136
|
+
const pk = (_c = opts === null || opts === void 0 ? void 0 : opts.userPk) !== null && _c !== void 0 ? _c : (_e = (_d = opts === null || opts === void 0 ? void 0 : opts.adminUser) === null || _d === void 0 ? void 0 : _d.dbUser) === null || _e === void 0 ? void 0 : _e[pkName];
|
|
76
137
|
if (!pk)
|
|
77
138
|
return { error: "User PK is required" };
|
|
78
139
|
const user = yield connector.getRecordByPrimaryKey(authRes, pk);
|
|
@@ -82,7 +143,17 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
82
143
|
if (!secret)
|
|
83
144
|
return { error: "2FA is not set up for this user" };
|
|
84
145
|
const verified = twofactor.verifyToken(secret, code, this.options.timeStepWindow);
|
|
85
|
-
|
|
146
|
+
//* SET GRACE COOKIE *//
|
|
147
|
+
if (verified) {
|
|
148
|
+
if (this.options.stepUpMfaGracePeriodSeconds) {
|
|
149
|
+
this.issueTempSkip2FAGraceJWT(opts);
|
|
150
|
+
}
|
|
151
|
+
return { ok: true };
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
return { error: "Wrong or expired OTP code" };
|
|
155
|
+
}
|
|
156
|
+
;
|
|
86
157
|
}
|
|
87
158
|
else if (confirmationResult.mode === "passkey") {
|
|
88
159
|
const passkeysCookies = this.adminforth.auth.getCustomCookie({ cookies: opts.cookies, name: `passkeyLoginTemporaryJWT` });
|
|
@@ -95,6 +166,10 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
95
166
|
}
|
|
96
167
|
const verificationResult = yield this.verifyPasskeyResponse(confirmationResult.result, opts.userPk, decodedPasskeysCookies);
|
|
97
168
|
if (verificationResult.ok && verificationResult.passkeyConfirmed) {
|
|
169
|
+
//* SET GRACE COOKIE *//
|
|
170
|
+
if (this.options.stepUpMfaGracePeriodSeconds) {
|
|
171
|
+
this.issueTempSkip2FAGraceJWT(opts);
|
|
172
|
+
}
|
|
98
173
|
return { ok: true };
|
|
99
174
|
}
|
|
100
175
|
return { error: "Invalid passkey" };
|
|
@@ -543,7 +618,7 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
543
618
|
server.endpoint({
|
|
544
619
|
method: "GET",
|
|
545
620
|
path: "/plugin/twofa/skip-allow-modal",
|
|
546
|
-
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ adminUser }) {
|
|
621
|
+
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ adminUser, headers, cookies }) {
|
|
547
622
|
if (this.options.usersFilterToApply) {
|
|
548
623
|
const res = this.options.usersFilterToApply(adminUser);
|
|
549
624
|
if (res === false) {
|
|
@@ -556,6 +631,12 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
556
631
|
return { skipAllowed: true };
|
|
557
632
|
}
|
|
558
633
|
}
|
|
634
|
+
if (this.options.stepUpMfaGracePeriodSeconds) {
|
|
635
|
+
const verificationResult = yield this.isTempSkip2FAGraceValid(headers, cookies, true);
|
|
636
|
+
if (verificationResult === true) {
|
|
637
|
+
return { skipAllowed: true };
|
|
638
|
+
}
|
|
639
|
+
}
|
|
559
640
|
return { skipAllowed: false };
|
|
560
641
|
}),
|
|
561
642
|
});
|
|
@@ -582,14 +663,16 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
582
663
|
method: 'POST',
|
|
583
664
|
path: `/plugin/passkeys/registerPasskeyRequest`,
|
|
584
665
|
noAuth: false,
|
|
585
|
-
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response, cookies }) {
|
|
666
|
+
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response, cookies, extra }) {
|
|
586
667
|
var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
587
668
|
const mode = body === null || body === void 0 ? void 0 : body.mode;
|
|
588
669
|
const confirmationResult = body === null || body === void 0 ? void 0 : body.confirmationResult;
|
|
589
670
|
const verificationResult = yield this.verify(confirmationResult, {
|
|
590
671
|
adminUser: adminUser,
|
|
591
672
|
userPk: adminUser.pk,
|
|
592
|
-
cookies: cookies
|
|
673
|
+
cookies: cookies,
|
|
674
|
+
response: response,
|
|
675
|
+
extra: Object.assign({}, extra)
|
|
593
676
|
});
|
|
594
677
|
if (!verificationResult || !('ok' in verificationResult)) {
|
|
595
678
|
return { ok: false, error: 'Verification failed' };
|
package/index.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from '@simplewebauthn/server';
|
|
11
11
|
import { isoUint8Array, isoBase64URL } from '@simplewebauthn/server/helpers';
|
|
12
12
|
import aaguids from './custom/aaguid.json';
|
|
13
|
+
import crypto from 'crypto';
|
|
13
14
|
|
|
14
15
|
export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
15
16
|
options: PluginOptions;
|
|
@@ -54,9 +55,62 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
54
55
|
return { skipAllowed: false };
|
|
55
56
|
}
|
|
56
57
|
|
|
58
|
+
private generateHashForStepUpMfaGraceCookie(headers): string {
|
|
59
|
+
const ip = this.adminforth.auth.getClientIp(headers);
|
|
60
|
+
const userAgent = headers['user-agent'] || '';
|
|
61
|
+
const acceptLanguage = headers['accept-language'] || '';
|
|
62
|
+
if (!ip || !userAgent || !acceptLanguage) {
|
|
63
|
+
console.error("❗️❗️❗️ Cannot set step-up MFA grace cookie: missing required request headers to identify client ❗️❗️❗️");
|
|
64
|
+
return null;
|
|
65
|
+
} else {
|
|
66
|
+
const hmac = crypto.createHmac('sha256', process.env.ADMINFORTH_SECRET)
|
|
67
|
+
.update(`${acceptLanguage}_${userAgent}_${ip}`)
|
|
68
|
+
.digest('hex');
|
|
69
|
+
return hmac;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private issueTempSkip2FAGraceJWT(opts): void {
|
|
74
|
+
if (opts.response) {
|
|
75
|
+
if (opts.extra.headers) {
|
|
76
|
+
const hash = this.generateHashForStepUpMfaGraceCookie(opts.extra.headers);
|
|
77
|
+
if (!hash) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const jwt = this.adminforth.auth.issueJWT({ hash: hash }, 'MfaGrace', `${this.options.stepUpMfaGracePeriodSeconds}s`);
|
|
81
|
+
//TODO: fix ts-ignore after releasing new version of adminforth with updated types
|
|
82
|
+
//@ts-ignore
|
|
83
|
+
this.adminforth.auth.setCustomCookie({response: opts.response, payload: {name: "TempSkip2FA_Modal_JWT", value: jwt, expirySeconds: this.options.stepUpMfaGracePeriodSeconds, httpOnly: true}});
|
|
84
|
+
}
|
|
85
|
+
} else {
|
|
86
|
+
console.error("❗️❗️❗️ Cannot set step-up MFA grace cookie: response object is missing. You probably called verify() method without response parameter ❗️❗️❗️");
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private async isTempSkip2FAGraceValid(headers, cookies, checkIfJWTAboutToExpire: boolean = false): Promise<boolean> {
|
|
91
|
+
const hash = this.generateHashForStepUpMfaGraceCookie(headers);
|
|
92
|
+
if (!hash) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
const jwt = this.adminforth.auth.getCustomCookie({cookies: cookies, name: "TempSkip2FA_Modal_JWT"});
|
|
96
|
+
const jwtVerificationResult = await this.adminforth.auth.verify(jwt, 'MfaGrace', false)
|
|
97
|
+
if (!jwtVerificationResult) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
const jwtHash = jwtVerificationResult['hash'];
|
|
101
|
+
if (checkIfJWTAboutToExpire && (jwtVerificationResult["exp"] - ( Date.now() / 1000)) < 30 ) {
|
|
102
|
+
console.error("❗️❗️❗️ Cannot validate step-up MFA grace cookie: token is expired or about to expire ❗️❗️❗️");
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
if (hash === jwtHash) {
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
|
|
57
111
|
public async verify(
|
|
58
112
|
confirmationResult: Record<string, any>,
|
|
59
|
-
opts?: { adminUser?: AdminUser; userPk?: string; cookies?: any }
|
|
113
|
+
opts?: { adminUser?: AdminUser; userPk?: string; cookies?: any, response?: IAdminForthHttpResponse, extra?: HttpExtra }
|
|
60
114
|
): Promise<{ ok: true } | { error: string }> {
|
|
61
115
|
if (!confirmationResult) return { error: "Confirmation result is required" };
|
|
62
116
|
if (this.options.usersFilterToApply) {
|
|
@@ -71,6 +125,13 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
71
125
|
return { ok: true };
|
|
72
126
|
}
|
|
73
127
|
}
|
|
128
|
+
if ( this.options.stepUpMfaGracePeriodSeconds && opts.extra?.headers && !confirmationResult.mode) {
|
|
129
|
+
const verificationResult = await this.isTempSkip2FAGraceValid(opts.extra.headers, opts.cookies);
|
|
130
|
+
if ( verificationResult === true ) {
|
|
131
|
+
return { ok: true };
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
74
135
|
if (confirmationResult.mode === "totp") {
|
|
75
136
|
const code = confirmationResult.result;
|
|
76
137
|
const authRes = this.adminforth.config.resources
|
|
@@ -92,7 +153,16 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
92
153
|
if (!secret) return { error: "2FA is not set up for this user" };
|
|
93
154
|
|
|
94
155
|
const verified = twofactor.verifyToken(secret, code, this.options.timeStepWindow);
|
|
95
|
-
|
|
156
|
+
|
|
157
|
+
//* SET GRACE COOKIE *//
|
|
158
|
+
if ( verified ) {
|
|
159
|
+
if (this.options.stepUpMfaGracePeriodSeconds) {
|
|
160
|
+
this.issueTempSkip2FAGraceJWT(opts);
|
|
161
|
+
}
|
|
162
|
+
return { ok: true }
|
|
163
|
+
} else {
|
|
164
|
+
return { error: "Wrong or expired OTP code" }
|
|
165
|
+
};
|
|
96
166
|
} else if (confirmationResult.mode === "passkey") {
|
|
97
167
|
const passkeysCookies = this.adminforth.auth.getCustomCookie({cookies: opts.cookies, name: `passkeyLoginTemporaryJWT`});
|
|
98
168
|
if (!passkeysCookies) {
|
|
@@ -106,6 +176,11 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
106
176
|
const verificationResult = await this.verifyPasskeyResponse(confirmationResult.result, opts.userPk, decodedPasskeysCookies );
|
|
107
177
|
|
|
108
178
|
if (verificationResult.ok && verificationResult.passkeyConfirmed) {
|
|
179
|
+
|
|
180
|
+
//* SET GRACE COOKIE *//
|
|
181
|
+
if (this.options.stepUpMfaGracePeriodSeconds) {
|
|
182
|
+
this.issueTempSkip2FAGraceJWT(opts);
|
|
183
|
+
}
|
|
109
184
|
return { ok: true }
|
|
110
185
|
}
|
|
111
186
|
return { error: "Invalid passkey" }
|
|
@@ -601,7 +676,7 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
601
676
|
server.endpoint({
|
|
602
677
|
method: "GET",
|
|
603
678
|
path: "/plugin/twofa/skip-allow-modal",
|
|
604
|
-
handler: async ({ adminUser }) => {
|
|
679
|
+
handler: async ({ adminUser, headers, cookies }) => {
|
|
605
680
|
if ( this.options.usersFilterToApply ) {
|
|
606
681
|
const res = this.options.usersFilterToApply(adminUser);
|
|
607
682
|
if ( res === false ) {
|
|
@@ -614,6 +689,14 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
614
689
|
return { skipAllowed: true };
|
|
615
690
|
}
|
|
616
691
|
}
|
|
692
|
+
|
|
693
|
+
if ( this.options.stepUpMfaGracePeriodSeconds ) {
|
|
694
|
+
const verificationResult = await this.isTempSkip2FAGraceValid(headers, cookies, true);
|
|
695
|
+
if ( verificationResult === true ) {
|
|
696
|
+
return { skipAllowed: true };
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
617
700
|
return { skipAllowed: false };
|
|
618
701
|
},
|
|
619
702
|
});
|
|
@@ -641,14 +724,16 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
641
724
|
method: 'POST',
|
|
642
725
|
path: `/plugin/passkeys/registerPasskeyRequest`,
|
|
643
726
|
noAuth: false,
|
|
644
|
-
handler: async ({ body, adminUser, response, cookies }) => {
|
|
727
|
+
handler: async ({ body, adminUser, response, cookies, extra }) => {
|
|
645
728
|
const mode = body?.mode;
|
|
646
729
|
|
|
647
730
|
const confirmationResult = body?.confirmationResult;
|
|
648
731
|
const verificationResult = await this.verify(confirmationResult, {
|
|
649
732
|
adminUser: adminUser,
|
|
650
733
|
userPk: adminUser.pk,
|
|
651
|
-
cookies: cookies
|
|
734
|
+
cookies: cookies,
|
|
735
|
+
response: response,
|
|
736
|
+
extra: { ...extra }
|
|
652
737
|
});
|
|
653
738
|
if ( !verificationResult || !('ok' in verificationResult) ) {
|
|
654
739
|
return { ok: false, error: 'Verification failed' };
|
package/package.json
CHANGED
package/types.ts
CHANGED
|
@@ -16,6 +16,12 @@ export type PluginOptions = {
|
|
|
16
16
|
*/
|
|
17
17
|
timeStepWindow?: number;
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Number of seconds for step-up MFA grace period.
|
|
21
|
+
* During this period, user will not be challenged with 2FA on sensitive actions.
|
|
22
|
+
*/
|
|
23
|
+
stepUpMfaGracePeriodSeconds?: number;
|
|
24
|
+
|
|
19
25
|
customBrandPrefix?: string;
|
|
20
26
|
|
|
21
27
|
/**
|