diff --git a/frontend/src/components/auth/TotpLoginModal.vue b/frontend/src/components/auth/TotpLoginModal.vue index 0ae2f482..5f68b9a7 100644 --- a/frontend/src/components/auth/TotpLoginModal.vue +++ b/frontend/src/components/auth/TotpLoginModal.vue @@ -24,6 +24,18 @@
+ +
(['', '', '', '', '', '']) const inputRefs = ref<(HTMLInputElement | null)[]>([]) +const hiddenOtpInputRef = ref(null) // Watch for code changes and auto-submit when 6 digits are entered watch( @@ -104,6 +118,10 @@ defineExpose({ inputRefs.value.forEach(input => { if (input) input.value = '' }) + // Clear hidden autofill input + if (hiddenOtpInputRef.value) { + hiddenOtpInputRef.value.value = '' + } nextTick(() => { inputRefs.value[0]?.focus() }) @@ -126,6 +144,26 @@ const handleCodeInput = (event: Event, index: number) => { } } +// Handle autofill from password managers via the hidden autocomplete="one-time-code" input +const handleHiddenOtpInput = (event: Event) => { + const input = event.target as HTMLInputElement + const digits = input.value.replace(/[^0-9]/g, '').slice(0, 6).split('') + + digits.forEach((digit, i) => { + code.value[i] = digit + if (inputRefs.value[i]) { + inputRefs.value[i]!.value = digit + } + }) + + for (let i = digits.length; i < 6; i++) { + code.value[i] = '' + if (inputRefs.value[i]) { + inputRefs.value[i]!.value = '' + } + } +} + const handleKeydown = (event: KeyboardEvent, index: number) => { if (event.key === 'Backspace') { const input = event.target as HTMLInputElement