@adminforth/two-factors-auth 2.6.2 → 2.7.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/build.log +2 -2
- package/custom/LoginWithPasskeyButton.vue +5 -3
- package/custom/TwoFAModal.vue +9 -9
- package/custom/TwoFactorsConfirmation.vue +13 -13
- package/custom/TwoFactorsPasskeysSettings.vue +12 -13
- package/dist/custom/LoginWithPasskeyButton.vue +5 -3
- package/dist/custom/TwoFAModal.vue +9 -9
- package/dist/custom/TwoFactorsConfirmation.vue +13 -13
- package/dist/custom/TwoFactorsPasskeysSettings.vue +12 -13
- package/dist/index.js +7 -7
- package/index.ts +4 -4
- package/package.json +1 -1
- package/types.ts +4 -0
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 272,
|
|
19
|
-
total size is 271,
|
|
18
|
+
sent 272,788 bytes received 210 bytes 545,996.00 bytes/sec
|
|
19
|
+
total size is 271,961 speedup is 1.00
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
@click="handleLoginWithPasskey"
|
|
6
6
|
>
|
|
7
7
|
<IconShieldOutline class="w-7 h-8" />
|
|
8
|
-
<span class="ml-2">Continue with Passkey</span>
|
|
8
|
+
<span class="ml-2">{{ $t('Continue with Passkey') }}</span>
|
|
9
9
|
</Button>
|
|
10
10
|
|
|
11
11
|
</template>
|
|
@@ -20,7 +20,9 @@
|
|
|
20
20
|
import { Button } from '@/afcl';
|
|
21
21
|
import adminforth from '@/adminforth';
|
|
22
22
|
import { useRouter, useRoute } from 'vue-router';
|
|
23
|
+
import { useI18n } from 'vue-i18n';
|
|
23
24
|
|
|
25
|
+
const { t } = useI18n();
|
|
24
26
|
const userStore = useUserStore();
|
|
25
27
|
const router = useRouter();
|
|
26
28
|
|
|
@@ -36,7 +38,7 @@
|
|
|
36
38
|
});
|
|
37
39
|
if ( resp.error ) {
|
|
38
40
|
console.error("Login failed:", resp.error);
|
|
39
|
-
adminforth.alert({message: 'Error: ' + resp.error, variant: 'warning'});
|
|
41
|
+
adminforth.alert({message: t('Error: ') + resp.error, variant: 'warning'});
|
|
40
42
|
return;
|
|
41
43
|
}
|
|
42
44
|
|
|
@@ -46,7 +48,7 @@
|
|
|
46
48
|
router.push(resp.redirectTo);
|
|
47
49
|
} else {
|
|
48
50
|
console.error("Login not allowed:", resp.error);
|
|
49
|
-
adminforth.alert({message: 'Error: ' + (resp.error || 'Login not allowed'), variant: 'warning'});
|
|
51
|
+
adminforth.alert({message: t('Error: ') + (resp.error || t('Login not allowed')), variant: 'warning'});
|
|
50
52
|
}
|
|
51
53
|
} catch (error) {
|
|
52
54
|
console.error("Error during passkey login:", error);
|
package/custom/TwoFAModal.vue
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
</div>
|
|
24
24
|
|
|
25
25
|
<div class="mt-6 flex justify-center items-center gap-32 w-full">
|
|
26
|
-
<p v-if="doesUserHavePasskeys===true" class="underline hover:no-underline text-lightPrimary whitespace-nowrap hover:cursor-pointer" @click="modalMode = 'passkey'" >use passkey</p>
|
|
26
|
+
<p v-if="doesUserHavePasskeys===true" class="underline hover:no-underline text-lightPrimary whitespace-nowrap hover:cursor-pointer" @click="modalMode = 'passkey'" >{{$t('use passkey')}}</p>
|
|
27
27
|
<Button
|
|
28
28
|
class="px-4 py-2 rounded border"
|
|
29
29
|
@click="onCancel"
|
|
@@ -43,21 +43,21 @@
|
|
|
43
43
|
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
|
44
44
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|
|
45
45
|
</svg>
|
|
46
|
-
<span class="sr-only">Close modal</span>
|
|
46
|
+
<span class="sr-only">{{$t('Close modal')}}</span>
|
|
47
47
|
</button>
|
|
48
48
|
<IconShieldOutline class="w-16 h-16 text-lightPrimary dark:text-darkPrimary"/>
|
|
49
|
-
<p class="text-4xl font-semibold mb-4 text:gray-900 dark:text-gray-200 ">Passkey</p>
|
|
49
|
+
<p class="text-4xl font-semibold mb-4 text:gray-900 dark:text-gray-200 ">{{$t('Passkey')}}</p>
|
|
50
50
|
<div class="mb-2 max-w-[300px] text:gray-900 dark:text-gray-200">
|
|
51
51
|
<p class="mb-2">{{customDialogTitle}} </p>
|
|
52
|
-
<p>Authenticate yourself using the button below</p>
|
|
52
|
+
<p>{{$t('Authenticate yourself using the button below')}}</p>
|
|
53
53
|
</div>
|
|
54
54
|
<Button @click="usePasskeyButtonClick" :disabled="isFetchingPasskey" :loader="isFetchingPasskey" class="w-full mx-16">
|
|
55
|
-
Use passkey
|
|
55
|
+
{{$t('Use passkey')}}
|
|
56
56
|
</Button>
|
|
57
57
|
<div v-if="modalMode === 'passkey'" class="max-w-sm px-6 pt-3 w-full bg-white border border-gray-200 rounded-lg shadow-sm dark:bg-gray-800 dark:border-gray-700">
|
|
58
58
|
<div class="mb-3 font-normal text-gray-700 dark:text-gray-400">
|
|
59
|
-
<p>
|
|
60
|
-
<p class="underline hover:no-underline text-lightPrimary whitespace-nowrap hover:cursor-pointer" @click="modalMode = 'totp'" >use TOTP</p>
|
|
59
|
+
<p>{{$t('Have issues with passkey?')}}</p>
|
|
60
|
+
<p class="underline hover:no-underline text-lightPrimary whitespace-nowrap hover:cursor-pointer" @click="modalMode = 'totp'" >{{$t('use TOTP')}}</p>
|
|
61
61
|
</div>
|
|
62
62
|
</div>
|
|
63
63
|
|
|
@@ -168,11 +168,11 @@
|
|
|
168
168
|
onCancel();
|
|
169
169
|
return null;
|
|
170
170
|
} else if (name === 'NotAllowedError') {
|
|
171
|
-
adminforth.alert({ message:
|
|
171
|
+
adminforth.alert({ message: t('The operation either timed out or was not allowed'), variant: 'warning' });
|
|
172
172
|
onCancel();
|
|
173
173
|
return null;
|
|
174
174
|
} else {
|
|
175
|
-
adminforth.alert({message: `Error during authentication: ${error}
|
|
175
|
+
adminforth.alert({message: t(`Error during authentication: ${error}`), variant: 'warning'});
|
|
176
176
|
onCancel();
|
|
177
177
|
return null;
|
|
178
178
|
}
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
@on-complete="handleOnComplete"
|
|
30
30
|
/>
|
|
31
31
|
<div class="flex items-center justify-between w-full">
|
|
32
|
-
<Link v-if="confirmationMode === 'code' && doesUserHavePasskeys" :to="{ hash: '#passkey' }" class="w-max underline hover:no-underline hover:cursor-pointer text-lightPrimary whitespace-nowrap">Use passkey</Link>
|
|
32
|
+
<Link v-if="confirmationMode === 'code' && doesUserHavePasskeys" :to="{ hash: '#passkey' }" class="w-max underline hover:no-underline hover:cursor-pointer text-lightPrimary whitespace-nowrap">{{$t('Use passkey')}}</Link>
|
|
33
33
|
<Link
|
|
34
34
|
v-if="confirmationMode === 'code'"
|
|
35
35
|
to="/login"
|
|
@@ -42,17 +42,17 @@
|
|
|
42
42
|
</div>
|
|
43
43
|
<div v-else class="af-passkey-confirmation flex flex-col items-center justify-center py-4 gap-6">
|
|
44
44
|
<IconShieldOutline class="w-16 h-16 text-lightPrimary dark:text-darkPrimary"/>
|
|
45
|
-
<p class="text-4xl font-semibold mb-4">Passkey</p>
|
|
46
|
-
<p class="mb-2 max-w-[300px]">When you are ready, authenticate using the button below</p>
|
|
45
|
+
<p class="text-4xl font-semibold mb-4">{{$t('Passkey')}}</p>
|
|
46
|
+
<p class="mb-2 max-w-[300px]">{{$t('When you are ready, authenticate using the button below')}}</p>
|
|
47
47
|
<Button @click="usePasskeyButton" :disabled="isFetchingPasskey" :loader="isFetchingPasskey" class="w-full mx-16">
|
|
48
|
-
Use passkey
|
|
48
|
+
{{$t('Use passkey')}}
|
|
49
49
|
</Button>
|
|
50
50
|
<div v-if="confirmationMode === 'passkey'" class="max-w-sm px-6 pt-3 w-full bg-white border border-gray-200 rounded-lg shadow-sm dark:bg-gray-800 dark:border-gray-700">
|
|
51
51
|
<p class="mb-3 font-normal text-gray-700 dark:text-gray-400">
|
|
52
|
-
Have issues with passkey?
|
|
52
|
+
{{$t('Have issues with passkey?')}}
|
|
53
53
|
<div v-if="doesUserHavePasskeys" class="flex justify-start cursor-pointer gap-2" >
|
|
54
|
-
<Link v-if="confirmationMode === 'passkey'" :to="{ hash: '#code' }" class="underline hover:no-underline text-lightPrimary whitespace-nowrap">use TOTP</Link>
|
|
55
|
-
<p> or
|
|
54
|
+
<Link v-if="confirmationMode === 'passkey'" :to="{ hash: '#code' }" class="underline hover:no-underline text-lightPrimary whitespace-nowrap">{{$t('use TOTP')}}</Link>
|
|
55
|
+
<p> {{$t('or')}}</p>
|
|
56
56
|
<Link
|
|
57
57
|
to="/login"
|
|
58
58
|
class="w-full"
|
|
@@ -318,7 +318,7 @@
|
|
|
318
318
|
options = PublicKeyCredential.parseRequestOptionsFromJSON(_options);
|
|
319
319
|
} catch (e) {
|
|
320
320
|
console.error('Error parsing request options:', e);
|
|
321
|
-
adminforth.alert({message: 'Error initiating passkey authentication.', variant: 'warning'});
|
|
321
|
+
adminforth.alert({message: t('Error initiating passkey authentication.'), variant: 'warning'});
|
|
322
322
|
return;
|
|
323
323
|
}
|
|
324
324
|
const credential = await authenticate(options);
|
|
@@ -350,7 +350,7 @@
|
|
|
350
350
|
if (response.ok === true) {
|
|
351
351
|
return { _options: response.data, challengeId: response.challengeId };
|
|
352
352
|
} else {
|
|
353
|
-
adminforth.alert({message: 'Error creating sign-in request.', variant: 'warning'});
|
|
353
|
+
adminforth.alert({message: t('Error creating sign-in request.'), variant: 'warning'});
|
|
354
354
|
codeError.value = 'Error creating sign-in request.';
|
|
355
355
|
}
|
|
356
356
|
}
|
|
@@ -389,12 +389,12 @@
|
|
|
389
389
|
codeError.value = t('A previous passkey attempt was still pending. Please try again.');
|
|
390
390
|
return null;
|
|
391
391
|
} else if (name === 'NotAllowedError') {
|
|
392
|
-
adminforth.alert({ message:
|
|
393
|
-
codeError.value = 'The operation either timed out or was not allowed.';
|
|
392
|
+
adminforth.alert({ message: t('The operation either timed out or was not allowed'), variant: 'warning' });
|
|
393
|
+
codeError.value = t('The operation either timed out or was not allowed.');
|
|
394
394
|
return null;
|
|
395
395
|
} else {
|
|
396
|
-
adminforth.alert({message: `Error during authentication: ${error}
|
|
397
|
-
codeError.value = 'Error during authentication.';
|
|
396
|
+
adminforth.alert({message: t(`Error during authentication: ${error}`), variant: 'warning'});
|
|
397
|
+
codeError.value = t('Error during authentication.');
|
|
398
398
|
return null;
|
|
399
399
|
}
|
|
400
400
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="text-3xl text-lightBreadcrumbsText dark:text-darkBreadcrumbsText font-semibold max-w-2xl mr-6 flex-col justify-center items-center">
|
|
3
|
-
<p class="flex items-start justify-start leading-none">Passkeys</p>
|
|
3
|
+
<p class="flex items-start justify-start leading-none">{{$t('Passkeys')}}</p>
|
|
4
4
|
<div class="flex flex-col items-end">
|
|
5
5
|
<Table
|
|
6
6
|
class="mt-4 w-full"
|
|
@@ -37,12 +37,12 @@
|
|
|
37
37
|
</div>
|
|
38
38
|
|
|
39
39
|
<template #tooltip>
|
|
40
|
-
Rename passkey
|
|
40
|
+
{{$t('Rename passkey')}}
|
|
41
41
|
</template>
|
|
42
42
|
</Tooltip>
|
|
43
43
|
</template>
|
|
44
44
|
<div>
|
|
45
|
-
<p>Enter new passkey name
|
|
45
|
+
<p>{{$t('Enter new passkey name:')}}</p>
|
|
46
46
|
<input
|
|
47
47
|
v-model="passkeysNewName"
|
|
48
48
|
type="text"
|
|
@@ -67,19 +67,19 @@
|
|
|
67
67
|
/>
|
|
68
68
|
</div>
|
|
69
69
|
<template #tooltip>
|
|
70
|
-
Delete passkey
|
|
70
|
+
{{$t('Delete passkey')}}
|
|
71
71
|
</template>
|
|
72
72
|
</Tooltip>
|
|
73
73
|
</template>
|
|
74
74
|
<div>
|
|
75
|
-
<p>Are you sure you want to delete this passkey
|
|
75
|
+
<p>{{$t('Are you sure you want to delete this passkey?')}}</p>
|
|
76
76
|
</div>
|
|
77
77
|
</Dialog>
|
|
78
78
|
</div>
|
|
79
79
|
</template>
|
|
80
80
|
<template #cell:last_used_at="{item}">
|
|
81
81
|
<span v-if="item.last_used_at">{{ formatDateTime(item.last_used_at) }}</span>
|
|
82
|
-
<span v-else class="text-gray-400">Never</span>
|
|
82
|
+
<span v-else class="text-gray-400">{{$t('Never')}}</span>
|
|
83
83
|
</template>
|
|
84
84
|
</Table>
|
|
85
85
|
<div class="flex space-x-4 mt-4" v-if="isInitialFinished">
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
</div>
|
|
97
97
|
</template>
|
|
98
98
|
</ButtonGroup>
|
|
99
|
-
<p v-else class="flex items-center justify-center gap-2 text-base">Processing <Spinner class="w-4 h-4 inline-block" /></p>
|
|
99
|
+
<p v-else class="flex items-center justify-center gap-2 text-base">{{$t('Processing')}} <Spinner class="w-4 h-4 inline-block" /></p>
|
|
100
100
|
</div>
|
|
101
101
|
<div v-if="isCardsVisible" id="cards-container" class="w-80 mt-2 border-gray-400 p-2 bg-white dark:bg-gray-800 rounded-lg shadow-md flex flex-col space-y-2">
|
|
102
102
|
<div v-if="isPasskeySupported" class="flex justify-between gap-4" :class="!isPasskeySupported ? 'opacity-50 pointer-events-none' : ''">
|
|
@@ -126,8 +126,8 @@
|
|
|
126
126
|
</div>
|
|
127
127
|
|
|
128
128
|
<template #tooltip>
|
|
129
|
-
<p class="max-w-64">This browser or device is reporting partial passkey support
|
|
130
|
-
<p class="max-w-64">
|
|
129
|
+
<p class="max-w-64">{{$t('This browser or device is reporting partial passkey support.')}}</p>
|
|
130
|
+
<p class="max-w-64">{{$t('To fix it try to install extention or change browser.')}}</p>
|
|
131
131
|
</template>
|
|
132
132
|
</Tooltip>
|
|
133
133
|
<div class="border-t border-gray-300"></div>
|
|
@@ -161,12 +161,12 @@
|
|
|
161
161
|
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
|
162
162
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|
|
163
163
|
</svg>
|
|
164
|
-
<span class="sr-only">Close dialog</span>
|
|
164
|
+
<span class="sr-only">{{$t('Close dialog')}}</span>
|
|
165
165
|
</button>
|
|
166
166
|
<div class="flex items-center justify-center space-y-4">
|
|
167
|
-
Now you can add a passkey.
|
|
167
|
+
{{$t('Now you can add a passkey.')}}
|
|
168
168
|
</div>
|
|
169
|
-
<Button class="mt-4" @click="addPasskeyFinishAction(fetchedOptions); closeDialog();">Add Passkey</Button>
|
|
169
|
+
<Button class="mt-4" @click="addPasskeyFinishAction(fetchedOptions); closeDialog();">{{$t('Add Passkey')}}</Button>
|
|
170
170
|
</div>
|
|
171
171
|
</Dialog>
|
|
172
172
|
</div>
|
|
@@ -377,7 +377,6 @@
|
|
|
377
377
|
async function callWebAuthn(options: any) {
|
|
378
378
|
let credential;
|
|
379
379
|
try {
|
|
380
|
-
console.log('Creating credential with options:', options);
|
|
381
380
|
credential = await navigator.credentials.create({
|
|
382
381
|
publicKey: options
|
|
383
382
|
});
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
@click="handleLoginWithPasskey"
|
|
6
6
|
>
|
|
7
7
|
<IconShieldOutline class="w-7 h-8" />
|
|
8
|
-
<span class="ml-2">Continue with Passkey</span>
|
|
8
|
+
<span class="ml-2">{{ $t('Continue with Passkey') }}</span>
|
|
9
9
|
</Button>
|
|
10
10
|
|
|
11
11
|
</template>
|
|
@@ -20,7 +20,9 @@
|
|
|
20
20
|
import { Button } from '@/afcl';
|
|
21
21
|
import adminforth from '@/adminforth';
|
|
22
22
|
import { useRouter, useRoute } from 'vue-router';
|
|
23
|
+
import { useI18n } from 'vue-i18n';
|
|
23
24
|
|
|
25
|
+
const { t } = useI18n();
|
|
24
26
|
const userStore = useUserStore();
|
|
25
27
|
const router = useRouter();
|
|
26
28
|
|
|
@@ -36,7 +38,7 @@
|
|
|
36
38
|
});
|
|
37
39
|
if ( resp.error ) {
|
|
38
40
|
console.error("Login failed:", resp.error);
|
|
39
|
-
adminforth.alert({message: 'Error: ' + resp.error, variant: 'warning'});
|
|
41
|
+
adminforth.alert({message: t('Error: ') + resp.error, variant: 'warning'});
|
|
40
42
|
return;
|
|
41
43
|
}
|
|
42
44
|
|
|
@@ -46,7 +48,7 @@
|
|
|
46
48
|
router.push(resp.redirectTo);
|
|
47
49
|
} else {
|
|
48
50
|
console.error("Login not allowed:", resp.error);
|
|
49
|
-
adminforth.alert({message: 'Error: ' + (resp.error || 'Login not allowed'), variant: 'warning'});
|
|
51
|
+
adminforth.alert({message: t('Error: ') + (resp.error || t('Login not allowed')), variant: 'warning'});
|
|
50
52
|
}
|
|
51
53
|
} catch (error) {
|
|
52
54
|
console.error("Error during passkey login:", error);
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
</div>
|
|
24
24
|
|
|
25
25
|
<div class="mt-6 flex justify-center items-center gap-32 w-full">
|
|
26
|
-
<p v-if="doesUserHavePasskeys===true" class="underline hover:no-underline text-lightPrimary whitespace-nowrap hover:cursor-pointer" @click="modalMode = 'passkey'" >use passkey</p>
|
|
26
|
+
<p v-if="doesUserHavePasskeys===true" class="underline hover:no-underline text-lightPrimary whitespace-nowrap hover:cursor-pointer" @click="modalMode = 'passkey'" >{{$t('use passkey')}}</p>
|
|
27
27
|
<Button
|
|
28
28
|
class="px-4 py-2 rounded border"
|
|
29
29
|
@click="onCancel"
|
|
@@ -43,21 +43,21 @@
|
|
|
43
43
|
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
|
44
44
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|
|
45
45
|
</svg>
|
|
46
|
-
<span class="sr-only">Close modal</span>
|
|
46
|
+
<span class="sr-only">{{$t('Close modal')}}</span>
|
|
47
47
|
</button>
|
|
48
48
|
<IconShieldOutline class="w-16 h-16 text-lightPrimary dark:text-darkPrimary"/>
|
|
49
|
-
<p class="text-4xl font-semibold mb-4 text:gray-900 dark:text-gray-200 ">Passkey</p>
|
|
49
|
+
<p class="text-4xl font-semibold mb-4 text:gray-900 dark:text-gray-200 ">{{$t('Passkey')}}</p>
|
|
50
50
|
<div class="mb-2 max-w-[300px] text:gray-900 dark:text-gray-200">
|
|
51
51
|
<p class="mb-2">{{customDialogTitle}} </p>
|
|
52
|
-
<p>Authenticate yourself using the button below</p>
|
|
52
|
+
<p>{{$t('Authenticate yourself using the button below')}}</p>
|
|
53
53
|
</div>
|
|
54
54
|
<Button @click="usePasskeyButtonClick" :disabled="isFetchingPasskey" :loader="isFetchingPasskey" class="w-full mx-16">
|
|
55
|
-
Use passkey
|
|
55
|
+
{{$t('Use passkey')}}
|
|
56
56
|
</Button>
|
|
57
57
|
<div v-if="modalMode === 'passkey'" class="max-w-sm px-6 pt-3 w-full bg-white border border-gray-200 rounded-lg shadow-sm dark:bg-gray-800 dark:border-gray-700">
|
|
58
58
|
<div class="mb-3 font-normal text-gray-700 dark:text-gray-400">
|
|
59
|
-
<p>
|
|
60
|
-
<p class="underline hover:no-underline text-lightPrimary whitespace-nowrap hover:cursor-pointer" @click="modalMode = 'totp'" >use TOTP</p>
|
|
59
|
+
<p>{{$t('Have issues with passkey?')}}</p>
|
|
60
|
+
<p class="underline hover:no-underline text-lightPrimary whitespace-nowrap hover:cursor-pointer" @click="modalMode = 'totp'" >{{$t('use TOTP')}}</p>
|
|
61
61
|
</div>
|
|
62
62
|
</div>
|
|
63
63
|
|
|
@@ -168,11 +168,11 @@
|
|
|
168
168
|
onCancel();
|
|
169
169
|
return null;
|
|
170
170
|
} else if (name === 'NotAllowedError') {
|
|
171
|
-
adminforth.alert({ message:
|
|
171
|
+
adminforth.alert({ message: t('The operation either timed out or was not allowed'), variant: 'warning' });
|
|
172
172
|
onCancel();
|
|
173
173
|
return null;
|
|
174
174
|
} else {
|
|
175
|
-
adminforth.alert({message: `Error during authentication: ${error}
|
|
175
|
+
adminforth.alert({message: t(`Error during authentication: ${error}`), variant: 'warning'});
|
|
176
176
|
onCancel();
|
|
177
177
|
return null;
|
|
178
178
|
}
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
@on-complete="handleOnComplete"
|
|
30
30
|
/>
|
|
31
31
|
<div class="flex items-center justify-between w-full">
|
|
32
|
-
<Link v-if="confirmationMode === 'code' && doesUserHavePasskeys" :to="{ hash: '#passkey' }" class="w-max underline hover:no-underline hover:cursor-pointer text-lightPrimary whitespace-nowrap">Use passkey</Link>
|
|
32
|
+
<Link v-if="confirmationMode === 'code' && doesUserHavePasskeys" :to="{ hash: '#passkey' }" class="w-max underline hover:no-underline hover:cursor-pointer text-lightPrimary whitespace-nowrap">{{$t('Use passkey')}}</Link>
|
|
33
33
|
<Link
|
|
34
34
|
v-if="confirmationMode === 'code'"
|
|
35
35
|
to="/login"
|
|
@@ -42,17 +42,17 @@
|
|
|
42
42
|
</div>
|
|
43
43
|
<div v-else class="af-passkey-confirmation flex flex-col items-center justify-center py-4 gap-6">
|
|
44
44
|
<IconShieldOutline class="w-16 h-16 text-lightPrimary dark:text-darkPrimary"/>
|
|
45
|
-
<p class="text-4xl font-semibold mb-4">Passkey</p>
|
|
46
|
-
<p class="mb-2 max-w-[300px]">When you are ready, authenticate using the button below</p>
|
|
45
|
+
<p class="text-4xl font-semibold mb-4">{{$t('Passkey')}}</p>
|
|
46
|
+
<p class="mb-2 max-w-[300px]">{{$t('When you are ready, authenticate using the button below')}}</p>
|
|
47
47
|
<Button @click="usePasskeyButton" :disabled="isFetchingPasskey" :loader="isFetchingPasskey" class="w-full mx-16">
|
|
48
|
-
Use passkey
|
|
48
|
+
{{$t('Use passkey')}}
|
|
49
49
|
</Button>
|
|
50
50
|
<div v-if="confirmationMode === 'passkey'" class="max-w-sm px-6 pt-3 w-full bg-white border border-gray-200 rounded-lg shadow-sm dark:bg-gray-800 dark:border-gray-700">
|
|
51
51
|
<p class="mb-3 font-normal text-gray-700 dark:text-gray-400">
|
|
52
|
-
Have issues with passkey?
|
|
52
|
+
{{$t('Have issues with passkey?')}}
|
|
53
53
|
<div v-if="doesUserHavePasskeys" class="flex justify-start cursor-pointer gap-2" >
|
|
54
|
-
<Link v-if="confirmationMode === 'passkey'" :to="{ hash: '#code' }" class="underline hover:no-underline text-lightPrimary whitespace-nowrap">use TOTP</Link>
|
|
55
|
-
<p> or
|
|
54
|
+
<Link v-if="confirmationMode === 'passkey'" :to="{ hash: '#code' }" class="underline hover:no-underline text-lightPrimary whitespace-nowrap">{{$t('use TOTP')}}</Link>
|
|
55
|
+
<p> {{$t('or')}}</p>
|
|
56
56
|
<Link
|
|
57
57
|
to="/login"
|
|
58
58
|
class="w-full"
|
|
@@ -318,7 +318,7 @@
|
|
|
318
318
|
options = PublicKeyCredential.parseRequestOptionsFromJSON(_options);
|
|
319
319
|
} catch (e) {
|
|
320
320
|
console.error('Error parsing request options:', e);
|
|
321
|
-
adminforth.alert({message: 'Error initiating passkey authentication.', variant: 'warning'});
|
|
321
|
+
adminforth.alert({message: t('Error initiating passkey authentication.'), variant: 'warning'});
|
|
322
322
|
return;
|
|
323
323
|
}
|
|
324
324
|
const credential = await authenticate(options);
|
|
@@ -350,7 +350,7 @@
|
|
|
350
350
|
if (response.ok === true) {
|
|
351
351
|
return { _options: response.data, challengeId: response.challengeId };
|
|
352
352
|
} else {
|
|
353
|
-
adminforth.alert({message: 'Error creating sign-in request.', variant: 'warning'});
|
|
353
|
+
adminforth.alert({message: t('Error creating sign-in request.'), variant: 'warning'});
|
|
354
354
|
codeError.value = 'Error creating sign-in request.';
|
|
355
355
|
}
|
|
356
356
|
}
|
|
@@ -389,12 +389,12 @@
|
|
|
389
389
|
codeError.value = t('A previous passkey attempt was still pending. Please try again.');
|
|
390
390
|
return null;
|
|
391
391
|
} else if (name === 'NotAllowedError') {
|
|
392
|
-
adminforth.alert({ message:
|
|
393
|
-
codeError.value = 'The operation either timed out or was not allowed.';
|
|
392
|
+
adminforth.alert({ message: t('The operation either timed out or was not allowed'), variant: 'warning' });
|
|
393
|
+
codeError.value = t('The operation either timed out or was not allowed.');
|
|
394
394
|
return null;
|
|
395
395
|
} else {
|
|
396
|
-
adminforth.alert({message: `Error during authentication: ${error}
|
|
397
|
-
codeError.value = 'Error during authentication.';
|
|
396
|
+
adminforth.alert({message: t(`Error during authentication: ${error}`), variant: 'warning'});
|
|
397
|
+
codeError.value = t('Error during authentication.');
|
|
398
398
|
return null;
|
|
399
399
|
}
|
|
400
400
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="text-3xl text-lightBreadcrumbsText dark:text-darkBreadcrumbsText font-semibold max-w-2xl mr-6 flex-col justify-center items-center">
|
|
3
|
-
<p class="flex items-start justify-start leading-none">Passkeys</p>
|
|
3
|
+
<p class="flex items-start justify-start leading-none">{{$t('Passkeys')}}</p>
|
|
4
4
|
<div class="flex flex-col items-end">
|
|
5
5
|
<Table
|
|
6
6
|
class="mt-4 w-full"
|
|
@@ -37,12 +37,12 @@
|
|
|
37
37
|
</div>
|
|
38
38
|
|
|
39
39
|
<template #tooltip>
|
|
40
|
-
Rename passkey
|
|
40
|
+
{{$t('Rename passkey')}}
|
|
41
41
|
</template>
|
|
42
42
|
</Tooltip>
|
|
43
43
|
</template>
|
|
44
44
|
<div>
|
|
45
|
-
<p>Enter new passkey name
|
|
45
|
+
<p>{{$t('Enter new passkey name:')}}</p>
|
|
46
46
|
<input
|
|
47
47
|
v-model="passkeysNewName"
|
|
48
48
|
type="text"
|
|
@@ -67,19 +67,19 @@
|
|
|
67
67
|
/>
|
|
68
68
|
</div>
|
|
69
69
|
<template #tooltip>
|
|
70
|
-
Delete passkey
|
|
70
|
+
{{$t('Delete passkey')}}
|
|
71
71
|
</template>
|
|
72
72
|
</Tooltip>
|
|
73
73
|
</template>
|
|
74
74
|
<div>
|
|
75
|
-
<p>Are you sure you want to delete this passkey
|
|
75
|
+
<p>{{$t('Are you sure you want to delete this passkey?')}}</p>
|
|
76
76
|
</div>
|
|
77
77
|
</Dialog>
|
|
78
78
|
</div>
|
|
79
79
|
</template>
|
|
80
80
|
<template #cell:last_used_at="{item}">
|
|
81
81
|
<span v-if="item.last_used_at">{{ formatDateTime(item.last_used_at) }}</span>
|
|
82
|
-
<span v-else class="text-gray-400">Never</span>
|
|
82
|
+
<span v-else class="text-gray-400">{{$t('Never')}}</span>
|
|
83
83
|
</template>
|
|
84
84
|
</Table>
|
|
85
85
|
<div class="flex space-x-4 mt-4" v-if="isInitialFinished">
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
</div>
|
|
97
97
|
</template>
|
|
98
98
|
</ButtonGroup>
|
|
99
|
-
<p v-else class="flex items-center justify-center gap-2 text-base">Processing <Spinner class="w-4 h-4 inline-block" /></p>
|
|
99
|
+
<p v-else class="flex items-center justify-center gap-2 text-base">{{$t('Processing')}} <Spinner class="w-4 h-4 inline-block" /></p>
|
|
100
100
|
</div>
|
|
101
101
|
<div v-if="isCardsVisible" id="cards-container" class="w-80 mt-2 border-gray-400 p-2 bg-white dark:bg-gray-800 rounded-lg shadow-md flex flex-col space-y-2">
|
|
102
102
|
<div v-if="isPasskeySupported" class="flex justify-between gap-4" :class="!isPasskeySupported ? 'opacity-50 pointer-events-none' : ''">
|
|
@@ -126,8 +126,8 @@
|
|
|
126
126
|
</div>
|
|
127
127
|
|
|
128
128
|
<template #tooltip>
|
|
129
|
-
<p class="max-w-64">This browser or device is reporting partial passkey support
|
|
130
|
-
<p class="max-w-64">
|
|
129
|
+
<p class="max-w-64">{{$t('This browser or device is reporting partial passkey support.')}}</p>
|
|
130
|
+
<p class="max-w-64">{{$t('To fix it try to install extention or change browser.')}}</p>
|
|
131
131
|
</template>
|
|
132
132
|
</Tooltip>
|
|
133
133
|
<div class="border-t border-gray-300"></div>
|
|
@@ -161,12 +161,12 @@
|
|
|
161
161
|
<svg class="w-3 h-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
|
162
162
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|
|
163
163
|
</svg>
|
|
164
|
-
<span class="sr-only">Close dialog</span>
|
|
164
|
+
<span class="sr-only">{{$t('Close dialog')}}</span>
|
|
165
165
|
</button>
|
|
166
166
|
<div class="flex items-center justify-center space-y-4">
|
|
167
|
-
Now you can add a passkey.
|
|
167
|
+
{{$t('Now you can add a passkey.')}}
|
|
168
168
|
</div>
|
|
169
|
-
<Button class="mt-4" @click="addPasskeyFinishAction(fetchedOptions); closeDialog();">Add Passkey</Button>
|
|
169
|
+
<Button class="mt-4" @click="addPasskeyFinishAction(fetchedOptions); closeDialog();">{{$t('Add Passkey')}}</Button>
|
|
170
170
|
</div>
|
|
171
171
|
</Dialog>
|
|
172
172
|
</div>
|
|
@@ -377,7 +377,6 @@
|
|
|
377
377
|
async function callWebAuthn(options: any) {
|
|
378
378
|
let credential;
|
|
379
379
|
try {
|
|
380
|
-
console.log('Creating credential with options:', options);
|
|
381
380
|
credential = await navigator.credentials.create({
|
|
382
381
|
publicKey: options
|
|
383
382
|
});
|
package/dist/index.js
CHANGED
|
@@ -255,7 +255,7 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
255
255
|
const beforeLoginConfirmation = this.adminforth.config.auth.beforeLoginConfirmation;
|
|
256
256
|
const beforeLoginConfirmationArray = Array.isArray(beforeLoginConfirmation) ? beforeLoginConfirmation : [beforeLoginConfirmation];
|
|
257
257
|
beforeLoginConfirmationArray.push((_a) => __awaiter(this, [_a], void 0, function* ({ adminUser, response, extra }) {
|
|
258
|
-
var _b, _c;
|
|
258
|
+
var _b, _c, _d, _e;
|
|
259
259
|
if (((_b = extra === null || extra === void 0 ? void 0 : extra.body) === null || _b === void 0 ? void 0 : _b.loginAllowedByPasskeyDirectSignIn) === true) {
|
|
260
260
|
return { body: { loginAllowed: true }, ok: true };
|
|
261
261
|
}
|
|
@@ -280,7 +280,7 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
280
280
|
if (!secret) {
|
|
281
281
|
const tempSecret = twofactor.generateSecret({ name: issuerName, account: userName });
|
|
282
282
|
newSecret = tempSecret.secret;
|
|
283
|
-
const totpTemporaryJWT = this.adminforth.auth.issueJWT({ userName, newSecret, issuer: issuerName, pk: userPk, userCanSkipSetup, rememberMeDays }, 'temp2FA', '
|
|
283
|
+
const totpTemporaryJWT = this.adminforth.auth.issueJWT({ userName, newSecret, issuer: issuerName, pk: userPk, userCanSkipSetup, rememberMeDays }, 'temp2FA', ((_d = this.options.passkeys) === null || _d === void 0 ? void 0 : _d.challengeValidityPeriod) || '1m');
|
|
284
284
|
this.adminforth.auth.setCustomCookie({ response, payload: { name: "2FaTemporaryJWT", value: totpTemporaryJWT, expiry: undefined, expirySeconds: 10 * 60, httpOnly: true } });
|
|
285
285
|
return {
|
|
286
286
|
body: {
|
|
@@ -291,7 +291,7 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
291
291
|
};
|
|
292
292
|
}
|
|
293
293
|
else {
|
|
294
|
-
const value = this.adminforth.auth.issueJWT({ userName, issuer: issuerName, pk: userPk, userCanSkipSetup, rememberMeDays }, 'temp2FA', '
|
|
294
|
+
const value = this.adminforth.auth.issueJWT({ userName, issuer: issuerName, pk: userPk, userCanSkipSetup, rememberMeDays }, 'temp2FA', ((_e = this.options.passkeys) === null || _e === void 0 ? void 0 : _e.challengeValidityPeriod) || '1m');
|
|
295
295
|
this.adminforth.auth.setCustomCookie({ response, payload: { name: "2FaTemporaryJWT", value: value, expiry: undefined, expirySeconds: 10 * 60, httpOnly: true } });
|
|
296
296
|
return {
|
|
297
297
|
body: {
|
|
@@ -511,7 +511,7 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
511
511
|
path: `/plugin/passkeys/registerPasskeyRequest`,
|
|
512
512
|
noAuth: false,
|
|
513
513
|
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ body, adminUser, response, cookies }) {
|
|
514
|
-
var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
514
|
+
var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
515
515
|
const mode = body === null || body === void 0 ? void 0 : body.mode;
|
|
516
516
|
const confirmationResult = body === null || body === void 0 ? void 0 : body.confirmationResult;
|
|
517
517
|
const verificationResult = yield this.verify(confirmationResult, {
|
|
@@ -562,7 +562,7 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
562
562
|
userVerification: ((_m = this.options.passkeys) === null || _m === void 0 ? void 0 : _m.settings.authenticatorSelection.userVerification) || "required"
|
|
563
563
|
},
|
|
564
564
|
});
|
|
565
|
-
const value = this.adminforth.auth.issueJWT({ "challenge": options.challenge, "user_id": adminUser.pk }, 'registerTempPasskeyChallenge', '
|
|
565
|
+
const value = this.adminforth.auth.issueJWT({ "challenge": options.challenge, "user_id": adminUser.pk }, 'registerTempPasskeyChallenge', ((_o = this.options.passkeys) === null || _o === void 0 ? void 0 : _o.challengeValidityPeriod) || '1m');
|
|
566
566
|
this.adminforth.auth.setCustomCookie({ response, payload: { name: "registerPasskeyTemporaryJWT", value: value, expiry: undefined, expirySeconds: 10 * 60, httpOnly: true } });
|
|
567
567
|
return { ok: true, data: options };
|
|
568
568
|
})
|
|
@@ -644,13 +644,13 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
644
644
|
path: `/plugin/passkeys/signInRequest`,
|
|
645
645
|
noAuth: true,
|
|
646
646
|
handler: (_a) => __awaiter(this, [_a], void 0, function* ({ response }) {
|
|
647
|
-
var _b, _c;
|
|
647
|
+
var _b, _c, _d;
|
|
648
648
|
try {
|
|
649
649
|
const options = yield generateAuthenticationOptions({
|
|
650
650
|
rpID: (_b = this.options.passkeys) === null || _b === void 0 ? void 0 : _b.settings.rp.id,
|
|
651
651
|
userVerification: ((_c = this.options.passkeys) === null || _c === void 0 ? void 0 : _c.settings.authenticatorSelection.userVerification) || "required"
|
|
652
652
|
});
|
|
653
|
-
const value = this.adminforth.auth.issueJWT({ "challenge": options.challenge }, 'tempLoginPasskeyChallenge', '
|
|
653
|
+
const value = this.adminforth.auth.issueJWT({ "challenge": options.challenge }, 'tempLoginPasskeyChallenge', ((_d = this.options.passkeys) === null || _d === void 0 ? void 0 : _d.challengeValidityPeriod) || '1m');
|
|
654
654
|
this.adminforth.auth.setCustomCookie({ response, payload: { name: `passkeyLoginTemporaryJWT`, value: value, expiry: undefined, expirySeconds: 10 * 60, httpOnly: true } });
|
|
655
655
|
return { ok: true, data: options };
|
|
656
656
|
}
|
package/index.ts
CHANGED
|
@@ -316,7 +316,7 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
316
316
|
const tempSecret = twofactor.generateSecret({name: issuerName,account: userName})
|
|
317
317
|
newSecret = tempSecret.secret;
|
|
318
318
|
|
|
319
|
-
const totpTemporaryJWT = this.adminforth.auth.issueJWT({userName, newSecret, issuer:issuerName, pk:userPk, userCanSkipSetup, rememberMeDays }, 'temp2FA', '
|
|
319
|
+
const totpTemporaryJWT = this.adminforth.auth.issueJWT({userName, newSecret, issuer:issuerName, pk:userPk, userCanSkipSetup, rememberMeDays }, 'temp2FA', this.options.passkeys?.challengeValidityPeriod || '1m');
|
|
320
320
|
this.adminforth.auth.setCustomCookie({response, payload: {name: "2FaTemporaryJWT", value: totpTemporaryJWT, expiry: undefined, expirySeconds: 10 * 60, httpOnly: true}});
|
|
321
321
|
|
|
322
322
|
return {
|
|
@@ -328,7 +328,7 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
328
328
|
}
|
|
329
329
|
|
|
330
330
|
} else {
|
|
331
|
-
const value = this.adminforth.auth.issueJWT({userName, issuer:issuerName, pk:userPk, userCanSkipSetup, rememberMeDays }, 'temp2FA', '
|
|
331
|
+
const value = this.adminforth.auth.issueJWT({userName, issuer:issuerName, pk:userPk, userCanSkipSetup, rememberMeDays }, 'temp2FA', this.options.passkeys?.challengeValidityPeriod || '1m');
|
|
332
332
|
this.adminforth.auth.setCustomCookie({response, payload: {name: "2FaTemporaryJWT", value: value, expiry: undefined, expirySeconds: 10 * 60, httpOnly: true}});
|
|
333
333
|
|
|
334
334
|
return {
|
|
@@ -615,7 +615,7 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
615
615
|
userVerification: this.options.passkeys?.settings.authenticatorSelection.userVerification || "required"
|
|
616
616
|
},
|
|
617
617
|
});
|
|
618
|
-
const value = this.adminforth.auth.issueJWT({ "challenge": options.challenge, "user_id": adminUser.pk }, 'registerTempPasskeyChallenge', '
|
|
618
|
+
const value = this.adminforth.auth.issueJWT({ "challenge": options.challenge, "user_id": adminUser.pk }, 'registerTempPasskeyChallenge', this.options.passkeys?.challengeValidityPeriod || '1m');
|
|
619
619
|
this.adminforth.auth.setCustomCookie({response, payload: {name: "registerPasskeyTemporaryJWT", value: value, expiry: undefined, expirySeconds: 10 * 60, httpOnly: true}});
|
|
620
620
|
return { ok: true, data: options };
|
|
621
621
|
}
|
|
@@ -707,7 +707,7 @@ export default class TwoFactorsAuthPlugin extends AdminForthPlugin {
|
|
|
707
707
|
rpID: this.options.passkeys?.settings.rp.id,
|
|
708
708
|
userVerification: this.options.passkeys?.settings.authenticatorSelection.userVerification || "required"
|
|
709
709
|
});
|
|
710
|
-
const value = this.adminforth.auth.issueJWT({ "challenge": options.challenge }, 'tempLoginPasskeyChallenge', '
|
|
710
|
+
const value = this.adminforth.auth.issueJWT({ "challenge": options.challenge }, 'tempLoginPasskeyChallenge', this.options.passkeys?.challengeValidityPeriod || '1m');
|
|
711
711
|
this.adminforth.auth.setCustomCookie({response, payload: {name: `passkeyLoginTemporaryJWT`, value: value, expiry: undefined, expirySeconds: 10 * 60, httpOnly: true}});
|
|
712
712
|
return { ok: true, data: options };
|
|
713
713
|
} catch (e) {
|
package/package.json
CHANGED
package/types.ts
CHANGED
|
@@ -45,6 +45,10 @@ export type PluginOptions = {
|
|
|
45
45
|
* Period between showing alert suggesting to set up Passkeys if not set up yet.
|
|
46
46
|
*/
|
|
47
47
|
suggestionPeriod?: string; // e.g. '30d', '12h', '15m'. Default is '5d'
|
|
48
|
+
/**
|
|
49
|
+
* Challenge period for Passkeys authentication.
|
|
50
|
+
*/
|
|
51
|
+
challengeValidityPeriod?: string; // e.g. '5m', '10m', '15m'. Default is '1m'
|
|
48
52
|
/**
|
|
49
53
|
* Passkeys settings for WebAuthn API.
|
|
50
54
|
*/
|