Skip to content

Fix sub-org user creation failure when root SMS sender uses client-credential auth#391

Merged
SujanSanjula96 merged 1 commit into
wso2-extensions:masterfrom
VIHANGAGIT:fix/sms-client-credential-token-refresh-suborg
Jul 8, 2026
Merged

Fix sub-org user creation failure when root SMS sender uses client-credential auth#391
SujanSanjula96 merged 1 commit into
wso2-extensions:masterfrom
VIHANGAGIT:fix/sms-client-credential-token-refresh-suborg

Conversation

@VIHANGAGIT

@VIHANGAGIT VIHANGAGIT commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Purpose

  • Fixes a failure when creating a user in a sub-organization while the root org has an SMS notification sender configured with client-credential authentication.

Related Issue

Root cause

In NotificationSenderManagementServiceImpl.rebuildAuthHeaderWithNewToken:

  1. Returns early for non client-credential auth — this is why none/basic work.
  2. Reads the sender via getSMSSender(name), which is org-aware and falls back to the primary/root tenant.
  3. Mints a fresh token, then calls updateSMSSender(...) to persist it.
  4. updateSMSSender looks the resource up with the current-tenant-only getPublisherResource(name) and throws ERROR_CODE_NO_RESOURCE_EXISTS when it is absent.
  5. In a sub-org the resource exists only in the root org, so the persist throws.

Changes

1. Org-aware token persistence (NotificationSenderManagementServiceImpl)

The refresh path now persists the refreshed token to the tenant that owns the sender resource,
via a new private overload updateSMSSender(SMSSenderDTO, boolean inheritTenantSettings) (mirrors
getSMSSender(name, boolean)):

  • Owned by the current tenant (root/normal tenant, or a sub-org with its own sender) → persist as before.
  • Inherited by a sub-org (inheritTenantSettings && isOrganization && resource absent locally) → resolve the primary (root) tenant and run updateSMSSender inside a FrameworkUtils tenant flow switched to that tenant, so the refreshed token is written to the root resource.
  • Non-org / genuinely missing → still surfaces ERROR_CODE_NO_RESOURCE_EXISTS.

A tenant flow is required because ConfigurationManager exposes a tenant-scoped read
(getResourceByTenantId) but no tenant-scoped write — all writes target the current carbon-context
tenant. SMS stores the token as a plain resource attribute (auth.internal.accessToken), so the
write is a straightforward replaceResource in the primary tenant.

Persisting to root (rather than discarding) keeps the token cache coherent and shared across
sub-orgs: a later send reads the refreshed token via getSMSSender's existing primary-tenant
fallback, avoiding a fresh token mint on every OTP send.

2. Auth header cache refresh (Authentication)

buildAuthenticationHeader() now updates the cached authHeader field before returning it.
Previously it built a fresh header but left the cache untouched, so getAuthHeader() (used by the SMS
provider to attach the Bearer header on each send) could return a stale, cached token after a
refresh. Now the cache is refreshed on every build, so a subsequent getAuthHeader() returns the
freshly built header. Returned values for all auth types are unchanged — only the cache-update side effect is added.

Summary by CodeRabbit

  • Bug Fixes
    • Improved SMS sender authentication so refreshed access tokens are applied consistently during authentication header creation.
    • Ensured SMS sender configuration updates are persisted correctly, including when the sender settings belong to a primary (owning) tenant.
    • Fixed cases where inherited sender settings were not stored properly when owned by a primary tenant.

@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

rebuildAuthHeaderWithNewToken now refreshes the SMS sender token, updates the DTO, and persists the sender in the owning tenant context before rebuilding the auth header. Inherited senders are updated through a primary-tenant flow.

Changes

SMS Token Refresh Persistence

Layer / File(s) Summary
Conditional token persistence by tenant ownership
components/notification-sender-config/.../NotificationSenderManagementServiceImpl.java
Adds tenant resolution and tenant-flow switching imports, updates rebuildAuthHeaderWithNewToken, and introduces updateSMSSenderInOwningTenant to persist SMS sender changes in the owning tenant or through the primary tenant flow.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ⚠️ Warning The PR description covers purpose, issue, root cause, and changes, but most required template sections are missing or unfilled. Fill the required template sections, especially goals, approach, user stories, checklist, release note, docs, tests, security, and related links.
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main fix: sub-org user creation failure caused by root SMS sender client-credential auth.
✨ 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.

@codecov

codecov Bot commented Jun 25, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 40.30%. Comparing base (c4d0ec5) to head (993d3a3).

Additional details and impacted files
@@             Coverage Diff              @@
##             master     #391      +/-   ##
============================================
- Coverage     40.32%   40.30%   -0.02%     
+ Complexity      673      668       -5     
============================================
  Files            78       78              
  Lines          5986     5979       -7     
  Branches        896      896              
============================================
- Hits           2414     2410       -4     
+ Misses         3311     3308       -3     
  Partials        261      261              
Flag Coverage Δ
unit 28.25% <ø> (ø)

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

☔ View full report in Codecov by Harness.
📢 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.

@VIHANGAGIT
VIHANGAGIT marked this pull request as ready for review June 25, 2026 06:01

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
components/notification-sender-config/org.wso2.carbon.identity.notification.sender.tenant.config/src/test/java/org/wso2/carbon/identity/notification/sender/tenant/config/NotificationSenderManagementServiceImplTest.java (2)

295-317: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Cover the stale cached-header path.

Seed the old header before calling rebuildAuthHeaderWithNewToken; otherwise this test misses the case where getAuthHeader() returns a previously cached Bearer old-token.

🧪 Proposed test hardening
         SMSSenderDTO inputSender = constructClientCredentialSender("old-token");
+        inputSender.getAuthentication().getAuthHeader();
         Resource inheritedResource = constructClientCredentialResource("old-token");
🤖 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
`@components/notification-sender-config/org.wso2.carbon.identity.notification.sender.tenant.config/src/test/java/org/wso2/carbon/identity/notification/sender/tenant/config/NotificationSenderManagementServiceImplTest.java`
around lines 295 - 317, The test for
NotificationSenderManagementServiceImpl.rebuildAuthHeaderWithNewToken currently
only covers rebuilding from the inherited resource state and misses the stale
cached-header path. Before invoking rebuildAuthHeaderWithNewToken, seed the
sender’s cached auth header so getAuthHeader() returns the old Bearer token,
then verify the method replaces it with the new token. Use the existing helpers
and symbols constructClientCredentialSender, constructClientCredentialResource,
and retrieveNewAccessToken to keep the test focused on the cached-header
behavior.

337-352: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Assert the persisted sender carries the refreshed token.

This only verifies persistence was invoked; it would still pass if updateSMSSender received the old token. Match the argument so the owned-sender persistence contract is covered.

🧪 Proposed assertion
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
-        verify(defaultChannelConfigurationHandler, times(1)).updateSMSSender(any(SMSSenderDTO.class));
+        verify(defaultChannelConfigurationHandler, times(1)).updateSMSSender(argThat(updatedSender ->
+                "new-token".equals(updatedSender.getAuthentication().getInternalProperties().get(ACCESS_TOKEN_PROP))));
🤖 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
`@components/notification-sender-config/org.wso2.carbon.identity.notification.sender.tenant.config/src/test/java/org/wso2/carbon/identity/notification/sender/tenant/config/NotificationSenderManagementServiceImplTest.java`
around lines 337 - 352, The test for
NotificationSenderManagementServiceImpl.rebuildAuthHeaderWithNewToken only
verifies that defaultChannelConfigurationHandler.updateSMSSender was called, but
it does not assert the persisted SMSSenderDTO contains the refreshed token.
Update the verification to capture or match the argument passed to
updateSMSSender and assert it carries the new access token, so the persistence
contract for the owned sender is covered.
🤖 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
`@components/notification-sender-config/org.wso2.carbon.identity.notification.sender.tenant.config/src/main/java/org/wso2/carbon/identity/notification/sender/tenant/config/NotificationSenderManagementServiceImpl.java`:
- Around line 749-751: The refresh path in
NotificationSenderManagementServiceImpl is still returning a potentially cached
auth header because getAuthHeader() may reuse the old value after
buildAuthenticationHeader(). Update the return flow after rebuilding the header
so the method returns the freshly constructed header object from authentication,
and make sure the logic around ACCESS_TOKEN_PROP, buildAuthenticationHeader(),
and getAuthHeader() does not allow an expired bearer token to slip through.

---

Nitpick comments:
In
`@components/notification-sender-config/org.wso2.carbon.identity.notification.sender.tenant.config/src/test/java/org/wso2/carbon/identity/notification/sender/tenant/config/NotificationSenderManagementServiceImplTest.java`:
- Around line 295-317: The test for
NotificationSenderManagementServiceImpl.rebuildAuthHeaderWithNewToken currently
only covers rebuilding from the inherited resource state and misses the stale
cached-header path. Before invoking rebuildAuthHeaderWithNewToken, seed the
sender’s cached auth header so getAuthHeader() returns the old Bearer token,
then verify the method replaces it with the new token. Use the existing helpers
and symbols constructClientCredentialSender, constructClientCredentialResource,
and retrieveNewAccessToken to keep the test focused on the cached-header
behavior.
- Around line 337-352: The test for
NotificationSenderManagementServiceImpl.rebuildAuthHeaderWithNewToken only
verifies that defaultChannelConfigurationHandler.updateSMSSender was called, but
it does not assert the persisted SMSSenderDTO contains the refreshed token.
Update the verification to capture or match the argument passed to
updateSMSSender and assert it carries the new access token, so the persistence
contract for the owned sender is covered.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: de10830d-220d-4eab-b2a6-2c114bb6d67e

📥 Commits

Reviewing files that changed from the base of the PR and between c4d0ec5 and 00a4a04.

📒 Files selected for processing (2)
  • components/notification-sender-config/org.wso2.carbon.identity.notification.sender.tenant.config/src/main/java/org/wso2/carbon/identity/notification/sender/tenant/config/NotificationSenderManagementServiceImpl.java
  • components/notification-sender-config/org.wso2.carbon.identity.notification.sender.tenant.config/src/test/java/org/wso2/carbon/identity/notification/sender/tenant/config/NotificationSenderManagementServiceImplTest.java

@VIHANGAGIT
VIHANGAGIT force-pushed the fix/sms-client-credential-token-refresh-suborg branch 3 times, most recently from 4775497 to 131a846 Compare June 30, 2026 11:43

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

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 current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@components/notification-sender-config/org.wso2.carbon.identity.notification.sender.tenant.config/src/main/java/org/wso2/carbon/identity/notification/sender/tenant/config/NotificationSenderManagementServiceImpl.java`:
- Line 44: The inherited sub-org sender flow is still persisting changes by
calling updateSMSSender after switching to the primary tenant, which should only
refresh the token for the current request. Update the logic in
NotificationSenderManagementServiceImpl so the primary-tenant lookup path does
not write the sender back for inherited senders; instead, keep the refreshed
token in-flight and return it without invoking the persistence/update path. Use
the existing sender-handling flow around the sub-org/primary-tenant switch to
ensure only root-owned senders are persisted.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3dd038bb-8914-41cf-9abf-82753f896401

📥 Commits

Reviewing files that changed from the base of the PR and between 4775497 and 131a846.

📒 Files selected for processing (1)
  • components/notification-sender-config/org.wso2.carbon.identity.notification.sender.tenant.config/src/main/java/org/wso2/carbon/identity/notification/sender/tenant/config/NotificationSenderManagementServiceImpl.java

@VIHANGAGIT
VIHANGAGIT force-pushed the fix/sms-client-credential-token-refresh-suborg branch 2 times, most recently from b100c33 to 03124f5 Compare July 3, 2026 03:11
@VIHANGAGIT
VIHANGAGIT force-pushed the fix/sms-client-credential-token-refresh-suborg branch from 03124f5 to 89b7b66 Compare July 3, 2026 06:11
@VIHANGAGIT
VIHANGAGIT force-pushed the fix/sms-client-credential-token-refresh-suborg branch 2 times, most recently from ad6fdbf to 1bd23ba Compare July 6, 2026 10:07
@VIHANGAGIT
VIHANGAGIT force-pushed the fix/sms-client-credential-token-refresh-suborg branch from ae6951f to 993d3a3 Compare July 6, 2026 10:21
@jenkins-is-staging

Copy link
Copy Markdown

PR builder started
Link: https://github.com/wso2/product-is/actions/runs/28842020848

@jenkins-is-staging

Copy link
Copy Markdown

PR builder completed
Link: https://github.com/wso2/product-is/actions/runs/28842020848
Status: success

@jenkins-is-staging jenkins-is-staging left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Approving the pull request based on the successful pr build https://github.com/wso2/product-is/actions/runs/28842020848

@SujanSanjula96
SujanSanjula96 merged commit e6ec78b into wso2-extensions:master Jul 8, 2026
5 checks passed
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.

4 participants