@adminforth/two-factors-auth 1.10.4 → 1.10.5
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 +43 -11
- package/index.ts +56 -12
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
-
import { AdminForthPlugin, Filters } from "adminforth";
|
|
10
|
+
import { AdminForthPlugin, Filters, suggestIfTypo } from "adminforth";
|
|
11
11
|
import twofactor from 'node-2fa';
|
|
12
12
|
import { generateRegistrationOptions, verifyRegistrationResponse, generateAuthenticationOptions, verifyAuthenticationResponse } from '@simplewebauthn/server';
|
|
13
13
|
import { isoUint8Array, isoBase64URL } from '@simplewebauthn/server/helpers';
|
|
@@ -161,9 +161,23 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
161
161
|
if (!this.options.passkeys.credentialIdFieldName) {
|
|
162
162
|
throw new Error('Passkeys credentialIdFieldName is required');
|
|
163
163
|
}
|
|
164
|
+
const credentialResource = adminforth.config.resources.find(r => r.resourceId === this.options.passkeys.credentialResourceID);
|
|
165
|
+
const credentialIDField = credentialResource.columns.find(c => c.name === this.options.passkeys.credentialIdFieldName);
|
|
166
|
+
if (!credentialIDField) {
|
|
167
|
+
const similar = suggestIfTypo(credentialResource.columns.map(c => c.name), this.options.passkeys.credentialIdFieldName);
|
|
168
|
+
throw new Error(`Passkeys credentialIdFieldName '${this.options.passkeys.credentialIdFieldName}' not found in resource '${this.options.passkeys.credentialResourceID}'. ${similar ? `Did you mean '${similar}'?` : ''}`);
|
|
169
|
+
}
|
|
170
|
+
credentialIDField.backendOnly = true;
|
|
164
171
|
if (!this.options.passkeys.credentialMetaFieldName) {
|
|
165
172
|
throw new Error('Passkeys credentialMetaFieldName is required');
|
|
166
173
|
}
|
|
174
|
+
const metaResource = adminforth.config.resources.find(r => r.resourceId === this.options.passkeys.credentialMetaFieldName);
|
|
175
|
+
const metaField = credentialResource.columns.find(c => c.name === this.options.passkeys.credentialMetaFieldName);
|
|
176
|
+
if (!metaField) {
|
|
177
|
+
const similar = suggestIfTypo(metaResource.columns.map(c => c.name), this.options.passkeys.credentialMetaFieldName);
|
|
178
|
+
throw new Error(`Passkeys credentialMetaFieldName '${this.options.passkeys.credentialMetaFieldName}' not found in resource '${this.options.passkeys.credentialMetaFieldName}'. ${similar ? `Did you mean '${similar}'?` : ''}`);
|
|
179
|
+
}
|
|
180
|
+
metaField.backendOnly = true;
|
|
167
181
|
if (!this.options.passkeys.credentialUserIdFieldName) {
|
|
168
182
|
throw new Error('Passkeys credentialUserIdFieldName is required');
|
|
169
183
|
}
|
|
@@ -429,7 +443,7 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
429
443
|
userVerification: ((_m = this.options.passkeys) === null || _m === void 0 ? void 0 : _m.settings.authenticatorSelection.userVerification) || "required"
|
|
430
444
|
},
|
|
431
445
|
});
|
|
432
|
-
const value = this.adminforth.auth.issueJWT({ "challenge": options.challenge, "user_id": adminUser.pk }, '
|
|
446
|
+
const value = this.adminforth.auth.issueJWT({ "challenge": options.challenge, "user_id": adminUser.pk }, 'registerTempPasskeyChallenge', '10m');
|
|
433
447
|
this.adminforth.auth.setCustomCookie({ response, payload: { name: "registerPasskeyTemporaryJWT", value: value, expiry: undefined, expirySeconds: 10 * 60, httpOnly: true } });
|
|
434
448
|
return { ok: true, data: options };
|
|
435
449
|
})
|
|
@@ -444,7 +458,7 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
444
458
|
if (!passkeysCookies) {
|
|
445
459
|
return { error: 'Passkey token is required' };
|
|
446
460
|
}
|
|
447
|
-
const decodedPasskeysCookies = yield this.adminforth.auth.verify(passkeysCookies, '
|
|
461
|
+
const decodedPasskeysCookies = yield this.adminforth.auth.verify(passkeysCookies, 'registerTempPasskeyChallenge', false);
|
|
448
462
|
if (!decodedPasskeysCookies) {
|
|
449
463
|
return { error: 'Invalid passkey token' };
|
|
450
464
|
}
|
|
@@ -556,11 +570,11 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
556
570
|
path: `/plugin/passkeys/deletePasskey`,
|
|
557
571
|
noAuth: false,
|
|
558
572
|
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser }) {
|
|
559
|
-
const
|
|
560
|
-
if (!
|
|
573
|
+
const credentialID = body.passkeyId;
|
|
574
|
+
if (!credentialID) {
|
|
561
575
|
return { ok: false, error: 'Passkey ID is required' };
|
|
562
576
|
}
|
|
563
|
-
const passkeyRecord = yield this.adminforth.resource(this.options.passkeys.credentialResourceID).get([Filters.EQ(this.options.passkeys.credentialIdFieldName,
|
|
577
|
+
const passkeyRecord = yield this.adminforth.resource(this.options.passkeys.credentialResourceID).get([Filters.EQ(this.options.passkeys.credentialIdFieldName, credentialID), Filters.EQ(this.options.passkeys.credentialUserIdFieldName, adminUser.pk)]);
|
|
564
578
|
if (!passkeyRecord) {
|
|
565
579
|
return { ok: false, error: 'Passkey not found' };
|
|
566
580
|
}
|
|
@@ -569,9 +583,18 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
569
583
|
if (!credResource) {
|
|
570
584
|
throw new Error('Credential resource not found.');
|
|
571
585
|
}
|
|
586
|
+
const credResourcePKColumn = credResource.columns.find(c => c.primaryKey);
|
|
587
|
+
if (!credResourcePKColumn) {
|
|
588
|
+
throw new Error('Credential resource primary key not found.');
|
|
589
|
+
}
|
|
590
|
+
const credResourcePKName = credResourcePKColumn.name;
|
|
591
|
+
const passkeyRecord = yield this.adminforth.resource(this.options.passkeys.credentialResourceID).get([Filters.EQ(this.options.passkeys.credentialIdFieldName, credentialID), Filters.EQ(this.options.passkeys.credentialUserIdFieldName, adminUser.pk)]);
|
|
592
|
+
if (!passkeyRecord) {
|
|
593
|
+
return { ok: false, error: 'Passkey not found' };
|
|
594
|
+
}
|
|
572
595
|
yield this.adminforth.deleteResourceRecord({
|
|
573
596
|
resource: credResource,
|
|
574
|
-
recordId:
|
|
597
|
+
recordId: passkeyRecord[credResourcePKName],
|
|
575
598
|
record: passkeyRecord,
|
|
576
599
|
adminUser: adminUser,
|
|
577
600
|
});
|
|
@@ -587,15 +610,24 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
587
610
|
path: `/plugin/passkeys/renamePasskey`,
|
|
588
611
|
noAuth: false,
|
|
589
612
|
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser }) {
|
|
590
|
-
const
|
|
613
|
+
const credentialID = body.passkeyId;
|
|
591
614
|
const newName = body.newName;
|
|
592
|
-
if (!
|
|
615
|
+
if (!credentialID) {
|
|
593
616
|
return { ok: false, error: 'Passkey ID is required' };
|
|
594
617
|
}
|
|
595
618
|
if (!newName) {
|
|
596
619
|
return { ok: false, error: 'New name is required' };
|
|
597
620
|
}
|
|
598
|
-
const
|
|
621
|
+
const credResource = this.adminforth.config.resources.find(r => r.resourceId === this.options.passkeys.credentialResourceID);
|
|
622
|
+
if (!credResource) {
|
|
623
|
+
throw new Error('Credential resource not found.');
|
|
624
|
+
}
|
|
625
|
+
const credResourcePKColumn = credResource.columns.find(c => c.primaryKey);
|
|
626
|
+
if (!credResourcePKColumn) {
|
|
627
|
+
throw new Error('Credential resource primary key not found.');
|
|
628
|
+
}
|
|
629
|
+
const credResourcePKName = credResourcePKColumn.name;
|
|
630
|
+
const passkeyRecord = yield this.adminforth.resource(this.options.passkeys.credentialResourceID).get([Filters.EQ(this.options.passkeys.credentialIdFieldName, credentialID), Filters.EQ(this.options.passkeys.credentialUserIdFieldName, adminUser.pk)]);
|
|
599
631
|
if (!passkeyRecord) {
|
|
600
632
|
return { ok: false, error: 'Passkey not found' };
|
|
601
633
|
}
|
|
@@ -609,7 +641,7 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
609
641
|
}
|
|
610
642
|
yield this.adminforth.updateResourceRecord({
|
|
611
643
|
resource: credResource,
|
|
612
|
-
recordId:
|
|
644
|
+
recordId: passkeyRecord[credResourcePKName],
|
|
613
645
|
oldRecord: passkeyRecord,
|
|
614
646
|
record: newRecord,
|
|
615
647
|
adminUser: adminUser
|
package/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AdminForthPlugin, Filters } from "adminforth";
|
|
1
|
+
import { AdminForthPlugin, Filters, suggestIfTypo } from "adminforth";
|
|
2
2
|
import type { AdminForthResource, AdminUser, IAdminForth, IHttpServer, IAdminForthAuth, BeforeLoginConfirmationFunction, IAdminForthHttpResponse } from "adminforth";
|
|
3
3
|
import twofactor from 'node-2fa';
|
|
4
4
|
import { PluginOptions } from "./types.js"
|
|
@@ -173,9 +173,36 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
173
173
|
if (!this.options.passkeys.credentialIdFieldName) {
|
|
174
174
|
throw new Error('Passkeys credentialIdFieldName is required');
|
|
175
175
|
}
|
|
176
|
+
|
|
177
|
+
const credentialResource = adminforth.config.resources.find(r => r.resourceId === this.options.passkeys.credentialResourceID);
|
|
178
|
+
const credentialIDField = credentialResource.columns.find(c => c.name === this.options.passkeys.credentialIdFieldName);
|
|
179
|
+
if ( !credentialIDField ) {
|
|
180
|
+
const similar = suggestIfTypo(credentialResource.columns.map(c => c.name), this.options.passkeys.credentialIdFieldName);
|
|
181
|
+
throw new Error(
|
|
182
|
+
`Passkeys credentialIdFieldName '${this.options.passkeys.credentialIdFieldName}' not found in resource '${this.options.passkeys.credentialResourceID}'. ${
|
|
183
|
+
similar ? `Did you mean '${similar}'?` : ''
|
|
184
|
+
}`
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
credentialIDField.backendOnly = true;
|
|
188
|
+
|
|
176
189
|
if (!this.options.passkeys.credentialMetaFieldName) {
|
|
177
190
|
throw new Error('Passkeys credentialMetaFieldName is required');
|
|
178
191
|
}
|
|
192
|
+
|
|
193
|
+
const metaResource = adminforth.config.resources.find(r => r.resourceId === this.options.passkeys.credentialMetaFieldName);
|
|
194
|
+
const metaField = credentialResource.columns.find(c => c.name === this.options.passkeys.credentialMetaFieldName);
|
|
195
|
+
if ( !metaField ) {
|
|
196
|
+
const similar = suggestIfTypo(metaResource.columns.map(c => c.name), this.options.passkeys.credentialMetaFieldName);
|
|
197
|
+
throw new Error(
|
|
198
|
+
`Passkeys credentialMetaFieldName '${this.options.passkeys.credentialMetaFieldName}' not found in resource '${this.options.passkeys.credentialMetaFieldName}'. ${
|
|
199
|
+
similar ? `Did you mean '${similar}'?` : ''
|
|
200
|
+
}`
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
metaField.backendOnly = true;
|
|
204
|
+
|
|
205
|
+
|
|
179
206
|
if (!this.options.passkeys.credentialUserIdFieldName) {
|
|
180
207
|
throw new Error('Passkeys credentialUserIdFieldName is required');
|
|
181
208
|
}
|
|
@@ -455,7 +482,7 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
455
482
|
userVerification: this.options.passkeys?.settings.authenticatorSelection.userVerification || "required"
|
|
456
483
|
},
|
|
457
484
|
});
|
|
458
|
-
const value = this.adminforth.auth.issueJWT({ "challenge": options.challenge, "user_id": adminUser.pk }, '
|
|
485
|
+
const value = this.adminforth.auth.issueJWT({ "challenge": options.challenge, "user_id": adminUser.pk }, 'registerTempPasskeyChallenge', '10m');
|
|
459
486
|
this.adminforth.auth.setCustomCookie({response, payload: {name: "registerPasskeyTemporaryJWT", value: value, expiry: undefined, expirySeconds: 10 * 60, httpOnly: true}});
|
|
460
487
|
return { ok: true, data: options };
|
|
461
488
|
}
|
|
@@ -469,7 +496,7 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
469
496
|
if (!passkeysCookies) {
|
|
470
497
|
return { error: 'Passkey token is required' };
|
|
471
498
|
}
|
|
472
|
-
const decodedPasskeysCookies = await this.adminforth.auth.verify(passkeysCookies, '
|
|
499
|
+
const decodedPasskeysCookies = await this.adminforth.auth.verify(passkeysCookies, 'registerTempPasskeyChallenge', false);
|
|
473
500
|
if (!decodedPasskeysCookies) {
|
|
474
501
|
return { error: 'Invalid passkey token' };
|
|
475
502
|
}
|
|
@@ -584,12 +611,12 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
584
611
|
path: `/plugin/passkeys/deletePasskey`,
|
|
585
612
|
noAuth: false,
|
|
586
613
|
handler: async ({body, adminUser }) => {
|
|
587
|
-
const
|
|
588
|
-
if (!
|
|
614
|
+
const credentialID = body.passkeyId;
|
|
615
|
+
if (!credentialID) {
|
|
589
616
|
return { ok: false, error: 'Passkey ID is required' };
|
|
590
617
|
}
|
|
591
618
|
|
|
592
|
-
const passkeyRecord = await this.adminforth.resource(this.options.passkeys.credentialResourceID).get([Filters.EQ(this.options.passkeys.credentialIdFieldName,
|
|
619
|
+
const passkeyRecord = await this.adminforth.resource(this.options.passkeys.credentialResourceID).get([Filters.EQ(this.options.passkeys.credentialIdFieldName, credentialID), Filters.EQ(this.options.passkeys.credentialUserIdFieldName, adminUser.pk)]);
|
|
593
620
|
if (!passkeyRecord) {
|
|
594
621
|
return { ok: false, error: 'Passkey not found' };
|
|
595
622
|
}
|
|
@@ -598,9 +625,18 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
598
625
|
if (!credResource) {
|
|
599
626
|
throw new Error('Credential resource not found.');
|
|
600
627
|
}
|
|
628
|
+
const credResourcePKColumn = credResource.columns.find(c => c.primaryKey);
|
|
629
|
+
if (!credResourcePKColumn) {
|
|
630
|
+
throw new Error('Credential resource primary key not found.');
|
|
631
|
+
}
|
|
632
|
+
const credResourcePKName = credResourcePKColumn.name;
|
|
633
|
+
const passkeyRecord = await this.adminforth.resource(this.options.passkeys.credentialResourceID).get([Filters.EQ(this.options.passkeys.credentialIdFieldName, credentialID), Filters.EQ(this.options.passkeys.credentialUserIdFieldName, adminUser.pk)]);
|
|
634
|
+
if (!passkeyRecord) {
|
|
635
|
+
return { ok: false, error: 'Passkey not found' };
|
|
636
|
+
}
|
|
601
637
|
await this.adminforth.deleteResourceRecord({
|
|
602
638
|
resource: credResource,
|
|
603
|
-
recordId:
|
|
639
|
+
recordId: passkeyRecord[credResourcePKName],
|
|
604
640
|
record: passkeyRecord,
|
|
605
641
|
adminUser: adminUser,
|
|
606
642
|
});
|
|
@@ -616,16 +652,24 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
616
652
|
path: `/plugin/passkeys/renamePasskey`,
|
|
617
653
|
noAuth: false,
|
|
618
654
|
handler: async ({body, adminUser }) => {
|
|
619
|
-
const
|
|
655
|
+
const credentialID = body.passkeyId;
|
|
620
656
|
const newName = body.newName;
|
|
621
|
-
if (!
|
|
657
|
+
if (!credentialID) {
|
|
622
658
|
return { ok: false, error: 'Passkey ID is required' };
|
|
623
659
|
}
|
|
624
660
|
if (!newName) {
|
|
625
661
|
return { ok: false, error: 'New name is required' };
|
|
626
662
|
}
|
|
627
|
-
|
|
628
|
-
|
|
663
|
+
const credResource = this.adminforth.config.resources.find(r => r.resourceId === this.options.passkeys.credentialResourceID);
|
|
664
|
+
if (!credResource) {
|
|
665
|
+
throw new Error('Credential resource not found.');
|
|
666
|
+
}
|
|
667
|
+
const credResourcePKColumn = credResource.columns.find(c => c.primaryKey);
|
|
668
|
+
if (!credResourcePKColumn) {
|
|
669
|
+
throw new Error('Credential resource primary key not found.');
|
|
670
|
+
}
|
|
671
|
+
const credResourcePKName = credResourcePKColumn.name;
|
|
672
|
+
const passkeyRecord = await this.adminforth.resource(this.options.passkeys.credentialResourceID).get([Filters.EQ(this.options.passkeys.credentialIdFieldName, credentialID), Filters.EQ(this.options.passkeys.credentialUserIdFieldName, adminUser.pk)]);
|
|
629
673
|
if (!passkeyRecord) {
|
|
630
674
|
return { ok: false, error: 'Passkey not found' };
|
|
631
675
|
}
|
|
@@ -639,7 +683,7 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
639
683
|
}
|
|
640
684
|
await this.adminforth.updateResourceRecord({
|
|
641
685
|
resource: credResource,
|
|
642
|
-
recordId:
|
|
686
|
+
recordId: passkeyRecord[credResourcePKName],
|
|
643
687
|
oldRecord: passkeyRecord,
|
|
644
688
|
record: newRecord,
|
|
645
689
|
adminUser: adminUser
|