@adminforth/two-factors-auth 4.8.3 → 6.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/LoginWithPasskeyButton.vue +2 -2
- package/custom/TwoFAModal.vue +13 -18
- package/custom/TwoFaCard.vue +20 -10
- package/custom/TwoFaCodeInput.vue +18 -104
- package/custom/TwoFaHint.vue +2 -2
- package/custom/TwoFaLinkButton.vue +7 -2
- package/custom/TwoFactorsConfirmation.vue +6 -7
- package/custom/TwoFactorsPasskeysSettings.vue +37 -53
- package/custom/TwoFactorsSetup.vue +19 -24
- package/custom/package-lock.json +29 -26
- package/custom/package.json +1 -3
- package/custom/pnpm-lock.yaml +0 -20
- package/dist/custom/LoginWithPasskeyButton.vue +2 -2
- package/dist/custom/TwoFAModal.vue +13 -18
- package/dist/custom/TwoFaCard.vue +20 -10
- package/dist/custom/TwoFaCodeInput.vue +18 -104
- package/dist/custom/TwoFaHint.vue +2 -2
- package/dist/custom/TwoFaLinkButton.vue +7 -2
- package/dist/custom/TwoFactorsConfirmation.vue +6 -7
- package/dist/custom/TwoFactorsPasskeysSettings.vue +37 -53
- package/dist/custom/TwoFactorsSetup.vue +19 -24
- package/dist/custom/package-lock.json +29 -26
- package/dist/custom/package.json +1 -3
- package/dist/custom/pnpm-lock.yaml +0 -20
- package/package.json +2 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="relative flex items-center justify-center min-h-screen bg-
|
|
2
|
+
<div class="relative flex items-center justify-center min-h-screen bg-lightHtml dark:bg-darkHtml"
|
|
3
3
|
:style="coreStore.config?.loginBackgroundImage && coreStore.config?.loginBackgroundPosition === 'over' ? {
|
|
4
4
|
'background-image': 'url(' + loadFile(coreStore.config?.loginBackgroundImage) + ')',
|
|
5
5
|
'background-size': 'cover',
|
|
@@ -16,37 +16,38 @@
|
|
|
16
16
|
:error="codeError"
|
|
17
17
|
>
|
|
18
18
|
<div>
|
|
19
|
-
<p class="mb-3 flex items-center gap-2.5 text-sm font-medium text-
|
|
19
|
+
<p class="mb-3 flex items-center gap-2.5 text-sm font-medium text-lightInputText dark:text-darkInputText">
|
|
20
20
|
<span class="flex w-6 h-6 shrink-0 items-center justify-center rounded-full bg-lightPrimary/10 text-xs font-semibold text-lightPrimary dark:bg-darkPrimary/20 dark:text-darkPrimary">1</span>
|
|
21
21
|
{{ $t('Scan the QR code with your authenticator app') }}
|
|
22
22
|
</p>
|
|
23
|
-
<div class="flex items-center gap-4 rounded-lg border border-lightDialogBreakLine bg-
|
|
23
|
+
<div class="flex items-center gap-4 rounded-lg border border-lightDialogBreakLine bg-lightSecondaryDarken p-3.5 dark:border-darkDialogBreakLine dark:bg-darkSecondaryLighten">
|
|
24
|
+
<!-- a QR code needs a white ground to be scanned, in the dark theme too -->
|
|
24
25
|
<div class="w-32 h-32 shrink-0 rounded-lg bg-white p-2">
|
|
25
26
|
<img v-if="totpQrCode" :src="totpQrCode" class="af-qr-code w-full h-full" alt="QR code" />
|
|
26
27
|
</div>
|
|
27
28
|
<div class="flex min-w-0 flex-col gap-2">
|
|
28
|
-
<p class="text-sm leading-5 text-
|
|
29
|
-
<
|
|
29
|
+
<p class="text-sm leading-5 text-lightInputIconColor dark:text-darkInputIconColor">{{ $t('Setting up on this device?') }}</p>
|
|
30
|
+
<Link :to="totpUri" class="inline-flex items-center gap-1.5 text-sm">
|
|
30
31
|
{{ $t('Open in authenticator app') }}
|
|
31
|
-
<
|
|
32
|
-
</
|
|
32
|
+
<IconExternalLink class="w-4 h-4" />
|
|
33
|
+
</Link>
|
|
33
34
|
</div>
|
|
34
35
|
</div>
|
|
35
36
|
</div>
|
|
36
37
|
|
|
37
38
|
<div>
|
|
38
|
-
<p class="mb-2 text-xs font-medium uppercase tracking-wider text-
|
|
39
|
-
<div class="flex items-center gap-2 rounded-lg border border-lightDialogBreakLine bg-
|
|
40
|
-
<span class="af-2fa-secret flex-1 min-w-0 select-all break-all font-mono text-sm tracking-wider text-
|
|
41
|
-
<
|
|
42
|
-
<
|
|
43
|
-
</
|
|
39
|
+
<p class="mb-2 text-xs font-medium uppercase tracking-wider text-lightInputIconColor dark:text-darkInputIconColor">{{ $t('Or enter the key manually') }}</p>
|
|
40
|
+
<div class="flex items-center gap-2 rounded-lg border border-lightDialogBreakLine bg-lightSecondaryDarken py-1.5 pl-3.5 pr-1.5 dark:border-darkDialogBreakLine dark:bg-darkSecondaryLighten">
|
|
41
|
+
<span class="af-2fa-secret flex-1 min-w-0 select-all break-all font-mono text-sm tracking-wider text-lightInputText dark:text-darkInputText">{{ totp.newSecret }}</span>
|
|
42
|
+
<Button type="button" variant="ghost" iconOnly :aria-label="$t('Copy key')" class="af-2fa-copy-btn" @click="onCopyClick">
|
|
43
|
+
<IconCopy aria-hidden="true" />
|
|
44
|
+
</Button>
|
|
44
45
|
</div>
|
|
45
46
|
</div>
|
|
46
47
|
|
|
47
48
|
<TwoFaCodeInput ref="codeInput" :invalid="Boolean(codeError)" @complete="handleOnComplete">
|
|
48
49
|
<template #label>
|
|
49
|
-
<p class="mb-3 flex items-center gap-2.5 text-sm font-medium text-
|
|
50
|
+
<p class="mb-3 flex items-center gap-2.5 text-sm font-medium text-lightInputText dark:text-darkInputText">
|
|
50
51
|
<span class="flex w-6 h-6 shrink-0 items-center justify-center rounded-full bg-lightPrimary/10 text-xs font-semibold text-lightPrimary dark:bg-darkPrimary/20 dark:text-darkPrimary">2</span>
|
|
51
52
|
{{ $t('Enter the 6-digit code from the app') }}
|
|
52
53
|
</p>
|
|
@@ -54,9 +55,9 @@
|
|
|
54
55
|
</TwoFaCodeInput>
|
|
55
56
|
|
|
56
57
|
<template #footer>
|
|
57
|
-
<
|
|
58
|
+
<Button v-if="skipAllowed" type="button" variant="ghost" class="af-2fa-skip-btn -ms-3.5" @click="handleSkip">
|
|
58
59
|
{{ $t('Skip for now') }}
|
|
59
|
-
</
|
|
60
|
+
</Button>
|
|
60
61
|
<LinkButton to="/login" variant="secondary" class="af-2fa-back-to-login ms-auto whitespace-nowrap">{{ $t('Back to login') }}</LinkButton>
|
|
61
62
|
</template>
|
|
62
63
|
</TwoFaCard>
|
|
@@ -70,19 +71,16 @@
|
|
|
70
71
|
import { onMounted, ref, computed, onBeforeMount } from 'vue';
|
|
71
72
|
import { useCoreStore } from '@/stores/core';
|
|
72
73
|
import { useUserStore } from '@/stores/user';
|
|
73
|
-
import {
|
|
74
|
+
import { IconCopy, IconExternalLink } from '@iconify-prerendered/vue-lucide';
|
|
74
75
|
import { callAdminForthApi, loadFile } from '@/utils';
|
|
75
76
|
import { useRouter } from 'vue-router';
|
|
76
|
-
import {
|
|
77
|
-
import { LinkButton } from '@/afcl';
|
|
78
|
-
import Vue2FACodeInput from '@loltech/vue3-2fa-code-input';
|
|
77
|
+
import { Button, Link, LinkButton } from '@/afcl';
|
|
79
78
|
import adminforth from '@/adminforth';
|
|
80
79
|
import TwoFaCard from './TwoFaCard.vue';
|
|
81
80
|
import TwoFaCodeInput from './TwoFaCodeInput.vue';
|
|
82
81
|
import { useI18n } from 'vue-i18n';
|
|
83
82
|
import { useRoute } from 'vue-router';
|
|
84
83
|
import { handlePasskeyAlert } from './utils.js';
|
|
85
|
-
import ErrorMessage from '@/components/ErrorMessage.vue';
|
|
86
84
|
import { useQRCode } from '@vueuse/integrations/useQRCode'
|
|
87
85
|
|
|
88
86
|
const { t } = useI18n();
|
|
@@ -113,9 +111,6 @@ const user = useUserStore();
|
|
|
113
111
|
|
|
114
112
|
const skipAllowed = ref(false);
|
|
115
113
|
|
|
116
|
-
// use this simple function to automatically focus on the next input
|
|
117
|
-
|
|
118
|
-
const showPw = ref(false);
|
|
119
114
|
|
|
120
115
|
const error = ref(null);
|
|
121
116
|
const totp = ref<{ issuer?: string; userName?: string; newSecret?: string }>({});
|
package/custom/package-lock.json
CHANGED
|
@@ -9,12 +9,10 @@
|
|
|
9
9
|
"version": "1.0.0",
|
|
10
10
|
"license": "ISC",
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@loltech/vue3-2fa-code-input": "^1.1.0",
|
|
13
12
|
"@vueuse/core": "^14.3.0",
|
|
14
13
|
"@vueuse/integrations": "^14.4.0",
|
|
15
14
|
"dayjs": "^1.11.18",
|
|
16
|
-
"qrcode": "^1.5.4"
|
|
17
|
-
"vue3-otp-input": "^0.5.21"
|
|
15
|
+
"qrcode": "^1.5.4"
|
|
18
16
|
}
|
|
19
17
|
},
|
|
20
18
|
"node_modules/@babel/helper-string-parser": {
|
|
@@ -22,6 +20,7 @@
|
|
|
22
20
|
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
|
|
23
21
|
"integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
|
|
24
22
|
"license": "MIT",
|
|
23
|
+
"peer": true,
|
|
25
24
|
"engines": {
|
|
26
25
|
"node": ">=6.9.0"
|
|
27
26
|
}
|
|
@@ -31,6 +30,7 @@
|
|
|
31
30
|
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
|
|
32
31
|
"integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
|
|
33
32
|
"license": "MIT",
|
|
33
|
+
"peer": true,
|
|
34
34
|
"engines": {
|
|
35
35
|
"node": ">=6.9.0"
|
|
36
36
|
}
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz",
|
|
41
41
|
"integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==",
|
|
42
42
|
"license": "MIT",
|
|
43
|
+
"peer": true,
|
|
43
44
|
"dependencies": {
|
|
44
45
|
"@babel/types": "^7.29.0"
|
|
45
46
|
},
|
|
@@ -55,6 +56,7 @@
|
|
|
55
56
|
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
|
|
56
57
|
"integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
|
|
57
58
|
"license": "MIT",
|
|
59
|
+
"peer": true,
|
|
58
60
|
"dependencies": {
|
|
59
61
|
"@babel/helper-string-parser": "^7.27.1",
|
|
60
62
|
"@babel/helper-validator-identifier": "^7.28.5"
|
|
@@ -67,13 +69,8 @@
|
|
|
67
69
|
"version": "1.5.5",
|
|
68
70
|
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
|
|
69
71
|
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
|
|
70
|
-
"license": "MIT"
|
|
71
|
-
|
|
72
|
-
"node_modules/@loltech/vue3-2fa-code-input": {
|
|
73
|
-
"version": "1.1.0",
|
|
74
|
-
"resolved": "https://registry.npmjs.org/@loltech/vue3-2fa-code-input/-/vue3-2fa-code-input-1.1.0.tgz",
|
|
75
|
-
"integrity": "sha512-styyoQjsARSeVf6E5qMvYIVkLRrwYtfL8xDl5W+g9wKF8qlr2XGU2gi9maEg0X4Us41kJryXBzfRMUcv5tKIBA==",
|
|
76
|
-
"license": "MIT"
|
|
72
|
+
"license": "MIT",
|
|
73
|
+
"peer": true
|
|
77
74
|
},
|
|
78
75
|
"node_modules/@types/web-bluetooth": {
|
|
79
76
|
"version": "0.0.21",
|
|
@@ -86,6 +83,7 @@
|
|
|
86
83
|
"resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.30.tgz",
|
|
87
84
|
"integrity": "sha512-s3DfdZkcu/qExZ+td75015ljzHc6vE+30cFMGRPROYjqkroYI5NV2X1yAMX9UeyBNWB9MxCfPcsjpLS11nzkkw==",
|
|
88
85
|
"license": "MIT",
|
|
86
|
+
"peer": true,
|
|
89
87
|
"dependencies": {
|
|
90
88
|
"@babel/parser": "^7.29.0",
|
|
91
89
|
"@vue/shared": "3.5.30",
|
|
@@ -99,6 +97,7 @@
|
|
|
99
97
|
"resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.30.tgz",
|
|
100
98
|
"integrity": "sha512-eCFYESUEVYHhiMuK4SQTldO3RYxyMR/UQL4KdGD1Yrkfdx4m/HYuZ9jSfPdA+nWJY34VWndiYdW/wZXyiPEB9g==",
|
|
101
99
|
"license": "MIT",
|
|
100
|
+
"peer": true,
|
|
102
101
|
"dependencies": {
|
|
103
102
|
"@vue/compiler-core": "3.5.30",
|
|
104
103
|
"@vue/shared": "3.5.30"
|
|
@@ -109,6 +108,7 @@
|
|
|
109
108
|
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.30.tgz",
|
|
110
109
|
"integrity": "sha512-LqmFPDn89dtU9vI3wHJnwaV6GfTRD87AjWpTWpyrdVOObVtjIuSeZr181z5C4PmVx/V3j2p+0f7edFKGRMpQ5A==",
|
|
111
110
|
"license": "MIT",
|
|
111
|
+
"peer": true,
|
|
112
112
|
"dependencies": {
|
|
113
113
|
"@babel/parser": "^7.29.0",
|
|
114
114
|
"@vue/compiler-core": "3.5.30",
|
|
@@ -126,6 +126,7 @@
|
|
|
126
126
|
"resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.30.tgz",
|
|
127
127
|
"integrity": "sha512-NsYK6OMTnx109PSL2IAyf62JP6EUdk4Dmj6AkWcJGBvN0dQoMYtVekAmdqgTtWQgEJo+Okstbf/1p7qZr5H+bA==",
|
|
128
128
|
"license": "MIT",
|
|
129
|
+
"peer": true,
|
|
129
130
|
"dependencies": {
|
|
130
131
|
"@vue/compiler-dom": "3.5.30",
|
|
131
132
|
"@vue/shared": "3.5.30"
|
|
@@ -136,6 +137,7 @@
|
|
|
136
137
|
"resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.30.tgz",
|
|
137
138
|
"integrity": "sha512-179YNgKATuwj9gB+66snskRDOitDiuOZqkYia7mHKJaidOMo/WJxHKF8DuGc4V4XbYTJANlfEKb0yxTQotnx4Q==",
|
|
138
139
|
"license": "MIT",
|
|
140
|
+
"peer": true,
|
|
139
141
|
"dependencies": {
|
|
140
142
|
"@vue/shared": "3.5.30"
|
|
141
143
|
}
|
|
@@ -145,6 +147,7 @@
|
|
|
145
147
|
"resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.30.tgz",
|
|
146
148
|
"integrity": "sha512-e0Z+8PQsUTdwV8TtEsLzUM7SzC7lQwYKePydb7K2ZnmS6jjND+WJXkmmfh/swYzRyfP1EY3fpdesyYoymCzYfg==",
|
|
147
149
|
"license": "MIT",
|
|
150
|
+
"peer": true,
|
|
148
151
|
"dependencies": {
|
|
149
152
|
"@vue/reactivity": "3.5.30",
|
|
150
153
|
"@vue/shared": "3.5.30"
|
|
@@ -155,6 +158,7 @@
|
|
|
155
158
|
"resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.30.tgz",
|
|
156
159
|
"integrity": "sha512-2UIGakjU4WSQ0T4iwDEW0W7vQj6n7AFn7taqZ9Cvm0Q/RA2FFOziLESrDL4GmtI1wV3jXg5nMoJSYO66egDUBw==",
|
|
157
160
|
"license": "MIT",
|
|
161
|
+
"peer": true,
|
|
158
162
|
"dependencies": {
|
|
159
163
|
"@vue/reactivity": "3.5.30",
|
|
160
164
|
"@vue/runtime-core": "3.5.30",
|
|
@@ -167,6 +171,7 @@
|
|
|
167
171
|
"resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.30.tgz",
|
|
168
172
|
"integrity": "sha512-v+R34icapydRwbZRD0sXwtHqrQJv38JuMB4JxbOxd8NEpGLny7cncMp53W9UH/zo4j8eDHjQ1dEJXwzFQknjtQ==",
|
|
169
173
|
"license": "MIT",
|
|
174
|
+
"peer": true,
|
|
170
175
|
"dependencies": {
|
|
171
176
|
"@vue/compiler-ssr": "3.5.30",
|
|
172
177
|
"@vue/shared": "3.5.30"
|
|
@@ -179,7 +184,8 @@
|
|
|
179
184
|
"version": "3.5.30",
|
|
180
185
|
"resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.30.tgz",
|
|
181
186
|
"integrity": "sha512-YXgQ7JjaO18NeK2K9VTbDHaFy62WrObMa6XERNfNOkAhD1F1oDSf3ZJ7K6GqabZ0BvSDHajp8qfS5Sa2I9n8uQ==",
|
|
182
|
-
"license": "MIT"
|
|
187
|
+
"license": "MIT",
|
|
188
|
+
"peer": true
|
|
183
189
|
},
|
|
184
190
|
"node_modules/@vueuse/core": {
|
|
185
191
|
"version": "14.4.0",
|
|
@@ -351,7 +357,8 @@
|
|
|
351
357
|
"version": "3.2.3",
|
|
352
358
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
|
|
353
359
|
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
|
|
354
|
-
"license": "MIT"
|
|
360
|
+
"license": "MIT",
|
|
361
|
+
"peer": true
|
|
355
362
|
},
|
|
356
363
|
"node_modules/dayjs": {
|
|
357
364
|
"version": "1.11.19",
|
|
@@ -385,6 +392,7 @@
|
|
|
385
392
|
"resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
|
|
386
393
|
"integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
|
|
387
394
|
"license": "BSD-2-Clause",
|
|
395
|
+
"peer": true,
|
|
388
396
|
"engines": {
|
|
389
397
|
"node": ">=0.12"
|
|
390
398
|
},
|
|
@@ -396,7 +404,8 @@
|
|
|
396
404
|
"version": "2.0.2",
|
|
397
405
|
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
|
|
398
406
|
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
|
|
399
|
-
"license": "MIT"
|
|
407
|
+
"license": "MIT",
|
|
408
|
+
"peer": true
|
|
400
409
|
},
|
|
401
410
|
"node_modules/find-up": {
|
|
402
411
|
"version": "4.1.0",
|
|
@@ -446,6 +455,7 @@
|
|
|
446
455
|
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
|
|
447
456
|
"integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
|
|
448
457
|
"license": "MIT",
|
|
458
|
+
"peer": true,
|
|
449
459
|
"dependencies": {
|
|
450
460
|
"@jridgewell/sourcemap-codec": "^1.5.5"
|
|
451
461
|
}
|
|
@@ -461,6 +471,7 @@
|
|
|
461
471
|
}
|
|
462
472
|
],
|
|
463
473
|
"license": "MIT",
|
|
474
|
+
"peer": true,
|
|
464
475
|
"bin": {
|
|
465
476
|
"nanoid": "bin/nanoid.cjs"
|
|
466
477
|
},
|
|
@@ -517,7 +528,8 @@
|
|
|
517
528
|
"version": "1.1.1",
|
|
518
529
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
|
519
530
|
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
|
520
|
-
"license": "ISC"
|
|
531
|
+
"license": "ISC",
|
|
532
|
+
"peer": true
|
|
521
533
|
},
|
|
522
534
|
"node_modules/pngjs": {
|
|
523
535
|
"version": "5.0.0",
|
|
@@ -547,6 +559,7 @@
|
|
|
547
559
|
}
|
|
548
560
|
],
|
|
549
561
|
"license": "MIT",
|
|
562
|
+
"peer": true,
|
|
550
563
|
"dependencies": {
|
|
551
564
|
"nanoid": "^3.3.11",
|
|
552
565
|
"picocolors": "^1.1.1",
|
|
@@ -599,6 +612,7 @@
|
|
|
599
612
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
|
600
613
|
"integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
|
|
601
614
|
"license": "BSD-3-Clause",
|
|
615
|
+
"peer": true,
|
|
602
616
|
"engines": {
|
|
603
617
|
"node": ">=0.10.0"
|
|
604
618
|
}
|
|
@@ -634,6 +648,7 @@
|
|
|
634
648
|
"resolved": "https://registry.npmjs.org/vue/-/vue-3.5.30.tgz",
|
|
635
649
|
"integrity": "sha512-hTHLc6VNZyzzEH/l7PFGjpcTvUgiaPK5mdLkbjrTeWSRcEfxFrv56g/XckIYlE9ckuobsdwqd5mk2g1sBkMewg==",
|
|
636
650
|
"license": "MIT",
|
|
651
|
+
"peer": true,
|
|
637
652
|
"dependencies": {
|
|
638
653
|
"@vue/compiler-dom": "3.5.30",
|
|
639
654
|
"@vue/compiler-sfc": "3.5.30",
|
|
@@ -650,18 +665,6 @@
|
|
|
650
665
|
}
|
|
651
666
|
}
|
|
652
667
|
},
|
|
653
|
-
"node_modules/vue3-otp-input": {
|
|
654
|
-
"version": "0.5.40",
|
|
655
|
-
"resolved": "https://registry.npmjs.org/vue3-otp-input/-/vue3-otp-input-0.5.40.tgz",
|
|
656
|
-
"integrity": "sha512-3AMYHqNz9ZDa9y7ICwcEcsJG7XdZGaLAr6IRLIl3whvseFE95F5Duc9q963HcqEbu8CeMWilkmbAt/0eZOZxow==",
|
|
657
|
-
"license": "MIT",
|
|
658
|
-
"dependencies": {
|
|
659
|
-
"vue": "^3.4.27"
|
|
660
|
-
},
|
|
661
|
-
"peerDependencies": {
|
|
662
|
-
"vue": "^3.0.*"
|
|
663
|
-
}
|
|
664
|
-
},
|
|
665
668
|
"node_modules/which-module": {
|
|
666
669
|
"version": "2.0.1",
|
|
667
670
|
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
|
package/custom/package.json
CHANGED
|
@@ -10,11 +10,9 @@
|
|
|
10
10
|
"license": "ISC",
|
|
11
11
|
"description": "",
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@loltech/vue3-2fa-code-input": "^1.1.0",
|
|
14
13
|
"@vueuse/core": "^14.3.0",
|
|
15
14
|
"@vueuse/integrations": "^14.4.0",
|
|
16
15
|
"dayjs": "^1.11.18",
|
|
17
|
-
"qrcode": "^1.5.4"
|
|
18
|
-
"vue3-otp-input": "^0.5.21"
|
|
16
|
+
"qrcode": "^1.5.4"
|
|
19
17
|
}
|
|
20
18
|
}
|
package/custom/pnpm-lock.yaml
CHANGED
|
@@ -8,9 +8,6 @@ importers:
|
|
|
8
8
|
|
|
9
9
|
.:
|
|
10
10
|
dependencies:
|
|
11
|
-
'@loltech/vue3-2fa-code-input':
|
|
12
|
-
specifier: ^1.1.0
|
|
13
|
-
version: 1.1.0
|
|
14
11
|
'@vueuse/core':
|
|
15
12
|
specifier: ^14.3.0
|
|
16
13
|
version: 14.4.0(vue@3.5.29)
|
|
@@ -23,9 +20,6 @@ importers:
|
|
|
23
20
|
qrcode:
|
|
24
21
|
specifier: ^1.5.4
|
|
25
22
|
version: 1.5.4
|
|
26
|
-
vue3-otp-input:
|
|
27
|
-
specifier: ^0.5.21
|
|
28
|
-
version: 0.5.40(vue@3.5.29)
|
|
29
23
|
|
|
30
24
|
packages:
|
|
31
25
|
|
|
@@ -49,9 +43,6 @@ packages:
|
|
|
49
43
|
'@jridgewell/sourcemap-codec@1.5.5':
|
|
50
44
|
resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
|
|
51
45
|
|
|
52
|
-
'@loltech/vue3-2fa-code-input@1.1.0':
|
|
53
|
-
resolution: {integrity: sha512-styyoQjsARSeVf6E5qMvYIVkLRrwYtfL8xDl5W+g9wKF8qlr2XGU2gi9maEg0X4Us41kJryXBzfRMUcv5tKIBA==}
|
|
54
|
-
|
|
55
46
|
'@types/web-bluetooth@0.0.21':
|
|
56
47
|
resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==}
|
|
57
48
|
|
|
@@ -262,11 +253,6 @@ packages:
|
|
|
262
253
|
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
|
|
263
254
|
engines: {node: '>=8'}
|
|
264
255
|
|
|
265
|
-
vue3-otp-input@0.5.40:
|
|
266
|
-
resolution: {integrity: sha512-3AMYHqNz9ZDa9y7ICwcEcsJG7XdZGaLAr6IRLIl3whvseFE95F5Duc9q963HcqEbu8CeMWilkmbAt/0eZOZxow==}
|
|
267
|
-
peerDependencies:
|
|
268
|
-
vue: ^3.0.*
|
|
269
|
-
|
|
270
256
|
vue@3.5.29:
|
|
271
257
|
resolution: {integrity: sha512-BZqN4Ze6mDQVNAni0IHeMJ5mwr8VAJ3MQC9FmprRhcBYENw+wOAAjRj8jfmN6FLl0j96OXbR+CjWhmAmM+QGnA==}
|
|
272
258
|
peerDependencies:
|
|
@@ -310,8 +296,6 @@ snapshots:
|
|
|
310
296
|
|
|
311
297
|
'@jridgewell/sourcemap-codec@1.5.5': {}
|
|
312
298
|
|
|
313
|
-
'@loltech/vue3-2fa-code-input@1.1.0': {}
|
|
314
|
-
|
|
315
299
|
'@types/web-bluetooth@0.0.21': {}
|
|
316
300
|
|
|
317
301
|
'@vue/compiler-core@3.5.29':
|
|
@@ -488,10 +472,6 @@ snapshots:
|
|
|
488
472
|
dependencies:
|
|
489
473
|
ansi-regex: 5.0.1
|
|
490
474
|
|
|
491
|
-
vue3-otp-input@0.5.40(vue@3.5.29):
|
|
492
|
-
dependencies:
|
|
493
|
-
vue: 3.5.29
|
|
494
|
-
|
|
495
475
|
vue@3.5.29:
|
|
496
476
|
dependencies:
|
|
497
477
|
'@vue/compiler-dom': 3.5.29
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
class="af-login-with-passkey-btn w-full gap-2"
|
|
6
6
|
@click="handleLoginWithPasskey"
|
|
7
7
|
>
|
|
8
|
-
<
|
|
8
|
+
<IconFingerprint class="w-5 h-5 text-lightPrimary dark:text-darkPrimary" />
|
|
9
9
|
{{ $t('Continue with Passkey') }}
|
|
10
10
|
</Button>
|
|
11
11
|
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
<script setup lang="ts">
|
|
17
|
-
import {
|
|
17
|
+
import { IconFingerprint } from '@iconify-prerendered/vue-lucide';
|
|
18
18
|
import { getPasskey } from "./utils.js";
|
|
19
19
|
import { callAdminForthApi } from '@/utils';
|
|
20
20
|
import { useUserStore } from '@/stores/user';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="af-two-factors-modal fixed inset-0 z-[9999] flex items-center justify-center bg-
|
|
2
|
+
<div class="af-two-factors-modal fixed inset-0 z-[9999] flex items-center justify-center bg-lightScrim dark:bg-darkScrim top-0 bottom-0 left-0 right-0 px-4"
|
|
3
3
|
v-if="modelShow && (isLoading === false)">
|
|
4
4
|
<TwoFaCard
|
|
5
5
|
:class="`af-two-factor-modal-${modalMode}`"
|
|
@@ -9,45 +9,45 @@
|
|
|
9
9
|
closable
|
|
10
10
|
@close="onCancel"
|
|
11
11
|
>
|
|
12
|
-
<pre v-if="dialogDetails" class="af-2fa-details max-h-60 overflow-auto rounded-lg border border-lightDialogBreakLine bg-
|
|
12
|
+
<pre v-if="dialogDetails" class="af-2fa-details max-h-60 overflow-auto rounded-lg border border-lightDialogBreakLine bg-lightSecondaryDarken p-3 text-xs text-lightInputText dark:border-darkDialogBreakLine dark:bg-darkSecondaryLighten dark:text-darkInputText">{{ dialogDetails }}</pre>
|
|
13
13
|
|
|
14
14
|
<div v-if="confirmItems.length" class="af-2fa-actions">
|
|
15
|
-
<p class="mb-2 text-xs font-medium uppercase tracking-wider text-
|
|
16
|
-
<ul class="af-2fa-actions-list max-h-60 overflow-auto divide-y divide-lightDialogBreakLine rounded-lg border border-lightDialogBreakLine bg-
|
|
15
|
+
<p class="mb-2 text-xs font-medium uppercase tracking-wider text-lightInputIconColor dark:text-darkInputIconColor">{{ $t('You are approving') }}</p>
|
|
16
|
+
<ul class="af-2fa-actions-list max-h-60 overflow-auto divide-y divide-lightDialogBreakLine rounded-lg border border-lightDialogBreakLine bg-lightSecondaryDarken dark:divide-darkDialogBreakLine dark:border-darkDialogBreakLine dark:bg-darkSecondaryLighten">
|
|
17
17
|
<li v-for="(item, i) in confirmItems" :key="i" class="flex gap-3 px-3.5 py-3">
|
|
18
18
|
<span aria-hidden="true" class="mt-2 w-1.5 h-1.5 shrink-0 rounded-full bg-lightPrimary dark:bg-darkPrimary"></span>
|
|
19
|
-
<span class="min-w-0 text-base leading-6 break-words text-
|
|
19
|
+
<span class="min-w-0 text-base leading-6 break-words text-lightInputText dark:text-darkInputText">{{ item }}</span>
|
|
20
20
|
</li>
|
|
21
21
|
</ul>
|
|
22
22
|
</div>
|
|
23
23
|
|
|
24
24
|
<TwoFaCodeInput v-if="modalMode === 'totp'" ref="codeInput" :invalid="Boolean(codeError)" @complete="sendConfirmationResult" />
|
|
25
25
|
<TwoFaHint v-else-if="modalMode === 'passkey'" class="af-2fa-passkey-hint">
|
|
26
|
-
<template #icon><
|
|
26
|
+
<template #icon><IconFingerprint class="w-5 h-5" /></template>
|
|
27
27
|
{{ $t('Your browser will ask for Touch ID, Windows Hello or a security key.') }}
|
|
28
28
|
</TwoFaHint>
|
|
29
|
-
<p v-else role="status" class="af-2fa-grace-notice flex items-start gap-2.5 rounded-lg border border-
|
|
30
|
-
<
|
|
29
|
+
<p v-else role="status" class="af-2fa-grace-notice flex items-start gap-2.5 rounded-lg border border-lightSuccessBorder bg-lightSuccessSoft px-3.5 py-3 text-sm leading-5 text-lightSuccess dark:border-darkSuccessBorder dark:bg-darkSuccessSoft dark:text-darkSuccess">
|
|
30
|
+
<IconCircleCheck class="w-5 h-5 shrink-0" />
|
|
31
31
|
{{ $t('You verified recently, so no code is needed.') }}
|
|
32
32
|
</p>
|
|
33
33
|
|
|
34
34
|
<template #footer>
|
|
35
35
|
<TwoFaLinkButton v-if="modalMode === 'totp' && doesUserHavePasskeys" class="af-2fa-switch-to-passkey" @click="modalMode = 'passkey'">
|
|
36
|
-
<
|
|
36
|
+
<IconFingerprint class="w-4 h-4" />
|
|
37
37
|
{{ $t('Use passkey instead') }}
|
|
38
38
|
</TwoFaLinkButton>
|
|
39
39
|
<TwoFaLinkButton v-else-if="modalMode === 'passkey'" class="af-2fa-switch-to-totp" @click="modalMode = 'totp'">
|
|
40
|
-
<
|
|
40
|
+
<IconSmartphone class="w-4 h-4" />
|
|
41
41
|
{{ $t('Use code instead') }}
|
|
42
42
|
</TwoFaLinkButton>
|
|
43
43
|
<div class="af-2fa-buttons ms-auto flex gap-2 whitespace-nowrap">
|
|
44
44
|
<Button variant="secondary" class="af-2fa-cancel-btn" @click="onCancel">{{ $t('Cancel') }}</Button>
|
|
45
45
|
<Button v-if="modalMode === 'passkey'" @click="usePasskeyButtonClick" :disabled="isFetchingPasskey" :loader="isFetchingPasskey" class="af-2fa-passkey-btn gap-2">
|
|
46
|
-
<
|
|
46
|
+
<IconFingerprint class="w-4 h-4" />
|
|
47
47
|
{{ $t('Use passkey') }}
|
|
48
48
|
</Button>
|
|
49
49
|
<Button v-else-if="modalMode === 'grace'" class="af-2fa-approve-btn gap-2" @click="approveInGracePeriod">
|
|
50
|
-
<
|
|
50
|
+
<IconCheck class="w-4 h-4" />
|
|
51
51
|
{{ approveLabel }}
|
|
52
52
|
</Button>
|
|
53
53
|
</div>
|
|
@@ -63,12 +63,7 @@
|
|
|
63
63
|
import { useI18n } from 'vue-i18n';
|
|
64
64
|
import { callAdminForthApi } from '@/utils';
|
|
65
65
|
import { Link, Button } from '@/afcl';
|
|
66
|
-
import {
|
|
67
|
-
IconCheckCircleOutline,
|
|
68
|
-
IconCheckOutline,
|
|
69
|
-
IconFingerprintOutline,
|
|
70
|
-
IconMobilePhoneOutline,
|
|
71
|
-
} from '@iconify-prerendered/vue-flowbite';
|
|
66
|
+
import { IconCheck, IconCircleCheck, IconFingerprint, IconSmartphone } from '@iconify-prerendered/vue-lucide';
|
|
72
67
|
import { getPasskey } from './utils.js'
|
|
73
68
|
import TwoFaCard from './TwoFaCard.vue';
|
|
74
69
|
import TwoFaCodeInput from './TwoFaCodeInput.vue';
|
|
@@ -1,34 +1,44 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div class="af-2fa-card relative flex flex-col w-full max-w-md overflow-hidden rounded-xl border border-lightDialogBreakLine bg-lightDialogBackgorund shadow-
|
|
2
|
+
<div class="af-2fa-card relative flex flex-col w-full max-w-md overflow-hidden rounded-xl border border-lightDialogBreakLine bg-lightDialogBackgorund shadow-popover dark:border-darkDialogBreakLine dark:bg-darkDialogBackgorund dark:shadow-darkPopover">
|
|
3
3
|
<div class="af-2fa-header flex items-start gap-3.5 border-b border-lightDialogBreakLine py-5 pl-6 dark:border-darkDialogBreakLine" :class="closable ? 'pr-4' : 'pr-6'">
|
|
4
4
|
<div class="af-2fa-icon-wrap flex w-10 h-10 shrink-0 items-center justify-center rounded-lg bg-lightPrimary/10 text-lightPrimary dark:bg-darkPrimary/20 dark:text-darkPrimary">
|
|
5
|
-
<
|
|
5
|
+
<IconShieldCheck class="af-2fa-shield-icon w-5 h-5" />
|
|
6
6
|
</div>
|
|
7
7
|
<div class="af-2fa-title-wrap flex-1 min-w-0">
|
|
8
8
|
<p class="af-2fa-title break-words text-lg leading-6 font-semibold text-lightDialogHeaderText dark:text-darkDialogHeaderText">{{ title }}</p>
|
|
9
|
-
<p class="af-2fa-subtitle mt-0.5 text-sm leading-5 text-
|
|
9
|
+
<p class="af-2fa-subtitle mt-0.5 text-sm leading-5 text-lightInputIconColor dark:text-darkInputIconColor">{{ subtitle }}</p>
|
|
10
10
|
</div>
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
<Button
|
|
12
|
+
v-if="closable"
|
|
13
|
+
type="button"
|
|
14
|
+
variant="ghost"
|
|
15
|
+
size="sm"
|
|
16
|
+
iconOnly
|
|
17
|
+
:aria-label="$t('Cancel')"
|
|
18
|
+
class="af-2fa-close-btn -mt-0.5 text-lightDialogCloseButton hover:text-lightDialogCloseButtonHover hover:bg-lightDialogCloseButtonHoverBackground dark:text-darkDialogCloseButton dark:hover:text-darkDialogCloseButtonHover dark:hover:bg-darkDialogCloseButtonHoverBackground"
|
|
19
|
+
@click="emit('close')"
|
|
20
|
+
>
|
|
21
|
+
<IconX aria-hidden="true" />
|
|
22
|
+
</Button>
|
|
14
23
|
</div>
|
|
15
24
|
|
|
16
25
|
<div class="af-2fa-body flex flex-col gap-5 px-6 pt-5 pb-6">
|
|
17
26
|
<slot />
|
|
18
|
-
<p v-if="error" role="alert" class="af-2fa-code-error -mt-2.5 flex items-center gap-1.5 text-sm text-
|
|
19
|
-
<
|
|
27
|
+
<p v-if="error" role="alert" class="af-2fa-code-error -mt-2.5 flex items-center gap-1.5 text-sm text-lightDanger dark:text-darkDanger">
|
|
28
|
+
<IconCircleAlert class="w-4 h-4 shrink-0" />
|
|
20
29
|
{{ error }}
|
|
21
30
|
</p>
|
|
22
31
|
</div>
|
|
23
32
|
|
|
24
|
-
<div v-if="$slots.footer" class="af-2fa-footer flex flex-wrap items-center gap-x-3 gap-y-2 border-t border-lightDialogBreakLine bg-
|
|
33
|
+
<div v-if="$slots.footer" class="af-2fa-footer flex flex-wrap items-center gap-x-3 gap-y-2 border-t border-lightDialogBreakLine bg-lightFormHeading py-3 pl-6 pr-5 dark:border-darkDialogBreakLine dark:bg-darkFormHeading">
|
|
25
34
|
<slot name="footer" />
|
|
26
35
|
</div>
|
|
27
36
|
</div>
|
|
28
37
|
</template>
|
|
29
38
|
|
|
30
39
|
<script setup lang="ts">
|
|
31
|
-
import {
|
|
40
|
+
import { IconCircleAlert, IconShieldCheck, IconX } from '@iconify-prerendered/vue-lucide';
|
|
41
|
+
import { Button } from '@/afcl';
|
|
32
42
|
|
|
33
43
|
defineProps<{
|
|
34
44
|
title: string;
|