@spinajs/rbac-http-user 2.0.502 → 2.0.505
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/lib/cjs/2fa/Default2FaToken.d.ts +8 -0
- package/lib/cjs/2fa/Default2FaToken.d.ts.map +1 -1
- package/lib/cjs/2fa/Default2FaToken.js +25 -3
- package/lib/cjs/2fa/Default2FaToken.js.map +1 -1
- package/lib/cjs/actions/2fa.d.ts +22 -3
- package/lib/cjs/actions/2fa.d.ts.map +1 -1
- package/lib/cjs/actions/2fa.js +44 -3
- package/lib/cjs/actions/2fa.js.map +1 -1
- package/lib/cjs/config/rbac-http.d.ts.map +1 -1
- package/lib/cjs/config/rbac-http.js +6 -0
- package/lib/cjs/config/rbac-http.js.map +1 -1
- package/lib/cjs/controllers/LoginController.d.ts.map +1 -1
- package/lib/cjs/controllers/LoginController.js +10 -1
- package/lib/cjs/controllers/LoginController.js.map +1 -1
- package/lib/cjs/controllers/TwoFactorAuthController.d.ts +13 -3
- package/lib/cjs/controllers/TwoFactorAuthController.d.ts.map +1 -1
- package/lib/cjs/controllers/TwoFactorAuthController.js +32 -6
- package/lib/cjs/controllers/TwoFactorAuthController.js.map +1 -1
- package/lib/cjs/controllers/TwoFactorAuthUserController.d.ts +71 -4
- package/lib/cjs/controllers/TwoFactorAuthUserController.d.ts.map +1 -1
- package/lib/cjs/controllers/TwoFactorAuthUserController.js +136 -16
- package/lib/cjs/controllers/TwoFactorAuthUserController.js.map +1 -1
- package/lib/cjs/policies/2FaPolicy.d.ts.map +1 -1
- package/lib/cjs/policies/2FaPolicy.js +27 -6
- package/lib/cjs/policies/2FaPolicy.js.map +1 -1
- package/lib/mjs/2fa/Default2FaToken.d.ts +8 -0
- package/lib/mjs/2fa/Default2FaToken.d.ts.map +1 -1
- package/lib/mjs/2fa/Default2FaToken.js +25 -3
- package/lib/mjs/2fa/Default2FaToken.js.map +1 -1
- package/lib/mjs/actions/2fa.d.ts +22 -3
- package/lib/mjs/actions/2fa.d.ts.map +1 -1
- package/lib/mjs/actions/2fa.js +41 -3
- package/lib/mjs/actions/2fa.js.map +1 -1
- package/lib/mjs/config/rbac-http.d.ts.map +1 -1
- package/lib/mjs/config/rbac-http.js +6 -0
- package/lib/mjs/config/rbac-http.js.map +1 -1
- package/lib/mjs/controllers/LoginController.d.ts.map +1 -1
- package/lib/mjs/controllers/LoginController.js +10 -1
- package/lib/mjs/controllers/LoginController.js.map +1 -1
- package/lib/mjs/controllers/TwoFactorAuthController.d.ts +13 -3
- package/lib/mjs/controllers/TwoFactorAuthController.d.ts.map +1 -1
- package/lib/mjs/controllers/TwoFactorAuthController.js +35 -9
- package/lib/mjs/controllers/TwoFactorAuthController.js.map +1 -1
- package/lib/mjs/controllers/TwoFactorAuthUserController.d.ts +71 -4
- package/lib/mjs/controllers/TwoFactorAuthUserController.d.ts.map +1 -1
- package/lib/mjs/controllers/TwoFactorAuthUserController.js +140 -20
- package/lib/mjs/controllers/TwoFactorAuthUserController.js.map +1 -1
- package/lib/mjs/policies/2FaPolicy.d.ts.map +1 -1
- package/lib/mjs/policies/2FaPolicy.js +26 -5
- package/lib/mjs/policies/2FaPolicy.js.map +1 -1
- package/lib/tsconfig.cjs.tsbuildinfo +1 -1
- package/lib/tsconfig.mjs.tsbuildinfo +1 -1
- package/package.json +15 -15
|
@@ -19,11 +19,12 @@ const di_1 = require("@spinajs/di");
|
|
|
19
19
|
const configuration_1 = require("@spinajs/configuration");
|
|
20
20
|
const rbac_http_1 = require("@spinajs/rbac-http");
|
|
21
21
|
const exceptions_1 = require("@spinajs/exceptions");
|
|
22
|
-
const _2FaPolicy_js_1 = require("../policies/2FaPolicy.js");
|
|
23
22
|
const confirm_password_dto_js_1 = require("../dto/confirm-password-dto.js");
|
|
23
|
+
const token_dto_js_1 = require("../dto/token-dto.js");
|
|
24
24
|
const SessionCookies_js_1 = require("../services/SessionCookies.js");
|
|
25
25
|
const _2fa_js_1 = require("../actions/2fa.js");
|
|
26
26
|
const Default2FaToken_js_1 = require("../2fa/Default2FaToken.js");
|
|
27
|
+
const _2FaPolicy_js_1 = require("../policies/2FaPolicy.js");
|
|
27
28
|
/**
|
|
28
29
|
* Self-service two-factor authentication.
|
|
29
30
|
*
|
|
@@ -38,6 +39,13 @@ const Default2FaToken_js_1 = require("../2fa/Default2FaToken.js");
|
|
|
38
39
|
* not be enough to attach an attacker-controlled authenticator or to strip the
|
|
39
40
|
* second factor off the account.
|
|
40
41
|
*
|
|
42
|
+
* The system-wide switch (`rbac.twoFactorAuth.enabled`) is enforced by
|
|
43
|
+
* `TwoFactorAuthEnabled`, passed to `@Permission` on each mutating route so it
|
|
44
|
+
* shares a policy group with the permission check and is therefore required
|
|
45
|
+
* rather than merely offered as an alternative. It is deliberately NOT a class
|
|
46
|
+
* policy: `status` stays readable while the switch is off, which is how the
|
|
47
|
+
* frontend learns to hide its 2FA controls.
|
|
48
|
+
*
|
|
41
49
|
* @tags Two-Factor Settings
|
|
42
50
|
*/
|
|
43
51
|
let TwoFactorAuthUserController = class TwoFactorAuthUserController extends http_1.BaseController {
|
|
@@ -50,12 +58,19 @@ let TwoFactorAuthUserController = class TwoFactorAuthUserController extends http
|
|
|
50
58
|
* @response 403 Forbidden — insufficient permissions
|
|
51
59
|
*/
|
|
52
60
|
async status(user) {
|
|
53
|
-
|
|
61
|
+
const enabled = Boolean(user.Metadata[Default2FaToken_js_1.TWO_FA_METATADATA_KEYS.ENABLED]);
|
|
62
|
+
return new http_1.Ok({
|
|
63
|
+
Enabled: enabled,
|
|
64
|
+
Pending: !enabled && Boolean(user.Metadata[Default2FaToken_js_1.TWO_FA_METATADATA_KEYS.TOKEN]),
|
|
65
|
+
SystemEnabled: this.TwoFactorConfig?.enabled !== false,
|
|
66
|
+
});
|
|
54
67
|
}
|
|
55
68
|
/**
|
|
56
69
|
* Enable own two-factor authentication
|
|
57
70
|
* Generates a TOTP secret for the authenticated user and returns the OTP provisioning
|
|
58
71
|
* URI to scan with an authenticator app. Requires the account password to be re-entered.
|
|
72
|
+
* 2FA stays off until the new code is confirmed with `POST /user/2fa/confirm` — a caller
|
|
73
|
+
* that stops after this call leaves the account unprotected, believing it is enrolled.
|
|
59
74
|
* @security cookieAuth
|
|
60
75
|
* @returns {IEnable2faResponse} OTP provisioning URI to scan with an authenticator app
|
|
61
76
|
* @response 400 Two-factor authentication is already enabled for this user
|
|
@@ -64,20 +79,58 @@ let TwoFactorAuthUserController = class TwoFactorAuthUserController extends http
|
|
|
64
79
|
*/
|
|
65
80
|
async enable(user, confirmation, session) {
|
|
66
81
|
if (user.Metadata[Default2FaToken_js_1.TWO_FA_METATADATA_KEYS.ENABLED]) {
|
|
67
|
-
throw new exceptions_1.
|
|
82
|
+
throw new exceptions_1.BadRequest(`User ${user.Uuid} already has 2fa enabled`);
|
|
68
83
|
}
|
|
69
84
|
const confirmed = await this.confirmPassword(user, confirmation.Password);
|
|
70
85
|
if (confirmed !== true) {
|
|
71
86
|
return confirmed;
|
|
72
87
|
}
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
// the
|
|
76
|
-
//
|
|
77
|
-
// themselves out and needs an administrator 2FA reset to recover.
|
|
88
|
+
// The secret is stored but 2fa stays OFF until `POST /user/2fa/confirm`
|
|
89
|
+
// accepts a code generated from it. A user who never scans is left pending,
|
|
90
|
+
// which the login check treats exactly like having no device — no lockout,
|
|
91
|
+
// and the next attempt simply issues a new secret.
|
|
78
92
|
const result = await this.enrol(user);
|
|
79
93
|
return new http_1.Ok({ otp: result }, await this.rotate(session));
|
|
80
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Confirm own two-factor enrolment
|
|
97
|
+
* Verifies a code generated from the secret handed out by `POST /user/2fa/enable`
|
|
98
|
+
* or `POST /user/2fa/reset`, and only then switches 2FA on.
|
|
99
|
+
* @security cookieAuth
|
|
100
|
+
* @response 200 Two-factor authentication is now active
|
|
101
|
+
* @response 400 There is no pending enrolment to confirm
|
|
102
|
+
* @response 401 Unauthorized — valid session required
|
|
103
|
+
* @response 403 Invalid or expired TOTP code
|
|
104
|
+
*/
|
|
105
|
+
async confirm(user, token, session) {
|
|
106
|
+
// `activate()` never clears the stored token, so an already-enabled
|
|
107
|
+
// account still has one and would otherwise be able to call this route
|
|
108
|
+
// again with a currently-valid code. Only the pending state — a token
|
|
109
|
+
// without 2fa being enabled yet — is an enrolment left to confirm.
|
|
110
|
+
if (user.Metadata[Default2FaToken_js_1.TWO_FA_METATADATA_KEYS.ENABLED] || !user.Metadata[Default2FaToken_js_1.TWO_FA_METATADATA_KEYS.TOKEN]) {
|
|
111
|
+
throw new exceptions_1.BadRequest(`User ${user.Uuid} has no pending 2fa enrolment`);
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
await this.confirmEnrolment(user, token.Token);
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
// `confirmUser2Fa` throws `Unauthorized` for a rejected code — the only
|
|
118
|
+
// case this route should report as "wrong code". Anything else (a
|
|
119
|
+
// database outage during activation, for instance) must surface as a
|
|
120
|
+
// 500, not be misreported to the user as a bad code.
|
|
121
|
+
if (!(err instanceof http_1.Unauthorized)) {
|
|
122
|
+
throw err;
|
|
123
|
+
}
|
|
124
|
+
this._log.warn(`2fa confirmation rejected for ${user.Uuid}`, { error: err });
|
|
125
|
+
return new http_1.ForbiddenResponse({
|
|
126
|
+
error: { code: 'E_2FA_FAILED', message: '2fa check failed' },
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
// No password here on purpose: the password was confirmed by the enable or
|
|
130
|
+
// reset call that issued this secret, and the code is itself the proof this
|
|
131
|
+
// route exists to demand.
|
|
132
|
+
return new http_1.Ok(null, await this.rotate(session));
|
|
133
|
+
}
|
|
81
134
|
/**
|
|
82
135
|
* Disable own two-factor authentication
|
|
83
136
|
* Removes the TOTP secret and disables 2FA for the authenticated user.
|
|
@@ -90,7 +143,7 @@ let TwoFactorAuthUserController = class TwoFactorAuthUserController extends http
|
|
|
90
143
|
*/
|
|
91
144
|
async disable(user, confirmation, session) {
|
|
92
145
|
if (!user.Metadata[Default2FaToken_js_1.TWO_FA_METATADATA_KEYS.ENABLED]) {
|
|
93
|
-
throw new exceptions_1.
|
|
146
|
+
throw new exceptions_1.BadRequest(`User ${user.Uuid} already has 2fa disabled`);
|
|
94
147
|
}
|
|
95
148
|
const confirmed = await this.confirmPassword(user, confirmation.Password);
|
|
96
149
|
if (confirmed !== true) {
|
|
@@ -99,6 +152,45 @@ let TwoFactorAuthUserController = class TwoFactorAuthUserController extends http
|
|
|
99
152
|
await this.unenrol(user);
|
|
100
153
|
return new http_1.Ok(null, await this.rotate(session));
|
|
101
154
|
}
|
|
155
|
+
/**
|
|
156
|
+
* Reset own two-factor authentication
|
|
157
|
+
* Removes the current TOTP device and issues a new secret in a single request,
|
|
158
|
+
* returning the provisioning URI to scan. 2FA stays off until the new code is
|
|
159
|
+
* confirmed with `POST /user/2fa/confirm`. Requires the account password.
|
|
160
|
+
*
|
|
161
|
+
* Folding disable-then-enable into one request removes the client-side gap
|
|
162
|
+
* where abandoning the flow between the two calls could strand the account
|
|
163
|
+
* without a second factor. It does not remove the server-side one: if
|
|
164
|
+
* `enrol` fails after `unenrol` already removed the old device, the account
|
|
165
|
+
* is left with no device at all — there is nothing to roll back to, since
|
|
166
|
+
* the old secret is already gone. That leaves the account in the ordinary
|
|
167
|
+
* `none` state, which every route here already handles: `GET /user/2fa`
|
|
168
|
+
* reports it and `POST /user/2fa/enable` starts a fresh enrolment.
|
|
169
|
+
*
|
|
170
|
+
* A caller who abandons before confirming is left password-only — the same
|
|
171
|
+
* gap as `enable`. The only built-in recovery is `rbac.twoFactorAuth.forceUser`
|
|
172
|
+
* pushing the account back through enrolment at next login, and this package's
|
|
173
|
+
* own default for that flag is `false` (see `src/config/rbac-http.ts`), so a
|
|
174
|
+
* consumer that has not set it will see the downgrade become silent and
|
|
175
|
+
* permanent rather than recovered.
|
|
176
|
+
* @security cookieAuth
|
|
177
|
+
* @returns {IEnable2faResponse} OTP provisioning URI to scan with an authenticator app
|
|
178
|
+
* @response 400 There is no two-factor device to reset
|
|
179
|
+
* @response 401 Unauthorized — valid session required, or password invalid
|
|
180
|
+
* @response 403 Forbidden — insufficient permissions
|
|
181
|
+
*/
|
|
182
|
+
async reset(user, confirmation, session) {
|
|
183
|
+
if (!user.Metadata[Default2FaToken_js_1.TWO_FA_METATADATA_KEYS.ENABLED] && !user.Metadata[Default2FaToken_js_1.TWO_FA_METATADATA_KEYS.TOKEN]) {
|
|
184
|
+
throw new exceptions_1.BadRequest(`User ${user.Uuid} has no 2fa to reset`);
|
|
185
|
+
}
|
|
186
|
+
const confirmed = await this.confirmPassword(user, confirmation.Password);
|
|
187
|
+
if (confirmed !== true) {
|
|
188
|
+
return confirmed;
|
|
189
|
+
}
|
|
190
|
+
await this.unenrol(user);
|
|
191
|
+
const result = await this.enrol(user);
|
|
192
|
+
return new http_1.Ok({ otp: result }, await this.rotate(session));
|
|
193
|
+
}
|
|
102
194
|
/**
|
|
103
195
|
* Rotates the session id after the account's authentication requirements
|
|
104
196
|
* changed, and returns the response options carrying the new cookie.
|
|
@@ -120,11 +212,16 @@ let TwoFactorAuthUserController = class TwoFactorAuthUserController extends http
|
|
|
120
212
|
};
|
|
121
213
|
}
|
|
122
214
|
/**
|
|
123
|
-
*
|
|
124
|
-
* the module-level action so tests can stub it without a
|
|
215
|
+
* Store the TOTP secret and return the provisioning URI, leaving 2fa off until
|
|
216
|
+
* confirmed. Wraps the module-level action so tests can stub it without a
|
|
217
|
+
* TOTP/DB setup.
|
|
125
218
|
*/
|
|
126
219
|
enrol(user) {
|
|
127
|
-
return (0, _2fa_js_1.
|
|
220
|
+
return (0, _2fa_js_1.beginUser2FaEnrolment)(user);
|
|
221
|
+
}
|
|
222
|
+
/** Verify a code against the pending secret and switch 2fa on. */
|
|
223
|
+
confirmEnrolment(user, token) {
|
|
224
|
+
return (0, _2fa_js_1.confirmUser2Fa)(user, token);
|
|
128
225
|
}
|
|
129
226
|
/** Clear the TOTP secret. Wrapped for the same reason as {@link enrol}. */
|
|
130
227
|
unenrol(user) {
|
|
@@ -158,6 +255,10 @@ __decorate([
|
|
|
158
255
|
(0, di_1.Autoinject)(SessionCookies_js_1.SessionCookieFactory),
|
|
159
256
|
__metadata("design:type", SessionCookies_js_1.SessionCookieFactory)
|
|
160
257
|
], TwoFactorAuthUserController.prototype, "SessionCookies", void 0);
|
|
258
|
+
__decorate([
|
|
259
|
+
(0, configuration_1.Config)('rbac.twoFactorAuth'),
|
|
260
|
+
__metadata("design:type", Object)
|
|
261
|
+
], TwoFactorAuthUserController.prototype, "TwoFactorConfig", void 0);
|
|
161
262
|
__decorate([
|
|
162
263
|
(0, http_1.Get)('2fa'),
|
|
163
264
|
(0, rbac_http_1.Permission)(['readOwn']),
|
|
@@ -168,7 +269,7 @@ __decorate([
|
|
|
168
269
|
], TwoFactorAuthUserController.prototype, "status", null);
|
|
169
270
|
__decorate([
|
|
170
271
|
(0, http_1.Post)('2fa/enable'),
|
|
171
|
-
(0, rbac_http_1.Permission)(['updateOwn']),
|
|
272
|
+
(0, rbac_http_1.Permission)(['updateOwn'], _2FaPolicy_js_1.TwoFactorAuthEnabled),
|
|
172
273
|
__param(0, (0, rbac_http_1.User)()),
|
|
173
274
|
__param(1, (0, http_1.Body)()),
|
|
174
275
|
__param(2, (0, rbac_http_1.Session)()),
|
|
@@ -176,9 +277,19 @@ __decorate([
|
|
|
176
277
|
__metadata("design:paramtypes", [rbac_1.User, confirm_password_dto_js_1.ConfirmPasswordDto, Object]),
|
|
177
278
|
__metadata("design:returntype", Promise)
|
|
178
279
|
], TwoFactorAuthUserController.prototype, "enable", null);
|
|
280
|
+
__decorate([
|
|
281
|
+
(0, http_1.Post)('2fa/confirm'),
|
|
282
|
+
(0, rbac_http_1.Permission)(['updateOwn'], _2FaPolicy_js_1.TwoFactorAuthEnabled),
|
|
283
|
+
__param(0, (0, rbac_http_1.User)()),
|
|
284
|
+
__param(1, (0, http_1.Body)()),
|
|
285
|
+
__param(2, (0, rbac_http_1.Session)()),
|
|
286
|
+
__metadata("design:type", Function),
|
|
287
|
+
__metadata("design:paramtypes", [rbac_1.User, token_dto_js_1.TokenDto, Object]),
|
|
288
|
+
__metadata("design:returntype", Promise)
|
|
289
|
+
], TwoFactorAuthUserController.prototype, "confirm", null);
|
|
179
290
|
__decorate([
|
|
180
291
|
(0, http_1.Post)('2fa/disable'),
|
|
181
|
-
(0, rbac_http_1.Permission)(['updateOwn']),
|
|
292
|
+
(0, rbac_http_1.Permission)(['updateOwn'], _2FaPolicy_js_1.TwoFactorAuthEnabled),
|
|
182
293
|
__param(0, (0, rbac_http_1.User)()),
|
|
183
294
|
__param(1, (0, http_1.Body)()),
|
|
184
295
|
__param(2, (0, rbac_http_1.Session)()),
|
|
@@ -186,10 +297,19 @@ __decorate([
|
|
|
186
297
|
__metadata("design:paramtypes", [rbac_1.User, confirm_password_dto_js_1.ConfirmPasswordDto, Object]),
|
|
187
298
|
__metadata("design:returntype", Promise)
|
|
188
299
|
], TwoFactorAuthUserController.prototype, "disable", null);
|
|
300
|
+
__decorate([
|
|
301
|
+
(0, http_1.Post)('2fa/reset'),
|
|
302
|
+
(0, rbac_http_1.Permission)(['updateOwn'], _2FaPolicy_js_1.TwoFactorAuthEnabled),
|
|
303
|
+
__param(0, (0, rbac_http_1.User)()),
|
|
304
|
+
__param(1, (0, http_1.Body)()),
|
|
305
|
+
__param(2, (0, rbac_http_1.Session)()),
|
|
306
|
+
__metadata("design:type", Function),
|
|
307
|
+
__metadata("design:paramtypes", [rbac_1.User, confirm_password_dto_js_1.ConfirmPasswordDto, Object]),
|
|
308
|
+
__metadata("design:returntype", Promise)
|
|
309
|
+
], TwoFactorAuthUserController.prototype, "reset", null);
|
|
189
310
|
exports.TwoFactorAuthUserController = TwoFactorAuthUserController = __decorate([
|
|
190
311
|
(0, http_1.BasePath)('user'),
|
|
191
312
|
(0, rbac_http_1.Resource)('user'),
|
|
192
|
-
(0, http_1.Policy)(rbac_http_1.AuthorizedPolicy)
|
|
193
|
-
(0, http_1.Policy)(_2FaPolicy_js_1.TwoFactorAuthEnabled)
|
|
313
|
+
(0, http_1.Policy)(rbac_http_1.AuthorizedPolicy)
|
|
194
314
|
], TwoFactorAuthUserController);
|
|
195
315
|
//# sourceMappingURL=TwoFactorAuthUserController.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TwoFactorAuthUserController.js","sourceRoot":"","sources":["../../../src/controllers/TwoFactorAuthUserController.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,
|
|
1
|
+
{"version":3,"file":"TwoFactorAuthUserController.js","sourceRoot":"","sources":["../../../src/controllers/TwoFactorAuthUserController.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"2FaPolicy.d.ts","sourceRoot":"","sources":["../../../src/policies/2FaPolicy.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"2FaPolicy.d.ts","sourceRoot":"","sources":["../../../src/policies/2FaPolicy.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAGzD;;;;;;GAMG;AACH,qBAAa,oBAAqB,SAAQ,UAAU;IAElD,SAAS,CAAC,eAAe,EAAE,mBAAmB,CAAC;IAExC,SAAS,IAAI,OAAO,CAE1B;IAEY,OAAO,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CA2BlD;CACF;AAED;;;;;;;;;GASG;AACH,qBAAa,kBAAmB,SAAQ,oBAAoB;IAC7C,OAAO,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAejD;CACF"}
|
|
@@ -10,10 +10,9 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.TwoFacRouteEnabled = exports.TwoFactorAuthEnabled = void 0;
|
|
13
|
-
const exceptions_1 = require("@spinajs/exceptions");
|
|
14
13
|
const configuration_1 = require("@spinajs/configuration");
|
|
15
14
|
const http_1 = require("@spinajs/http");
|
|
16
|
-
const
|
|
15
|
+
const exceptions_1 = require("@spinajs/exceptions");
|
|
17
16
|
/**
|
|
18
17
|
* Guards routes that only make sense when 2FA is switched on system-wide.
|
|
19
18
|
*
|
|
@@ -25,11 +24,33 @@ class TwoFactorAuthEnabled extends http_1.BasePolicy {
|
|
|
25
24
|
isEnabled() {
|
|
26
25
|
return true;
|
|
27
26
|
}
|
|
28
|
-
execute(_req) {
|
|
27
|
+
async execute(_req) {
|
|
29
28
|
if (this.TwoFactorConfig.enabled === false) {
|
|
30
|
-
|
|
29
|
+
// Deliberately Forbidden and not InvalidOperation: the latter has no
|
|
30
|
+
// @HandleException mapping in @spinajs/http and reaches the client as a
|
|
31
|
+
// 500, which a caller cannot distinguish from a fault.
|
|
32
|
+
//
|
|
33
|
+
// `Forbidden`'s constructor only accepts a message string (see
|
|
34
|
+
// `Exception` in @spinajs/exceptions), so the structured payload that
|
|
35
|
+
// the HTTP error handler serializes into the response body is attached
|
|
36
|
+
// as an extra `error` property after construction.
|
|
37
|
+
//
|
|
38
|
+
// This method must stay `async`: `createPolicyGate` (see
|
|
39
|
+
// packages/http/src/route-builder.ts) builds its wait list with
|
|
40
|
+
// `enabledPolicies.map(p => p.execute(...).then(...))`. A synchronous
|
|
41
|
+
// `throw` here would escape that `.map()` call entirely instead of
|
|
42
|
+
// becoming a rejected promise in the array, so `Promise.allSettled`
|
|
43
|
+
// would never even run — the whole policy gate blows up for every
|
|
44
|
+
// caller, authorized or not, instead of just this policy losing the
|
|
45
|
+
// race. Marking the method `async` turns the throw into a promise
|
|
46
|
+
// rejection, which is what the gate is built to handle.
|
|
47
|
+
throw Object.assign(new exceptions_1.Forbidden('2 factor auth is not enabled'), {
|
|
48
|
+
error: {
|
|
49
|
+
code: 'E_2FA_SYSTEM_DISABLED',
|
|
50
|
+
message: '2 factor auth is not enabled',
|
|
51
|
+
},
|
|
52
|
+
});
|
|
31
53
|
}
|
|
32
|
-
return Promise.resolve();
|
|
33
54
|
}
|
|
34
55
|
}
|
|
35
56
|
exports.TwoFactorAuthEnabled = TwoFactorAuthEnabled;
|
|
@@ -57,7 +78,7 @@ class TwoFacRouteEnabled extends TwoFactorAuthEnabled {
|
|
|
57
78
|
* Check only if user passed login page and waiting for TwoFactorAuth
|
|
58
79
|
*/
|
|
59
80
|
if (!req.storage.Session?.Data.get('TwoFactorAuth')) {
|
|
60
|
-
throw new
|
|
81
|
+
throw new exceptions_1.Forbidden('user does not have 2fa enabled');
|
|
61
82
|
}
|
|
62
83
|
return Promise.resolve();
|
|
63
84
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"2FaPolicy.js","sourceRoot":"","sources":["../../../src/policies/2FaPolicy.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,
|
|
1
|
+
{"version":3,"file":"2FaPolicy.js","sourceRoot":"","sources":["../../../src/policies/2FaPolicy.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,0DAAgD;AAChD,wCAAgE;AAEhE,oDAAkE;AAElE;;;;;;GAMG;AACH,0BAAkC,SAAQ,iBAAU;IAI3C,SAAS;QACd,OAAO,IAAI,CAAC;IACd,CAAC;IAEM,KAAK,CAAC,OAAO,CAAC,IAAc;QACjC,IAAI,IAAI,CAAC,eAAe,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC;YAC3C,qEAAqE;YACrE,wEAAwE;YACxE,uDAAuD;YACvD,EAAE;YACF,+DAA+D;YAC/D,sEAAsE;YACtE,uEAAuE;YACvE,mDAAmD;YACnD,EAAE;YACF,yDAAyD;YACzD,gEAAgE;YAChE,sEAAsE;YACtE,mEAAmE;YACnE,oEAAoE;YACpE,kEAAkE;YAClE,oEAAoE;YACpE,kEAAkE;YAClE,wDAAwD;YACxD,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,sBAAS,CAAC,8BAA8B,CAAC,EAAE;gBACjE,KAAK,EAAE;oBACL,IAAI,EAAE,uBAAuB;oBAC7B,OAAO,EAAE,8BAA8B;iBACxC;aACF,CAAC,CAAC;QACL,CAAC;IACH,CAAC;CACF;;AAlCW;IADT,IAAA,sBAAM,EAAC,oBAAoB,CAAC;;6DACkB;AAoCjD;;;;;;;;;GASG;AACH,wBAAgC,SAAQ,oBAAoB;IACnD,KAAK,CAAC,OAAO,CAAC,GAAa;QAChC,MAAM,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAEzB,IAAI,CAAC,GAAG,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;YACzC,MAAM,IAAI,6BAAgB,CAAC,oBAAoB,CAAC,CAAC;QACnD,CAAC;QAED;;WAEG;QACH,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,EAAE,CAAC;YACpD,MAAM,IAAI,sBAAS,CAAC,gCAAgC,CAAC,CAAC;QACxD,CAAC;QAED,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;CACF"}
|
|
@@ -14,6 +14,14 @@ export declare class Default2FaToken extends TwoFactorAuthProvider {
|
|
|
14
14
|
execute(_: User): Promise<void>;
|
|
15
15
|
verifyToken(token: string, user: User): Promise<boolean>;
|
|
16
16
|
disable(user: User): Promise<void>;
|
|
17
|
+
beginEnrolment(user: User): Promise<any>;
|
|
18
|
+
activate(user: User): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Enrol and switch on in one step. This is what the CLI command, the
|
|
21
|
+
* administrator reset and user seeding use — they hand the secret to an
|
|
22
|
+
* operator rather than to the account owner, so there is nobody to confirm
|
|
23
|
+
* possession with.
|
|
24
|
+
*/
|
|
17
25
|
initialize(user: User): Promise<any>;
|
|
18
26
|
getOtpAuthUrl(user: User): Promise<string | null>;
|
|
19
27
|
isEnabled(user: User): Promise<boolean>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Default2FaToken.d.ts","sourceRoot":"","sources":["../../../src/2fa/Default2FaToken.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAErC,OAAO,EAAE,GAAG,EAAU,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAI3D,oBAAY,sBAAsB;IAC9B,KAAK,cAAc;IACnB,OAAO,gBAAgB;IACvB,GAAG,YAAY;CAClB;AAED,qBACa,eAAgB,SAAQ,qBAAqB;IAEtD,SAAS,CAAC,MAAM,EAAE,GAAG,CAAC;IAGtB,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC;IAEnB,cAEC;IAED,OAAO,CAAC,OAAO;IAYR,OAAO,CAAC,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAIrC;IAEY,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAcpE;IAEY,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAY9C;IAEY,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"Default2FaToken.d.ts","sourceRoot":"","sources":["../../../src/2fa/Default2FaToken.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAErC,OAAO,EAAE,GAAG,EAAU,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAI3D,oBAAY,sBAAsB;IAC9B,KAAK,cAAc;IACnB,OAAO,gBAAgB;IACvB,GAAG,YAAY;CAClB;AAED,qBACa,eAAgB,SAAQ,qBAAqB;IAEtD,SAAS,CAAC,MAAM,EAAE,GAAG,CAAC;IAGtB,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC;IAEnB,cAEC;IAED,OAAO,CAAC,OAAO;IAYR,OAAO,CAAC,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAIrC;IAEY,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAcpE;IAEY,OAAO,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAY9C;IAEY,cAAc,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAwBpD;IAEY,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAc/C;IAED;;;;;OAKG;IACU,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAKhD;IAEY,aAAa,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAU7D;IAEY,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAGnD;IAEY,aAAa,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,CAGvD;CACJ"}
|
|
@@ -60,17 +60,16 @@ let Default2FaToken = class Default2FaToken extends TwoFactorAuthProvider {
|
|
|
60
60
|
},
|
|
61
61
|
});
|
|
62
62
|
}
|
|
63
|
-
async
|
|
63
|
+
async beginEnrolment(user) {
|
|
64
64
|
if (user.Metadata[TWO_FA_METATADATA_KEYS.ENABLED]) {
|
|
65
65
|
throw new InvalidOperation(`user ${user.Uuid} alread have enabled 2f, disable it first.`);
|
|
66
66
|
}
|
|
67
67
|
const secret = new OTPAuth.Secret({ size: this.Config.secretSize });
|
|
68
68
|
const totp = this._getOTP(user, secret.base32);
|
|
69
69
|
user.Metadata[TWO_FA_METATADATA_KEYS.TOKEN] = secret.base32;
|
|
70
|
-
user.Metadata[TWO_FA_METATADATA_KEYS.ENABLED] = true;
|
|
71
70
|
user.Metadata[TWO_FA_METATADATA_KEYS.OTP] = totp.toString();
|
|
72
71
|
await user.Metadata.update();
|
|
73
|
-
this.Log.trace(`2fa
|
|
72
|
+
this.Log.trace(`2fa enrolment started for user ${user.Uuid}`, {
|
|
74
73
|
user: {
|
|
75
74
|
Uuid: user.Uuid
|
|
76
75
|
},
|
|
@@ -80,6 +79,29 @@ let Default2FaToken = class Default2FaToken extends TwoFactorAuthProvider {
|
|
|
80
79
|
*/
|
|
81
80
|
return totp.toString();
|
|
82
81
|
}
|
|
82
|
+
async activate(user) {
|
|
83
|
+
if (!user.Metadata[TWO_FA_METATADATA_KEYS.TOKEN]) {
|
|
84
|
+
throw new InvalidOperation(`user ${user.Uuid} has no 2fa secret to activate`);
|
|
85
|
+
}
|
|
86
|
+
user.Metadata[TWO_FA_METATADATA_KEYS.ENABLED] = true;
|
|
87
|
+
await user.Metadata.update();
|
|
88
|
+
this.Log.trace(`2fa activated for user ${user.Uuid}`, {
|
|
89
|
+
user: {
|
|
90
|
+
Uuid: user.Uuid
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Enrol and switch on in one step. This is what the CLI command, the
|
|
96
|
+
* administrator reset and user seeding use — they hand the secret to an
|
|
97
|
+
* operator rather than to the account owner, so there is nobody to confirm
|
|
98
|
+
* possession with.
|
|
99
|
+
*/
|
|
100
|
+
async initialize(user) {
|
|
101
|
+
const otp = await this.beginEnrolment(user);
|
|
102
|
+
await this.activate(user);
|
|
103
|
+
return otp;
|
|
104
|
+
}
|
|
83
105
|
async getOtpAuthUrl(user) {
|
|
84
106
|
const token = user.Metadata[TWO_FA_METATADATA_KEYS.TOKEN];
|
|
85
107
|
if (!token) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Default2FaToken.js","sourceRoot":"","sources":["../../../src/2fa/Default2FaToken.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAElE,MAAM,CAAN,IAAY,sBAIX;AAJD,WAAY,sBAAsB;IAC9B,6CAAmB,CAAA;IACnB,iDAAuB,CAAA;IACvB,yCAAe,CAAA;AACnB,CAAC,EAJW,sBAAsB,KAAtB,sBAAsB,QAIjC;AAGM,IAAM,eAAe,GAArB,MAAM,eAAgB,SAAQ,qBAAqB;IAOtD;QACI,KAAK,EAAE,CAAC;IACZ,CAAC;IAEO,OAAO,CAAC,IAAU,EAAE,MAAc;QACtC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;YACpB,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM;YAC1B,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,SAAS;YAChC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM;YAC1B,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM;YAC1B,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;SAC5C,CAAC,CAAC;IAEP,CAAC;IAEM,OAAO,CAAC,CAAO;QAClB,yDAAyD;QACzD,gCAAgC;QAChC,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC7B,CAAC;IAEM,KAAK,CAAC,WAAW,CAAC,KAAa,EAAE,IAAU;QAC9C,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC;QAE/D,IAAI,CAAC,UAAU,EAAE,CAAC;YACd,MAAM,IAAI,SAAS,CAAC,kDAAkD,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QACvF,CAAC;QAED,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;QAC5C,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;YAC3B,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM;SAC7B,CAAC,CAAC;QAEH,OAAO,QAAQ,KAAK,IAAI,CAAC;IAC7B,CAAC;IAEM,KAAK,CAAC,OAAO,CAAC,IAAU;QAE3B,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC;QACzD,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC;QAC3D,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,sBAAsB,CAAC,GAAG,CAAC,CAAC;QAGvD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,8BAA8B,IAAI,CAAC,IAAI,EAAE,EAAE;YACtD,IAAI,EAAE;gBACF,IAAI,EAAE,IAAI,CAAC,IAAI;aAClB;SACJ,CAAC,CAAC;IACP,CAAC;IAEM,KAAK,CAAC,
|
|
1
|
+
{"version":3,"file":"Default2FaToken.js","sourceRoot":"","sources":["../../../src/2fa/Default2FaToken.ts"],"names":[],"mappings":"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"}
|
package/lib/mjs/actions/2fa.d.ts
CHANGED
|
@@ -1,11 +1,30 @@
|
|
|
1
1
|
import { User } from '@spinajs/rbac';
|
|
2
2
|
/**
|
|
3
|
-
* NOTE for
|
|
4
|
-
*
|
|
3
|
+
* NOTE for enableUser2Fa: the event must be emitted for the USER, not for
|
|
4
|
+
* whatever the provider call returned ( `initialize()` returns the otpauth
|
|
5
5
|
* url ). `_user_ev` reads `Uuid` off its argument, so it is bound to `u`
|
|
6
|
-
* explicitly
|
|
6
|
+
* explicitly via `_tap`, which fires the event as a side effect while passing
|
|
7
|
+
* the provider result through untouched as the chain's result.
|
|
7
8
|
*/
|
|
8
9
|
export declare function enableUser2Fa(identifier: number | string | User): Promise<unknown>;
|
|
10
|
+
/**
|
|
11
|
+
* Hand the user a secret without switching 2fa on. No event is emitted: an
|
|
12
|
+
* enrolment nobody confirmed is not a fact about the account, and consumers
|
|
13
|
+
* listening for User2FaEnabled would otherwise fire on an abandoned attempt.
|
|
14
|
+
*/
|
|
15
|
+
export declare function beginUser2FaEnrolment(identifier: number | string | User): Promise<unknown>;
|
|
16
|
+
/**
|
|
17
|
+
* Switch 2fa on for an account whose code was already verified by the caller.
|
|
18
|
+
* The login-window flow needs this split: `auth2Fa` has already checked the
|
|
19
|
+
* code and emitted User2FaPassed by the time the session is authorized.
|
|
20
|
+
*/
|
|
21
|
+
export declare function activateUser2Fa(identifier: number | string | User): Promise<unknown>;
|
|
22
|
+
/**
|
|
23
|
+
* Complete an enrolment started by `beginUser2FaEnrolment`: verify the code
|
|
24
|
+
* against the stored secret, then switch 2fa on. Throws Unauthorized when the
|
|
25
|
+
* code does not match, leaving the enrolment pending so the user can retry.
|
|
26
|
+
*/
|
|
27
|
+
export declare function confirmUser2Fa(identifier: number | string | User, token: string): Promise<unknown>;
|
|
9
28
|
export declare function disableUser2Fa(identifier: number | string | User): Promise<unknown>;
|
|
10
29
|
export declare function resetUser2Fa(identifier: number | string | User): Promise<unknown>;
|
|
11
30
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"2fa.d.ts","sourceRoot":"","sources":["../../../src/actions/2fa.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAyD,MAAM,eAAe,CAAC;AAa5F
|
|
1
|
+
{"version":3,"file":"2fa.d.ts","sourceRoot":"","sources":["../../../src/actions/2fa.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAyD,MAAM,eAAe,CAAC;AAa5F;;;;;;GAMG;AAEH,wBAAsB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,oBAYrE;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,oBAU7E;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,oBAWvE;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,oBAqBrF;AAED,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,oBAWtE;AAED,wBAAsB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,oBAiBpE;AAED;;;;;;;GAOG;AACH,wBAAsB,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,MAAM,oBA8B9E"}
|
package/lib/mjs/actions/2fa.js
CHANGED
|
@@ -9,10 +9,11 @@ import { TwoFactorAuthProvider, } from '@spinajs/rbac-http';
|
|
|
9
9
|
import { User2FaReset } from '../events/User2FaReset.js';
|
|
10
10
|
import { User2FaDisabled } from '../events/User2FaDisabled.js';
|
|
11
11
|
/**
|
|
12
|
-
* NOTE for
|
|
13
|
-
*
|
|
12
|
+
* NOTE for enableUser2Fa: the event must be emitted for the USER, not for
|
|
13
|
+
* whatever the provider call returned ( `initialize()` returns the otpauth
|
|
14
14
|
* url ). `_user_ev` reads `Uuid` off its argument, so it is bound to `u`
|
|
15
|
-
* explicitly
|
|
15
|
+
* explicitly via `_tap`, which fires the event as a side effect while passing
|
|
16
|
+
* the provider result through untouched as the chain's result.
|
|
16
17
|
*/
|
|
17
18
|
export async function enableUser2Fa(identifier) {
|
|
18
19
|
return _chain(_user_unsafe(identifier), (u) => {
|
|
@@ -21,6 +22,43 @@ export async function enableUser2Fa(identifier) {
|
|
|
21
22
|
_tap(async () => _user_ev(User2FaEnabled)(u)));
|
|
22
23
|
});
|
|
23
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Hand the user a secret without switching 2fa on. No event is emitted: an
|
|
27
|
+
* enrolment nobody confirmed is not a fact about the account, and consumers
|
|
28
|
+
* listening for User2FaEnabled would otherwise fire on an abandoned attempt.
|
|
29
|
+
*/
|
|
30
|
+
export async function beginUser2FaEnrolment(identifier) {
|
|
31
|
+
return _chain(_user_unsafe(identifier), (u) => {
|
|
32
|
+
return _chain(_service('rbac.twoFactorAuth', TwoFactorAuthProvider), async (twoFa) => twoFa.beginEnrolment(u));
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Switch 2fa on for an account whose code was already verified by the caller.
|
|
37
|
+
* The login-window flow needs this split: `auth2Fa` has already checked the
|
|
38
|
+
* code and emitted User2FaPassed by the time the session is authorized.
|
|
39
|
+
*/
|
|
40
|
+
export async function activateUser2Fa(identifier) {
|
|
41
|
+
return _chain(_user_unsafe(identifier), (u) => {
|
|
42
|
+
return _chain(_service('rbac.twoFactorAuth', TwoFactorAuthProvider), async (twoFa) => twoFa.activate(u), () => _user_ev(User2FaEnabled)(u));
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Complete an enrolment started by `beginUser2FaEnrolment`: verify the code
|
|
47
|
+
* against the stored secret, then switch 2fa on. Throws Unauthorized when the
|
|
48
|
+
* code does not match, leaving the enrolment pending so the user can retry.
|
|
49
|
+
*/
|
|
50
|
+
export async function confirmUser2Fa(identifier, token) {
|
|
51
|
+
token = _check_arg(_trim(), _non_empty())(token, 'token');
|
|
52
|
+
return _chain(_user_unsafe(identifier), (u) => {
|
|
53
|
+
return _chain(_service('rbac.twoFactorAuth', TwoFactorAuthProvider), _either((twoFa) => twoFa.verifyToken(token, u),
|
|
54
|
+
// Delegates to `activateUser2Fa`, which re-resolves the provider
|
|
55
|
+
// itself. It is shared with the login-window flow, where the
|
|
56
|
+
// code is already verified by the time activation is needed.
|
|
57
|
+
() => activateUser2Fa(u), () => {
|
|
58
|
+
throw new Unauthorized('2fa confirmation failed');
|
|
59
|
+
}));
|
|
60
|
+
});
|
|
61
|
+
}
|
|
24
62
|
export async function disableUser2Fa(identifier) {
|
|
25
63
|
return _chain(_user_unsafe(identifier), (u) => {
|
|
26
64
|
return _chain(_service('rbac.twoFactorAuth', TwoFactorAuthProvider), async (twoFa) => twoFa.disable(u), () => _user_ev(User2FaDisabled)(u));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"2fa.js","sourceRoot":"","sources":["../../../src/actions/2fa.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC5F,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAmB,KAAK,EAAQ,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACpH,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,qBAAqB,GAAG,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAG/D
|
|
1
|
+
{"version":3,"file":"2fa.js","sourceRoot":"","sources":["../../../src/actions/2fa.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rbac-http.d.ts","sourceRoot":"","sources":["../../../src/config/rbac-http.ts"],"names":[],"mappings":"AAQA,QAAA,MAAM,QAAQ;IACZ,MAAM;QACJ,IAAI;YACF,WAAW;YACX,GAAG;;;IAGP,KAAK;QACH,OAAO;YACL,eAAe;gBAAI,UAAU;;YAC7B,cAAc;gBAAI,UAAU;;YAC5B,aAAa;gBAAI,UAAU;;YAC3B,YAAY;gBAAI,UAAU;;;;IAG9B,IAAI;QACF,OAAO;YACL;;eAEG;YACH,MAAM;YAEN;;eAEG;YACH,SAAS;YACT,MAAM;YACN,MAAM;YACN,MAAM;;QAER,aAAa;YACX,OAAO;
|
|
1
|
+
{"version":3,"file":"rbac-http.d.ts","sourceRoot":"","sources":["../../../src/config/rbac-http.ts"],"names":[],"mappings":"AAQA,QAAA,MAAM,QAAQ;IACZ,MAAM;QACJ,IAAI;YACF,WAAW;YACX,GAAG;;;IAGP,KAAK;QACH,OAAO;YACL,eAAe;gBAAI,UAAU;;YAC7B,cAAc;gBAAI,UAAU;;YAC5B,aAAa;gBAAI,UAAU;;YAC3B,YAAY;gBAAI,UAAU;;;;IAG9B,IAAI;QACF,OAAO;YACL;;eAEG;YACH,MAAM;YAEN;;eAEG;YACH,SAAS;YACT,MAAM;YACN,MAAM;YACN,MAAM;;QAER,aAAa;YACX,OAAO;YAQP,SAAS;YACT,OAAO;;QAMT,QAAQ;YAIN;;eAEG;YACH,kBAAkB;;;IAGtB,IAAI;CAKL,CAAC;eAEa,QAAQ"}
|
|
@@ -34,6 +34,12 @@ const rbacHttp = {
|
|
|
34
34
|
},
|
|
35
35
|
twoFactorAuth: {
|
|
36
36
|
enabled: true,
|
|
37
|
+
// `POST /user/2fa/reset` relies on this flag to recover an account that
|
|
38
|
+
// was left password-only after a reset was abandoned before the new
|
|
39
|
+
// secret was confirmed (see the docblock on `reset` in
|
|
40
|
+
// TwoFactorAuthUserController) — with it `false`, as it defaults here,
|
|
41
|
+
// that downgrade is silent and permanent for any consumer of this
|
|
42
|
+
// package that does not override it.
|
|
37
43
|
forceUser: false,
|
|
38
44
|
service: 'Default2FaToken',
|
|
39
45
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rbac-http.js","sourceRoot":"","sources":["../../../src/config/rbac-http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAEhD,SAAS,GAAG,CAAC,IAAY;IACvB,MAAM,UAAU,GAAG,OAAO,MAAM,KAAK,WAAW,CAAC;IACjD,OAAO,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,OAAO,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,UAAU,EAAE,gBAAgB,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;AAC3K,CAAC;AAGD,MAAM,QAAQ,GAAG;IACf,MAAM,EAAE;QACN,IAAI,EAAE;YACJ,WAAW,EAAE,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;YACjC,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;SAClB;KACF;IACD,KAAK,EAAE;QACL,OAAO,EAAE;YACP,eAAe,EAAE,EAAE,UAAU,EAAE,uBAAuB,EAAE;YACxD,cAAc,EAAE,EAAE,UAAU,EAAE,uBAAuB,EAAE;YACvD,aAAa,EAAE,EAAE,UAAU,EAAE,uBAAuB,EAAE;YACtD,YAAY,EAAE,EAAE,UAAU,EAAE,uBAAuB,EAAE;SACtD;KACF;IACD,IAAI,EAAE;QACJ,OAAO,EAAE;YACP;;eAEG;YACH,MAAM,EAAE,SAAS;YAEjB;;eAEG;YACH,SAAS,EAAE,MAAM;YACjB,MAAM,EAAE,CAAC;YACT,MAAM,EAAE,EAAE;YACV,MAAM,EAAE,CAAC;SACV;QACD,aAAa,EAAE;YACb,OAAO,EAAE,IAAI;
|
|
1
|
+
{"version":3,"file":"rbac-http.js","sourceRoot":"","sources":["../../../src/config/rbac-http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAEhD,SAAS,GAAG,CAAC,IAAY;IACvB,MAAM,UAAU,GAAG,OAAO,MAAM,KAAK,WAAW,CAAC;IACjD,OAAO,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,OAAO,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,UAAU,EAAE,gBAAgB,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;AAC3K,CAAC;AAGD,MAAM,QAAQ,GAAG;IACf,MAAM,EAAE;QACN,IAAI,EAAE;YACJ,WAAW,EAAE,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;YACjC,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;SAClB;KACF;IACD,KAAK,EAAE;QACL,OAAO,EAAE;YACP,eAAe,EAAE,EAAE,UAAU,EAAE,uBAAuB,EAAE;YACxD,cAAc,EAAE,EAAE,UAAU,EAAE,uBAAuB,EAAE;YACvD,aAAa,EAAE,EAAE,UAAU,EAAE,uBAAuB,EAAE;YACtD,YAAY,EAAE,EAAE,UAAU,EAAE,uBAAuB,EAAE;SACtD;KACF;IACD,IAAI,EAAE;QACJ,OAAO,EAAE;YACP;;eAEG;YACH,MAAM,EAAE,SAAS;YAEjB;;eAEG;YACH,SAAS,EAAE,MAAM;YACjB,MAAM,EAAE,CAAC;YACT,MAAM,EAAE,EAAE;YACV,MAAM,EAAE,CAAC;SACV;QACD,aAAa,EAAE;YACb,OAAO,EAAE,IAAI;YAEb,wEAAwE;YACxE,oEAAoE;YACpE,uDAAuD;YACvD,uEAAuE;YACvE,kEAAkE;YAClE,qCAAqC;YACrC,SAAS,EAAE,KAAK;YAChB,OAAO,EAAE,iBAAiB;SAC3B;QACD,sEAAsE;QACtE,0EAA0E;QAC1E,sEAAsE;QACtE,yCAAyC;QACzC,QAAQ,EAAE;YACR,qFAAqF;YACrF,4EAA4E;YAE5E;;eAEG;YACH,kBAAkB,EAAE,CAAC;SACtB;KACF;IACD,IAAI,EAAE;IACJ,iBAAiB;IACjB,+CAA+C;IAC/C,KAAK;KACN;CACF,CAAC;AAEF,eAAe,QAAQ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LoginController.d.ts","sourceRoot":"","sources":["../../../src/controllers/LoginController.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAwB,EAAE,EAAO,YAAY,EAAU,MAAM,eAAe,CAAC;AACpG,OAAO,EAAE,YAAY,EAAE,eAAe,EAAsB,aAAa,EAA0C,MAAM,eAAe,CAAC;AAGzI,OAAO,EAA6B,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAElF,OAAO,EAA0F,cAAc,EAAE,eAAe,EAAuB,MAAM,oBAAoB,CAAC;AAClL,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACrC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAKrE;;;;;GAKG;AACH,qBACa,eAAgB,SAAQ,cAAc;IAEjD,SAAS,CAAC,aAAa,EAAE,aAAa,CAAC;IAGvC,SAAS,CAAC,YAAY,EAAE,YAAY,CAAC;IAGrC,SAAS,CAAC,eAAe,EAAE,eAAe,CAAC;IAK3C,SAAS,CAAC,oBAAoB,EAAE,OAAO,CAAC;IAMxC,SAAS,CAAC,sBAAsB,EAAE,OAAO,CAAC;IAG1C,SAAS,CAAC,cAAc,EAAE,oBAAoB,CAAC;IAG/C,SAAS,CAAC,EAAE,EAAE,aAAa,CAAC;IAE5B;;;;;;;;;OASG;IAGU,KAAK,CAAiB,MAAM,EAAE,IAAI,EAAe,IAAI,EAAE,MAAM,EAAU,WAAW,EAAE,YAAY,GAAG,OAAO,CAAC,EAAE,CAAC,cAAc,CAAC,GAAG,YAAY,CAAC,
|
|
1
|
+
{"version":3,"file":"LoginController.d.ts","sourceRoot":"","sources":["../../../src/controllers/LoginController.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAwB,EAAE,EAAO,YAAY,EAAU,MAAM,eAAe,CAAC;AACpG,OAAO,EAAE,YAAY,EAAE,eAAe,EAAsB,aAAa,EAA0C,MAAM,eAAe,CAAC;AAGzI,OAAO,EAA6B,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAElF,OAAO,EAA0F,cAAc,EAAE,eAAe,EAAuB,MAAM,oBAAoB,CAAC;AAClL,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACrC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAKrE;;;;;GAKG;AACH,qBACa,eAAgB,SAAQ,cAAc;IAEjD,SAAS,CAAC,aAAa,EAAE,aAAa,CAAC;IAGvC,SAAS,CAAC,YAAY,EAAE,YAAY,CAAC;IAGrC,SAAS,CAAC,eAAe,EAAE,eAAe,CAAC;IAK3C,SAAS,CAAC,oBAAoB,EAAE,OAAO,CAAC;IAMxC,SAAS,CAAC,sBAAsB,EAAE,OAAO,CAAC;IAG1C,SAAS,CAAC,cAAc,EAAE,oBAAoB,CAAC;IAG/C,SAAS,CAAC,EAAE,EAAE,aAAa,CAAC;IAE5B;;;;;;;;;OASG;IAGU,KAAK,CAAiB,MAAM,EAAE,IAAI,EAAe,IAAI,EAAE,MAAM,EAAU,WAAW,EAAE,YAAY,GAAG,OAAO,CAAC,EAAE,CAAC,cAAc,CAAC,GAAG,YAAY,CAAC,CAqHzJ;IAED;;;;;;;;;;;;OAYG;IAGU,MAAM,CAAc,IAAI,EAAE,MAAM,EAAqB,OAAO,EAAE,QAAQ,EAAkB,IAAI,EAAE,IAAI,oBA8B9G;IAED;;;;;;;;;OASG;IAGU,MAAM,CAAiB,IAAI,EAAE,IAAI,EAAiB,UAAU,EAAE,MAAM,EAAqB,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,EAAE,CAAC,eAAe,CAAC,CAAC,CAmBrJ;CACF"}
|
|
@@ -63,7 +63,16 @@ let LoginController = class LoginController extends BaseController {
|
|
|
63
63
|
session.Data.set('Logged', true);
|
|
64
64
|
session.UserId = user.Id;
|
|
65
65
|
const twoFaEnabledForUser = Boolean(user.Metadata[TWO_FA_METATADATA_KEYS.ENABLED]);
|
|
66
|
-
|
|
66
|
+
// `forceUser` alone used to be enough to park the user in
|
|
67
|
+
// TwoFactorInitRequired. But every mutating `/user/2fa*` and
|
|
68
|
+
// `/auth/2fa/setup` route is now gated by `TwoFactorAuthEnabled` and
|
|
69
|
+
// answers 403 when `rbac.twoFactorAuth.enabled` is off — so a user forced into
|
|
70
|
+
// this branch while the system switch is off would have no route back
|
|
71
|
+
// out: setup2fa can no longer both enrol AND activate them. The
|
|
72
|
+
// system-wide switch has to gate this branch too, or every user without
|
|
73
|
+
// 2FA is locked out of the product the moment forceUser is on and the
|
|
74
|
+
// switch is off.
|
|
75
|
+
if (this.TwoFactorAuthEnabled && this.TwoFactorAuthForceUser && !twoFaEnabledForUser) {
|
|
67
76
|
this._log.trace('User logged in, 2fa init required', {
|
|
68
77
|
Uuid: user.Uuid
|
|
69
78
|
});
|