Skip to content

Refactor isMultiAuthAvailable method to include current authenticator check#9725

Draft
AfraHussaindeen wants to merge 1 commit intowso2:masterfrom
AfraHussaindeen:master_multioption-uri
Draft

Refactor isMultiAuthAvailable method to include current authenticator check#9725
AfraHussaindeen wants to merge 1 commit intowso2:masterfrom
AfraHussaindeen:master_multioption-uri

Conversation

@AfraHussaindeen
Copy link
Contributor

Purpose

This pull request refactors the logic for determining the availability of multi-authentication options across authentication JSP pages. It addresses a specific state-leakage issue where a multiOptionURI from a previous authentication step could "bleed" into a subsequent single-authenticator step, causing the UI to incorrectly display a "Choose a different option" link.

Key changes include:

The isMultiAuthAvailable method in all relevant JSP files extarcted to util class and refactored to:

  • Accept a currentAuthenticator parameter to validate that the multiOptionURI belongs to the current authentication step.

  • Background:

    • login.jsp is rendered by the framework when a step has multiple authenticator options — it builds a multiOptionURI (encoding the current step's authenticator list) and embeds it as a hidden field in the authenticator JSP (e.g., emailOtp.jsp). When the user submits, multiOptionURI travels in the POST body to commonauth.
    • When the next step has only a single authenticator, login.jsp is never rendered — the framework skips it entirely and calls the authenticator directly, forwarding the same request object. That request still carries the multiOptionURI from the previous multi-option step.
    • The authenticator (e.g., TOTPAuthenticator) blindly reads it and forwards it as a query parameter to the authenticator JSP (e.g., totp.jsp).
    • Since no one replaces or clears the old multiOptionURI, the JSP sees a non-null value and incorrectly renders the "Choose a different option" link.
  • The new check extracts the authenticator list from multiOptionURI and verifies that currentAuth (the current step's authenticator, obtained via request.getParameter("authenticators")) is present in it. If absent, the URI belongs to a prior step and is disregarded.

  • Use StringUtils.isBlank instead of a plain null check, also guarding against whitespace-only and "null" string values.

  • Apply a guard-clause pattern with early returns, eliminating the mutable isMultiAuthAvailable flag variable and reducing nesting.

Related Issue

Note : The decision to implement these changes within the portal layer, as opposed to a framework-level update, was finalized after an internal discussion

CC : @indeewari

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 9, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 996d964f-737e-4701-af10-ff4864f45912

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This change extracts multi-auth availability logic into a new JSP utility and updates multiple authentication JSPs to include it. Local isMultiAuthAvailable(String) helpers are removed; calls now use isMultiAuthAvailable(String multiOptionURI, String authenticators) with authenticators sourced from the request.

Changes

Cohort / File(s) Summary
Multi-Auth Utility
identity-apps-core/apps/authentication-portal/src/main/webapp/util/authenticator-utils.jsp
Added a utility JSP providing isMultiAuthAvailable(String multiOptionURI, String currentAuthenticator) and an overload isMultiAuthAvailable(String) that encapsulate multi-option parsing, validation, backup-code exclusion, and optional current-authenticator matching.
Authentication portal JSPs (includes + call-site updates)
identity-apps-core/apps/authentication-portal/src/main/webapp/emailOtp.jsp, .../enableTOTP.jsp, .../fido2-auth.jsp, .../fido2-identifierfirst.jsp, .../identifierauth.jsp, .../pushAuth.jsp, .../pushDeviceEnrollConsent.jsp, .../pushEnroll.jsp, .../smsOtp.jsp, .../totp.jsp
Replaced local isMultiAuthAvailable(String) implementations with an include of util/authenticator-utils.jsp. Updated call sites to isMultiAuthAvailable(multiOptionURI, request.getParameter("authenticators")), added/ensured multiOptionURI null checks in conditionals, and applied minor header year updates in several files. Attention: verify include paths and that request.getParameter("authenticators") is available in all call contexts.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main refactoring: extracting isMultiAuthAvailable method and adding a current authenticator check parameter. It is concise, specific, and directly summarizes the primary change.
Description check ✅ Passed The PR description provides comprehensive details about the refactoring purpose, background context explaining the state-leakage issue, key changes, and related issue references. It substantially addresses the template requirements despite not using exact section headings.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Changeset Required ✅ Passed The pull request includes three valid changeset files in the .changeset/ directory with proper metadata specifying affected packages and patch version updates.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
identity-apps-core/apps/authentication-portal/src/main/webapp/emailOtp.jsp (1)

155-158: ⚠️ Potential issue | 🟡 Minor

Duplicate closing </h2> tag.

Line 158 has an extra </h2> that should be removed.

                 <h2>
                     <%= i18n(resourceBundle, customText, "email.otp.heading") %>
                 </h2>
-                </h2>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@identity-apps-core/apps/authentication-portal/src/main/webapp/emailOtp.jsp`
around lines 155 - 158, In emailOtp.jsp the heading block contains a duplicate
closing </h2> tag; open the file (emailOtp.jsp) and remove the extra </h2>
immediately after the i18n heading expression so only one closing </h2> follows
the <h2> that renders i18n(resourceBundle, customText, "email.otp.heading").
identity-apps-core/apps/authentication-portal/src/main/webapp/identifierauth.jsp (1)

344-346: ⚠️ Potential issue | 🟠 Major

Inconsistent usage: single-argument isMultiAuthAvailable bypasses step-ownership validation.

Unlike other identifier-first flows (e.g., fido2-identifierfirst.jsp) which call isMultiAuthAvailable(multiOptionURI, request.getParameter("authenticators")), this file uses the single-argument overload that skips authenticator-matching validation. The authenticators parameter is available in the request stream and should be validated.

Update to match the pattern used in other identifier-first flows:

Suggested change
-            isMultiAuthAvailable(multiOptionURI)) {
+            isMultiAuthAvailable(multiOptionURI, request.getParameter("authenticators"))) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@identity-apps-core/apps/authentication-portal/src/main/webapp/identifierauth.jsp`
around lines 344 - 346, The code calls
AuthenticationEndpointUtil.isMultiAuthAvailable(multiOptionURI) which skips
step-ownership/authenticator validation; change the call to the two-argument
overload by passing the authenticators parameter from the request
(request.getParameter("authenticators"))—i.e., replace
isMultiAuthAvailable(multiOptionURI) with isMultiAuthAvailable(multiOptionURI,
request.getParameter("authenticators")) after ensuring multiOptionURI is
properly encoded (Encode.forJava) and
AuthenticationEndpointUtil.isValidMultiOptionURI(multiOptionURI) remains
checked.
🧹 Nitpick comments (1)
identity-apps-core/apps/authentication-portal/src/main/webapp/util/authenticator-utils.jsp (1)

38-38: Consider replacing magic number with inline comment.

The offset 15 is the length of "authenticators=". Adding a brief inline comment would improve clarity.

-        String authSubstring = multiOptionURI.substring(startIndex + 15);
+        String authSubstring = multiOptionURI.substring(startIndex + 15); // 15 = "authenticators=".length()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@identity-apps-core/apps/authentication-portal/src/main/webapp/util/authenticator-utils.jsp`
at line 38, The substring call uses a magic number 15 which is the length of the
literal "authenticators="; update the code around the authSubstring assignment
(variable authSubstring and multiOptionURI.substring) to replace the hard-coded
15 with either multiOptionURI.indexOf("authenticators=") +
"authenticators=".length() or add a brief inline comment after the 15 explaining
it equals the length of "authenticators=" so future readers understand the
offset.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@identity-apps-core/apps/authentication-portal/src/main/webapp/util/authenticator-utils.jsp`:
- Around line 19-79: authenticator-utils.jsp uses StringUtils, Arrays, and List
inside the isMultiAuthAvailable(String,String) and isMultiAuthAvailable(String)
methods but lacks explicit imports; add JSP page import directives for
org.apache.commons.lang.StringUtils, java.util.Arrays, and java.util.List at the
top of authenticator-utils.jsp so the utility becomes self-contained and
identifierauth.jsp (which doesn't include localize.jsp) will compile.

---

Outside diff comments:
In `@identity-apps-core/apps/authentication-portal/src/main/webapp/emailOtp.jsp`:
- Around line 155-158: In emailOtp.jsp the heading block contains a duplicate
closing </h2> tag; open the file (emailOtp.jsp) and remove the extra </h2>
immediately after the i18n heading expression so only one closing </h2> follows
the <h2> that renders i18n(resourceBundle, customText, "email.otp.heading").

In
`@identity-apps-core/apps/authentication-portal/src/main/webapp/identifierauth.jsp`:
- Around line 344-346: The code calls
AuthenticationEndpointUtil.isMultiAuthAvailable(multiOptionURI) which skips
step-ownership/authenticator validation; change the call to the two-argument
overload by passing the authenticators parameter from the request
(request.getParameter("authenticators"))—i.e., replace
isMultiAuthAvailable(multiOptionURI) with isMultiAuthAvailable(multiOptionURI,
request.getParameter("authenticators")) after ensuring multiOptionURI is
properly encoded (Encode.forJava) and
AuthenticationEndpointUtil.isValidMultiOptionURI(multiOptionURI) remains
checked.

---

Nitpick comments:
In
`@identity-apps-core/apps/authentication-portal/src/main/webapp/util/authenticator-utils.jsp`:
- Line 38: The substring call uses a magic number 15 which is the length of the
literal "authenticators="; update the code around the authSubstring assignment
(variable authSubstring and multiOptionURI.substring) to replace the hard-coded
15 with either multiOptionURI.indexOf("authenticators=") +
"authenticators=".length() or add a brief inline comment after the 15 explaining
it equals the length of "authenticators=" so future readers understand the
offset.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: 516340fd-407e-4734-aed3-3868195b454d

📥 Commits

Reviewing files that changed from the base of the PR and between d3810d6 and 12d4c6d.

📒 Files selected for processing (11)
  • identity-apps-core/apps/authentication-portal/src/main/webapp/emailOtp.jsp
  • identity-apps-core/apps/authentication-portal/src/main/webapp/enableTOTP.jsp
  • identity-apps-core/apps/authentication-portal/src/main/webapp/fido2-auth.jsp
  • identity-apps-core/apps/authentication-portal/src/main/webapp/fido2-identifierfirst.jsp
  • identity-apps-core/apps/authentication-portal/src/main/webapp/identifierauth.jsp
  • identity-apps-core/apps/authentication-portal/src/main/webapp/pushAuth.jsp
  • identity-apps-core/apps/authentication-portal/src/main/webapp/pushDeviceEnrollConsent.jsp
  • identity-apps-core/apps/authentication-portal/src/main/webapp/pushEnroll.jsp
  • identity-apps-core/apps/authentication-portal/src/main/webapp/smsOtp.jsp
  • identity-apps-core/apps/authentication-portal/src/main/webapp/totp.jsp
  • identity-apps-core/apps/authentication-portal/src/main/webapp/util/authenticator-utils.jsp

@AfraHussaindeen AfraHussaindeen force-pushed the master_multioption-uri branch from 12d4c6d to 49a878c Compare March 9, 2026 15:21
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@identity-apps-core/apps/authentication-portal/src/main/webapp/util/authenticator-utils.jsp`:
- Around line 61-65: currentAuthenticator and entries in authList are
servlet-decoded (e.g., "FIDOAuthenticator:LOCAL") but the code splits on
percent-encoded delimiters ("%3A" and "%3B"), so comparisons always fail; update
all splits where "%3A" and "%3B" are used (e.g., the
currentAuthenticator.split("%3A") call and the auth -> auth.split("%3A")
mapping, and any split on "%3B" earlier around line 47) to use the decoded
delimiters ":" and ";" respectively, ensuring you trim/validate parts as needed
before comparing the authenticator names.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: adbe4c60-4c63-4ca2-ae8a-cc94be4d4096

📥 Commits

Reviewing files that changed from the base of the PR and between 12d4c6d and 49a878c.

📒 Files selected for processing (11)
  • identity-apps-core/apps/authentication-portal/src/main/webapp/emailOtp.jsp
  • identity-apps-core/apps/authentication-portal/src/main/webapp/enableTOTP.jsp
  • identity-apps-core/apps/authentication-portal/src/main/webapp/fido2-auth.jsp
  • identity-apps-core/apps/authentication-portal/src/main/webapp/fido2-identifierfirst.jsp
  • identity-apps-core/apps/authentication-portal/src/main/webapp/identifierauth.jsp
  • identity-apps-core/apps/authentication-portal/src/main/webapp/pushAuth.jsp
  • identity-apps-core/apps/authentication-portal/src/main/webapp/pushDeviceEnrollConsent.jsp
  • identity-apps-core/apps/authentication-portal/src/main/webapp/pushEnroll.jsp
  • identity-apps-core/apps/authentication-portal/src/main/webapp/smsOtp.jsp
  • identity-apps-core/apps/authentication-portal/src/main/webapp/totp.jsp
  • identity-apps-core/apps/authentication-portal/src/main/webapp/util/authenticator-utils.jsp
🚧 Files skipped from review as they are similar to previous changes (3)
  • identity-apps-core/apps/authentication-portal/src/main/webapp/totp.jsp
  • identity-apps-core/apps/authentication-portal/src/main/webapp/identifierauth.jsp
  • identity-apps-core/apps/authentication-portal/src/main/webapp/fido2-auth.jsp

@AfraHussaindeen AfraHussaindeen marked this pull request as draft March 10, 2026 04:10
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Refactors multi-auth availability detection across authentication JSPs to prevent stale multiOptionURI values from previous steps causing incorrect “choose different option” UI rendering.

Changes:

  • Extracts isMultiAuthAvailable into a shared JSP util and adds “current authenticator belongs to this URI” verification.
  • Updates multiple authenticator JSPs to include the util and call the new overload with request.getParameter("authenticators").
  • Replaces ad-hoc null checks with StringUtils.isBlank + guard clauses and removes duplicated per-page helper implementations.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
identity-apps-core/apps/authentication-portal/src/main/webapp/util/authenticator-utils.jsp Adds shared isMultiAuthAvailable helper with current-authenticator ownership check.
identity-apps-core/apps/authentication-portal/src/main/webapp/totp.jsp Includes util and uses new overload to avoid stale multiOptionURI UI.
identity-apps-core/apps/authentication-portal/src/main/webapp/smsOtp.jsp Includes util and switches to new overload with current authenticator.
identity-apps-core/apps/authentication-portal/src/main/webapp/pushEnroll.jsp Includes util and switches to new overload with current authenticator.
identity-apps-core/apps/authentication-portal/src/main/webapp/pushDeviceEnrollConsent.jsp Includes util and switches to new overload with current authenticator.
identity-apps-core/apps/authentication-portal/src/main/webapp/pushAuth.jsp Includes util and switches to new overload with current authenticator.
identity-apps-core/apps/authentication-portal/src/main/webapp/identifierauth.jsp Removes local helper and includes util.
identity-apps-core/apps/authentication-portal/src/main/webapp/fido2-identifierfirst.jsp Removes local helper, includes util, and uses new overload.
identity-apps-core/apps/authentication-portal/src/main/webapp/fido2-auth.jsp Removes local helper, includes util, and uses new overload.
identity-apps-core/apps/authentication-portal/src/main/webapp/enableTOTP.jsp Removes local helper, includes util, and uses new overload.
identity-apps-core/apps/authentication-portal/src/main/webapp/emailOtp.jsp Removes local helper, includes util, and uses new overload.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

*/
private boolean isMultiAuthAvailable(String multiOptionURI, String currentAuthenticator) {

if (StringUtils.isBlank(multiOptionURI) || "null".equals(multiOptionURI)) {
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"null"is only filtered when it matches exactly; values like" null "or different casing (e.g.,"NULL"`) will pass this guard and be treated as a valid URI. Consider normalizing before comparison (e.g., trim + case-insensitive compare) so the “null string” protection is consistent with the intent described in the PR.

Suggested change
if (StringUtils.isBlank(multiOptionURI) || "null".equals(multiOptionURI)) {
if (StringUtils.isBlank(multiOptionURI)
|| StringUtils.equalsIgnoreCase("null", StringUtils.trim(multiOptionURI))) {

Copilot uses AI. Check for mistakes.
Comment on lines +61 to +68
if (StringUtils.isNotBlank(currentAuthenticator)) {
String currentAuthName = currentAuthenticator.split(":")[0];
if (authList.stream()
.map(auth -> auth.split("%3A")[0])
.noneMatch(currentAuthName::equals)) {
return false;
}
}
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This compares an unencoded delimiter in currentAuthenticator (":") against an encoded delimiter in authList ("%3A"). If currentAuthenticator is ever passed in encoded form (e.g., contains "%3A"), currentAuthName won’t be extracted correctly and the method will incorrectly return false. Consider normalizing currentAuthenticator to the same encoding form as authList (or decoding both) before splitting/comparing.

Copilot uses AI. Check for mistakes.
return false;
}

int startIndex = multiOptionURI.indexOf("authenticators=");
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are several protocol “magic values” embedded inline ("authenticators=", the 15 offset, "%3B", "%3A", and the backup-code literal). This makes the parsing brittle and harder to update safely. Prefer deriving offsets from the actual key length (e.g., "authenticators=".length()) and centralizing these tokens as named constants (with a short comment on why the list is expected to be percent-encoded).

Copilot uses AI. Check for mistakes.
return false;
}

String authSubstring = multiOptionURI.substring(startIndex + 15);
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are several protocol “magic values” embedded inline ("authenticators=", the 15 offset, "%3B", "%3A", and the backup-code literal). This makes the parsing brittle and harder to update safely. Prefer deriving offsets from the actual key length (e.g., "authenticators=".length()) and centralizing these tokens as named constants (with a short comment on why the list is expected to be percent-encoded).

Copilot uses AI. Check for mistakes.

// Extract the authenticators list.
String authenticators = (endIndex != -1) ? authSubstring.substring(0, endIndex) : authSubstring;
List<String> authList = Arrays.asList(authenticators.split("%3B"));
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are several protocol “magic values” embedded inline ("authenticators=", the 15 offset, "%3B", "%3A", and the backup-code literal). This makes the parsing brittle and harder to update safely. Prefer deriving offsets from the actual key length (e.g., "authenticators=".length()) and centralizing these tokens as named constants (with a short comment on why the list is expected to be percent-encoded).

Copilot uses AI. Check for mistakes.
}

// Backup codes are not considered a separate "option" for the UI toggle.
if (authList.size() == 2 && authList.contains("backup-code-authenticator%3ALOCAL")) {
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are several protocol “magic values” embedded inline ("authenticators=", the 15 offset, "%3B", "%3A", and the backup-code literal). This makes the parsing brittle and harder to update safely. Prefer deriving offsets from the actual key length (e.g., "authenticators=".length()) and centralizing these tokens as named constants (with a short comment on why the list is expected to be percent-encoded).

Copilot uses AI. Check for mistakes.
~ under the License.
--%>

<%@ page import="org.apache.commons.lang.StringUtils" %>
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please double-check that this module uses Commons Lang 2 (org.apache.commons.lang.StringUtils) rather than Commons Lang 3 (org.apache.commons.lang3.StringUtils). If the runtime only includes Lang 3 (common in newer builds), this import will cause JSP compilation failures; switching to the correct package would prevent that.

Suggested change
<%@ page import="org.apache.commons.lang.StringUtils" %>
<%@ page import="org.apache.commons.lang3.StringUtils" %>

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants