Skip to content

Enable modification of sub-attributes in complex OIDC claims & fix org switch default resolution in pre-issue id token action#3205

Open
Lashen1227 wants to merge 13 commits intowso2-extensions:masterfrom
Lashen1227:fix/next/oidc-preissue-idtoken-complex-claims
Open

Enable modification of sub-attributes in complex OIDC claims & fix org switch default resolution in pre-issue id token action#3205
Lashen1227 wants to merge 13 commits intowso2-extensions:masterfrom
Lashen1227:fix/next/oidc-preissue-idtoken-complex-claims

Conversation

@Lashen1227
Copy link
Copy Markdown
Member

This pull request improves the handling of nested claims in the pre-issue id token action. It introduces support for remove and replace operations on nested claim structures, and enables add custom object claims to the id token

Nested Claim Support and Path Handling:

  • Added recursive logic in PreIssueIDTokenRequestBuilder to generate paths for nested claims, enabling precise targeting of sub-claims within complex objects.
  • Implemented extraction and handling of nested claim paths in remove and replace operations, allowing modifications deep within claim hierarchies in PreIssueIDTokenResponseProcessor.

Remove and Replace Operations for Nested Claims:

  • Added removeNestedClaim and replaceNestedClaim methods to recursively remove or update values within nested claim maps, ensuring correct updates and cleanup of empty parent claims.

Validation and Primitive Type Handling:

  • Introduced isValidMapValue to allow maps (objects) as valid claim values, extending support for complex claim types in addition to primitives and lists.

Other Improvements

  • Improved organization switching logic in user resolution to handle cases where the accessing organization is not set, defaulting to the root organization

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 17, 2026

📝 Walkthrough

Walkthrough

The changes enhance OAuth/OpenID Connect token processing by refactoring stream-based operations to explicit iteration, adding support for nested and complex claim objects including Maps and Lists, implementing nested claim removal/replacement logic, and improving organization resolution for org-switch flows.

Changes

Cohort / File(s) Summary
Token Request Building
components/org.wso2.carbon.identity.oauth/src/main/java/.../PreIssueIDTokenRequestBuilder.java
Replaced stream-based collectors with explicit iteration for path generation; added recursive nested claim path traversal via collectNestedClaimPaths helper; extended array claim handling with trailing-slash paths; improved org-switch flow by resolving accessing organization from tenant domain when null.
Token Response Processing
components/org.wso2.carbon.identity.oauth/src/main/java/.../PreIssueIDTokenResponseProcessor.java
Extended addToOtherClaims to support Map and List claim values; added nested claim removal/replacement logic with new helpers (removeNestedClaim, replaceNestedClaim, extractNestedClaimPath, isArrayIndexPath) to handle complex claim structures; routes non-array-index nested paths to specialized handlers.
Request Builder Tests
components/org.wso2.carbon.identity.oauth/src/test/java/.../PreIssueIDTokenRequestBuilderTest.java
Added test for nested path collection with invalid keys; strengthened org-switch test assertions; introduced assertComplexPaths helper; added tests for deeply nested claims and array claim path generation.
Response Processor Tests
components/org.wso2.carbon.identity.oauth/src/test/java/.../PreIssueIDTokenResponseProcessorTest.java
Renamed existing test to reflect valid object value handling; added tests for array and Map claim values; expanded coverage with nested claim removal/replacement and group operation tests; verifies complex claim storage and nested path manipulation.

Suggested reviewers

  • ashanthamara
  • ImalshaD
  • jenkins-is-staging
🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers the main improvements (nested claim support, remove/replace operations, validation) but lacks required template sections like Purpose (with issue links), Goals, Approach, User stories, Release note, Documentation, Training, Certification, Marketing, and detailed test/security information. Complete the description by adding missing required sections from the repository template, particularly Purpose with issue references, Goals, comprehensive Approach, and test coverage details.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the two main changes: enabling modification of sub-attributes in complex OIDC claims and fixing org switch default resolution in pre-issue ID token action, matching the substantial refactoring across both processor and builder classes.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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
Copy Markdown
Contributor

@wso2-engineering wso2-engineering Bot left a comment

Choose a reason for hiding this comment

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

AI Agent Log Improvement Checklist

⚠️ Warning: AI-Generated Review Comments

  • The log-related comments and suggestions in this review were generated by an AI tool to assist with identifying potential improvements. Purpose of reviewing the code for log improvements is to improve the troubleshooting capabilities of our products.
  • Please make sure to manually review and validate all suggestions before applying any changes. Not every code suggestion would make sense or add value to our purpose. Therefore, you have the freedom to decide which of the suggestions are helpful.

✅ Before merging this pull request:

  • Review all AI-generated comments for accuracy and relevance.
  • Complete and verify the table below. We need your feedback to measure the accuracy of these suggestions and the value they add. If you are rejecting a certain code suggestion, please mention the reason briefly in the suggestion for us to capture it.
Comment Accepted (Y/N) Reason
#### Log Improvement Suggestion No: 1
#### Log Improvement Suggestion No: 2
#### Log Improvement Suggestion No: 3
#### Log Improvement Suggestion No: 4

Copy link
Copy Markdown

@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: 3

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

Inline comments:
In
`@components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/openidconnect/action/preissueidtoken/execution/PreIssueIDTokenResponseProcessor.java`:
- Around line 499-506: The code rebuilds rootValue from requestIDToken
(requestIDToken.getClaim(rootClaimName)) which ignores prior modifications;
change it to fetch the current response claim state (the claim value already
stored in the operation/response object) before calling removeFromNestedMap so
nested operations apply against the latest response, not the original request.
In other words, locate where requestIDToken.getClaim(rootClaimName) is used in
PreIssueIDTokenResponseProcessor, replace it with the response's current claim
retrieval (the claim object/value maintained on the Operation or response token)
and then call removeFromNestedMap(rootValue, nestedPath, 0) on that existing
map; keep the same null/type checks and return OperationExecutionResult on
failure. Ensure the same fix is applied to the other occurrence around lines
677-679 (same method/logic).
- Around line 522-535: removeFromNestedMap currently only deletes the leaf key
and leaves empty parent maps; update removeFromNestedMap to, after recursing
into the child map (the call in removeFromNestedMap((Map<String, Object>) next,
path, index + 1)), check if the child map is now empty and if so remove the
child key from the current map and return true; preserve the existing behavior
for the leaf removal (current.remove(key) != null) and ensure you only attempt
the empty-check when next is a Map and the recursive call returned true or the
child map size == 0 so parent maps get pruned up the unwind.

In
`@components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/openidconnect/action/preissueidtoken/execution/PreIssueIDTokenResponseProcessorTest.java`:
- Around line 851-852: The test mutates a shared requestIDTokenBuilder created
in `@BeforeClass` which leaks state; fix by constructing a fresh IDToken.Builder
within each test that needs extra claims (replace uses of the shared
requestIDTokenBuilder.addClaim("rootClaim", ...) with a new local
IDToken.Builder copy or new builder instance), or move reset logic into a
`@BeforeMethod` to reinitialize requestIDTokenBuilder before each test; update the
tests that call executeProcessSuccessResponseForToken (e.g., the test using
requestIDTokenBuilder and the one at lines ~1130-1131) to pass the new per-test
builder so state is not shared across tests.
🪄 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: 48bb7270-f9f3-4230-a572-bca6297d7252

📥 Commits

Reviewing files that changed from the base of the PR and between 75bce3d and c9778e1.

📒 Files selected for processing (4)
  • components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/openidconnect/action/preissueidtoken/execution/PreIssueIDTokenRequestBuilder.java
  • components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/openidconnect/action/preissueidtoken/execution/PreIssueIDTokenResponseProcessor.java
  • components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/openidconnect/action/preissueidtoken/execution/PreIssueIDTokenRequestBuilderTest.java
  • components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/openidconnect/action/preissueidtoken/execution/PreIssueIDTokenResponseProcessorTest.java

Comment on lines +499 to +506
IDToken.Claim rootClaim = requestIDToken.getClaim(rootClaimName);
if (rootClaim == null || !(rootClaim.getValue() instanceof Map)) {
return new OperationExecutionResult(operation, OperationExecutionResult.Status.FAILURE,
"Root claim is not a complex object.");
}

Map<String, Object> rootValue = new HashMap<>((Map<String, Object>) rootClaim.getValue());
boolean removed = removeFromNestedMap(rootValue, nestedPath, 0);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Apply nested edits against the current response state.

Both nested helpers rebuild rootValue from requestIDToken, so a second nested operation on the same root claim can resurrect data that a previous operation already removed or changed.

Suggested fix
-        IDToken.Claim rootClaim = requestIDToken.getClaim(rootClaimName);
-        if (rootClaim == null || !(rootClaim.getValue() instanceof Map)) {
+        Object currentRoot = responseIDTokenDTO.getCustomOIDCClaims().get(rootClaimName);
+        if (currentRoot == null) {
+            IDToken.Claim rootClaim = requestIDToken.getClaim(rootClaimName);
+            currentRoot = rootClaim != null ? rootClaim.getValue() : null;
+        }
+        if (!(currentRoot instanceof Map)) {
             return new OperationExecutionResult(operation, OperationExecutionResult.Status.FAILURE,
                     "Root claim is not a complex object.");
         }
 
-        Map<String, Object> rootValue = new HashMap<>((Map<String, Object>) rootClaim.getValue());
+        Map<String, Object> rootValue = new HashMap<>((Map<String, Object>) currentRoot);

Also applies to: 677-679

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/openidconnect/action/preissueidtoken/execution/PreIssueIDTokenResponseProcessor.java`
around lines 499 - 506, The code rebuilds rootValue from requestIDToken
(requestIDToken.getClaim(rootClaimName)) which ignores prior modifications;
change it to fetch the current response claim state (the claim value already
stored in the operation/response object) before calling removeFromNestedMap so
nested operations apply against the latest response, not the original request.
In other words, locate where requestIDToken.getClaim(rootClaimName) is used in
PreIssueIDTokenResponseProcessor, replace it with the response's current claim
retrieval (the claim object/value maintained on the Operation or response token)
and then call removeFromNestedMap(rootValue, nestedPath, 0) on that existing
map; keep the same null/type checks and return OperationExecutionResult on
failure. Ensure the same fix is applied to the other occurrence around lines
677-679 (same method/logic).

Comment on lines +522 to +535
private boolean removeFromNestedMap(Map<String, Object> current, List<String> path, int index) {

String key = path.get(index);
if (index == path.size() - 1) {
return current.remove(key) != null;
}

Object next = current.get(key);
if (!(next instanceof Map)) {
return false;
}

return removeFromNestedMap((Map<String, Object>) next, path, index + 1);
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Prune empty parent maps after nested removals.

removeFromNestedMap deletes only the leaf. Removing /root/a/b from {a:{b:"v"}} leaves {a:{}}, so the response still contains empty objects even though replaceInNestedMap already cleans them up on unwind.

Suggested fix
     private boolean removeFromNestedMap(Map<String, Object> current, List<String> path, int index) {
 
         String key = path.get(index);
         if (index == path.size() - 1) {
             return current.remove(key) != null;
         }
 
         Object next = current.get(key);
         if (!(next instanceof Map)) {
             return false;
         }
 
-        return removeFromNestedMap((Map<String, Object>) next, path, index + 1);
+        boolean removed = removeFromNestedMap((Map<String, Object>) next, path, index + 1);
+        Map<String, Object> nextMap = (Map<String, Object>) next;
+        if (removed && nextMap.isEmpty()) {
+            current.remove(key);
+        }
+        return removed;
     }
📝 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.

Suggested change
private boolean removeFromNestedMap(Map<String, Object> current, List<String> path, int index) {
String key = path.get(index);
if (index == path.size() - 1) {
return current.remove(key) != null;
}
Object next = current.get(key);
if (!(next instanceof Map)) {
return false;
}
return removeFromNestedMap((Map<String, Object>) next, path, index + 1);
}
private boolean removeFromNestedMap(Map<String, Object> current, List<String> path, int index) {
String key = path.get(index);
if (index == path.size() - 1) {
return current.remove(key) != null;
}
Object next = current.get(key);
if (!(next instanceof Map)) {
return false;
}
boolean removed = removeFromNestedMap((Map<String, Object>) next, path, index + 1);
Map<String, Object> nextMap = (Map<String, Object>) next;
if (removed && nextMap.isEmpty()) {
current.remove(key);
}
return removed;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/openidconnect/action/preissueidtoken/execution/PreIssueIDTokenResponseProcessor.java`
around lines 522 - 535, removeFromNestedMap currently only deletes the leaf key
and leaves empty parent maps; update removeFromNestedMap to, after recursing
into the child map (the call in removeFromNestedMap((Map<String, Object>) next,
path, index + 1)), check if the child map is now empty and if so remove the
child key from the current map and return true; preserve the existing behavior
for the leaf removal (current.remove(key) != null) and ensure you only attempt
the empty-check when next is a Map and the recursive call returned true or the
child map size == 0 so parent maps get pruned up the unwind.

Comment on lines +851 to +852
requestIDTokenBuilder.addClaim("rootClaim", nestedClaim);
IDTokenDTO idTokenDTO = executeProcessSuccessResponseForToken(operationsToPerform, existingClaims);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Avoid mutating the shared requestIDTokenBuilder across tests.

This builder is created once in @BeforeClass, so adding rootClaim/complexClaim here leaks state into later tests and makes outcomes depend on execution order. Build a fresh IDToken.Builder in each test that needs extra claims, or reset the fixture in @BeforeMethod.

Also applies to: 1130-1131

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/openidconnect/action/preissueidtoken/execution/PreIssueIDTokenResponseProcessorTest.java`
around lines 851 - 852, The test mutates a shared requestIDTokenBuilder created
in `@BeforeClass` which leaks state; fix by constructing a fresh IDToken.Builder
within each test that needs extra claims (replace uses of the shared
requestIDTokenBuilder.addClaim("rootClaim", ...) with a new local
IDToken.Builder copy or new builder instance), or move reset logic into a
`@BeforeMethod` to reinitialize requestIDTokenBuilder before each test; update the
tests that call executeProcessSuccessResponseForToken (e.g., the test using
requestIDTokenBuilder and the one at lines ~1130-1131) to pass the new per-test
builder so state is not shared across tests.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 17, 2026

Codecov Report

❌ Patch coverage is 70.58824% with 30 lines in your changes missing coverage. Please review.
✅ Project coverage is 59.25%. Comparing base (3ecff83) to head (c9778e1).
⚠️ Report is 186 commits behind head on master.

Files with missing lines Patch % Lines
...en/execution/PreIssueIDTokenResponseProcessor.java 65.75% 13 Missing and 12 partials ⚠️
...token/execution/PreIssueIDTokenRequestBuilder.java 82.75% 3 Missing and 2 partials ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master    #3205      +/-   ##
============================================
+ Coverage     59.19%   59.25%   +0.05%     
- Complexity    10342    10351       +9     
============================================
  Files           711      712       +1     
  Lines         56756    58570    +1814     
  Branches      13685    14096     +411     
============================================
+ Hits          33598    34703    +1105     
- Misses        18673    19255     +582     
- Partials       4485     4612     +127     
Flag Coverage Δ
unit 43.02% <70.58%> (+0.25%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Lashen1227 Lashen1227 changed the title [next] Enable modification of sub-attributes in complex OIDC claims & fix org switch default resolution in pre-issue id token action Enable modification of sub-attributes in complex OIDC claims & fix org switch default resolution in pre-issue id token action May 7, 2026
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.

1 participant