Resolve mobile keyboard OTP autofill truncation in segmented inputs#10467
Resolve mobile keyboard OTP autofill truncation in segmented inputs#10467AfraHussaindeen wants to merge 1 commit into
Conversation
📝 WalkthroughWalkthroughOTP entry screens in the authentication and recovery portals now use single-input segmented flows with synchronized hidden values, guarded submit handling, and centralized paste/sanitization logic. The login theme adds matching segmented OTP styles, and the email OTP field markup receives a small attribute fix. ChangesSegmented OTP flow
Suggested reviewers
Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (1 error)
✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #10467 +/- ##
========================================
Coverage 72.72% 72.73%
========================================
Files 469 469
Lines 70888 70908 +20
Branches 484 240 -244
========================================
+ Hits 51555 51572 +17
- Misses 19037 19225 +188
+ Partials 296 111 -185 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@identity-apps-core/apps/authentication-portal/src/main/webapp/backup-code.jsp`:
- Around line 283-297: The typed-input path in handleInput() is inconsistent
with handlePaste(): paste already enforces digits-only, but typing only removes
whitespace. Update handleInput() in backup-code.jsp so it sanitizes the entered
value to digits only before assigning to backupCodeField,
updateBackupDigitBoxes(), updateBackupCursor(), and submitBtn.disabled, keeping
the behavior aligned with handlePaste() and the OTP length check.
In `@identity-apps-core/apps/authentication-portal/src/main/webapp/smsOtp.jsp`:
- Around line 279-281: The segmented SMS OTP branch in smsOtp.jsp is now using a
native submit control for `#subButton`, which can bypass the reCAPTCHA callback
path. Update the reCaptchaEnabled && otpLength <= 6 flow so submission still
goes through sub() and the data-callback="sub" token flow, including Enter-key
submits from `#OTPCode`. Make the relevant submit control and any related form
handlers in the smsOtp.jsp branches at the affected sections consistent so no
native form submit can skip the captcha gate.
In
`@identity-apps-core/apps/recovery-portal/src/main/webapp/sms-and-email-otp.jsp`:
- Around line 409-423: The OTP input sanitization is inconsistent between paste
and typing: `handlePaste()` already restricts values to `[A-Za-z0-9_]`, but
`handleInput()` only strips whitespace, allowing invalid characters to be
entered and submitted. Update `handleInput()` in `sms-and-email-otp.jsp` to
apply the same character filter used in `handlePaste()` before calling
`updateDigitBoxes()`, `updateCursor()`, and toggling `submitBtn.disabled`, so
typed input matches the segmented OTP validation rules.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: 3b9bbb58-5496-491b-b837-88d862b91230
📒 Files selected for processing (7)
identity-apps-core/apps/authentication-portal/src/main/webapp/backup-code.jspidentity-apps-core/apps/authentication-portal/src/main/webapp/emailOtp.jspidentity-apps-core/apps/authentication-portal/src/main/webapp/smsOtp.jspidentity-apps-core/apps/authentication-portal/src/main/webapp/sms_otp_verify.jspidentity-apps-core/apps/authentication-portal/src/main/webapp/totp.jspidentity-apps-core/apps/recovery-portal/src/main/webapp/sms-and-email-otp.jspmodules/theme/src/theme-core/definitions/apps/login-portal.less
| function handlePaste(e) { | ||
| var clipboardData, value; | ||
| // Stop data actually being pasted into element | ||
| e.stopPropagation(); | ||
| e.preventDefault(); | ||
| // Get pasted data via clipboard API | ||
| clipboardData = e.clipboardData || window.clipboardData; | ||
| value = clipboardData.getData('Text'); | ||
| const reg = new RegExp(/^\d+$/); | ||
| if (reg.test(value)) { | ||
| for (n = 0; n < 6; ++n) { | ||
| $("#pincode-" + (n+1)).val(value[n]); | ||
| $("#pincode-" + (n+1)).focus(); | ||
| var pasted = (e.clipboardData || window.clipboardData).getData('Text').replace(/\s/g, ''); | ||
| if (!/^\d+$/.test(pasted)) return; | ||
| backupInput.value = pasted.slice(0, <%= otpLength %>); | ||
| backupInput.dispatchEvent(new InputEvent('input', { bubbles: true })); | ||
| } | ||
|
|
||
| function handleInput() { | ||
| var sanitized = backupInput.value.replace(/\s/g, ''); | ||
| if (sanitized !== backupInput.value) backupInput.value = sanitized; | ||
| backupCodeField.value = sanitized; | ||
| updateBackupDigitBoxes(sanitized); | ||
| updateBackupCursor(sanitized); | ||
| submitBtn.disabled = sanitized.length !== <%= otpLength %>; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Match typed input sanitization to the digits-only paste path.
handlePaste() only accepts digits, but handleInput() only strips whitespace. That lets users type letters/punctuation, fill the boxes, and enable submit with values this UI is otherwise trying to reject.
Proposed fix
function handleInput() {
- var sanitized = backupInput.value.replace(/\s/g, '');
+ var sanitized = backupInput.value.replace(/\D/g, '');
if (sanitized !== backupInput.value) backupInput.value = sanitized;
backupCodeField.value = sanitized;
updateBackupDigitBoxes(sanitized);
updateBackupCursor(sanitized);
submitBtn.disabled = sanitized.length !== <%= otpLength %>;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| function handlePaste(e) { | |
| var clipboardData, value; | |
| // Stop data actually being pasted into element | |
| e.stopPropagation(); | |
| e.preventDefault(); | |
| // Get pasted data via clipboard API | |
| clipboardData = e.clipboardData || window.clipboardData; | |
| value = clipboardData.getData('Text'); | |
| const reg = new RegExp(/^\d+$/); | |
| if (reg.test(value)) { | |
| for (n = 0; n < 6; ++n) { | |
| $("#pincode-" + (n+1)).val(value[n]); | |
| $("#pincode-" + (n+1)).focus(); | |
| var pasted = (e.clipboardData || window.clipboardData).getData('Text').replace(/\s/g, ''); | |
| if (!/^\d+$/.test(pasted)) return; | |
| backupInput.value = pasted.slice(0, <%= otpLength %>); | |
| backupInput.dispatchEvent(new InputEvent('input', { bubbles: true })); | |
| } | |
| function handleInput() { | |
| var sanitized = backupInput.value.replace(/\s/g, ''); | |
| if (sanitized !== backupInput.value) backupInput.value = sanitized; | |
| backupCodeField.value = sanitized; | |
| updateBackupDigitBoxes(sanitized); | |
| updateBackupCursor(sanitized); | |
| submitBtn.disabled = sanitized.length !== <%= otpLength %>; | |
| function handlePaste(e) { | |
| e.preventDefault(); | |
| var pasted = (e.clipboardData || window.clipboardData).getData('Text').replace(/\s/g, ''); | |
| if (!/^\d+$/.test(pasted)) return; | |
| backupInput.value = pasted.slice(0, <%= otpLength %>); | |
| backupInput.dispatchEvent(new InputEvent('input', { bubbles: true })); | |
| } | |
| function handleInput() { | |
| var sanitized = backupInput.value.replace(/\D/g, ''); | |
| if (sanitized !== backupInput.value) backupInput.value = sanitized; | |
| backupCodeField.value = sanitized; | |
| updateBackupDigitBoxes(sanitized); | |
| updateBackupCursor(sanitized); | |
| submitBtn.disabled = sanitized.length !== <%= otpLength %>; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@identity-apps-core/apps/authentication-portal/src/main/webapp/backup-code.jsp`
around lines 283 - 297, The typed-input path in handleInput() is inconsistent
with handlePaste(): paste already enforces digits-only, but typing only removes
whitespace. Update handleInput() in backup-code.jsp so it sanitizes the entered
value to digits only before assigning to backupCodeField,
updateBackupDigitBoxes(), updateBackupCursor(), and submitBtn.disabled, keeping
the behavior aligned with handlePaste() and the OTP length check.
| <input type="submit" | ||
| id="subButton" | ||
| disabled |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Avoid a native submit path when segmented SMS OTP is gated by reCAPTCHA.
In the reCaptchaEnabled && otpLength <= 6 branch, #subButton is now a submit control, but the captcha flow still depends on data-callback="sub". Pressing Enter in #OTPCode can submit the form without going through sub(), so the segmented flow can bypass the client-side captcha callback/token path.
Proposed fix
- <input type="submit"
+ <input type="<%= reCaptchaEnabled ? "button" : "submit" %>"
id="subButton"
disabled
value="<%=AuthenticationEndpointUtil.i18n(resourceBundle, "authenticate")%>"
class="ui primary fluid large button" />Also applies to: 327-337, 451-460
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@identity-apps-core/apps/authentication-portal/src/main/webapp/smsOtp.jsp`
around lines 279 - 281, The segmented SMS OTP branch in smsOtp.jsp is now using
a native submit control for `#subButton`, which can bypass the reCAPTCHA callback
path. Update the reCaptchaEnabled && otpLength <= 6 flow so submission still
goes through sub() and the data-callback="sub" token flow, including Enter-key
submits from `#OTPCode`. Make the relevant submit control and any related form
handlers in the smsOtp.jsp branches at the affected sections consistent so no
native form submit can skip the captcha gate.
| function handlePaste(e) { | ||
| e.preventDefault(); | ||
| var pasted = (e.clipboardData || window.clipboardData).getData('Text').replace(/\s/g, ''); | ||
| if (!/^[a-zA-Z0-9_]+$/.test(pasted)) return; | ||
| OTPInput.value = pasted.slice(0, otpLen); | ||
| OTPInput.dispatchEvent(new InputEvent('input', { bubbles: true })); | ||
| } | ||
|
|
||
| } | ||
|
|
||
| // Handle paste events | ||
| function handlePaste(e) { | ||
| var clipboardData, value; | ||
|
|
||
| // Stop data get being pasted into element | ||
| e.stopPropagation(); | ||
| e.preventDefault(); | ||
|
|
||
| // Get pasted data via clipboard API | ||
| clipboardData = e.clipboardData || window.clipboardData; | ||
| value = clipboardData.getData('Text'); | ||
| const reg = new RegExp(/^[a-zA-Z0-9_]+$/); | ||
| if (reg.test(value)) { | ||
| for (n = 0; n < 6; ++n) { | ||
| $("#pincode-" + (n+1)).val(value[n]); | ||
| $("#pincode-" + (n+1)).focus(); | ||
| } | ||
| function handleInput() { | ||
| var sanitized = OTPInput.value.replace(/\s/g, ''); | ||
| if (sanitized !== OTPInput.value) OTPInput.value = sanitized; | ||
| updateDigitBoxes(sanitized); | ||
| updateCursor(sanitized); | ||
| if (submitBtn) submitBtn.disabled = sanitized.length !== otpLen; | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Use the same character filter for typing that you already enforce for paste.
handlePaste() rejects anything outside [A-Za-z0-9_], but handleInput() only removes whitespace. Users can type punctuation, reach otpLen, and submit values the segmented UI otherwise treats as invalid.
Proposed fix
function handleInput() {
- var sanitized = OTPInput.value.replace(/\s/g, '');
+ var sanitized = OTPInput.value.replace(/[^a-zA-Z0-9_]/g, '');
if (sanitized !== OTPInput.value) OTPInput.value = sanitized;
updateDigitBoxes(sanitized);
updateCursor(sanitized);
if (submitBtn) submitBtn.disabled = sanitized.length !== otpLen;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| function handlePaste(e) { | |
| e.preventDefault(); | |
| var pasted = (e.clipboardData || window.clipboardData).getData('Text').replace(/\s/g, ''); | |
| if (!/^[a-zA-Z0-9_]+$/.test(pasted)) return; | |
| OTPInput.value = pasted.slice(0, otpLen); | |
| OTPInput.dispatchEvent(new InputEvent('input', { bubbles: true })); | |
| } | |
| } | |
| // Handle paste events | |
| function handlePaste(e) { | |
| var clipboardData, value; | |
| // Stop data get being pasted into element | |
| e.stopPropagation(); | |
| e.preventDefault(); | |
| // Get pasted data via clipboard API | |
| clipboardData = e.clipboardData || window.clipboardData; | |
| value = clipboardData.getData('Text'); | |
| const reg = new RegExp(/^[a-zA-Z0-9_]+$/); | |
| if (reg.test(value)) { | |
| for (n = 0; n < 6; ++n) { | |
| $("#pincode-" + (n+1)).val(value[n]); | |
| $("#pincode-" + (n+1)).focus(); | |
| } | |
| function handleInput() { | |
| var sanitized = OTPInput.value.replace(/\s/g, ''); | |
| if (sanitized !== OTPInput.value) OTPInput.value = sanitized; | |
| updateDigitBoxes(sanitized); | |
| updateCursor(sanitized); | |
| if (submitBtn) submitBtn.disabled = sanitized.length !== otpLen; | |
| } | |
| function handlePaste(e) { | |
| e.preventDefault(); | |
| var pasted = (e.clipboardData || window.clipboardData).getData('Text').replace(/\s/g, ''); | |
| if (!/^[a-zA-Z0-9_]+$/.test(pasted)) return; | |
| OTPInput.value = pasted.slice(0, otpLen); | |
| OTPInput.dispatchEvent(new InputEvent('input', { bubbles: true })); | |
| } | |
| function handleInput() { | |
| var sanitized = OTPInput.value.replace(/[^a-zA-Z0-9_]/g, ''); | |
| if (sanitized !== OTPInput.value) OTPInput.value = sanitized; | |
| updateDigitBoxes(sanitized); | |
| updateCursor(sanitized); | |
| if (submitBtn) submitBtn.disabled = sanitized.length !== otpLen; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@identity-apps-core/apps/recovery-portal/src/main/webapp/sms-and-email-otp.jsp`
around lines 409 - 423, The OTP input sanitization is inconsistent between paste
and typing: `handlePaste()` already restricts values to `[A-Za-z0-9_]`, but
`handleInput()` only strips whitespace, allowing invalid characters to be
entered and submitted. Update `handleInput()` in `sms-and-email-otp.jsp` to
apply the same character filter used in `handlePaste()` before calling
`updateDigitBoxes()`, `updateCursor()`, and toggling `submitBtn.disabled`, so
typed input matches the segmented OTP validation rules.
Problem
The following issues were reported on the OTP authentication screens:
filling all boxes.
Fix
An invisible overlay
<input>is placed over the segmented digit box row. It carriesautocomplete="one-time-code"so the OS autofill banner targets it correctly, and JavaScript relays itsvalue into the visual boxes in real time.
Key changes across
totp.jsp,backup-code.jsp,smsOtp.jsp, andsms-and-email-otp.jsp:autocomplete="one-time-code", autocorrect="off", autocapitalize="off", spellcheck="false"handlePaste— intercepts paste events, strips whitespace, and populates the overlay input, triggering the input event chainhandleInput— syncs the overlay value to the hidden form field, updates digit boxes and cursor indicator, and enables/disables the submit button.segmented-otp-field,.segmented-otp-input,div[id^="pincode-"],.active-pincode,@keyframes otpCursorBlink) added tomodules/theme/src/theme-core/definitions/apps/login-portal.less, with!importanton critical positioning/transparency rules to prevent branding stylesheet overrides.sms-and-email-otp.jsp, the form element now also carries theotp-formclass so the segmented digit boxes don't wrap on mobile (mirrors the structure already present onbackup-code.jspandtotp.jsp).For
sms_otp_verify.jsp, which uses a plain text input (no segmented boxes),autocomplete="one-time-code", autocorrect="off", autocapitalize="off", and spellcheck="false"were added to enable browser/OS autofill suggestions and suppress unwanted text correction on mobile. The deadmovetoNext/handlePastefunctions referencing non-existentpincode-*elements were removed.For
emailOtp.jsp, a straycHTML attribute on the<input>(c size='30') was removed.Behaviour Change
The TOTP change aligns the input with RFC 6238, which defines TOTP codes as numeric. SMS OTP and backup codes remain unchanged in their character-set acceptance.
Before the fix
backupcode.mp4
totp.mp4
smsotp.mp4
legacy_password_recovery_emailotp.mp4
legacy_password_recovery_smsotp.mp4
After the fix
backupcode.mp4
TOTP.mp4
SMSOTP.mp4
SMSOTP_gt_6_alpha.mp4
SMSOTP_gt_6.mp4
EmailOTP.mp4
leg_pswd_rec_emailotp.mp4
leg-pswd-rec-sms.mp4
Related Issue