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