@adminforth/two-factors-auth 3.3.9 → 4.0.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/build.log +2 -2
- package/custom/TwoFAModal.vue +4 -4
- package/custom/TwoFactorsConfirmation.vue +9 -10
- package/custom/TwoFactorsPasskeysSettings.vue +10 -10
- package/custom/utils.js +7 -8
- package/dist/custom/TwoFAModal.vue +4 -4
- package/dist/custom/TwoFactorsConfirmation.vue +9 -10
- package/dist/custom/TwoFactorsPasskeysSettings.vue +10 -10
- package/dist/custom/utils.js +7 -8
- package/dist/endpoints/registerPasskeyEndpoints.d.ts +2 -0
- package/dist/endpoints/registerPasskeyEndpoints.js +59 -0
- package/dist/endpoints/registerTwoFaEndpoints.d.ts +2 -0
- package/dist/endpoints/registerTwoFaEndpoints.js +46 -0
- package/dist/handlers/createPasskeyHandlers.d.ts +48 -0
- package/dist/handlers/createPasskeyHandlers.js +99 -0
- package/dist/handlers/createTwoFaHandlers.d.ts +56 -0
- package/dist/handlers/createTwoFaHandlers.js +167 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +51 -858
- package/dist/repositories/passkeyRepository.d.ts +18 -0
- package/dist/repositories/passkeyRepository.js +89 -0
- package/dist/repositories/userRepository.d.ts +14 -0
- package/dist/repositories/userRepository.js +45 -0
- package/dist/services/cookieService.d.ts +52 -0
- package/dist/services/cookieService.js +144 -0
- package/dist/services/passkeyService.d.ts +84 -0
- package/dist/services/passkeyService.js +369 -0
- package/dist/services/totpService.d.ts +21 -0
- package/dist/services/totpService.js +43 -0
- package/dist/services/verifyMfaConfirmation.d.ts +30 -0
- package/dist/services/verifyMfaConfirmation.js +67 -0
- package/dist/types.d.ts +116 -0
- package/dist/utils/errors.d.ts +4 -0
- package/dist/utils/errors.js +9 -0
- package/dist/utils/parsePeriod.d.ts +1 -0
- package/dist/utils/parsePeriod.js +17 -0
- package/dist/utils/types.d.ts +13 -0
- package/dist/utils/types.js +1 -0
- package/endpoints/registerPasskeyEndpoints.ts +61 -0
- package/endpoints/registerTwoFaEndpoints.ts +45 -0
- package/handlers/createPasskeyHandlers.ts +103 -0
- package/handlers/createTwoFaHandlers.ts +172 -0
- package/index.ts +57 -886
- package/package.json +1 -1
- package/repositories/passkeyRepository.ts +78 -0
- package/repositories/userRepository.ts +42 -0
- package/services/cookieService.ts +165 -0
- package/services/passkeyService.ts +375 -0
- package/services/totpService.ts +37 -0
- package/services/verifyMfaConfirmation.ts +92 -0
- package/tsconfig.json +1 -1
- package/utils/errors.ts +13 -0
- package/utils/parsePeriod.ts +19 -0
- package/utils/types.ts +17 -0
package/build.log
CHANGED
|
@@ -16,5 +16,5 @@ custom/pnpm-lock.yaml
|
|
|
16
16
|
custom/tsconfig.json
|
|
17
17
|
custom/utils.js
|
|
18
18
|
|
|
19
|
-
sent 286,
|
|
20
|
-
total size is 285,
|
|
19
|
+
sent 286,243 bytes received 229 bytes 572,944.00 bytes/sec
|
|
20
|
+
total size is 285,337 speedup is 1.00
|
package/custom/TwoFAModal.vue
CHANGED
|
@@ -175,7 +175,7 @@
|
|
|
175
175
|
path: "/plugin/passkeys/resolveVerifyAuto",
|
|
176
176
|
body: { confirmationResult, sessionsIds: sessionsIdsToResolve.value }
|
|
177
177
|
});
|
|
178
|
-
if (!response.ok && response.
|
|
178
|
+
if (!response.ok && response.code === 'VERIFICATION_CANCELLED'){
|
|
179
179
|
alert({message: t('Verification session finished or cancelled.'), variant: 'warning'});
|
|
180
180
|
} else if (!response.ok) {
|
|
181
181
|
alert({message: response.error || t('Verification failed'), variant: 'danger'});
|
|
@@ -184,7 +184,7 @@
|
|
|
184
184
|
}
|
|
185
185
|
sessionsIdsToResolve.value = [];
|
|
186
186
|
} catch (error) {
|
|
187
|
-
console.error(
|
|
187
|
+
console.error('Error resolving automatic 2FA verification:', error);
|
|
188
188
|
}
|
|
189
189
|
allowAddNewSessions = true;
|
|
190
190
|
});
|
|
@@ -412,7 +412,7 @@
|
|
|
412
412
|
}
|
|
413
413
|
}
|
|
414
414
|
} catch (error) {
|
|
415
|
-
console.error(
|
|
415
|
+
console.error('Error checking passkeys:', error);
|
|
416
416
|
// Fallback to TOTP if there's an error
|
|
417
417
|
doesUserHavePasskeys.value = false;
|
|
418
418
|
modalMode.value = "totp";
|
|
@@ -474,7 +474,7 @@
|
|
|
474
474
|
return false;
|
|
475
475
|
}
|
|
476
476
|
} catch (error) {
|
|
477
|
-
console.error(
|
|
477
|
+
console.error('Error checking skip allow modal:', error);
|
|
478
478
|
return false;
|
|
479
479
|
}
|
|
480
480
|
}
|
|
@@ -309,7 +309,7 @@
|
|
|
309
309
|
}
|
|
310
310
|
});
|
|
311
311
|
} catch (error) {
|
|
312
|
-
console.error(
|
|
312
|
+
console.error('Error checking if user has passkeys:', error);
|
|
313
313
|
}
|
|
314
314
|
}
|
|
315
315
|
|
|
@@ -318,7 +318,7 @@
|
|
|
318
318
|
cancelPendingWebAuthn('button-pressed');
|
|
319
319
|
codeError.value = null;
|
|
320
320
|
isFetchingPasskey.value = true;
|
|
321
|
-
const signIn = await
|
|
321
|
+
const signIn = await createLoginOptions();
|
|
322
322
|
if (!signIn) {
|
|
323
323
|
isFetchingPasskey.value = false;
|
|
324
324
|
return;
|
|
@@ -347,15 +347,15 @@
|
|
|
347
347
|
isFetchingPasskey.value = false;
|
|
348
348
|
}
|
|
349
349
|
|
|
350
|
-
async function
|
|
350
|
+
async function createLoginOptions() {
|
|
351
351
|
let response;
|
|
352
352
|
try {
|
|
353
353
|
response = await callAdminForthApi({
|
|
354
|
-
path: `/plugin/passkeys/
|
|
354
|
+
path: `/plugin/passkeys/loginOptions`,
|
|
355
355
|
method: 'POST',
|
|
356
356
|
});
|
|
357
357
|
} catch (error) {
|
|
358
|
-
console.error(
|
|
358
|
+
console.error('Error creating sign-in request:', error);
|
|
359
359
|
return;
|
|
360
360
|
}
|
|
361
361
|
if (response.ok === true) {
|
|
@@ -388,22 +388,21 @@
|
|
|
388
388
|
});
|
|
389
389
|
return credential;
|
|
390
390
|
} catch (error) {
|
|
391
|
-
console.error(t('Error during authentication:', error));
|
|
392
391
|
// Handle specific concurrent/pending request error cases gracefully
|
|
393
392
|
const name = (error && (error.name || error.constructor?.name)) || '';
|
|
394
393
|
const message = (error && error.message) || '';
|
|
395
394
|
if (name === 'AbortError') {
|
|
396
395
|
// Aborted intentionally; no user-facing error needed
|
|
397
396
|
return null;
|
|
397
|
+
} else if (name === 'NotAllowedError') {
|
|
398
|
+
// User cancelled or ignored the browser passkey prompt.
|
|
399
|
+
return null;
|
|
398
400
|
} else if (name === 'InvalidStateError' || name === 'OperationError' || /pending/i.test(message)) {
|
|
399
401
|
adminforth.alert({ message: t('Another security prompt is already open. Please try again.'), variant: 'warning' });
|
|
400
402
|
codeError.value = t('A previous passkey attempt was still pending. Please try again.');
|
|
401
403
|
return null;
|
|
402
|
-
} else if (name === 'NotAllowedError') {
|
|
403
|
-
adminforth.alert({ message: t('The operation either timed out or was not allowed'), variant: 'danger' });
|
|
404
|
-
codeError.value = t('The operation either timed out or was not allowed.');
|
|
405
|
-
return null;
|
|
406
404
|
} else {
|
|
405
|
+
console.error('Error during authentication:', error);
|
|
407
406
|
adminforth.alert({message: t(`Error during authentication: ${error}`), variant: 'warning'});
|
|
408
407
|
codeError.value = t('Error during authentication.');
|
|
409
408
|
return null;
|
|
@@ -210,7 +210,7 @@
|
|
|
210
210
|
passkeys.value = response.data;
|
|
211
211
|
authenticatorAttachment.value = response.authenticatorAttachment;
|
|
212
212
|
} catch (error) {
|
|
213
|
-
console.error(
|
|
213
|
+
console.error('Error fetching passkeys:', error);
|
|
214
214
|
if (coreStore.adminUser?.username) {
|
|
215
215
|
adminforth.alert({ message: t('Error fetching passkeys.'), variant: 'warning' });
|
|
216
216
|
}
|
|
@@ -222,7 +222,7 @@
|
|
|
222
222
|
async function requestPasskeyChallenge(confirmationResult: ConfirmationResult) {
|
|
223
223
|
try {
|
|
224
224
|
const response = await callAdminForthApi({
|
|
225
|
-
path: '/plugin/passkeys/
|
|
225
|
+
path: '/plugin/passkeys/registrationOptions',
|
|
226
226
|
method: 'POST',
|
|
227
227
|
body: { mode: addPasskeyMode.value, confirmationResult },
|
|
228
228
|
});
|
|
@@ -230,7 +230,7 @@
|
|
|
230
230
|
const options = PublicKeyCredential.parseCreationOptionsFromJSON(response.data);
|
|
231
231
|
return { options, challengeId: response.challengeId };
|
|
232
232
|
} catch (error) {
|
|
233
|
-
console.error(
|
|
233
|
+
console.error('Error requesting passkey challenge:', error);
|
|
234
234
|
return { error: t('Failed to request passkey challenge' )};
|
|
235
235
|
}
|
|
236
236
|
}
|
|
@@ -240,7 +240,7 @@
|
|
|
240
240
|
const credential = await navigator.credentials.create({ publicKey: options });
|
|
241
241
|
return JSON.stringify((credential as PublicKeyCredential).toJSON());
|
|
242
242
|
} catch (error) {
|
|
243
|
-
console.error(
|
|
243
|
+
console.error('Error creating WebAuthn credential:', error);
|
|
244
244
|
|
|
245
245
|
let message = t('Failed to create passkey.');
|
|
246
246
|
|
|
@@ -265,7 +265,7 @@
|
|
|
265
265
|
async function registerPasskey(credential: string) {
|
|
266
266
|
try {
|
|
267
267
|
const res = await callAdminForthApi({
|
|
268
|
-
path: '/plugin/passkeys/
|
|
268
|
+
path: '/plugin/passkeys/finishRegistration',
|
|
269
269
|
method: 'POST',
|
|
270
270
|
body: { credential, origin: window.location.origin },
|
|
271
271
|
});
|
|
@@ -276,7 +276,7 @@
|
|
|
276
276
|
adminforth.alert({ message: t('Error registering passkey.'), variant: 'warning' });
|
|
277
277
|
}
|
|
278
278
|
} catch (error) {
|
|
279
|
-
console.error(
|
|
279
|
+
console.error('Error registering passkey:', error);
|
|
280
280
|
}
|
|
281
281
|
}
|
|
282
282
|
|
|
@@ -302,11 +302,11 @@
|
|
|
302
302
|
adminforth.alert({ message: t('Passkey deleted successfully!'), variant: 'success' });
|
|
303
303
|
getPasskeys();
|
|
304
304
|
} else {
|
|
305
|
-
console.error(
|
|
305
|
+
console.error('Error deleting passkey:', response?.error);
|
|
306
306
|
adminforth.alert({ message: t('Error deleting passkey.'), variant: 'warning' });
|
|
307
307
|
}
|
|
308
308
|
} catch (error) {
|
|
309
|
-
console.error(
|
|
309
|
+
console.error('Error deleting passkey:', error);
|
|
310
310
|
adminforth.alert({ message: t('Error deleting passkey.'), variant: 'warning' });
|
|
311
311
|
}
|
|
312
312
|
}
|
|
@@ -322,11 +322,11 @@
|
|
|
322
322
|
adminforth.alert({ message: t('Passkey updated successfully!'), variant: 'success' });
|
|
323
323
|
getPasskeys();
|
|
324
324
|
} else {
|
|
325
|
-
console.error(
|
|
325
|
+
console.error('Error updating passkey:', response.error);
|
|
326
326
|
adminforth.alert({ message: t('Error updating passkey.'), variant: 'warning' });
|
|
327
327
|
}
|
|
328
328
|
} catch (error) {
|
|
329
|
-
console.error(
|
|
329
|
+
console.error('Error updating passkey:', error);
|
|
330
330
|
adminforth.alert({ message: t('Error updating passkey.'), variant: 'warning' });
|
|
331
331
|
}
|
|
332
332
|
}
|
package/custom/utils.js
CHANGED
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
export async function getPasskey() {
|
|
60
|
-
const { _options } = await
|
|
60
|
+
const { _options } = await createLoginOptions();
|
|
61
61
|
let options;
|
|
62
62
|
try {
|
|
63
63
|
options = PublicKeyCredential.parseRequestOptionsFromJSON(_options);
|
|
@@ -79,11 +79,11 @@
|
|
|
79
79
|
return passkeyOptions;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
async function
|
|
82
|
+
async function createLoginOptions() {
|
|
83
83
|
let response;
|
|
84
84
|
try {
|
|
85
85
|
response = await callAdminForthApi({
|
|
86
|
-
path: `/plugin/passkeys/
|
|
86
|
+
path: `/plugin/passkeys/loginOptions`,
|
|
87
87
|
method: 'POST',
|
|
88
88
|
});
|
|
89
89
|
} catch (error) {
|
|
@@ -110,22 +110,21 @@
|
|
|
110
110
|
});
|
|
111
111
|
return credential;
|
|
112
112
|
} catch (error) {
|
|
113
|
-
console.error('Error during authentication:', error);
|
|
114
113
|
// Handle specific concurrent/pending request error cases gracefully
|
|
115
114
|
const name = (error && (error.name || error.constructor?.name)) || '';
|
|
116
115
|
const message = (error && error.message) || '';
|
|
117
116
|
if (name === 'AbortError') {
|
|
118
117
|
// Aborted intentionally; no user-facing error needed
|
|
119
118
|
return null;
|
|
119
|
+
} else if (name === 'NotAllowedError') {
|
|
120
|
+
// User cancelled or ignored the browser passkey prompt.
|
|
121
|
+
return null;
|
|
120
122
|
} else if (name === 'InvalidStateError' || name === 'OperationError' || /pending/i.test(message)) {
|
|
121
123
|
adminforth.alert({ message: t('Another security prompt is already open. Please try again.'), variant: 'warning' });
|
|
122
124
|
codeError.value = t('A previous passkey attempt was still pending. Please try again.');
|
|
123
125
|
return null;
|
|
124
|
-
} else if (name === 'NotAllowedError') {
|
|
125
|
-
adminforth.alert({ message: `The operation either timed out or was not allowed`, variant: 'danger' });
|
|
126
|
-
codeError.value = 'The operation either timed out or was not allowed.';
|
|
127
|
-
return null;
|
|
128
126
|
} else {
|
|
127
|
+
console.error('Error during authentication:', error);
|
|
129
128
|
adminforth.alert({message: `Error during authentication: ${error}`, variant: 'warning'});
|
|
130
129
|
codeError.value = 'Error during authentication.';
|
|
131
130
|
return null;
|
|
@@ -175,7 +175,7 @@
|
|
|
175
175
|
path: "/plugin/passkeys/resolveVerifyAuto",
|
|
176
176
|
body: { confirmationResult, sessionsIds: sessionsIdsToResolve.value }
|
|
177
177
|
});
|
|
178
|
-
if (!response.ok && response.
|
|
178
|
+
if (!response.ok && response.code === 'VERIFICATION_CANCELLED'){
|
|
179
179
|
alert({message: t('Verification session finished or cancelled.'), variant: 'warning'});
|
|
180
180
|
} else if (!response.ok) {
|
|
181
181
|
alert({message: response.error || t('Verification failed'), variant: 'danger'});
|
|
@@ -184,7 +184,7 @@
|
|
|
184
184
|
}
|
|
185
185
|
sessionsIdsToResolve.value = [];
|
|
186
186
|
} catch (error) {
|
|
187
|
-
console.error(
|
|
187
|
+
console.error('Error resolving automatic 2FA verification:', error);
|
|
188
188
|
}
|
|
189
189
|
allowAddNewSessions = true;
|
|
190
190
|
});
|
|
@@ -412,7 +412,7 @@
|
|
|
412
412
|
}
|
|
413
413
|
}
|
|
414
414
|
} catch (error) {
|
|
415
|
-
console.error(
|
|
415
|
+
console.error('Error checking passkeys:', error);
|
|
416
416
|
// Fallback to TOTP if there's an error
|
|
417
417
|
doesUserHavePasskeys.value = false;
|
|
418
418
|
modalMode.value = "totp";
|
|
@@ -474,7 +474,7 @@
|
|
|
474
474
|
return false;
|
|
475
475
|
}
|
|
476
476
|
} catch (error) {
|
|
477
|
-
console.error(
|
|
477
|
+
console.error('Error checking skip allow modal:', error);
|
|
478
478
|
return false;
|
|
479
479
|
}
|
|
480
480
|
}
|
|
@@ -309,7 +309,7 @@
|
|
|
309
309
|
}
|
|
310
310
|
});
|
|
311
311
|
} catch (error) {
|
|
312
|
-
console.error(
|
|
312
|
+
console.error('Error checking if user has passkeys:', error);
|
|
313
313
|
}
|
|
314
314
|
}
|
|
315
315
|
|
|
@@ -318,7 +318,7 @@
|
|
|
318
318
|
cancelPendingWebAuthn('button-pressed');
|
|
319
319
|
codeError.value = null;
|
|
320
320
|
isFetchingPasskey.value = true;
|
|
321
|
-
const signIn = await
|
|
321
|
+
const signIn = await createLoginOptions();
|
|
322
322
|
if (!signIn) {
|
|
323
323
|
isFetchingPasskey.value = false;
|
|
324
324
|
return;
|
|
@@ -347,15 +347,15 @@
|
|
|
347
347
|
isFetchingPasskey.value = false;
|
|
348
348
|
}
|
|
349
349
|
|
|
350
|
-
async function
|
|
350
|
+
async function createLoginOptions() {
|
|
351
351
|
let response;
|
|
352
352
|
try {
|
|
353
353
|
response = await callAdminForthApi({
|
|
354
|
-
path: `/plugin/passkeys/
|
|
354
|
+
path: `/plugin/passkeys/loginOptions`,
|
|
355
355
|
method: 'POST',
|
|
356
356
|
});
|
|
357
357
|
} catch (error) {
|
|
358
|
-
console.error(
|
|
358
|
+
console.error('Error creating sign-in request:', error);
|
|
359
359
|
return;
|
|
360
360
|
}
|
|
361
361
|
if (response.ok === true) {
|
|
@@ -388,22 +388,21 @@
|
|
|
388
388
|
});
|
|
389
389
|
return credential;
|
|
390
390
|
} catch (error) {
|
|
391
|
-
console.error(t('Error during authentication:', error));
|
|
392
391
|
// Handle specific concurrent/pending request error cases gracefully
|
|
393
392
|
const name = (error && (error.name || error.constructor?.name)) || '';
|
|
394
393
|
const message = (error && error.message) || '';
|
|
395
394
|
if (name === 'AbortError') {
|
|
396
395
|
// Aborted intentionally; no user-facing error needed
|
|
397
396
|
return null;
|
|
397
|
+
} else if (name === 'NotAllowedError') {
|
|
398
|
+
// User cancelled or ignored the browser passkey prompt.
|
|
399
|
+
return null;
|
|
398
400
|
} else if (name === 'InvalidStateError' || name === 'OperationError' || /pending/i.test(message)) {
|
|
399
401
|
adminforth.alert({ message: t('Another security prompt is already open. Please try again.'), variant: 'warning' });
|
|
400
402
|
codeError.value = t('A previous passkey attempt was still pending. Please try again.');
|
|
401
403
|
return null;
|
|
402
|
-
} else if (name === 'NotAllowedError') {
|
|
403
|
-
adminforth.alert({ message: t('The operation either timed out or was not allowed'), variant: 'danger' });
|
|
404
|
-
codeError.value = t('The operation either timed out or was not allowed.');
|
|
405
|
-
return null;
|
|
406
404
|
} else {
|
|
405
|
+
console.error('Error during authentication:', error);
|
|
407
406
|
adminforth.alert({message: t(`Error during authentication: ${error}`), variant: 'warning'});
|
|
408
407
|
codeError.value = t('Error during authentication.');
|
|
409
408
|
return null;
|
|
@@ -210,7 +210,7 @@
|
|
|
210
210
|
passkeys.value = response.data;
|
|
211
211
|
authenticatorAttachment.value = response.authenticatorAttachment;
|
|
212
212
|
} catch (error) {
|
|
213
|
-
console.error(
|
|
213
|
+
console.error('Error fetching passkeys:', error);
|
|
214
214
|
if (coreStore.adminUser?.username) {
|
|
215
215
|
adminforth.alert({ message: t('Error fetching passkeys.'), variant: 'warning' });
|
|
216
216
|
}
|
|
@@ -222,7 +222,7 @@
|
|
|
222
222
|
async function requestPasskeyChallenge(confirmationResult: ConfirmationResult) {
|
|
223
223
|
try {
|
|
224
224
|
const response = await callAdminForthApi({
|
|
225
|
-
path: '/plugin/passkeys/
|
|
225
|
+
path: '/plugin/passkeys/registrationOptions',
|
|
226
226
|
method: 'POST',
|
|
227
227
|
body: { mode: addPasskeyMode.value, confirmationResult },
|
|
228
228
|
});
|
|
@@ -230,7 +230,7 @@
|
|
|
230
230
|
const options = PublicKeyCredential.parseCreationOptionsFromJSON(response.data);
|
|
231
231
|
return { options, challengeId: response.challengeId };
|
|
232
232
|
} catch (error) {
|
|
233
|
-
console.error(
|
|
233
|
+
console.error('Error requesting passkey challenge:', error);
|
|
234
234
|
return { error: t('Failed to request passkey challenge' )};
|
|
235
235
|
}
|
|
236
236
|
}
|
|
@@ -240,7 +240,7 @@
|
|
|
240
240
|
const credential = await navigator.credentials.create({ publicKey: options });
|
|
241
241
|
return JSON.stringify((credential as PublicKeyCredential).toJSON());
|
|
242
242
|
} catch (error) {
|
|
243
|
-
console.error(
|
|
243
|
+
console.error('Error creating WebAuthn credential:', error);
|
|
244
244
|
|
|
245
245
|
let message = t('Failed to create passkey.');
|
|
246
246
|
|
|
@@ -265,7 +265,7 @@
|
|
|
265
265
|
async function registerPasskey(credential: string) {
|
|
266
266
|
try {
|
|
267
267
|
const res = await callAdminForthApi({
|
|
268
|
-
path: '/plugin/passkeys/
|
|
268
|
+
path: '/plugin/passkeys/finishRegistration',
|
|
269
269
|
method: 'POST',
|
|
270
270
|
body: { credential, origin: window.location.origin },
|
|
271
271
|
});
|
|
@@ -276,7 +276,7 @@
|
|
|
276
276
|
adminforth.alert({ message: t('Error registering passkey.'), variant: 'warning' });
|
|
277
277
|
}
|
|
278
278
|
} catch (error) {
|
|
279
|
-
console.error(
|
|
279
|
+
console.error('Error registering passkey:', error);
|
|
280
280
|
}
|
|
281
281
|
}
|
|
282
282
|
|
|
@@ -302,11 +302,11 @@
|
|
|
302
302
|
adminforth.alert({ message: t('Passkey deleted successfully!'), variant: 'success' });
|
|
303
303
|
getPasskeys();
|
|
304
304
|
} else {
|
|
305
|
-
console.error(
|
|
305
|
+
console.error('Error deleting passkey:', response?.error);
|
|
306
306
|
adminforth.alert({ message: t('Error deleting passkey.'), variant: 'warning' });
|
|
307
307
|
}
|
|
308
308
|
} catch (error) {
|
|
309
|
-
console.error(
|
|
309
|
+
console.error('Error deleting passkey:', error);
|
|
310
310
|
adminforth.alert({ message: t('Error deleting passkey.'), variant: 'warning' });
|
|
311
311
|
}
|
|
312
312
|
}
|
|
@@ -322,11 +322,11 @@
|
|
|
322
322
|
adminforth.alert({ message: t('Passkey updated successfully!'), variant: 'success' });
|
|
323
323
|
getPasskeys();
|
|
324
324
|
} else {
|
|
325
|
-
console.error(
|
|
325
|
+
console.error('Error updating passkey:', response.error);
|
|
326
326
|
adminforth.alert({ message: t('Error updating passkey.'), variant: 'warning' });
|
|
327
327
|
}
|
|
328
328
|
} catch (error) {
|
|
329
|
-
console.error(
|
|
329
|
+
console.error('Error updating passkey:', error);
|
|
330
330
|
adminforth.alert({ message: t('Error updating passkey.'), variant: 'warning' });
|
|
331
331
|
}
|
|
332
332
|
}
|
package/dist/custom/utils.js
CHANGED
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
export async function getPasskey() {
|
|
60
|
-
const { _options } = await
|
|
60
|
+
const { _options } = await createLoginOptions();
|
|
61
61
|
let options;
|
|
62
62
|
try {
|
|
63
63
|
options = PublicKeyCredential.parseRequestOptionsFromJSON(_options);
|
|
@@ -79,11 +79,11 @@
|
|
|
79
79
|
return passkeyOptions;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
async function
|
|
82
|
+
async function createLoginOptions() {
|
|
83
83
|
let response;
|
|
84
84
|
try {
|
|
85
85
|
response = await callAdminForthApi({
|
|
86
|
-
path: `/plugin/passkeys/
|
|
86
|
+
path: `/plugin/passkeys/loginOptions`,
|
|
87
87
|
method: 'POST',
|
|
88
88
|
});
|
|
89
89
|
} catch (error) {
|
|
@@ -110,22 +110,21 @@
|
|
|
110
110
|
});
|
|
111
111
|
return credential;
|
|
112
112
|
} catch (error) {
|
|
113
|
-
console.error('Error during authentication:', error);
|
|
114
113
|
// Handle specific concurrent/pending request error cases gracefully
|
|
115
114
|
const name = (error && (error.name || error.constructor?.name)) || '';
|
|
116
115
|
const message = (error && error.message) || '';
|
|
117
116
|
if (name === 'AbortError') {
|
|
118
117
|
// Aborted intentionally; no user-facing error needed
|
|
119
118
|
return null;
|
|
119
|
+
} else if (name === 'NotAllowedError') {
|
|
120
|
+
// User cancelled or ignored the browser passkey prompt.
|
|
121
|
+
return null;
|
|
120
122
|
} else if (name === 'InvalidStateError' || name === 'OperationError' || /pending/i.test(message)) {
|
|
121
123
|
adminforth.alert({ message: t('Another security prompt is already open. Please try again.'), variant: 'warning' });
|
|
122
124
|
codeError.value = t('A previous passkey attempt was still pending. Please try again.');
|
|
123
125
|
return null;
|
|
124
|
-
} else if (name === 'NotAllowedError') {
|
|
125
|
-
adminforth.alert({ message: `The operation either timed out or was not allowed`, variant: 'danger' });
|
|
126
|
-
codeError.value = 'The operation either timed out or was not allowed.';
|
|
127
|
-
return null;
|
|
128
126
|
} else {
|
|
127
|
+
console.error('Error during authentication:', error);
|
|
129
128
|
adminforth.alert({message: `Error during authentication: ${error}`, variant: 'warning'});
|
|
130
129
|
codeError.value = 'Error during authentication.';
|
|
131
130
|
return null;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
export function registerPasskeyEndpoints(server, handlers) {
|
|
11
|
+
server.endpoint({
|
|
12
|
+
method: 'POST',
|
|
13
|
+
path: `/plugin/passkeys/registrationOptions`,
|
|
14
|
+
noAuth: false,
|
|
15
|
+
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response, cookies, headers }) { return handlers.createRegistrationOptions({ body, adminUser, response, cookies, headers }); }),
|
|
16
|
+
});
|
|
17
|
+
server.endpoint({
|
|
18
|
+
method: 'POST',
|
|
19
|
+
path: `/plugin/passkeys/finishRegistration`,
|
|
20
|
+
noAuth: false,
|
|
21
|
+
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, cookies }) { return handlers.finishRegistration({ body, adminUser, cookies }); }),
|
|
22
|
+
});
|
|
23
|
+
server.endpoint({
|
|
24
|
+
method: 'POST',
|
|
25
|
+
path: `/plugin/passkeys/loginOptions`,
|
|
26
|
+
noAuth: true,
|
|
27
|
+
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ response }) { return handlers.createLoginOptions({ response }); }),
|
|
28
|
+
});
|
|
29
|
+
server.endpoint({
|
|
30
|
+
method: 'GET',
|
|
31
|
+
path: `/plugin/passkeys/getPasskeys`,
|
|
32
|
+
noAuth: false,
|
|
33
|
+
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ adminUser }) { return handlers.getPasskeys({ adminUser }); }),
|
|
34
|
+
});
|
|
35
|
+
server.endpoint({
|
|
36
|
+
method: 'DELETE',
|
|
37
|
+
path: `/plugin/passkeys/deletePasskey`,
|
|
38
|
+
noAuth: false,
|
|
39
|
+
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser }) { return handlers.deletePasskey({ body, adminUser }); }),
|
|
40
|
+
});
|
|
41
|
+
server.endpoint({
|
|
42
|
+
method: 'POST',
|
|
43
|
+
path: `/plugin/passkeys/renamePasskey`,
|
|
44
|
+
noAuth: false,
|
|
45
|
+
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser }) { return handlers.renamePasskey({ body, adminUser }); }),
|
|
46
|
+
});
|
|
47
|
+
server.endpoint({
|
|
48
|
+
method: 'POST',
|
|
49
|
+
path: `/plugin/passkeys/checkIfUserHasPasskeys`,
|
|
50
|
+
noAuth: true,
|
|
51
|
+
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ cookies }) { return handlers.checkIfUserHasPasskeys({ cookies }); }),
|
|
52
|
+
});
|
|
53
|
+
server.endpoint({
|
|
54
|
+
method: 'POST',
|
|
55
|
+
path: `/plugin/passkeys/resolveVerifyAuto`,
|
|
56
|
+
noAuth: false,
|
|
57
|
+
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response, cookies, headers }) { return handlers.resolveVerifyAuto({ body, adminUser, response, cookies, headers }); }),
|
|
58
|
+
});
|
|
59
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
export function registerTwoFaEndpoints(server, handlers) {
|
|
11
|
+
server.endpoint({
|
|
12
|
+
method: 'POST',
|
|
13
|
+
path: `/plugin/twofa/initSetup`,
|
|
14
|
+
noAuth: true,
|
|
15
|
+
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ cookies }) { return handlers.initSetup({ cookies }); }),
|
|
16
|
+
});
|
|
17
|
+
server.endpoint({
|
|
18
|
+
method: 'POST',
|
|
19
|
+
path: `/plugin/twofa/confirmLogin`,
|
|
20
|
+
noAuth: true,
|
|
21
|
+
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, response, cookies }) { return handlers.confirmLogin({ body, response, cookies }); }),
|
|
22
|
+
});
|
|
23
|
+
server.endpoint({
|
|
24
|
+
method: 'POST',
|
|
25
|
+
path: `/plugin/twofa/confirmLoginWithPasskey`,
|
|
26
|
+
noAuth: true,
|
|
27
|
+
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, response, cookies, headers, requestUrl, query }) { return handlers.confirmLoginWithPasskey({ body, response, cookies, headers, requestUrl, query }); }),
|
|
28
|
+
});
|
|
29
|
+
server.endpoint({
|
|
30
|
+
method: "GET",
|
|
31
|
+
path: "/plugin/twofa/skip-allow",
|
|
32
|
+
noAuth: true,
|
|
33
|
+
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ cookies }) { return handlers.skipAllow({ cookies }); }),
|
|
34
|
+
});
|
|
35
|
+
server.endpoint({
|
|
36
|
+
method: "GET",
|
|
37
|
+
path: "/plugin/twofa/skip-allow-modal",
|
|
38
|
+
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ adminUser, headers, cookies }) { return handlers.skipAllowModal({ adminUser, headers, cookies }); }),
|
|
39
|
+
});
|
|
40
|
+
server.endpoint({
|
|
41
|
+
method: 'POST',
|
|
42
|
+
path: `/plugin/twofa/verify`,
|
|
43
|
+
noAuth: false,
|
|
44
|
+
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ adminUser, body }) { return handlers.verifyTotp({ adminUser, body }); }),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export declare function createPasskeyHandlers(ctx: any): {
|
|
2
|
+
createRegistrationOptions: ({ body, adminUser, response, cookies, headers }: {
|
|
3
|
+
body: any;
|
|
4
|
+
adminUser: any;
|
|
5
|
+
response: any;
|
|
6
|
+
cookies: any;
|
|
7
|
+
headers: any;
|
|
8
|
+
}) => Promise<any>;
|
|
9
|
+
finishRegistration: ({ body, adminUser, cookies }: {
|
|
10
|
+
body: any;
|
|
11
|
+
adminUser: any;
|
|
12
|
+
cookies: any;
|
|
13
|
+
}) => Promise<any>;
|
|
14
|
+
createLoginOptions: ({ response }: {
|
|
15
|
+
response: any;
|
|
16
|
+
}) => Promise<any>;
|
|
17
|
+
getPasskeys: ({ adminUser }: {
|
|
18
|
+
adminUser: any;
|
|
19
|
+
}) => Promise<any>;
|
|
20
|
+
deletePasskey: ({ body, adminUser }: {
|
|
21
|
+
body: any;
|
|
22
|
+
adminUser: any;
|
|
23
|
+
}) => Promise<any>;
|
|
24
|
+
renamePasskey: ({ body, adminUser }: {
|
|
25
|
+
body: any;
|
|
26
|
+
adminUser: any;
|
|
27
|
+
}) => Promise<any>;
|
|
28
|
+
checkIfUserHasPasskeys: ({ cookies }: {
|
|
29
|
+
cookies: any;
|
|
30
|
+
}) => Promise<any>;
|
|
31
|
+
resolveVerifyAuto: ({ body, adminUser, response, cookies, headers }: {
|
|
32
|
+
body: any;
|
|
33
|
+
adminUser: any;
|
|
34
|
+
response: any;
|
|
35
|
+
cookies: any;
|
|
36
|
+
headers: any;
|
|
37
|
+
}) => Promise<{
|
|
38
|
+
ok: boolean;
|
|
39
|
+
code: string;
|
|
40
|
+
error: any;
|
|
41
|
+
} | {
|
|
42
|
+
ok: boolean;
|
|
43
|
+
error: any;
|
|
44
|
+
code?: undefined;
|
|
45
|
+
} | {
|
|
46
|
+
ok: boolean;
|
|
47
|
+
}>;
|
|
48
|
+
};
|