@adminforth/two-factors-auth 1.8.10 → 1.9.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
CHANGED
|
@@ -11,5 +11,5 @@ custom/package-lock.json
|
|
|
11
11
|
custom/package.json
|
|
12
12
|
custom/tsconfig.json
|
|
13
13
|
|
|
14
|
-
sent
|
|
15
|
-
total size is
|
|
14
|
+
sent 34,655 bytes received 134 bytes 69,578.00 bytes/sec
|
|
15
|
+
total size is 34,157 speedup is 0.98
|
|
@@ -28,14 +28,15 @@
|
|
|
28
28
|
@on-complete="handleOnComplete"
|
|
29
29
|
/>
|
|
30
30
|
</div>
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
</
|
|
31
|
+
<ErrorMessage :error="codeError" />
|
|
32
|
+
<div class="mt-6 flex justify-center">
|
|
33
|
+
<LinkButton
|
|
34
|
+
to="/login"
|
|
35
|
+
class="w-full"
|
|
36
|
+
>
|
|
37
|
+
{{$t('Back to login')}}
|
|
38
|
+
</LinkButton>
|
|
39
|
+
</div>
|
|
39
40
|
</div>
|
|
40
41
|
</div>
|
|
41
42
|
</div>
|
|
@@ -57,11 +58,13 @@
|
|
|
57
58
|
import { LinkButton } from '@/afcl';
|
|
58
59
|
import VOtpInput from "vue3-otp-input";
|
|
59
60
|
import { useI18n } from 'vue-i18n';
|
|
61
|
+
import ErrorMessage from '@/components/ErrorMessage.vue';
|
|
60
62
|
|
|
61
63
|
const { t } = useI18n();
|
|
62
64
|
const code = ref(null);
|
|
63
65
|
const otpRoot = ref(null);
|
|
64
66
|
const bindValue = ref('');
|
|
67
|
+
const codeError = ref(null);
|
|
65
68
|
|
|
66
69
|
const handleOnComplete = (value) => {
|
|
67
70
|
sendCode(value);
|
|
@@ -89,10 +92,17 @@
|
|
|
89
92
|
onMounted(async () => {
|
|
90
93
|
await nextTick();
|
|
91
94
|
tagOtpInputs();
|
|
95
|
+
document.addEventListener('focusin', handleGlobalFocusIn, true);
|
|
96
|
+
focusFirstAvailableOtpInput();
|
|
97
|
+
const rootEl = otpRoot.value;
|
|
98
|
+
rootEl && rootEl.addEventListener('focusout', handleFocusOut, true);
|
|
92
99
|
});
|
|
93
100
|
|
|
94
101
|
onBeforeUnmount(() => {
|
|
95
102
|
window.removeEventListener('paste', handlePaste);
|
|
103
|
+
document.removeEventListener('focusin', handleGlobalFocusIn, true);
|
|
104
|
+
const rootEl = otpRoot.value;
|
|
105
|
+
rootEl && rootEl.removeEventListener('focusout', handleFocusOut, true);
|
|
96
106
|
});
|
|
97
107
|
|
|
98
108
|
async function sendCode (value) {
|
|
@@ -108,7 +118,7 @@
|
|
|
108
118
|
if (resp.allowedLogin){
|
|
109
119
|
await user.finishLogin();
|
|
110
120
|
} else {
|
|
111
|
-
|
|
121
|
+
codeError.value = 'Invalid code';
|
|
112
122
|
}
|
|
113
123
|
}
|
|
114
124
|
|
|
@@ -128,6 +138,42 @@
|
|
|
128
138
|
code.value?.fillInput(pastedText);
|
|
129
139
|
}
|
|
130
140
|
}
|
|
141
|
+
|
|
142
|
+
function getOtpInputs() {
|
|
143
|
+
const root = otpRoot.value;
|
|
144
|
+
if (!root) return [];
|
|
145
|
+
return Array.from(root.querySelectorAll('input.otp-input'));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function focusFirstAvailableOtpInput() {
|
|
149
|
+
const inputs = getOtpInputs();
|
|
150
|
+
if (!inputs.length) return;
|
|
151
|
+
const firstEmpty = inputs.find((i) => !i.value);
|
|
152
|
+
(firstEmpty || inputs[0]).focus();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function handleGlobalFocusIn(event) {
|
|
156
|
+
const inputs = getOtpInputs();
|
|
157
|
+
if (!inputs.length) return;
|
|
158
|
+
const target = event.target;
|
|
159
|
+
if (!target) return;
|
|
160
|
+
if (!inputs.includes(target)) {
|
|
161
|
+
requestAnimationFrame(() => {
|
|
162
|
+
focusFirstAvailableOtpInput();
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function handleFocusOut() {
|
|
168
|
+
requestAnimationFrame(() => {
|
|
169
|
+
const inputs = getOtpInputs();
|
|
170
|
+
if (!inputs.length) return;
|
|
171
|
+
const active = document.activeElement;
|
|
172
|
+
if (!active || !inputs.includes(active)) {
|
|
173
|
+
focusFirstAvailableOtpInput();
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
}
|
|
131
177
|
</script>
|
|
132
178
|
|
|
133
179
|
<style scoped lang='scss'>
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
@on-complete="handleOnComplete"
|
|
46
46
|
/>
|
|
47
47
|
</div>
|
|
48
|
+
<ErrorMessage :error="codeError" />
|
|
48
49
|
<!-- <Vue2FACodeInput v-model="code" autofocus /> -->
|
|
49
50
|
<div class="flex flex-row gap-2.5 h-12">
|
|
50
51
|
<LinkButton to="/login" class="w-full">
|
|
@@ -78,17 +79,20 @@ import Vue2FACodeInput from '@loltech/vue3-2fa-code-input';
|
|
|
78
79
|
import VOtpInput from "vue3-otp-input";
|
|
79
80
|
import adminforth from '@/adminforth';
|
|
80
81
|
import { useI18n } from 'vue-i18n';
|
|
82
|
+
import ErrorMessage from '@/components/ErrorMessage.vue';
|
|
81
83
|
|
|
82
84
|
const { t } = useI18n();
|
|
83
85
|
|
|
84
86
|
const code = ref(null);
|
|
87
|
+
const codeError = ref(null);
|
|
88
|
+
const bindValue = ref('');
|
|
85
89
|
const handleOnComplete = (value) => {
|
|
86
90
|
sendCode(value);
|
|
87
91
|
};
|
|
88
92
|
|
|
89
93
|
const router = useRouter();
|
|
90
94
|
const inProgress = ref(false);
|
|
91
|
-
const otpRoot = ref(null);
|
|
95
|
+
const otpRoot = ref<HTMLElement | null>(null);
|
|
92
96
|
|
|
93
97
|
const coreStore = useCoreStore();
|
|
94
98
|
const user = useUserStore();
|
|
@@ -151,12 +155,19 @@ onMounted(async () => {
|
|
|
151
155
|
}
|
|
152
156
|
|
|
153
157
|
window.addEventListener('paste', handlePaste);
|
|
158
|
+
document.addEventListener('focusin', handleGlobalFocusIn, true);
|
|
154
159
|
await nextTick();
|
|
155
160
|
tagOtpInputs();
|
|
161
|
+
focusFirstAvailableOtpInput();
|
|
162
|
+
const rootEl = otpRoot.value;
|
|
163
|
+
rootEl && rootEl.addEventListener('focusout', handleFocusOut, true);
|
|
156
164
|
});
|
|
157
165
|
|
|
158
166
|
onBeforeUnmount(() => {
|
|
159
167
|
window.removeEventListener('paste', handlePaste);
|
|
168
|
+
document.removeEventListener('focusin', handleGlobalFocusIn, true);
|
|
169
|
+
const rootEl = otpRoot.value;
|
|
170
|
+
rootEl && rootEl.removeEventListener('focusout', handleFocusOut, true);
|
|
160
171
|
});
|
|
161
172
|
|
|
162
173
|
function tagOtpInputs() {
|
|
@@ -172,6 +183,42 @@ function tagOtpInputs() {
|
|
|
172
183
|
});
|
|
173
184
|
}
|
|
174
185
|
|
|
186
|
+
function getOtpInputs(): HTMLInputElement[] {
|
|
187
|
+
const root = otpRoot.value;
|
|
188
|
+
if (!root) return [];
|
|
189
|
+
return Array.from(root.querySelectorAll('input.otp-input')) as HTMLInputElement[];
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function focusFirstAvailableOtpInput(): void {
|
|
193
|
+
const inputs = getOtpInputs();
|
|
194
|
+
if (!inputs.length) return;
|
|
195
|
+
const firstEmpty = inputs.find((i) => !i.value);
|
|
196
|
+
(firstEmpty || inputs[0]).focus();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function handleGlobalFocusIn(event: FocusEvent): void {
|
|
200
|
+
const inputs = getOtpInputs();
|
|
201
|
+
if (!inputs.length) return;
|
|
202
|
+
const target = event.target as HTMLElement | null;
|
|
203
|
+
if (!target) return;
|
|
204
|
+
if (!inputs.includes(target as HTMLInputElement)) {
|
|
205
|
+
requestAnimationFrame(() => {
|
|
206
|
+
focusFirstAvailableOtpInput();
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function handleFocusOut(): void {
|
|
212
|
+
requestAnimationFrame(() => {
|
|
213
|
+
const inputs = getOtpInputs();
|
|
214
|
+
if (!inputs.length) return;
|
|
215
|
+
const active = document.activeElement as HTMLElement | null;
|
|
216
|
+
if (!active || !inputs.includes(active as HTMLInputElement)) {
|
|
217
|
+
focusFirstAvailableOtpInput();
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
175
222
|
async function sendCode (value) {
|
|
176
223
|
inProgress.value = true;
|
|
177
224
|
|
|
@@ -187,7 +234,7 @@ async function sendCode (value) {
|
|
|
187
234
|
if (resp.allowedLogin){
|
|
188
235
|
await user.finishLogin()
|
|
189
236
|
} else {
|
|
190
|
-
|
|
237
|
+
codeError.value = 'Invalid code';
|
|
191
238
|
}
|
|
192
239
|
}
|
|
193
240
|
|
|
@@ -214,7 +261,7 @@ const handleSkip = async () => {
|
|
|
214
261
|
if (resp.allowedLogin){
|
|
215
262
|
await user.finishLogin()
|
|
216
263
|
} else {
|
|
217
|
-
|
|
264
|
+
codeError.value = 'Something went wrong';
|
|
218
265
|
}
|
|
219
266
|
}
|
|
220
267
|
</script>
|
|
@@ -28,14 +28,15 @@
|
|
|
28
28
|
@on-complete="handleOnComplete"
|
|
29
29
|
/>
|
|
30
30
|
</div>
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
</
|
|
31
|
+
<ErrorMessage :error="codeError" />
|
|
32
|
+
<div class="mt-6 flex justify-center">
|
|
33
|
+
<LinkButton
|
|
34
|
+
to="/login"
|
|
35
|
+
class="w-full"
|
|
36
|
+
>
|
|
37
|
+
{{$t('Back to login')}}
|
|
38
|
+
</LinkButton>
|
|
39
|
+
</div>
|
|
39
40
|
</div>
|
|
40
41
|
</div>
|
|
41
42
|
</div>
|
|
@@ -57,11 +58,13 @@
|
|
|
57
58
|
import { LinkButton } from '@/afcl';
|
|
58
59
|
import VOtpInput from "vue3-otp-input";
|
|
59
60
|
import { useI18n } from 'vue-i18n';
|
|
61
|
+
import ErrorMessage from '@/components/ErrorMessage.vue';
|
|
60
62
|
|
|
61
63
|
const { t } = useI18n();
|
|
62
64
|
const code = ref(null);
|
|
63
65
|
const otpRoot = ref(null);
|
|
64
66
|
const bindValue = ref('');
|
|
67
|
+
const codeError = ref(null);
|
|
65
68
|
|
|
66
69
|
const handleOnComplete = (value) => {
|
|
67
70
|
sendCode(value);
|
|
@@ -89,10 +92,17 @@
|
|
|
89
92
|
onMounted(async () => {
|
|
90
93
|
await nextTick();
|
|
91
94
|
tagOtpInputs();
|
|
95
|
+
document.addEventListener('focusin', handleGlobalFocusIn, true);
|
|
96
|
+
focusFirstAvailableOtpInput();
|
|
97
|
+
const rootEl = otpRoot.value;
|
|
98
|
+
rootEl && rootEl.addEventListener('focusout', handleFocusOut, true);
|
|
92
99
|
});
|
|
93
100
|
|
|
94
101
|
onBeforeUnmount(() => {
|
|
95
102
|
window.removeEventListener('paste', handlePaste);
|
|
103
|
+
document.removeEventListener('focusin', handleGlobalFocusIn, true);
|
|
104
|
+
const rootEl = otpRoot.value;
|
|
105
|
+
rootEl && rootEl.removeEventListener('focusout', handleFocusOut, true);
|
|
96
106
|
});
|
|
97
107
|
|
|
98
108
|
async function sendCode (value) {
|
|
@@ -108,7 +118,7 @@
|
|
|
108
118
|
if (resp.allowedLogin){
|
|
109
119
|
await user.finishLogin();
|
|
110
120
|
} else {
|
|
111
|
-
|
|
121
|
+
codeError.value = 'Invalid code';
|
|
112
122
|
}
|
|
113
123
|
}
|
|
114
124
|
|
|
@@ -128,6 +138,42 @@
|
|
|
128
138
|
code.value?.fillInput(pastedText);
|
|
129
139
|
}
|
|
130
140
|
}
|
|
141
|
+
|
|
142
|
+
function getOtpInputs() {
|
|
143
|
+
const root = otpRoot.value;
|
|
144
|
+
if (!root) return [];
|
|
145
|
+
return Array.from(root.querySelectorAll('input.otp-input'));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function focusFirstAvailableOtpInput() {
|
|
149
|
+
const inputs = getOtpInputs();
|
|
150
|
+
if (!inputs.length) return;
|
|
151
|
+
const firstEmpty = inputs.find((i) => !i.value);
|
|
152
|
+
(firstEmpty || inputs[0]).focus();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function handleGlobalFocusIn(event) {
|
|
156
|
+
const inputs = getOtpInputs();
|
|
157
|
+
if (!inputs.length) return;
|
|
158
|
+
const target = event.target;
|
|
159
|
+
if (!target) return;
|
|
160
|
+
if (!inputs.includes(target)) {
|
|
161
|
+
requestAnimationFrame(() => {
|
|
162
|
+
focusFirstAvailableOtpInput();
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function handleFocusOut() {
|
|
168
|
+
requestAnimationFrame(() => {
|
|
169
|
+
const inputs = getOtpInputs();
|
|
170
|
+
if (!inputs.length) return;
|
|
171
|
+
const active = document.activeElement;
|
|
172
|
+
if (!active || !inputs.includes(active)) {
|
|
173
|
+
focusFirstAvailableOtpInput();
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
}
|
|
131
177
|
</script>
|
|
132
178
|
|
|
133
179
|
<style scoped lang='scss'>
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
@on-complete="handleOnComplete"
|
|
46
46
|
/>
|
|
47
47
|
</div>
|
|
48
|
+
<ErrorMessage :error="codeError" />
|
|
48
49
|
<!-- <Vue2FACodeInput v-model="code" autofocus /> -->
|
|
49
50
|
<div class="flex flex-row gap-2.5 h-12">
|
|
50
51
|
<LinkButton to="/login" class="w-full">
|
|
@@ -78,17 +79,20 @@ import Vue2FACodeInput from '@loltech/vue3-2fa-code-input';
|
|
|
78
79
|
import VOtpInput from "vue3-otp-input";
|
|
79
80
|
import adminforth from '@/adminforth';
|
|
80
81
|
import { useI18n } from 'vue-i18n';
|
|
82
|
+
import ErrorMessage from '@/components/ErrorMessage.vue';
|
|
81
83
|
|
|
82
84
|
const { t } = useI18n();
|
|
83
85
|
|
|
84
86
|
const code = ref(null);
|
|
87
|
+
const codeError = ref(null);
|
|
88
|
+
const bindValue = ref('');
|
|
85
89
|
const handleOnComplete = (value) => {
|
|
86
90
|
sendCode(value);
|
|
87
91
|
};
|
|
88
92
|
|
|
89
93
|
const router = useRouter();
|
|
90
94
|
const inProgress = ref(false);
|
|
91
|
-
const otpRoot = ref(null);
|
|
95
|
+
const otpRoot = ref<HTMLElement | null>(null);
|
|
92
96
|
|
|
93
97
|
const coreStore = useCoreStore();
|
|
94
98
|
const user = useUserStore();
|
|
@@ -151,12 +155,19 @@ onMounted(async () => {
|
|
|
151
155
|
}
|
|
152
156
|
|
|
153
157
|
window.addEventListener('paste', handlePaste);
|
|
158
|
+
document.addEventListener('focusin', handleGlobalFocusIn, true);
|
|
154
159
|
await nextTick();
|
|
155
160
|
tagOtpInputs();
|
|
161
|
+
focusFirstAvailableOtpInput();
|
|
162
|
+
const rootEl = otpRoot.value;
|
|
163
|
+
rootEl && rootEl.addEventListener('focusout', handleFocusOut, true);
|
|
156
164
|
});
|
|
157
165
|
|
|
158
166
|
onBeforeUnmount(() => {
|
|
159
167
|
window.removeEventListener('paste', handlePaste);
|
|
168
|
+
document.removeEventListener('focusin', handleGlobalFocusIn, true);
|
|
169
|
+
const rootEl = otpRoot.value;
|
|
170
|
+
rootEl && rootEl.removeEventListener('focusout', handleFocusOut, true);
|
|
160
171
|
});
|
|
161
172
|
|
|
162
173
|
function tagOtpInputs() {
|
|
@@ -172,6 +183,42 @@ function tagOtpInputs() {
|
|
|
172
183
|
});
|
|
173
184
|
}
|
|
174
185
|
|
|
186
|
+
function getOtpInputs(): HTMLInputElement[] {
|
|
187
|
+
const root = otpRoot.value;
|
|
188
|
+
if (!root) return [];
|
|
189
|
+
return Array.from(root.querySelectorAll('input.otp-input')) as HTMLInputElement[];
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function focusFirstAvailableOtpInput(): void {
|
|
193
|
+
const inputs = getOtpInputs();
|
|
194
|
+
if (!inputs.length) return;
|
|
195
|
+
const firstEmpty = inputs.find((i) => !i.value);
|
|
196
|
+
(firstEmpty || inputs[0]).focus();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function handleGlobalFocusIn(event: FocusEvent): void {
|
|
200
|
+
const inputs = getOtpInputs();
|
|
201
|
+
if (!inputs.length) return;
|
|
202
|
+
const target = event.target as HTMLElement | null;
|
|
203
|
+
if (!target) return;
|
|
204
|
+
if (!inputs.includes(target as HTMLInputElement)) {
|
|
205
|
+
requestAnimationFrame(() => {
|
|
206
|
+
focusFirstAvailableOtpInput();
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function handleFocusOut(): void {
|
|
212
|
+
requestAnimationFrame(() => {
|
|
213
|
+
const inputs = getOtpInputs();
|
|
214
|
+
if (!inputs.length) return;
|
|
215
|
+
const active = document.activeElement as HTMLElement | null;
|
|
216
|
+
if (!active || !inputs.includes(active as HTMLInputElement)) {
|
|
217
|
+
focusFirstAvailableOtpInput();
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
175
222
|
async function sendCode (value) {
|
|
176
223
|
inProgress.value = true;
|
|
177
224
|
|
|
@@ -187,7 +234,7 @@ async function sendCode (value) {
|
|
|
187
234
|
if (resp.allowedLogin){
|
|
188
235
|
await user.finishLogin()
|
|
189
236
|
} else {
|
|
190
|
-
|
|
237
|
+
codeError.value = 'Invalid code';
|
|
191
238
|
}
|
|
192
239
|
}
|
|
193
240
|
|
|
@@ -214,7 +261,7 @@ const handleSkip = async () => {
|
|
|
214
261
|
if (resp.allowedLogin){
|
|
215
262
|
await user.finishLogin()
|
|
216
263
|
} else {
|
|
217
|
-
|
|
264
|
+
codeError.value = 'Something went wrong';
|
|
218
265
|
}
|
|
219
266
|
}
|
|
220
267
|
</script>
|