1212 <div class =" relative p-4 w-full max-w-md max-h-full" >
1313 <!-- Modal content -->
1414 <div class =" relative bg-white rounded-lg shadow dark:bg-gray-700 dark:shadow-black text-gray-500" >
15- <div class =" p-8 w-full max-w-md max-h-full" >
16- <div class =" m-3 " >{{$t('Please enter your authenticator code')}} </div >
17- <div class =" my-4 flex justify-center items-center " >
15+ <div class =" p-8 w-full max-w-md max-h-full custom-auth-wrapper " >
16+ <div id = " mfaCode-label " class =" m-4 " >{{$t('Please enter your authenticator code')}} </div >
17+ <div class =" my-4 w-full flex justify-center" ref = " otpRoot " >
1818 <v-otp-input
1919 ref =" code"
20+ container-class =" grid grid-cols-6 gap-3 w-full"
2021 input-classes =" bg-gray-50 text-center flex justify-center otp-input border leading-none border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-10 h-10 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
21- :conditionalClass = " ['one', 'two', 'three', 'four', 'five', 'six'] "
22+ :num-inputs = " 6 "
2223 inputType =" number"
2324 inputmode =" numeric"
24- :num-inputs =" 6"
25- v-model:value =" bindValue"
2625 :should-auto-focus =" true"
2726 :should-focus-order =" true"
27+ v-model:value =" bindValue"
2828 @on-complete =" handleOnComplete"
2929 />
3030 </div >
31- <!-- <Vue2FACodeInput v-model="code"/> -->
32- <LinkButton to =" /login" class =" w-full" >{{$t('Back to login')}}</LinkButton >
31+ <div class =" mt-6 flex justify-center" >
32+ <LinkButton
33+ to =" /login"
34+ class =" w-[290px] mx-4"
35+ >
36+ {{$t('Back to login')}}
37+ </LinkButton >
38+ </div >
3339 </div >
3440 </div >
3541 </div >
3945
4046
4147 </template >
42-
43-
48+
49+
4450 <script setup>
45-
46- import { onMounted , onBeforeUnmount , ref , watchEffect ,computed ,watch } from ' vue' ;
51+
52+ import { onMounted , nextTick , onBeforeUnmount , ref , watchEffect ,computed ,watch } from ' vue' ;
4753 import { useCoreStore } from ' @/stores/core' ;
4854 import { useUserStore } from ' @/stores/user' ;
49- import { IconEyeSolid , IconEyeSlashSolid } from ' @iconify-prerendered/vue-flowbite' ;
5055 import { callAdminForthApi , loadFile } from ' @/utils' ;
51- import { useRouter } from ' vue-router' ;
5256 import { showErrorTost } from ' @/composables/useFrontendApi' ;
5357 import { LinkButton } from ' @/afcl' ;
54- import Vue2FACodeInput from ' @loltech/vue3-2fa-code-input' ;
5558 import VOtpInput from " vue3-otp-input" ;
5659 import { useI18n } from ' vue-i18n' ;
5760
5861 const { t } = useI18n ();
5962 const code = ref (null );
63+ const otpRoot = ref (null );
64+ const bindValue = ref (' ' );
6065
6166 const handleOnComplete = (value ) => {
6267 sendCode (value);
6368 };
6469
65- const fillInput = (value ) => {
66- code .value ? .fillInput (value);
67- };
70+ function tagOtpInputs () {
71+ const root = otpRoot .value ;
72+ if (! root) return ;
73+ const inputs = root .querySelectorAll (' input.otp-input' );
74+ inputs .forEach ((el , idx ) => {
75+ el .setAttribute (' name' , ' mfaCode' );
76+ el .setAttribute (' id' , ` mfaCode-${ idx + 1 } ` );
77+ el .setAttribute (' autocomplete' , ' one-time-code' );
78+ el .setAttribute (' inputmode' , ' numeric' );
79+ el .setAttribute (' aria-labelledby' , ' mfaCode-label' );
80+ });
81+ }
6882
69- const router = useRouter ();
7083 const inProgress = ref (false );
71-
84+
7285 const coreStore = useCoreStore ();
7386 const user = useUserStore ();
7487
75-
76- // use this simple function to automatically focus on the next input
77-
78-
79-
80- const showPw = ref (false );
81-
82- const error = ref (null );
83- const totp = ref ({});
84- const totpJWT = ref (null );
85-
86-
87- function parseJwt (token ) {
88- // Split the token into its parts
89- const base64Url = token .split (' .' )[1 ];
90-
91- // Base64-decode the payload
92- const base64 = base64Url .replace (/ -/ g , ' +' ).replace (/ _/ g , ' /' );
93- const jsonPayload = decodeURIComponent (atob (base64).split (' ' ).map (function (c ) {
94- return ' %' + (' 00' + c .charCodeAt (0 ).toString (16 )).slice (- 2 );
95- }).join (' ' ));
96-
97- // Parse the JSON payload
98- return JSON .parse (jsonPayload);
99- }
100-
88+
10189 onMounted (async () => {
102- coreStore . getPublicConfig ();
103- window . addEventListener ( ' paste ' , handlePaste );
90+ await nextTick ();
91+ tagOtpInputs ( );
10492 });
10593
10694 onBeforeUnmount (() => {
161149 }
162150
163151 /**
164- * This particular piece of code makes the last input have a gap in the middle.
165- */
166- .spaced - code- input {
152+ * This particular piece of code makes the last input have a gap in the middle.
153+ */
154+ .spaced - code- input {
167155 & .vue3 - 2fa - code- input- box {
168156 & : nth- child (3 ) {
169157 @apply mr- 4 ;
175163 }
176164 }
177165 < / style>
178-
0 commit comments