diff --git a/changelog/entries/unreleased/feature/allow_pasting_in_auth_code_input.json b/changelog/entries/unreleased/feature/allow_pasting_in_auth_code_input.json
new file mode 100644
index 0000000000..42c62d9316
--- /dev/null
+++ b/changelog/entries/unreleased/feature/allow_pasting_in_auth_code_input.json
@@ -0,0 +1,9 @@
+{
+ "type": "feature",
+ "message": "Allow pasting in 6 digit auth code input.",
+ "issue_origin": "github",
+ "issue_number": null,
+ "domain": "core",
+ "bullet_points": [],
+ "created_at": "2025-12-17"
+}
diff --git a/web-frontend/modules/core/components/settings/twoFactorAuth/AuthCodeInput.vue b/web-frontend/modules/core/components/settings/twoFactorAuth/AuthCodeInput.vue
index 833c363d3a..ed4dc50c06 100644
--- a/web-frontend/modules/core/components/settings/twoFactorAuth/AuthCodeInput.vue
+++ b/web-frontend/modules/core/components/settings/twoFactorAuth/AuthCodeInput.vue
@@ -13,8 +13,10 @@
:class="{ 'auth-code-input__input--filled': allFilled }"
@keyup="handleKeyUp"
@keydown="handleKeyDown"
+ @paste="pasteAt(1, $event)"
/>
@@ -87,6 +98,7 @@ export default {
number5: '',
number6: '',
},
+ hasEmitted: false,
}
},
computed: {
@@ -152,10 +164,25 @@ export default {
return this.code.length === 6
},
},
+ watch: {
+ allFilled(isFilled) {
+ if (isFilled && !this.hasEmitted) {
+ this.hasEmitted = true
+ this.$emit('all-filled', this.code)
+ }
+ if (!isFilled) {
+ this.hasEmitted = false
+ }
+ },
+ },
mounted() {
this.reset()
},
methods: {
+ focusIndex(i) {
+ const el = this.$refs[`input${i}`]
+ if (el && typeof el.focus === 'function') el.focus()
+ },
reset() {
this.values.number1 = ''
this.values.number2 = ''
@@ -164,6 +191,7 @@ export default {
this.values.number5 = ''
this.values.number6 = ''
this.$refs.input1.focus()
+ this.hasEmitted = false
},
sanitizeInput(value) {
const sanitized = value.replace(/\D/g, '').slice(0, 1)
@@ -192,11 +220,31 @@ export default {
if (nextInput && nextInput.tagName === 'INPUT') {
nextInput.focus()
}
+ }
+ },
+ pasteAt(startIndex, event) {
+ event.preventDefault()
- if (this.allFilled) {
- this.$emit('all-filled', this.code)
- }
+ const raw =
+ (event.clipboardData && event.clipboardData.getData('text')) ||
+ (window.clipboardData && window.clipboardData.getData('Text')) ||
+ ''
+
+ const digits = raw.replace(/\D/g, '')
+ const maxLen = 7 - startIndex
+ const chunk = digits.slice(0, maxLen)
+
+ for (let i = startIndex; i <= 6; i++) {
+ this.values[`number${i}`] = ''
}
+
+ for (let offset = 0; offset < chunk.length; offset++) {
+ const i = startIndex + offset
+ this.values[`number${i}`] = chunk[offset]
+ }
+
+ const nextIndex = Math.min(startIndex + chunk.length, 6)
+ this.$nextTick(() => this.focusIndex(nextIndex))
},
},
}