@adminforth/two-factors-auth 2.5.4 → 2.5.6
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 +40 -31
- package/custom/TwoFactorsConfirmation.vue +29 -10
- package/dist/custom/TwoFAModal.vue +40 -31
- package/dist/custom/TwoFactorsConfirmation.vue +29 -10
- package/dist/index.js +1 -1
- package/index.ts +1 -1
- package/package.json +1 -1
package/build.log
CHANGED
|
@@ -15,5 +15,5 @@ custom/package.json
|
|
|
15
15
|
custom/tsconfig.json
|
|
16
16
|
custom/utils.js
|
|
17
17
|
|
|
18
|
-
sent
|
|
19
|
-
total size is
|
|
18
|
+
sent 272,054 bytes received 210 bytes 544,528.00 bytes/sec
|
|
19
|
+
total size is 271,238 speedup is 1.00
|
package/custom/TwoFAModal.vue
CHANGED
|
@@ -68,6 +68,7 @@
|
|
|
68
68
|
</template>
|
|
69
69
|
|
|
70
70
|
<script setup lang="ts">
|
|
71
|
+
|
|
71
72
|
import VOtpInput from 'vue3-otp-input';
|
|
72
73
|
import { ref, nextTick, watch, onMounted } from 'vue';
|
|
73
74
|
import { useUserStore } from '@/stores/user';
|
|
@@ -98,27 +99,26 @@
|
|
|
98
99
|
(e: 'closed'): void
|
|
99
100
|
}>();
|
|
100
101
|
|
|
101
|
-
function
|
|
102
|
+
async function addEventListenerForOTPInput(){
|
|
103
|
+
document.addEventListener('focusin', handleGlobalFocusIn, true);
|
|
104
|
+
focusFirstAvailableOtpInput();
|
|
105
|
+
isLoading.value = false;
|
|
106
|
+
await nextTick();
|
|
107
|
+
const rootEl = otpRoot.value;
|
|
108
|
+
rootEl && rootEl.addEventListener('focusout', handleFocusOut, true);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function removeEventListenerForOTPInput() {
|
|
102
112
|
window.removeEventListener('paste', handlePaste);
|
|
103
113
|
document.removeEventListener('focusin', handleGlobalFocusIn, true);
|
|
104
114
|
const rootEl = otpRoot.value;
|
|
105
115
|
rootEl && rootEl.removeEventListener('focusout', handleFocusOut, true);
|
|
116
|
+
// Abort any in-flight WebAuthn request when leaving the component
|
|
106
117
|
}
|
|
107
118
|
|
|
108
|
-
async function addListeners() {
|
|
109
|
-
document.addEventListener('focusin', handleGlobalFocusIn, true);
|
|
110
|
-
|
|
111
|
-
// Wait for DOM to be ready and OTP inputs to be rendered
|
|
112
|
-
await nextTick();
|
|
113
|
-
await new Promise(resolve => setTimeout(resolve, 100)); // Small delay for v-otp-input to render
|
|
114
|
-
|
|
115
|
-
focusFirstAvailableOtpInput();
|
|
116
|
-
const rootEl = otpRoot.value;
|
|
117
|
-
rootEl && rootEl.addEventListener('focusout', handleFocusOut, true);
|
|
118
|
-
}
|
|
119
119
|
|
|
120
120
|
const modelShow = ref(false);
|
|
121
|
-
let resolveFn: ((confirmationResult:
|
|
121
|
+
let resolveFn: ((confirmationResult: any) => void) | null = null;
|
|
122
122
|
let verifyingCallback: ((confirmationResult: string) => boolean) | null = null;
|
|
123
123
|
let verifyFn: null | ((confirmationResult: string) => Promise<boolean> | boolean) = null;
|
|
124
124
|
let rejectFn: ((err?: any) => void) | null = null;
|
|
@@ -133,7 +133,9 @@
|
|
|
133
133
|
customDialogTitle.value = title;
|
|
134
134
|
}
|
|
135
135
|
modelShow.value = true;
|
|
136
|
-
|
|
136
|
+
if (modalMode.value === 'totp') {
|
|
137
|
+
await addEventListenerForOTPInput();
|
|
138
|
+
}
|
|
137
139
|
resolveFn = resolve;
|
|
138
140
|
rejectFn = reject;
|
|
139
141
|
verifyFn = verifyingCallback ?? null;
|
|
@@ -163,15 +165,17 @@
|
|
|
163
165
|
return null;
|
|
164
166
|
} else if (name === 'InvalidStateError' || name === 'OperationError' || /pending/i.test(message)) {
|
|
165
167
|
adminforth.alert({ message: t('Another security prompt is already open. Please try again.'), variant: 'warning' });
|
|
168
|
+
onCancel();
|
|
166
169
|
return null;
|
|
167
170
|
} else if (name === 'NotAllowedError') {
|
|
168
171
|
adminforth.alert({ message: `The operation either timed out or was not allowed`, variant: 'warning' });
|
|
172
|
+
onCancel();
|
|
169
173
|
return null;
|
|
170
174
|
} else {
|
|
171
175
|
adminforth.alert({message: `Error during authentication: ${error}`, variant: 'warning'});
|
|
176
|
+
onCancel();
|
|
172
177
|
return null;
|
|
173
178
|
}
|
|
174
|
-
onCancel();
|
|
175
179
|
}
|
|
176
180
|
modelShow.value = false;
|
|
177
181
|
const dataToReturn = {
|
|
@@ -179,7 +183,7 @@
|
|
|
179
183
|
result: passkeyData
|
|
180
184
|
}
|
|
181
185
|
customDialogTitle.value = "";
|
|
182
|
-
|
|
186
|
+
removeEventListenerForOTPInput();
|
|
183
187
|
resolveFn(dataToReturn);
|
|
184
188
|
}
|
|
185
189
|
|
|
@@ -231,7 +235,7 @@
|
|
|
231
235
|
result: value
|
|
232
236
|
}
|
|
233
237
|
customDialogTitle.value = "";
|
|
234
|
-
|
|
238
|
+
removeEventListenerForOTPInput();
|
|
235
239
|
resolveFn(dataToReturn);
|
|
236
240
|
}
|
|
237
241
|
|
|
@@ -240,19 +244,17 @@
|
|
|
240
244
|
modelShow.value = false;
|
|
241
245
|
bindValue.value = '';
|
|
242
246
|
confirmationResult.value?.clearInput();
|
|
243
|
-
|
|
247
|
+
removeEventListenerForOTPInput();
|
|
244
248
|
rejectFn("Cancel");
|
|
245
249
|
emit('rejected', new Error('cancelled'));
|
|
246
250
|
emit('closed');
|
|
247
251
|
}
|
|
248
252
|
|
|
249
253
|
watch(modalMode, async (newMode) => {
|
|
250
|
-
if (newMode === 'totp'
|
|
251
|
-
await
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
focusFirstAvailableOtpInput();
|
|
255
|
-
}, 100);
|
|
254
|
+
if (newMode === 'totp') {
|
|
255
|
+
await addEventListenerForOTPInput();
|
|
256
|
+
} else {
|
|
257
|
+
removeEventListenerForOTPInput();
|
|
256
258
|
}
|
|
257
259
|
});
|
|
258
260
|
|
|
@@ -287,22 +289,29 @@
|
|
|
287
289
|
|
|
288
290
|
async function checkIfUserHasPasskeys() {
|
|
289
291
|
isLoading.value = true;
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
292
|
+
try {
|
|
293
|
+
const response = await callAdminForthApi({
|
|
294
|
+
method: 'GET',
|
|
295
|
+
path: '/plugin/passkeys/getPasskeys',
|
|
296
|
+
});
|
|
297
|
+
|
|
294
298
|
if (response.ok) {
|
|
295
299
|
if (response.data.length >= 1) {
|
|
296
300
|
doesUserHavePasskeys.value = true;
|
|
297
301
|
modalMode.value = "passkey";
|
|
298
|
-
isLoading.value = false;
|
|
299
302
|
} else {
|
|
300
303
|
doesUserHavePasskeys.value = false;
|
|
301
304
|
modalMode.value = "totp";
|
|
302
|
-
isLoading.value = false;
|
|
303
305
|
}
|
|
304
306
|
}
|
|
305
|
-
})
|
|
307
|
+
} catch (error) {
|
|
308
|
+
console.error('Error checking passkeys:', error);
|
|
309
|
+
// Fallback to TOTP if there's an error
|
|
310
|
+
doesUserHavePasskeys.value = false;
|
|
311
|
+
modalMode.value = "totp";
|
|
312
|
+
} finally {
|
|
313
|
+
isLoading.value = false;
|
|
314
|
+
}
|
|
306
315
|
}
|
|
307
316
|
|
|
308
317
|
function getOtpInputs() {
|
|
@@ -181,14 +181,20 @@
|
|
|
181
181
|
if (isPasskeysSupported.value === true) {
|
|
182
182
|
await checkIfUserHasPasskeys();
|
|
183
183
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
rootEl && rootEl.addEventListener('focusout', handleFocusOut, true);
|
|
184
|
+
if (confirmationMode.value === 'code') {
|
|
185
|
+
await addEventListenerForOTPInput();
|
|
186
|
+
}
|
|
188
187
|
}
|
|
189
188
|
isLoading.value = false;
|
|
190
189
|
});
|
|
191
190
|
|
|
191
|
+
watch(confirmationMode, async (newMode) => {
|
|
192
|
+
if (newMode === 'code') {
|
|
193
|
+
await addEventListenerForOTPInput();
|
|
194
|
+
} else {
|
|
195
|
+
removeEventListenerForOTPInput();
|
|
196
|
+
}
|
|
197
|
+
});
|
|
192
198
|
watch(route, (newRoute) => {
|
|
193
199
|
codeError.value = null;
|
|
194
200
|
if ( newRoute.hash === '#passkey' ) {
|
|
@@ -200,6 +206,24 @@
|
|
|
200
206
|
}
|
|
201
207
|
});
|
|
202
208
|
|
|
209
|
+
async function addEventListenerForOTPInput(){
|
|
210
|
+
document.addEventListener('focusin', handleGlobalFocusIn, true);
|
|
211
|
+
focusFirstAvailableOtpInput();
|
|
212
|
+
isLoading.value = false;
|
|
213
|
+
await nextTick();
|
|
214
|
+
const rootEl = otpRoot.value;
|
|
215
|
+
rootEl && rootEl.addEventListener('focusout', handleFocusOut, true);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function removeEventListenerForOTPInput() {
|
|
219
|
+
window.removeEventListener('paste', handlePaste);
|
|
220
|
+
document.removeEventListener('focusin', handleGlobalFocusIn, true);
|
|
221
|
+
const rootEl = otpRoot.value;
|
|
222
|
+
rootEl && rootEl.removeEventListener('focusout', handleFocusOut, true);
|
|
223
|
+
// Abort any in-flight WebAuthn request when leaving the component
|
|
224
|
+
cancelPendingWebAuthn('component-unmount');
|
|
225
|
+
}
|
|
226
|
+
|
|
203
227
|
async function isCMAAvailable() {
|
|
204
228
|
if (window.PublicKeyCredential &&
|
|
205
229
|
PublicKeyCredential.isConditionalMediationAvailable) {
|
|
@@ -211,12 +235,7 @@
|
|
|
211
235
|
}
|
|
212
236
|
|
|
213
237
|
onBeforeUnmount(() => {
|
|
214
|
-
|
|
215
|
-
document.removeEventListener('focusin', handleGlobalFocusIn, true);
|
|
216
|
-
const rootEl = otpRoot.value;
|
|
217
|
-
rootEl && rootEl.removeEventListener('focusout', handleFocusOut, true);
|
|
218
|
-
// Abort any in-flight WebAuthn request when leaving the component
|
|
219
|
-
cancelPendingWebAuthn('component-unmount');
|
|
238
|
+
removeEventListenerForOTPInput();
|
|
220
239
|
});
|
|
221
240
|
|
|
222
241
|
async function sendCode (value: any, factorMode: 'TOTP' | 'passkey', passkeyOptions: any) {
|
|
@@ -68,6 +68,7 @@
|
|
|
68
68
|
</template>
|
|
69
69
|
|
|
70
70
|
<script setup lang="ts">
|
|
71
|
+
|
|
71
72
|
import VOtpInput from 'vue3-otp-input';
|
|
72
73
|
import { ref, nextTick, watch, onMounted } from 'vue';
|
|
73
74
|
import { useUserStore } from '@/stores/user';
|
|
@@ -98,27 +99,26 @@
|
|
|
98
99
|
(e: 'closed'): void
|
|
99
100
|
}>();
|
|
100
101
|
|
|
101
|
-
function
|
|
102
|
+
async function addEventListenerForOTPInput(){
|
|
103
|
+
document.addEventListener('focusin', handleGlobalFocusIn, true);
|
|
104
|
+
focusFirstAvailableOtpInput();
|
|
105
|
+
isLoading.value = false;
|
|
106
|
+
await nextTick();
|
|
107
|
+
const rootEl = otpRoot.value;
|
|
108
|
+
rootEl && rootEl.addEventListener('focusout', handleFocusOut, true);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function removeEventListenerForOTPInput() {
|
|
102
112
|
window.removeEventListener('paste', handlePaste);
|
|
103
113
|
document.removeEventListener('focusin', handleGlobalFocusIn, true);
|
|
104
114
|
const rootEl = otpRoot.value;
|
|
105
115
|
rootEl && rootEl.removeEventListener('focusout', handleFocusOut, true);
|
|
116
|
+
// Abort any in-flight WebAuthn request when leaving the component
|
|
106
117
|
}
|
|
107
118
|
|
|
108
|
-
async function addListeners() {
|
|
109
|
-
document.addEventListener('focusin', handleGlobalFocusIn, true);
|
|
110
|
-
|
|
111
|
-
// Wait for DOM to be ready and OTP inputs to be rendered
|
|
112
|
-
await nextTick();
|
|
113
|
-
await new Promise(resolve => setTimeout(resolve, 100)); // Small delay for v-otp-input to render
|
|
114
|
-
|
|
115
|
-
focusFirstAvailableOtpInput();
|
|
116
|
-
const rootEl = otpRoot.value;
|
|
117
|
-
rootEl && rootEl.addEventListener('focusout', handleFocusOut, true);
|
|
118
|
-
}
|
|
119
119
|
|
|
120
120
|
const modelShow = ref(false);
|
|
121
|
-
let resolveFn: ((confirmationResult:
|
|
121
|
+
let resolveFn: ((confirmationResult: any) => void) | null = null;
|
|
122
122
|
let verifyingCallback: ((confirmationResult: string) => boolean) | null = null;
|
|
123
123
|
let verifyFn: null | ((confirmationResult: string) => Promise<boolean> | boolean) = null;
|
|
124
124
|
let rejectFn: ((err?: any) => void) | null = null;
|
|
@@ -133,7 +133,9 @@
|
|
|
133
133
|
customDialogTitle.value = title;
|
|
134
134
|
}
|
|
135
135
|
modelShow.value = true;
|
|
136
|
-
|
|
136
|
+
if (modalMode.value === 'totp') {
|
|
137
|
+
await addEventListenerForOTPInput();
|
|
138
|
+
}
|
|
137
139
|
resolveFn = resolve;
|
|
138
140
|
rejectFn = reject;
|
|
139
141
|
verifyFn = verifyingCallback ?? null;
|
|
@@ -163,15 +165,17 @@
|
|
|
163
165
|
return null;
|
|
164
166
|
} else if (name === 'InvalidStateError' || name === 'OperationError' || /pending/i.test(message)) {
|
|
165
167
|
adminforth.alert({ message: t('Another security prompt is already open. Please try again.'), variant: 'warning' });
|
|
168
|
+
onCancel();
|
|
166
169
|
return null;
|
|
167
170
|
} else if (name === 'NotAllowedError') {
|
|
168
171
|
adminforth.alert({ message: `The operation either timed out or was not allowed`, variant: 'warning' });
|
|
172
|
+
onCancel();
|
|
169
173
|
return null;
|
|
170
174
|
} else {
|
|
171
175
|
adminforth.alert({message: `Error during authentication: ${error}`, variant: 'warning'});
|
|
176
|
+
onCancel();
|
|
172
177
|
return null;
|
|
173
178
|
}
|
|
174
|
-
onCancel();
|
|
175
179
|
}
|
|
176
180
|
modelShow.value = false;
|
|
177
181
|
const dataToReturn = {
|
|
@@ -179,7 +183,7 @@
|
|
|
179
183
|
result: passkeyData
|
|
180
184
|
}
|
|
181
185
|
customDialogTitle.value = "";
|
|
182
|
-
|
|
186
|
+
removeEventListenerForOTPInput();
|
|
183
187
|
resolveFn(dataToReturn);
|
|
184
188
|
}
|
|
185
189
|
|
|
@@ -231,7 +235,7 @@
|
|
|
231
235
|
result: value
|
|
232
236
|
}
|
|
233
237
|
customDialogTitle.value = "";
|
|
234
|
-
|
|
238
|
+
removeEventListenerForOTPInput();
|
|
235
239
|
resolveFn(dataToReturn);
|
|
236
240
|
}
|
|
237
241
|
|
|
@@ -240,19 +244,17 @@
|
|
|
240
244
|
modelShow.value = false;
|
|
241
245
|
bindValue.value = '';
|
|
242
246
|
confirmationResult.value?.clearInput();
|
|
243
|
-
|
|
247
|
+
removeEventListenerForOTPInput();
|
|
244
248
|
rejectFn("Cancel");
|
|
245
249
|
emit('rejected', new Error('cancelled'));
|
|
246
250
|
emit('closed');
|
|
247
251
|
}
|
|
248
252
|
|
|
249
253
|
watch(modalMode, async (newMode) => {
|
|
250
|
-
if (newMode === 'totp'
|
|
251
|
-
await
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
focusFirstAvailableOtpInput();
|
|
255
|
-
}, 100);
|
|
254
|
+
if (newMode === 'totp') {
|
|
255
|
+
await addEventListenerForOTPInput();
|
|
256
|
+
} else {
|
|
257
|
+
removeEventListenerForOTPInput();
|
|
256
258
|
}
|
|
257
259
|
});
|
|
258
260
|
|
|
@@ -287,22 +289,29 @@
|
|
|
287
289
|
|
|
288
290
|
async function checkIfUserHasPasskeys() {
|
|
289
291
|
isLoading.value = true;
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
292
|
+
try {
|
|
293
|
+
const response = await callAdminForthApi({
|
|
294
|
+
method: 'GET',
|
|
295
|
+
path: '/plugin/passkeys/getPasskeys',
|
|
296
|
+
});
|
|
297
|
+
|
|
294
298
|
if (response.ok) {
|
|
295
299
|
if (response.data.length >= 1) {
|
|
296
300
|
doesUserHavePasskeys.value = true;
|
|
297
301
|
modalMode.value = "passkey";
|
|
298
|
-
isLoading.value = false;
|
|
299
302
|
} else {
|
|
300
303
|
doesUserHavePasskeys.value = false;
|
|
301
304
|
modalMode.value = "totp";
|
|
302
|
-
isLoading.value = false;
|
|
303
305
|
}
|
|
304
306
|
}
|
|
305
|
-
})
|
|
307
|
+
} catch (error) {
|
|
308
|
+
console.error('Error checking passkeys:', error);
|
|
309
|
+
// Fallback to TOTP if there's an error
|
|
310
|
+
doesUserHavePasskeys.value = false;
|
|
311
|
+
modalMode.value = "totp";
|
|
312
|
+
} finally {
|
|
313
|
+
isLoading.value = false;
|
|
314
|
+
}
|
|
306
315
|
}
|
|
307
316
|
|
|
308
317
|
function getOtpInputs() {
|
|
@@ -181,14 +181,20 @@
|
|
|
181
181
|
if (isPasskeysSupported.value === true) {
|
|
182
182
|
await checkIfUserHasPasskeys();
|
|
183
183
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
rootEl && rootEl.addEventListener('focusout', handleFocusOut, true);
|
|
184
|
+
if (confirmationMode.value === 'code') {
|
|
185
|
+
await addEventListenerForOTPInput();
|
|
186
|
+
}
|
|
188
187
|
}
|
|
189
188
|
isLoading.value = false;
|
|
190
189
|
});
|
|
191
190
|
|
|
191
|
+
watch(confirmationMode, async (newMode) => {
|
|
192
|
+
if (newMode === 'code') {
|
|
193
|
+
await addEventListenerForOTPInput();
|
|
194
|
+
} else {
|
|
195
|
+
removeEventListenerForOTPInput();
|
|
196
|
+
}
|
|
197
|
+
});
|
|
192
198
|
watch(route, (newRoute) => {
|
|
193
199
|
codeError.value = null;
|
|
194
200
|
if ( newRoute.hash === '#passkey' ) {
|
|
@@ -200,6 +206,24 @@
|
|
|
200
206
|
}
|
|
201
207
|
});
|
|
202
208
|
|
|
209
|
+
async function addEventListenerForOTPInput(){
|
|
210
|
+
document.addEventListener('focusin', handleGlobalFocusIn, true);
|
|
211
|
+
focusFirstAvailableOtpInput();
|
|
212
|
+
isLoading.value = false;
|
|
213
|
+
await nextTick();
|
|
214
|
+
const rootEl = otpRoot.value;
|
|
215
|
+
rootEl && rootEl.addEventListener('focusout', handleFocusOut, true);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function removeEventListenerForOTPInput() {
|
|
219
|
+
window.removeEventListener('paste', handlePaste);
|
|
220
|
+
document.removeEventListener('focusin', handleGlobalFocusIn, true);
|
|
221
|
+
const rootEl = otpRoot.value;
|
|
222
|
+
rootEl && rootEl.removeEventListener('focusout', handleFocusOut, true);
|
|
223
|
+
// Abort any in-flight WebAuthn request when leaving the component
|
|
224
|
+
cancelPendingWebAuthn('component-unmount');
|
|
225
|
+
}
|
|
226
|
+
|
|
203
227
|
async function isCMAAvailable() {
|
|
204
228
|
if (window.PublicKeyCredential &&
|
|
205
229
|
PublicKeyCredential.isConditionalMediationAvailable) {
|
|
@@ -211,12 +235,7 @@
|
|
|
211
235
|
}
|
|
212
236
|
|
|
213
237
|
onBeforeUnmount(() => {
|
|
214
|
-
|
|
215
|
-
document.removeEventListener('focusin', handleGlobalFocusIn, true);
|
|
216
|
-
const rootEl = otpRoot.value;
|
|
217
|
-
rootEl && rootEl.removeEventListener('focusout', handleFocusOut, true);
|
|
218
|
-
// Abort any in-flight WebAuthn request when leaving the component
|
|
219
|
-
cancelPendingWebAuthn('component-unmount');
|
|
238
|
+
removeEventListenerForOTPInput();
|
|
220
239
|
});
|
|
221
240
|
|
|
222
241
|
async function sendCode (value: any, factorMode: 'TOTP' | 'passkey', passkeyOptions: any) {
|
package/dist/index.js
CHANGED
|
@@ -418,7 +418,7 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
418
418
|
}
|
|
419
419
|
const passkeyRecord = yield this.adminforth.resource(this.options.passkeys.credentialResourceID).get([Filters.EQ(this.options.passkeys.credentialIdFieldName, credential_id)]);
|
|
420
420
|
if (!passkeyRecord) {
|
|
421
|
-
return { error: '
|
|
421
|
+
return { error: 'No registered passkey found, probably it was removed' };
|
|
422
422
|
}
|
|
423
423
|
const userPk = passkeyRecord[this.options.passkeys.credentialUserIdFieldName];
|
|
424
424
|
if (!userPk) {
|
package/index.ts
CHANGED
|
@@ -460,7 +460,7 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
460
460
|
|
|
461
461
|
const passkeyRecord = await this.adminforth.resource(this.options.passkeys.credentialResourceID).get([Filters.EQ(this.options.passkeys.credentialIdFieldName, credential_id)]);
|
|
462
462
|
if (!passkeyRecord) {
|
|
463
|
-
return { error: '
|
|
463
|
+
return { error: 'No registered passkey found, probably it was removed' };
|
|
464
464
|
}
|
|
465
465
|
|
|
466
466
|
const userPk = passkeyRecord[this.options.passkeys.credentialUserIdFieldName];
|