JIT provisioning fails unconditionally for any application shared into an organization
Nobody has claimed this yet.
- Dominant language
- Java
- Stars
- 877
- Forks
- 1k
- Avg merge
- 18h 48m
- Merged PRs (30d)
- 37
Description
Description
Summary
Any federated end-user who signs in through an organization's own IdP, into an application that is shared into that organization (as opposed to natively created inside it), never gets a local WSO2 account. JIT provisioning is correctly configured and genuinely runs, but fails every time with IdentityProvisioningException: Invalid service provider name. Because every application a customer's users sign into under the Organizations feature is necessarily a shared application, this means there is no way to get a persistent local SCIM2 account for a federated organization user in this version.
Root cause (traced against carbon-identity-framework source)
-
DefaultStepBasedSequenceHandler.setThreadLocalProvisioningServiceProvider(handler/sequence/impl/DefaultStepBasedSequenceHandler.java:819-828) stashes the plain application name (ApplicationConfig.getApplicationName()) into a thread-local, with noProvisioningServiceProviderType.OAUTHset — so the one branch that would resolve the app by client ID instead of name never runs. -
DefaultInboundUserProvisioningListener.provisionEntity(line 594-627) callsOutboundProvisioningManager.getInstance().provision(entity, "<app-name>", ..., tenantDomainName, ...), wheretenantDomainNamecomes from the listener's ownresolveTenantDomain()(line 639-654). This part is genuinely organization-aware — inside an organization it correctly asksOrganizationManager.resolveTenantDomain(appResidentOrganizationId)for that organization's own tenant-domain representation, not the physical tenant's. -
OutboundProvisioningManager.provision(line 365-372) then callsApplicationManagementService.getServiceProvider(name, tenantDomain)→ApplicationManagementServiceImpl.getServiceProvider(line 1374-1407) →ApplicationDAOImpl.getApplication(line 1864) →getApplicationIdByName(line 2926-2936):int tenantId = IdentityTenantUtil.getTenantId(tenantDomain); return getApplicationIDByName(applicationName, tenantId, connection);which runs
ApplicationMgtDBQueries.LOAD_APP_ID_BY_APP_NAME— a plainWHERE APP_NAME = ? AND TENANT_ID = ?query. There is no organization-id column, join, or parameter anywhere in this path. It is a pre-Organizations, tenant-only lookup with no concept of a shared/fragment application. -
Applications genuinely do have real per-organization identity once shared —
ApplicationManagementServiceImpl.getSharedAppId(mainAppId, ownerOrgId, sharedOrgId)/getMainAppId(sharedAppId)(lines 3045-3061) map a shared/fragment application to/from its owning application, keyed by organization id, not by name+tenant-domain. This is presumably what the org-scoped Applications REST API uses (it resolves the same application correctly).OutboundProvisioningManager's SP-name resolution was never wired to this mechanism.
In one sentence: JIT provisioning correctly resolves the current organization and asks for the application in that organization's own tenant-domain terms, but the underlying application lookup is a legacy, pre-Organizations, tenant-only query that has no idea a shared/fragment application exists.
Workarounds considered
- Per-org native (non-shared) copy of the application instead of sharing it — ruled out: application names are unique per tenant, not per organization (
409 APP-60007 "already exists in tenantDomain"when attempting this), and even with a different name each standalone app gets its own OAuth client ID/secret, which breaks any client designed around one shared app +orgHandlerouting. - Disabling outbound provisioning connectors — already true in our setup (both the SP's
provisioningConfigurations.outboundProvisioningIdpsand the IdP'sprovisioning.outboundConnectors.connectorswere empty) and made no difference — the SP-name lookup fails unconditionally as part of resolving any provisioning entity, not only when an outbound connector is actually configured. - Disabling
DefaultInboundUserProvisioningListenerentirely, via a[[event_listener]]block indeployment.toml(AbstractIdentityUserOperationEventListener.isEnable()gates every hook on this). This works — the create-user event that triggers the broken lookup never fires, and login now produces a real SCIM2 account. But it is a global, server-wide switch:IdentityUtil's event-listener config is a single static map loaded once from oneidentity.xmlfor the whole server, with no per-tenant or per-organization scope anywhere in that path. It disables all outbound provisioning for every tenant on the instance, not just the organization-scoped case that's actually broken — not viable for any deployment where a different tenant on the same instance genuinely needs outbound provisioning.
Suggested fix direction
The application-mgt component already has what's needed to resolve a shared application correctly — it just isn't wired into the provisioning path. On a null result from the legacy name+tenant-domain lookup, OutboundProvisioningManager.provision() (or DefaultInboundUserProvisioningListener.provisionEntity, where the organization context is already available via PrivilegedCarbonContext.getApplicationResidentOrganizationId()) could check whether the operation is happening inside an organization and, if so, resolve via the shared-application mapping instead: find the application by name at the owning organization's tenant domain, use getSharedAppId to find its fragment id in the current organization, then load it by internal id via getServiceProvider(int appId) (ApplicationManagementServiceImpl.java:1438), sidestepping the name+tenant-domain path entirely.
Related issues
- #26544 — "SCIM outbound provisioning for sub-organizational applications" confirms the same underlying gap at the epic level ("SCIM outbound provisioning is currently limited to organization-level configuration in the root organization only"), targeted for 7.3.0-alpha, but this specific crash (JIT-triggered,
DefaultInboundUserProvisioningListener→OutboundProvisioningManager→ApplicationDAOImpl.getApplicationIdByName) is still present in the 7.3.0 GA build tested here. - #14335 — "Organization Authenticator fails to find the fragment application" is the same shape of bug (tenant-only resolution of a fragment app) in a different code path (the Organization Authenticator's own lookup), fixed back in 6.0.0-alpha3. That fix does not cover the outbound-provisioning listener's separate lookup reported here.
Steps to Reproduce
- Create organization
org-child(child oforg-parent), federating to an external OIDC IdP, with JIT provisioning enabled on that IdP:"provisioning": { "jit": { "isEnabled": true, "scheme": "PROVISION_SILENTLY", "userstore": "PRIMARY", "associateLocalUser": false, "attributeSyncMethod": "OVERRIDE_ALL", "idpGroupSyncMethod": "MERGE_WITH_EXISTING" } } - Register an application once at the tenant root and share it into
org-child(standard B2B pattern — "configure federation once per customer, share the app into every organization"). Confirmed viaGET /o/api/server/v1/applications/{id}returning"advancedConfigurations": {"fragment": true}and anisFragmentApp: "true"SP property. - A real end-user authenticates through the full redirect chain (
/oauth2/authorize?orgHandle=org-child→ external IdP login →/commonauth→ back to WSO2) and receives a valid authorization code/token. Login succeeds. GET /o/scim2/Usersonorg-childimmediately after still lists only the org's own fixture accounts — the federated user was never provisioned.- The container log for that exact request shows JIT provisioning ran and failed:
ERROR {DefaultStepBasedSequenceHandler} - User provisioning failed!
org.wso2.carbon.identity.application.authentication.framework.exception.FrameworkException:
Error while provisioning user : <federated-subject>
at DefaultProvisioningHandler.handleProvisioningException(DefaultProvisioningHandler.java:1072)
at DefaultProvisioningHandler.handleWithV2Roles(DefaultProvisioningHandler.java:215)
at DefaultStepBasedSequenceHandler.handleJitProvisioningWithV2Roles(DefaultStepBasedSequenceHandler.java:694)
at DefaultStepBasedSequenceHandler.callJitProvisioningWithV2Roles(DefaultStepBasedSequenceHandler.java:635)
at JITProvisioningPostAuthenticationHandler.callDefaultProvisioningHandler(JITProvisioningPostAuthenticationHandler.java:1133)
at JITProvisioningPostAuthenticationHandler.handleRequestFlow(JITProvisioningPostAuthenticationHandler.java:474)
at JITProvisioningPostAuthenticationHandler.handle(JITProvisioningPostAuthenticationHandler.java:178)
Caused by: org.wso2.carbon.user.core.UserStoreException: Error occurred while checking for user provisioning
at DefaultProvisioningHandler.handleUserProvisioning(DefaultProvisioningHandler.java:347)
Caused by: org.wso2.carbon.identity.provisioning.IdentityProvisioningException:
Error occurred while checking for user provisioning
at OutboundProvisioningManager.provision(OutboundProvisioningManager.java:698)
at DefaultInboundUserProvisioningListener.provisionEntity(DefaultInboundUserProvisioningListener.java:614)
at DefaultInboundUserProvisioningListener.doPreAddUser(DefaultInboundUserProvisioningListener.java:135)
Caused by: org.wso2.carbon.identity.provisioning.IdentityProvisioningException:
Invalid service provider name : <the shared application's name>
at OutboundProvisioningManager.provision(OutboundProvisioningManager.java:371)
The application named in the exception is real, currently shared into the organization, and returned correctly by the org-scoped Applications API. The failure is entirely internal to how JIT provisioning resolves it.
Please select the area issue is related to
B2B
Version
wso2/wso2is:7.3.0
Environment Details (with versions)
wso2/wso2is:7.3.0(carbon-identity-frameworkcomponent build7.10.156)- Postgres 16
- Reproduced in a devstack with WSO2 Organizations (B2B) enabled, one child organization, one federated (OIDC) IdP on the child org, and one application shared from the root tenant into that organization
Developer Checklist
- [Behavioural Change] Does this change introduce a behavioral change to the product?
- ↳ Approved by team lead
- ↳ Label
impact/behavioral-changeadded - [Migration Impact] Does this change have a migration impact?
- ↳ Migration label added (e.g.,
7.2.0-migration) - ↳ Migration issues created and linked
- [New Configuration] Does this change introduce a new configuration?
- ↳ Label
configadded - ↳ Configuration is properly documented
- [IAM CTL] Does this change affect the behavior of the IAM CTL tool?
- ↳ Necessary changes made in the IAM CTL tool
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with OutboundProvisioningManager.provision and DefaultInboundUserProvisioningListener.provisionEntity, then trace the ApplicationManagementService and ApplicationDAOImpl lookup paths named in the report. Read getSharedAppId, getMainAppId, and getServiceProvider(int appId) to understand shared-application resolution. Done means JIT provisioning creates a local SCIM2 account for a federated user signing into a shared application without the invalid service provider error.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- authentication, authorization, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100