@adminforth/two-factors-auth 1.10.7 → 1.11.1

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/dist/index.js CHANGED
@@ -19,30 +19,48 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
19
19
  instanceUniqueRepresentation(pluginOptions) {
20
20
  return `single`;
21
21
  }
22
- verify(code, opts) {
22
+ verify(confirmationResult, opts) {
23
23
  return __awaiter(this, void 0, void 0, function* () {
24
24
  var _a, _b, _c, _d;
25
- if (!code)
26
- return { error: "Code is required" };
27
- const authRes = this.adminforth.config.resources
28
- .find(r => r.resourceId === this.adminforth.config.auth.usersResourceId);
29
- if (!authRes)
30
- return { error: "Auth resource not found" };
31
- const connector = this.adminforth.connectors[authRes.dataSource];
32
- const pkName = (_a = authRes.columns.find(c => c.primaryKey)) === null || _a === void 0 ? void 0 : _a.name;
33
- if (!pkName)
34
- return { error: "Primary key not found on auth resource" };
35
- const pk = (_b = opts === null || opts === void 0 ? void 0 : opts.userPk) !== null && _b !== void 0 ? _b : (_d = (_c = opts === null || opts === void 0 ? void 0 : opts.adminUser) === null || _c === void 0 ? void 0 : _c.dbUser) === null || _d === void 0 ? void 0 : _d[pkName];
36
- if (!pk)
37
- return { error: "User PK is required" };
38
- const user = yield connector.getRecordByPrimaryKey(authRes, pk);
39
- if (!user)
40
- return { error: "User not found" };
41
- const secret = user[this.options.twoFaSecretFieldName];
42
- if (!secret)
43
- return { error: "2FA is not set up for this user" };
44
- const verified = twofactor.verifyToken(secret, code, this.options.timeStepWindow);
45
- return verified ? { ok: true } : { error: "Wrong or expired OTP code" };
25
+ if (!confirmationResult)
26
+ return { error: "Confirmation result is required" };
27
+ if (confirmationResult.mode === "totp") {
28
+ const code = confirmationResult.result;
29
+ const authRes = this.adminforth.config.resources
30
+ .find(r => r.resourceId === this.adminforth.config.auth.usersResourceId);
31
+ if (!authRes)
32
+ return { error: "Auth resource not found" };
33
+ const connector = this.adminforth.connectors[authRes.dataSource];
34
+ const pkName = (_a = authRes.columns.find(c => c.primaryKey)) === null || _a === void 0 ? void 0 : _a.name;
35
+ if (!pkName)
36
+ return { error: "Primary key not found on auth resource" };
37
+ const pk = (_b = opts === null || opts === void 0 ? void 0 : opts.userPk) !== null && _b !== void 0 ? _b : (_d = (_c = opts === null || opts === void 0 ? void 0 : opts.adminUser) === null || _c === void 0 ? void 0 : _c.dbUser) === null || _d === void 0 ? void 0 : _d[pkName];
38
+ if (!pk)
39
+ return { error: "User PK is required" };
40
+ const user = yield connector.getRecordByPrimaryKey(authRes, pk);
41
+ if (!user)
42
+ return { error: "User not found" };
43
+ const secret = user[this.options.twoFaSecretFieldName];
44
+ if (!secret)
45
+ return { error: "2FA is not set up for this user" };
46
+ const verified = twofactor.verifyToken(secret, code, this.options.timeStepWindow);
47
+ return verified ? { ok: true } : { error: "Wrong or expired OTP code" };
48
+ }
49
+ else if (confirmationResult.mode === "passkey") {
50
+ const passkeysCookies = this.adminforth.auth.getCustomCookie({ cookies: opts.cookies, name: `passkeyLoginTemporaryJWT` });
51
+ if (!passkeysCookies) {
52
+ return { error: 'Passkey token is required' };
53
+ }
54
+ const decodedPasskeysCookies = yield this.adminforth.auth.verify(passkeysCookies, 'tempLoginPasskeyChallenge', false);
55
+ if (!decodedPasskeysCookies) {
56
+ return { error: 'Invalid passkey' };
57
+ }
58
+ const verificationResult = yield this.verifyPasskeyResponse(confirmationResult.result, opts.userPk, decodedPasskeysCookies);
59
+ if (verificationResult.ok && verificationResult.passkeyConfirmed) {
60
+ return { ok: true };
61
+ }
62
+ return { error: "Invalid passkey" };
63
+ }
46
64
  });
47
65
  }
48
66
  parsePeriod(period) {
@@ -326,7 +344,7 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
326
344
  if (!passkeysCookies) {
327
345
  return { error: 'Passkey token is required' };
328
346
  }
329
- const decodedPasskeysCookies = yield this.adminforth.auth.verify(passkeysCookies, 'tempPasskeyChallenge', false);
347
+ const decodedPasskeysCookies = yield this.adminforth.auth.verify(passkeysCookies, 'tempLoginPasskeyChallenge', false);
330
348
  if (!decodedPasskeysCookies) {
331
349
  return { error: 'Invalid passkey' };
332
350
  }
@@ -400,9 +418,18 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
400
418
  method: 'POST',
401
419
  path: `/plugin/passkeys/registerPasskeyRequest`,
402
420
  noAuth: false,
403
- handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response }) {
421
+ handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response, cookies }) {
404
422
  var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
405
423
  const mode = body === null || body === void 0 ? void 0 : body.mode;
424
+ const confirmationResult = body === null || body === void 0 ? void 0 : body.confirmationResult;
425
+ const verificationResult = yield this.verify(confirmationResult, {
426
+ adminUser: adminUser,
427
+ userPk: adminUser.pk,
428
+ cookies: cookies
429
+ });
430
+ if (!verificationResult || !('ok' in verificationResult)) {
431
+ return { ok: false, error: 'Verification failed' };
432
+ }
406
433
  const settingsOrigin = (_b = this.options.passkeys) === null || _b === void 0 ? void 0 : _b.settings.expectedOrigin;
407
434
  const rp = {
408
435
  name: ((_c = this.options.passkeys) === null || _c === void 0 ? void 0 : _c.settings.rp.name) || this.adminforth.config.customization.brandName,
@@ -530,7 +557,7 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
530
557
  rpID: (_b = this.options.passkeys) === null || _b === void 0 ? void 0 : _b.settings.rp.id,
531
558
  userVerification: ((_c = this.options.passkeys) === null || _c === void 0 ? void 0 : _c.settings.authenticatorSelection.userVerification) || "required"
532
559
  });
533
- const value = this.adminforth.auth.issueJWT({ "challenge": options.challenge }, 'tempPasskeyChallenge', '10m');
560
+ const value = this.adminforth.auth.issueJWT({ "challenge": options.challenge }, 'tempLoginPasskeyChallenge', '10m');
534
561
  this.adminforth.auth.setCustomCookie({ response, payload: { name: `passkeyLoginTemporaryJWT`, value: value, expiry: undefined, expirySeconds: 10 * 60, httpOnly: true } });
535
562
  return { ok: true, data: options };
536
563
  }
package/index.ts CHANGED
@@ -27,31 +27,50 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
27
27
  }
28
28
 
29
29
  public async verify(
30
- code: string,
31
- opts?: { adminUser?: AdminUser; userPk?: string }
30
+ confirmationResult: Record<string, any>,
31
+ opts?: { adminUser?: AdminUser; userPk?: string; cookies?: any }
32
32
  ): Promise<{ ok: true } | { error: string }> {
33
- if (!code) return { error: "Code is required" };
33
+ if (!confirmationResult) return { error: "Confirmation result is required" };
34
34
 
35
- const authRes = this.adminforth.config.resources
36
- .find(r => r.resourceId === this.adminforth.config.auth.usersResourceId);
35
+ if (confirmationResult.mode === "totp") {
36
+ const code = confirmationResult.result;
37
+ const authRes = this.adminforth.config.resources
38
+ .find(r => r.resourceId === this.adminforth.config.auth.usersResourceId);
37
39
 
38
- if (!authRes) return { error: "Auth resource not found" };
40
+ if (!authRes) return { error: "Auth resource not found" };
39
41
 
40
- const connector = this.adminforth.connectors[authRes.dataSource];
41
- const pkName = authRes.columns.find(c => c.primaryKey)?.name;
42
- if (!pkName) return { error: "Primary key not found on auth resource" };
42
+ const connector = this.adminforth.connectors[authRes.dataSource];
43
+ const pkName = authRes.columns.find(c => c.primaryKey)?.name;
44
+ if (!pkName) return { error: "Primary key not found on auth resource" };
43
45
 
44
- const pk = opts?.userPk ?? opts?.adminUser?.dbUser?.[pkName];
45
- if (!pk) return { error: "User PK is required" };
46
+ const pk = opts?.userPk ?? opts?.adminUser?.dbUser?.[pkName];
47
+ if (!pk) return { error: "User PK is required" };
46
48
 
47
- const user = await connector.getRecordByPrimaryKey(authRes, pk);
48
- if (!user) return { error: "User not found" };
49
+ const user = await connector.getRecordByPrimaryKey(authRes, pk);
50
+ if (!user) return { error: "User not found" };
49
51
 
50
- const secret = user[this.options.twoFaSecretFieldName];
51
- if (!secret) return { error: "2FA is not set up for this user" };
52
+ const secret = user[this.options.twoFaSecretFieldName];
53
+ if (!secret) return { error: "2FA is not set up for this user" };
52
54
 
53
- const verified = twofactor.verifyToken(secret, code, this.options.timeStepWindow);
54
- return verified ? { ok: true } : { error: "Wrong or expired OTP code" };
55
+ const verified = twofactor.verifyToken(secret, code, this.options.timeStepWindow);
56
+ return verified ? { ok: true } : { error: "Wrong or expired OTP code" };
57
+ } else if (confirmationResult.mode === "passkey") {
58
+ const passkeysCookies = this.adminforth.auth.getCustomCookie({cookies: opts.cookies, name: `passkeyLoginTemporaryJWT`});
59
+ if (!passkeysCookies) {
60
+ return { error: 'Passkey token is required' };
61
+ }
62
+
63
+ const decodedPasskeysCookies = await this.adminforth.auth.verify(passkeysCookies, 'tempLoginPasskeyChallenge', false);
64
+ if (!decodedPasskeysCookies) {
65
+ return { error: 'Invalid passkey' };
66
+ }
67
+ const verificationResult = await this.verifyPasskeyResponse(confirmationResult.result, opts.userPk, decodedPasskeysCookies );
68
+
69
+ if (verificationResult.ok && verificationResult.passkeyConfirmed) {
70
+ return { ok: true }
71
+ }
72
+ return { error: "Invalid passkey" }
73
+ }
55
74
  }
56
75
 
57
76
  public parsePeriod(period?: string): number {
@@ -365,7 +384,7 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
365
384
  if (!passkeysCookies) {
366
385
  return { error: 'Passkey token is required' };
367
386
  }
368
- const decodedPasskeysCookies = await this.adminforth.auth.verify(passkeysCookies, 'tempPasskeyChallenge', false);
387
+ const decodedPasskeysCookies = await this.adminforth.auth.verify(passkeysCookies, 'tempLoginPasskeyChallenge', false);
369
388
  if (!decodedPasskeysCookies) {
370
389
  return { error: 'Invalid passkey' };
371
390
  }
@@ -440,8 +459,19 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
440
459
  method: 'POST',
441
460
  path: `/plugin/passkeys/registerPasskeyRequest`,
442
461
  noAuth: false,
443
- handler: async ({ body, adminUser, response }) => {
462
+ handler: async ({ body, adminUser, response, cookies }) => {
444
463
  const mode = body?.mode;
464
+
465
+ const confirmationResult = body?.confirmationResult;
466
+ const verificationResult = await this.verify(confirmationResult, {
467
+ adminUser: adminUser,
468
+ userPk: adminUser.pk,
469
+ cookies: cookies
470
+ });
471
+ if ( !verificationResult || !('ok' in verificationResult) ) {
472
+ return { ok: false, error: 'Verification failed' };
473
+ }
474
+
445
475
  const settingsOrigin = this.options.passkeys?.settings.expectedOrigin;
446
476
  const rp = {
447
477
  name: this.options.passkeys?.settings.rp.name || this.adminforth.config.customization.brandName,
@@ -574,7 +604,7 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
574
604
  rpID: this.options.passkeys?.settings.rp.id,
575
605
  userVerification: this.options.passkeys?.settings.authenticatorSelection.userVerification || "required"
576
606
  });
577
- const value = this.adminforth.auth.issueJWT({ "challenge": options.challenge }, 'tempPasskeyChallenge', '10m');
607
+ const value = this.adminforth.auth.issueJWT({ "challenge": options.challenge }, 'tempLoginPasskeyChallenge', '10m');
578
608
  this.adminforth.auth.setCustomCookie({response, payload: {name: `passkeyLoginTemporaryJWT`, value: value, expiry: undefined, expirySeconds: 10 * 60, httpOnly: true}});
579
609
  return { ok: true, data: options };
580
610
  } catch (e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adminforth/two-factors-auth",
3
- "version": "1.10.7",
3
+ "version": "1.11.1",
4
4
  "description": "AdminForth plugin for two factors authentication",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",