From d70822eb1cd8f2a105399a13c01c6225a0e241a2 Mon Sep 17 00:00:00 2001
From: Shan Chathusanda Jayathilaka <shanchathusanda@gmail.com>
Date: Sat, 25 Jan 2025 15:23:02 +0530
Subject: [PATCH] Allow sub organization applications to issue tokens to access
 the resources in sub organizations

---
 .../carbon/identity/oauth2/OAuth2Service.java | 27 ++++++++++--
 .../BasicAuthClientAuthenticator.java         | 25 ++++++++++-
 .../OAuthClientAuthnService.java              | 20 ++++++++-
 .../oauth2/dao/AccessTokenDAOImpl.java        | 20 ++++++++-
 .../oauth2/dao/TokenManagementDAOImpl.java    | 25 +++++++++--
 .../oauth2/token/AccessTokenIssuer.java       | 31 ++++++++++++-
 .../identity/oauth2/token/JWTTokenIssuer.java | 43 ++++++++++++++++--
 .../AbstractAuthorizationGrantHandler.java    | 35 +++++++++++++--
 .../handlers/grant/PasswordGrantHandler.java  | 34 +++++++++++++-
 .../handlers/grant/RefreshGrantHandler.java   | 12 ++---
 .../identity/oauth2/util/AuthzUtil.java       | 18 +++++---
 .../carbon/identity/oauth2/util/JWTUtils.java | 26 ++++++++++-
 .../DefaultOAuth2ScopeValidator.java          | 12 +++--
 .../validators/TokenValidationHandler.java    | 33 +++++++++++++-
 .../openidconnect/DefaultIDTokenBuilder.java  | 16 +++++--
 .../identity/oauth2/OAuth2ServiceTest.java    | 25 ++++++++---
 .../BasicAuthClientAuthenticatorTest.java     |  8 ++--
 .../OAuthClientAuthnServiceTest.java          |  4 +-
 .../oauth2/token/JWTTokenIssuerTest.java      | 44 +++++++++++++++----
 ...AbstractAuthorizationGrantHandlerTest.java | 23 ++++++++--
 .../grant/PasswordGrantHandlerTest.java       | 13 ++++++
 .../TokenValidationHandlerTest.java           |  7 ++-
 22 files changed, 436 insertions(+), 65 deletions(-)

diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/OAuth2Service.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/OAuth2Service.java
index d32a2abc8c1..c99a58a8771 100644
--- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/OAuth2Service.java
+++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/OAuth2Service.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, WSO2 LLC. (http://www.wso2.org) All Rights Reserved.
+ * Copyright (c) 2013-2025, WSO2 LLC. (http://www.wso2.org) All Rights Reserved.
  *
  * WSO2 Inc. licenses this file to you under the Apache License,
  * Version 2.0 (the "License"); you may not use this file except
@@ -23,6 +23,7 @@
 import org.apache.commons.logging.LogFactory;
 import org.apache.oltu.oauth2.common.message.types.GrantType;
 import org.owasp.encoder.Encode;
+import org.wso2.carbon.context.PrivilegedCarbonContext;
 import org.wso2.carbon.core.AbstractAdmin;
 import org.wso2.carbon.identity.base.IdentityException;
 import org.wso2.carbon.identity.central.log.mgt.utils.LogConstants;
@@ -63,6 +64,7 @@
 import org.wso2.carbon.identity.oauth2.token.bindings.TokenBinder;
 import org.wso2.carbon.identity.oauth2.util.OAuth2Util;
 import org.wso2.carbon.identity.openidconnect.model.Constants;
+import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementException;
 import org.wso2.carbon.user.api.Claim;
 import org.wso2.carbon.user.core.UserStoreManager;
 import org.wso2.carbon.utils.DiagnosticLog;
@@ -676,7 +678,9 @@ public OAuthRevocationResponseDTO revokeTokenByOAuthClient(OAuthRevocationReques
 
                 } else if (accessTokenDO != null) {
                     if (revokeRequestDTO.getConsumerKey().equals(accessTokenDO.getConsumerKey())) {
-                        if ((OAuth2Util.getAppInformationByClientId(accessTokenDO.getConsumerKey()).
+                        // Extracting the application details with consumer key and tenant domain.
+                        String tenantDomain = IdentityTenantUtil.getTenantDomain(accessTokenDO.getTenantID());
+                        if ((OAuth2Util.getAppInformationByClientId(accessTokenDO.getConsumerKey(), tenantDomain).
                                 isTokenBindingValidationEnabled()) && (!isValidTokenBinding(accessTokenDO.
                                 getTokenBinding(), revokeRequestDTO.getRequest()))) {
                             if (LoggerUtils.isDiagnosticLogsEnabled()) {
@@ -981,7 +985,24 @@ public Claim[] getUserClaims(String accessTokenIdentifier) {
     public String getOauthApplicationState(String consumerKey) {
 
         try {
-            OAuthAppDO appDO = OAuth2Util.getAppInformationByClientId(consumerKey);
+            String tenantDomain = IdentityTenantUtil.getTenantDomain(IdentityTenantUtil.getLoginTenantId());
+            String appOrgId = PrivilegedCarbonContext.getThreadLocalCarbonContext()
+                    .getApplicationResidentOrganizationId();
+            /*
+             If appOrgId is not empty, then the request comes for an application which is registered directly in the
+             organization of the appOrgId. Therefore, we need to resolve the tenant domain of the organization.
+            */
+            if (StringUtils.isNotEmpty(appOrgId)) {
+                try {
+                    tenantDomain = OAuthComponentServiceHolder.getInstance().getOrganizationManager()
+                            .resolveTenantDomain(appOrgId);
+                } catch (OrganizationManagementException e) {
+                    throw new IdentityOAuth2Exception("Error while resolving tenant domain for the organization ID: " +
+                            appOrgId, e);
+                }
+            }
+            // Getting the application information by consumer key and tenant domain.
+            OAuthAppDO appDO = OAuth2Util.getAppInformationByClientId(consumerKey, tenantDomain);
             return appDO.getState();
         } catch (IdentityOAuth2Exception e) {
             log.error("Error while finding application state for application with client_id: " + consumerKey, e);
diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/client/authentication/BasicAuthClientAuthenticator.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/client/authentication/BasicAuthClientAuthenticator.java
index 891fe5f1b15..e282643502c 100644
--- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/client/authentication/BasicAuthClientAuthenticator.java
+++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/client/authentication/BasicAuthClientAuthenticator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
+ * Copyright (c) 2018-2025, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
  *
  * WSO2 Inc. licenses this file to you under the Apache License,
  * Version 2.0 (the "License"); you may not use this file except
@@ -24,13 +24,17 @@
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.oltu.oauth2.common.OAuth;
+import org.wso2.carbon.context.PrivilegedCarbonContext;
+import org.wso2.carbon.identity.core.util.IdentityTenantUtil;
 import org.wso2.carbon.identity.oauth.IdentityOAuthAdminException;
 import org.wso2.carbon.identity.oauth.common.OAuth2ErrorCodes;
 import org.wso2.carbon.identity.oauth.common.exception.InvalidOAuthClientException;
+import org.wso2.carbon.identity.oauth.internal.OAuthComponentServiceHolder;
 import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception;
 import org.wso2.carbon.identity.oauth2.bean.OAuthClientAuthnContext;
 import org.wso2.carbon.identity.oauth2.model.ClientAuthenticationMethodModel;
 import org.wso2.carbon.identity.oauth2.util.OAuth2Util;
+import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementException;
 
 import java.util.ArrayList;
 import java.util.Base64;
@@ -89,8 +93,25 @@ public boolean authenticateClient(HttpServletRequest request, Map<String, List>
                 log.debug("Authenticating client : " + oAuthClientAuthnContext.getClientId() + " with client " +
                         "secret.");
             }
+            String tenantDomain = IdentityTenantUtil.resolveTenantDomain();
+            String appOrgId = PrivilegedCarbonContext.getThreadLocalCarbonContext()
+                    .getApplicationResidentOrganizationId();
+            /*
+             If appOrgId is not empty, then the request comes for an application which is registered directly in the
+             organization of the appOrgId. Therefore, we need to resolve the tenant domain of the organization.
+            */
+            if (StringUtils.isNotEmpty(appOrgId)) {
+                try {
+                    tenantDomain = OAuthComponentServiceHolder.getInstance().getOrganizationManager()
+                            .resolveTenantDomain(appOrgId);
+                } catch (OrganizationManagementException e) {
+                    throw new InvalidOAuthClientException("Error while resolving tenant domain for the organization " +
+                            "ID: " + appOrgId, e);
+                }
+            }
+            // Authenticating the client with the client id, the client secret and the extracted tenant domain.
             return OAuth2Util.authenticateClient(oAuthClientAuthnContext.getClientId(),
-                    (String) oAuthClientAuthnContext.getParameter(OAuth.OAUTH_CLIENT_SECRET));
+                    (String) oAuthClientAuthnContext.getParameter(OAuth.OAUTH_CLIENT_SECRET), tenantDomain);
         } catch (IdentityOAuthAdminException e) {
             throw new OAuthClientAuthnException("Error while authenticating client",
                     OAuth2ErrorCodes.INVALID_CLIENT, e);
diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/client/authentication/OAuthClientAuthnService.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/client/authentication/OAuthClientAuthnService.java
index a36fe3f1251..bed9461bbab 100644
--- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/client/authentication/OAuthClientAuthnService.java
+++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/client/authentication/OAuthClientAuthnService.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
+ * Copyright (c) 2018-2025, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
  *
  * WSO2 Inc. licenses this file to you under the Apache License,
  * Version 2.0 (the "License"); you may not use this file except
@@ -21,16 +21,19 @@
 import org.apache.commons.lang.StringUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.wso2.carbon.context.PrivilegedCarbonContext;
 import org.wso2.carbon.identity.core.util.IdentityTenantUtil;
 import org.wso2.carbon.identity.core.util.IdentityUtil;
 import org.wso2.carbon.identity.oauth.common.OAuth2ErrorCodes;
 import org.wso2.carbon.identity.oauth.common.exception.InvalidOAuthClientException;
 import org.wso2.carbon.identity.oauth.dao.OAuthAppDO;
+import org.wso2.carbon.identity.oauth.internal.OAuthComponentServiceHolder;
 import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception;
 import org.wso2.carbon.identity.oauth2.bean.OAuthClientAuthnContext;
 import org.wso2.carbon.identity.oauth2.internal.OAuth2ServiceComponentHolder;
 import org.wso2.carbon.identity.oauth2.model.ClientAuthenticationMethodModel;
 import org.wso2.carbon.identity.oauth2.util.OAuth2Util;
+import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementException;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -321,6 +324,21 @@ private List<OAuthClientAuthenticator> getConfiguredClientAuthMethods(String cli
             throws OAuthClientAuthnException, InvalidOAuthClientException {
 
         String tenantDomain = IdentityTenantUtil.resolveTenantDomain();
+        String appOrgId = PrivilegedCarbonContext.getThreadLocalCarbonContext()
+                .getApplicationResidentOrganizationId();
+        /*
+         If appOrgId is not empty, then the request comes for an application which is registered directly in the
+         organization of the appOrgId. Therefore, we need to resolve the tenant domain of the organization.
+        */
+        if (StringUtils.isNotEmpty(appOrgId)) {
+            try {
+                tenantDomain = OAuthComponentServiceHolder.getInstance().getOrganizationManager()
+                        .resolveTenantDomain(appOrgId);
+            } catch (OrganizationManagementException e) {
+                throw new InvalidOAuthClientException("Error while resolving tenant domain for the organization ID: " +
+                        appOrgId, e);
+            }
+        }
         List<String> configuredClientAuthMethods = new ArrayList<>();
         try {
             OAuthAppDO oAuthAppDO = OAuth2Util.getAppInformationByClientId(clientId, tenantDomain);
diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/AccessTokenDAOImpl.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/AccessTokenDAOImpl.java
index 64fac73b8e2..c3e8017dffb 100644
--- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/AccessTokenDAOImpl.java
+++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/AccessTokenDAOImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017-2024, WSO2 LLC. (http://www.wso2.com).
+ * Copyright (c) 2017-2025, WSO2 LLC. (http://www.wso2.com).
  *
  * WSO2 LLC. licenses this file to you under the Apache License,
  * Version 2.0 (the "License"); you may not use this file except
@@ -24,6 +24,7 @@
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
+import org.wso2.carbon.context.PrivilegedCarbonContext;
 import org.wso2.carbon.database.utils.jdbc.JdbcTemplate;
 import org.wso2.carbon.database.utils.jdbc.exceptions.DataAccessException;
 import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser;
@@ -251,6 +252,23 @@ private void insertAccessToken(String accessToken, String consumerKey, AccessTok
             insertTokenPrepStmt.setString(19, authorizedOrganization);
 
             int appTenantId = IdentityTenantUtil.getLoginTenantId();
+            String applicationResidentOrgId = PrivilegedCarbonContext.getThreadLocalCarbonContext()
+                    .getApplicationResidentOrganizationId();
+            /*
+             If applicationResidentOrgId is not empty, then the request comes for an application which is registered
+             directly in the organization of the applicationResidentOrgId. Therefore, we need to resolve the
+             tenant domain of the organization to get the application tenant id.
+            */
+            if (StringUtils.isNotEmpty(applicationResidentOrgId)) {
+                try {
+                    String tenantDomain = OAuthComponentServiceHolder.getInstance().getOrganizationManager()
+                            .resolveTenantDomain(applicationResidentOrgId);
+                    appTenantId = OAuth2Util.getTenantId(tenantDomain);
+                } catch (OrganizationManagementException e) {
+                    throw new IdentityOAuth2Exception("Error while resolving tenant domain from the organization id: "
+                            + applicationResidentOrgId, e);
+                }
+            }
             if (OAuth2ServiceComponentHolder.isIDPIdColumnEnabled()) {
                 if (OAuth2ServiceComponentHolder.isConsentedTokenColumnEnabled()) {
                     insertTokenPrepStmt.setString(20, Boolean.toString(accessTokenDO.isConsentedToken()));
diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/TokenManagementDAOImpl.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/TokenManagementDAOImpl.java
index aa7fe011a8c..cda4960b59a 100644
--- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/TokenManagementDAOImpl.java
+++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/TokenManagementDAOImpl.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017-2023, WSO2 LLC. (http://www.wso2.com).
+ * Copyright (c) 2017-2025, WSO2 LLC. (http://www.wso2.com).
  *
  * WSO2 LLC. licenses this file to you under the Apache License,
  * Version 2.0 (the "License"); you may not use this file except
@@ -24,6 +24,7 @@
 import org.apache.commons.lang3.tuple.Pair;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.wso2.carbon.context.PrivilegedCarbonContext;
 import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser;
 import org.wso2.carbon.identity.application.common.IdentityApplicationManagementException;
 import org.wso2.carbon.identity.application.common.model.ServiceProvider;
@@ -163,7 +164,25 @@ public RefreshTokenValidationDataDO validateRefreshToken(String consumerKey, Str
             prepStmt = connection.prepareStatement(sql);
 
             prepStmt.setString(1, getPersistenceProcessor().getProcessedClientId(consumerKey));
-            prepStmt.setInt(2, IdentityTenantUtil.getLoginTenantId());
+            int tenantId = IdentityTenantUtil.getLoginTenantId();
+            String applicationResidentOrgId = PrivilegedCarbonContext.getThreadLocalCarbonContext()
+                    .getApplicationResidentOrganizationId();
+            /*
+             If applicationResidentOrgId is not empty, then the request comes for an application which is registered
+             directly in the organization of the applicationResidentOrgId. Therefore, we need to resolve the
+             tenant domain of the organization to get the application tenant id.
+            */
+            if (StringUtils.isNotEmpty(applicationResidentOrgId)) {
+                try {
+                    String tenantDomain = OAuth2ServiceComponentHolder.getInstance().getOrganizationManager()
+                            .resolveTenantDomain(applicationResidentOrgId);
+                    tenantId = IdentityTenantUtil.getTenantId(tenantDomain);
+                } catch (OrganizationManagementException e) {
+                    throw new IdentityOAuth2Exception("Error while resolving tenant domain from the organization id: "
+                            + applicationResidentOrgId, e);
+                }
+            }
+            prepStmt.setInt(2, tenantId);
             if (refreshToken != null) {
                 prepStmt.setString(3, getHashingPersistenceProcessor().getProcessedRefreshToken(refreshToken));
             }
@@ -183,7 +202,7 @@ public RefreshTokenValidationDataDO validateRefreshToken(String consumerKey, Str
                         validationDataDO.setAccessToken(resultSet.getString(1));
                     }
                     String userName = resultSet.getString(2);
-                    int tenantId = resultSet.getInt(3);
+                    tenantId = resultSet.getInt(3);
                     String userDomain = resultSet.getString(4);
                     String tenantDomain = OAuth2Util.getTenantDomain(tenantId);
                     validationDataDO.setRefreshToken(refreshToken);
diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/AccessTokenIssuer.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/AccessTokenIssuer.java
index 9cf398b186f..bc77a0e8592 100644
--- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/AccessTokenIssuer.java
+++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/AccessTokenIssuer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017-2024, WSO2 LLC. (http://www.wso2.com).
+ * Copyright (c) 2017-2025, WSO2 LLC. (http://www.wso2.com).
  *
  * WSO2 LLC. licenses this file to you under the Apache License,
  * Version 2.0 (the "License"); you may not use this file except
@@ -26,6 +26,7 @@
 import org.apache.oltu.oauth2.common.error.OAuthError;
 import org.apache.oltu.oauth2.common.message.types.GrantType;
 import org.owasp.encoder.Encode;
+import org.wso2.carbon.context.PrivilegedCarbonContext;
 import org.wso2.carbon.identity.application.authentication.framework.exception.UserIdNotFoundException;
 import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser;
 import org.wso2.carbon.identity.application.common.IdentityApplicationManagementException;
@@ -316,6 +317,16 @@ public OAuth2AccessTokenRespDTO issue(OAuth2AccessTokenReqDTO tokenReqDTO)
         if (!isOfTypeApplicationUser) {
             tokReqMsgCtx.setAuthorizedUser(oAuthAppDO.getAppOwner());
             tokReqMsgCtx.addProperty(OAuthConstants.UserType.USER_TYPE, OAuthConstants.UserType.APPLICATION);
+            String applicationResidentOrgId = PrivilegedCarbonContext.getThreadLocalCarbonContext()
+                    .getApplicationResidentOrganizationId();
+            /*
+             If applicationResidentOrgId is not empty, then the request comes for an application which is registered
+             directly in the organization of the applicationResidentOrgId. Therefore, we are setting the authorized
+             user's accessing organization as the applicationResidentOrgId.
+            */
+            if (StringUtils.isNotEmpty(applicationResidentOrgId)) {
+                tokReqMsgCtx.getAuthorizedUser().setAccessingOrganization(applicationResidentOrgId);
+            }
         } else {
             tokReqMsgCtx.addProperty(OAuthConstants.UserType.USER_TYPE, OAuthConstants.UserType.APPLICATION_USER);
         }
@@ -1398,7 +1409,23 @@ private void setResponseHeaders(OAuthTokenReqMessageContext tokReqMsgCtx,
     private OAuthAppDO getOAuthApplication(String consumerKey) throws InvalidOAuthClientException,
             IdentityOAuth2Exception {
 
-        OAuthAppDO authAppDO = OAuth2Util.getAppInformationByClientId(consumerKey);
+        String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain();
+        String applicationResidentOrgId = PrivilegedCarbonContext.getThreadLocalCarbonContext()
+                .getApplicationResidentOrganizationId();
+        /*
+         If the applicationResidentOrgId is not null, resolve the tenant domain from the organization id to get the
+         application information by passing the consumer key and the tenant domain.
+        */
+        if (StringUtils.isNotEmpty(applicationResidentOrgId)) {
+            try {
+                tenantDomain = OAuthComponentServiceHolder.getInstance().getOrganizationManager()
+                        .resolveTenantDomain(applicationResidentOrgId);
+            } catch (OrganizationManagementException e) {
+                throw new IdentityOAuth2Exception("Error while resolving tenant domain from the organization id: "
+                        + applicationResidentOrgId, e);
+            }
+        }
+        OAuthAppDO authAppDO = OAuth2Util.getAppInformationByClientId(consumerKey, tenantDomain);
         String appState = authAppDO.getState();
         if (StringUtils.isEmpty(appState)) {
             if (log.isDebugEnabled()) {
diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuer.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuer.java
index 72b81f1e694..0bc3cf85f43 100644
--- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuer.java
+++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuer.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017-2024, WSO2 LLC. (http://www.wso2.com).
+ * Copyright (c) 2017-2025, WSO2 LLC. (http://www.wso2.com).
  *
  * WSO2 LLC. licenses this file to you under the Apache License,
  * Version 2.0 (the "License"); you may not use this file except
@@ -35,6 +35,7 @@
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
+import org.wso2.carbon.context.PrivilegedCarbonContext;
 import org.wso2.carbon.identity.application.authentication.framework.exception.UserIdNotFoundException;
 import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser;
 import org.wso2.carbon.identity.base.IdentityConstants;
@@ -55,6 +56,7 @@
 import org.wso2.carbon.identity.openidconnect.CustomClaimsCallbackHandler;
 import org.wso2.carbon.identity.openidconnect.OIDCClaimUtil;
 import org.wso2.carbon.identity.openidconnect.util.ClaimHandlerUtil;
+import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementException;
 
 import java.security.Key;
 import java.security.cert.Certificate;
@@ -418,7 +420,16 @@ private String getSigningTenantDomain(String clientID, AuthenticatedUser authent
             throws IdentityOAuth2Exception {
 
         String tenantDomain;
-        if (OAuthServerConfiguration.getInstance().getUseSPTenantDomainValue()) {
+        String applicationResidentOrgId = PrivilegedCarbonContext.getThreadLocalCarbonContext()
+                .getApplicationResidentOrganizationId();
+        /*
+         If applicationResidentOrgId is not empty, then the request comes for an application which is registered
+         directly in the organization of the applicationResidentOrgId. In this scenario, the signing tenant domain
+         should be the root tenant domain of the applicationResidentOrgId.
+        */
+        if (StringUtils.isNotEmpty(applicationResidentOrgId)) {
+            tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain();
+        } else if (OAuthServerConfiguration.getInstance().getUseSPTenantDomainValue()) {
             if (log.isDebugEnabled()) {
                 log.debug("Using the tenant domain of the SP to sign the token");
             }
@@ -566,8 +577,25 @@ protected JWTClaimsSet createJWTClaimSet(OAuthAuthzReqMessageContext authAuthzRe
 
         // loading the stored application data
         OAuthAppDO oAuthAppDO;
+        String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain();
+        String applicationResidentOrgId = PrivilegedCarbonContext.getThreadLocalCarbonContext()
+                .getApplicationResidentOrganizationId();
         try {
-            oAuthAppDO = OAuth2Util.getAppInformationByClientId(consumerKey);
+            /*
+             If applicationResidentOrgId is not empty, then the request comes for an application which is registered
+             directly in the organization of the applicationResidentOrgId. Therefore, the tenant domain should be
+             extracted from the organization id to get the information of the application.
+            */
+            if (StringUtils.isNotEmpty(applicationResidentOrgId)) {
+                try {
+                    tenantDomain = OAuth2ServiceComponentHolder.getInstance().getOrganizationManager()
+                            .resolveTenantDomain(applicationResidentOrgId);
+                } catch (OrganizationManagementException e) {
+                    throw new IdentityOAuth2Exception("Error while resolving tenant domain from the organization id: "
+                            + applicationResidentOrgId, e);
+                }
+            }
+            oAuthAppDO = OAuth2Util.getAppInformationByClientId(consumerKey, tenantDomain);
         } catch (InvalidOAuthClientException e) {
             throw new IdentityOAuth2Exception("Error while retrieving app information for clientId: " + consumerKey, e);
         }
@@ -584,6 +612,15 @@ protected JWTClaimsSet createJWTClaimSet(OAuthAuthzReqMessageContext authAuthzRe
             spTenantDomain = tokenReqMessageContext.getOauth2AccessTokenReqDTO().getTenantDomain();
         }
 
+        /*
+         If applicationResidentOrgId is not empty, then the request comes for an application which is registered
+         directly in the organization of the applicationResidentOrgId. spTenantDomain is used to get the idTokenIssuer
+         for the token. In this scenario, the tenant domain that needs to be used as the issuer is the root tenant.
+        */
+        if (StringUtils.isNotEmpty(applicationResidentOrgId)) {
+            spTenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain();;
+        }
+
         boolean isMTLSrequest;
         if (authAuthzReqMessageContext != null) {
             /* If the auth request is originated from a request object reference(ex: PAR), then that endpoint should be
diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/AbstractAuthorizationGrantHandler.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/AbstractAuthorizationGrantHandler.java
index 02aa8ddfa52..c039d87e2c9 100644
--- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/AbstractAuthorizationGrantHandler.java
+++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/AbstractAuthorizationGrantHandler.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2024, WSO2 LLC. (http://www.wso2.com).
+ * Copyright (c) 2013-2025, WSO2 LLC. (http://www.wso2.com).
  *
  * WSO2 LLC. licenses this file to you under the Apache License,
  * Version 2.0 (the "License"); you may not use this file except
@@ -68,6 +68,7 @@
 import org.wso2.carbon.identity.oauth2.validators.OAuth2ScopeHandler;
 import org.wso2.carbon.identity.oauth2.validators.scope.ScopeValidator;
 import org.wso2.carbon.identity.openidconnect.OIDCClaimUtil;
+import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementException;
 import org.wso2.carbon.utils.DiagnosticLog;
 
 import java.sql.Timestamp;
@@ -848,10 +849,24 @@ private OAuth2AccessTokenRespDTO createResponseWithTokenBean(AccessTokenDO exist
         OAuthAppDO oAuthAppDO;
         String consumerKey = existingAccessTokenDO.getConsumerKey();
         try {
-            oAuthAppDO = OAuth2Util.getAppInformationByClientId(consumerKey);
+            String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain();
+            String applicationResidentOrgId = PrivilegedCarbonContext.getThreadLocalCarbonContext()
+                    .getApplicationResidentOrganizationId();
+            /*
+             If applicationResidentOrgId is not empty, then the request comes for an application which is registered
+             directly in the organization of the applicationResidentOrgId. Therefore, the tenant domain should be
+             extracted from the organization id to get the information of the application.
+            */
+            if (StringUtils.isNotEmpty(applicationResidentOrgId)) {
+                tenantDomain = OAuth2ServiceComponentHolder.getInstance().getOrganizationManager()
+                        .resolveTenantDomain(applicationResidentOrgId);
+            }
+            oAuthAppDO = OAuth2Util.getAppInformationByClientId(consumerKey, tenantDomain);
         } catch (InvalidOAuthClientException e) {
             throw new IdentityOAuth2Exception("Error while retrieving app information for client_id : " + consumerKey,
                     e);
+        } catch (OrganizationManagementException e) {
+            throw new IdentityOAuth2Exception("Error while resolving tenant domain from the organization id: ", e);
         }
 
         if (issueRefreshToken(existingAccessTokenDO.getTokenType()) &&
@@ -895,7 +910,19 @@ private OAuthCacheKey getOAuthCacheKey(String scope, String consumerKey, String
     private OAuthAppDO getoAuthApp(String consumerKey) throws IdentityOAuth2Exception {
         OAuthAppDO oAuthAppBean;
         try {
-            oAuthAppBean = OAuth2Util.getAppInformationByClientId(consumerKey);
+            String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain();
+            String applicationResidentOrgId = PrivilegedCarbonContext.getThreadLocalCarbonContext()
+                    .getApplicationResidentOrganizationId();
+            /*
+             If applicationResidentOrgId is not empty, then the request comes for an application which is registered
+             directly in the organization of the applicationResidentOrgId. Therefore, the tenant domain should be
+             extracted from the organization id to get the information of the application.
+            */
+            if (StringUtils.isNotEmpty(applicationResidentOrgId)) {
+                tenantDomain = OAuth2ServiceComponentHolder.getInstance().getOrganizationManager()
+                        .resolveTenantDomain(applicationResidentOrgId);
+            }
+            oAuthAppBean = OAuth2Util.getAppInformationByClientId(consumerKey, tenantDomain);
             if (log.isDebugEnabled()) {
                 log.debug("Service Provider specific expiry time enabled for application : " + consumerKey +
                         ". Application access token expiry time : " + oAuthAppBean.getApplicationAccessTokenExpiryTime()
@@ -904,6 +931,8 @@ private OAuthAppDO getoAuthApp(String consumerKey) throws IdentityOAuth2Exceptio
             }
         } catch (InvalidOAuthClientException e) {
             throw new IdentityOAuth2Exception("Error while retrieving app information for clientId: " + consumerKey, e);
+        } catch (OrganizationManagementException e) {
+            throw new IdentityOAuth2Exception("Error while resolving tenant domain from the organization id: ", e);
         }
         return oAuthAppBean;
     }
diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/PasswordGrantHandler.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/PasswordGrantHandler.java
index 5822cd1f35a..031600057d6 100644
--- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/PasswordGrantHandler.java
+++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/PasswordGrantHandler.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
+ * Copyright (c) 2013-2025, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
  *
  * WSO2 Inc. licenses this file to you under the Apache License,
  * Version 2.0 (the "License"); you may not use this file except
@@ -24,6 +24,7 @@
 import org.apache.commons.logging.LogFactory;
 import org.wso2.carbon.CarbonConstants;
 import org.wso2.carbon.base.MultitenantConstants;
+import org.wso2.carbon.context.PrivilegedCarbonContext;
 import org.wso2.carbon.identity.application.authentication.framework.AuthenticationDataPublisher;
 import org.wso2.carbon.identity.application.authentication.framework.AuthenticationService;
 import org.wso2.carbon.identity.application.authentication.framework.AuthenticatorFlowStatus;
@@ -66,6 +67,7 @@
 import org.wso2.carbon.identity.oauth2.internal.OAuth2ServiceComponentHolder;
 import org.wso2.carbon.identity.oauth2.token.OAuthTokenReqMessageContext;
 import org.wso2.carbon.identity.oauth2.util.OAuth2Util;
+import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementException;
 import org.wso2.carbon.user.api.UserStoreException;
 import org.wso2.carbon.user.core.UserCoreConstants;
 import org.wso2.carbon.user.core.UserStoreClientException;
@@ -273,13 +275,30 @@ private void setPropertiesForTokenGeneration(OAuthTokenReqMessageContext tokReqM
         tokReqMsgCtx.setScope(tokenReq.getScope());
     }
 
-    private String getFullQualifiedUsername(OAuth2AccessTokenReqDTO tokenReq, ServiceProvider serviceProvider) {
+    private String getFullQualifiedUsername(OAuth2AccessTokenReqDTO tokenReq, ServiceProvider serviceProvider)
+            throws IdentityOAuth2Exception {
 
         boolean isEmailUserNameEnabled = MultitenantUtils.isEmailUserName();
         boolean isSaasApp = serviceProvider.isSaasApp();
         boolean isLegacySaaSAuthenticationEnabled = IdentityTenantUtil.isLegacySaaSAuthenticationEnabled();
         String usernameFromRequest = tokenReq.getResourceOwnerUsername();
         String tenantDomainFromContext = IdentityTenantUtil.resolveTenantDomain();
+        String applicationResidentOrgId = PrivilegedCarbonContext.getThreadLocalCarbonContext()
+                .getApplicationResidentOrganizationId();
+        /*
+         If applicationResidentOrgId is not empty, then the request comes for an application which is registered
+         directly in the organization of the applicationResidentOrgId. In this scenario the user is also in the
+         organization level and the tenant domain of the user should be resolved from the organization id.
+        */
+        if (StringUtils.isNotEmpty(applicationResidentOrgId)) {
+            try {
+                tenantDomainFromContext = OAuth2ServiceComponentHolder.getInstance().getOrganizationManager()
+                        .resolveTenantDomain(applicationResidentOrgId);
+            } catch (OrganizationManagementException e) {
+                throw new IdentityOAuth2Exception("Error while resolving tenant domain from the organization id: "
+                        + applicationResidentOrgId, e);
+            }
+        }
 
         if (!isSaasApp) {
             /*
@@ -393,6 +412,17 @@ private AuthenticatedUser validateUserCredentials(OAuth2AccessTokenReqDTO tokenR
                 log.debug(PASSWORD_GRANT_POST_AUTHENTICATION_EVENT + " event is triggered");
             }
             if (authenticatedUser.isPresent()) {
+                String applicationResidentOrgId = PrivilegedCarbonContext.getThreadLocalCarbonContext()
+                        .getApplicationResidentOrganizationId();
+                /*
+                 If applicationResidentOrgId is not empty, then the request comes for an application which is
+                 registered directly in the organization of the applicationResidentOrgId. In this scenario the user's
+                 accessing and resident organization will be the organization of the applicationResidentOrgId.
+                */
+                if (StringUtils.isNotEmpty(applicationResidentOrgId)) {
+                    authenticatedUser.get().setAccessingOrganization(applicationResidentOrgId);
+                    authenticatedUser.get().setUserResidentOrganization(applicationResidentOrgId);
+                }
                 return authenticatedUser.get();
             }
             if (isPublishPasswordGrantLoginEnabled) {
diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/RefreshGrantHandler.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/RefreshGrantHandler.java
index db2788c89dc..e5f3fb6d82c 100644
--- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/RefreshGrantHandler.java
+++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/RefreshGrantHandler.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2024, WSO2 LLC. (http://www.wso2.com).
+ * Copyright (c) 2013-2025, WSO2 LLC. (http://www.wso2.com).
  *
  * WSO2 LLC. licenses this file to you under the Apache License,
  * Version 2.0 (the "License"); you may not use this file except
@@ -581,11 +581,11 @@ private ResponseHeader[] getResponseHeaders(OAuthTokenReqMessageContext tokReqMs
         return respHeaders;
     }
 
-    private OAuthAppDO getOAuthApp(String clientId) throws IdentityOAuth2Exception {
+    private OAuthAppDO getOAuthApp(String clientId, String tenantDomain) throws IdentityOAuth2Exception {
 
         OAuthAppDO oAuthAppDO;
         try {
-            oAuthAppDO = OAuth2Util.getAppInformationByClientId(clientId);
+            oAuthAppDO = OAuth2Util.getAppInformationByClientId(clientId, tenantDomain);
         } catch (InvalidOAuthClientException e) {
             throw new IdentityOAuth2Exception("Error while retrieving app information for clientId: "
                     + clientId, e);
@@ -649,7 +649,8 @@ private void setTokenData(AccessTokenDO accessTokenDO, OAuthTokenReqMessageConte
                               RefreshTokenValidationDataDO validationBean, OAuth2AccessTokenReqDTO tokenReq,
                               Timestamp timestamp) throws IdentityOAuth2Exception {
 
-        OAuthAppDO oAuthAppDO = getOAuthApp(tokenReq.getClientId());
+        OAuthAppDO oAuthAppDO = getOAuthApp(tokenReq.getClientId(), validationBean.getAuthorizedUser().
+                getTenantDomain());
         createTokens(accessTokenDO, tokReqMsgCtx);
         setRefreshTokenData(accessTokenDO, tokenReq, validationBean, oAuthAppDO, accessTokenDO.getRefreshToken(),
                 timestamp, tokReqMsgCtx);
@@ -869,7 +870,8 @@ private boolean checkExecutePreIssueAccessTokensActions(RefreshTokenValidationDa
                                                             OAuthTokenReqMessageContext tokenReqMessageContext)
             throws IdentityOAuth2Exception {
 
-        OAuthAppDO oAuthAppBean = getOAuthApp(tokenReqMessageContext.getOauth2AccessTokenReqDTO().getClientId());
+        OAuthAppDO oAuthAppBean = getOAuthApp(tokenReqMessageContext.getOauth2AccessTokenReqDTO().getClientId(),
+                refreshTokenValidationDataDO.getAuthorizedUser().getTenantDomain());
         String grantType = refreshTokenValidationDataDO.getGrantType();
 
         // Allow if refresh token is issued for token requests from following grant types and,
diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/util/AuthzUtil.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/util/AuthzUtil.java
index 29620bd069d..466cf79bbf4 100644
--- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/util/AuthzUtil.java
+++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/util/AuthzUtil.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com).
+ * Copyright (c) 2023-2025, WSO2 LLC. (http://www.wso2.com).
  *
  * WSO2 LLC. licenses this file to you under the Apache License,
  * Version 2.0 (the "License"); you may not use this file except
@@ -255,13 +255,17 @@ public static boolean isUserAuthorized(AuthenticatedUser authenticatedUser, List
 
         // Application id is not required for basic authentication flow.
         List<String> roleIds = getUserRoles(authenticatedUser, null);
-        String tenantDomain = authenticatedUser.getTenantDomain();
-        if (StringUtils.isNotBlank(authenticatedUser.getAccessingOrganization()) &&
-                !authenticatedUser.getAccessingOrganization().
-                        equals(authenticatedUser.getUserResidentOrganization())) {
-            tenantDomain = getAccessingTenantDomain(authenticatedUser);
+        List<String> permissions;
+        /*
+         If the authenticatedUser contains an accessing organization, the the scopes should be checked against the
+         accessing organization.
+        */
+        if (StringUtils.isNotEmpty(authenticatedUser.getAccessingOrganization())) {
+            permissions = getAssociatedScopesForRoles(roleIds,
+                    authenticatedUser.getAccessingOrganization());
+        } else {
+            permissions = getAssociatedScopesForRoles(roleIds, authenticatedUser.getTenantDomain());
         }
-        List<String> permissions = getAssociatedScopesForRoles(roleIds, tenantDomain);
         if (OAuthServerConfiguration.getInstance().isUseLegacyPermissionAccessForUserBasedAuth()) {
             // Handling backward compatibility for previous access level.
             List<String> internalScopes = getInternalScopes(authenticatedUser.getTenantDomain());
diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/util/JWTUtils.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/util/JWTUtils.java
index af002c3fdc3..5be02c855ca 100644
--- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/util/JWTUtils.java
+++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/util/JWTUtils.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com).
+ * Copyright (c) 2023-2025, WSO2 LLC. (https://www.wso2.com).
  *
  * WSO2 LLC. licenses this file to you under the Apache License,
  * Version 2.0 (the "License"); you may not use this file except
@@ -30,6 +30,7 @@
 import org.wso2.carbon.context.PrivilegedCarbonContext;
 import org.wso2.carbon.identity.application.common.model.FederatedAuthenticatorConfig;
 import org.wso2.carbon.identity.application.common.model.IdentityProvider;
+import org.wso2.carbon.identity.application.common.model.ServiceProviderProperty;
 import org.wso2.carbon.identity.application.common.util.IdentityApplicationConstants;
 import org.wso2.carbon.identity.application.common.util.IdentityApplicationManagementUtil;
 import org.wso2.carbon.identity.central.log.mgt.utils.LoggerUtils;
@@ -54,12 +55,14 @@
 import java.security.cert.X509Certificate;
 import java.security.interfaces.RSAPublicKey;
 import java.text.ParseException;
+import java.util.Arrays;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 
+import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.IS_FRAGMENT_APP;
 import static org.wso2.carbon.identity.organization.management.service.constant
         .OrganizationManagementConstants.DEFAULT_SUB_ORG_LEVEL;
 import static org.wso2.carbon.identity.organization.management.service.constant
@@ -298,6 +301,16 @@ public static String getSigningTenantDomain(JWTClaimsSet claimsSet, AccessTokenD
                 if (log.isDebugEnabled()) {
                     log.debug("Getting signing tenant domain from OAuth app.");
                 }
+                /*
+                 Check if the OAuth application is a fragment application. Based on that we can define what
+                 is the tenant that signed the JWT. In this case the signing tenant is the root organization.
+                */
+                String appTenantDomain = IdentityTenantUtil.getTenantDomain(accessTokenDO.getTenantID());
+                ServiceProviderProperty[] serviceProviderProperties = OAuth2Util.getServiceProvider(
+                        accessTokenDO.getConsumerKey(), appTenantDomain).getSpProperties();
+                if (!isFragmentApp(serviceProviderProperties)) {
+                    return PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain();
+                }
                 return OAuth2Util.getTenantDomainOfOauthApp(accessTokenDO.getConsumerKey());
             } catch (InvalidOAuthClientException e) {
                 throw new IdentityOAuth2Exception("Error while getting tenant domain from OAuth app with consumer key: "
@@ -327,6 +340,17 @@ private static String getTenantDomain() {
         return tenantDomain;
     }
 
+    private static boolean isFragmentApp(ServiceProviderProperty[] serviceProviderProperties) {
+
+        if (serviceProviderProperties == null) {
+            return false;
+        }
+
+        return Arrays.stream(serviceProviderProperties).
+                anyMatch(property -> IS_FRAGMENT_APP.equals(property.getName()) &&
+                            Boolean.parseBoolean(property.getValue()));
+    }
+
     /**
      * Validates that the provided token's "Not Before" time has passed, considering the configured timestamp skew.
      * If the token is used before the "Not Before" time, an IdentityOAuth2Exception is thrown.
diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/DefaultOAuth2ScopeValidator.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/DefaultOAuth2ScopeValidator.java
index 1025c723f34..8108c4e1b2f 100644
--- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/DefaultOAuth2ScopeValidator.java
+++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/DefaultOAuth2ScopeValidator.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2023-2024, WSO2 LLC. (http://www.wso2.com).
+ * Copyright (c) 2023-2025, WSO2 LLC. (http://www.wso2.com).
  *
  * WSO2 LLC. licenses this file to you under the Apache License,
  * Version 2.0 (the "License"); you may not use this file except
@@ -23,6 +23,7 @@
 import org.apache.commons.lang.StringUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
+import org.wso2.carbon.context.PrivilegedCarbonContext;
 import org.wso2.carbon.identity.api.resource.mgt.APIResourceMgtException;
 import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser;
 import org.wso2.carbon.identity.application.common.IdentityApplicationManagementException;
@@ -129,8 +130,13 @@ public List<String> validateScope(OAuthTokenReqMessageContext tokenReqMessageCon
         String tenantDomain = tokenReqMessageContext.getOauth2AccessTokenReqDTO().getTenantDomain();
         String clientId = tokenReqMessageContext.getOauth2AccessTokenReqDTO().getClientId();
         String appId = getApplicationId(clientId, tenantDomain);
-        // When user is not accessing the resident organization, resolve the application id from the shared app table.
-        if (!AuthzUtil.isUserAccessingResidentOrganization(tokenReqMessageContext.getAuthorizedUser())) {
+        /*
+         When user is not accessing the resident organization and if the user is not accessing an application in
+         the organization level, resolve the application id from the shared app table.
+        */
+        if (!AuthzUtil.isUserAccessingResidentOrganization(tokenReqMessageContext.getAuthorizedUser()) &&
+                StringUtils.isEmpty(PrivilegedCarbonContext.getThreadLocalCarbonContext().
+                        getApplicationResidentOrganizationId())) {
             String orgId = tokenReqMessageContext.getAuthorizedUser().getAccessingOrganization();
             String appResideOrgId = resolveOrgIdByTenantDomain(tenantDomain);
             appId = SharedAppResolveDAO.resolveSharedApplication(appResideOrgId, appId, orgId);
diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/TokenValidationHandler.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/TokenValidationHandler.java
index fabb785dbba..8529949d4fd 100644
--- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/TokenValidationHandler.java
+++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/validators/TokenValidationHandler.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019-2023, WSO2 LLC. (http://www.wso2.com).
+ * Copyright (c) 2019-2025, WSO2 LLC. (http://www.wso2.com).
  *
  * WSO2 LLC. licenses this file to you under the Apache License,
  * Version 2.0 (the "License"); you may not use this file except
@@ -27,6 +27,7 @@
 import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser;
 import org.wso2.carbon.identity.application.common.IdentityApplicationManagementException;
 import org.wso2.carbon.identity.application.common.model.ServiceProvider;
+import org.wso2.carbon.identity.application.common.model.ServiceProviderProperty;
 import org.wso2.carbon.identity.application.mgt.ApplicationConstants;
 import org.wso2.carbon.identity.central.log.mgt.utils.LogConstants;
 import org.wso2.carbon.identity.central.log.mgt.utils.LoggerUtils;
@@ -47,13 +48,17 @@
 import org.wso2.carbon.identity.oauth2.internal.OAuth2ServiceComponentHolder;
 import org.wso2.carbon.identity.oauth2.model.AccessTokenDO;
 import org.wso2.carbon.identity.oauth2.util.OAuth2Util;
+import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementException;
+import org.wso2.carbon.identity.organization.management.service.util.OrganizationManagementUtil;
 import org.wso2.carbon.utils.DiagnosticLog;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 import java.util.TreeMap;
 
+import static org.wso2.carbon.identity.application.mgt.ApplicationConstants.IS_FRAGMENT_APP;
 import static org.wso2.carbon.identity.oauth2.util.OAuth2Util.isParsableJWT;
 
 /**
@@ -507,6 +512,18 @@ private OAuth2IntrospectionResponseDTO validateAccessToken(OAuth2TokenValidation
                 String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain();
                 accessTokenDO = OAuth2ServiceComponentHolder.getInstance().getTokenProvider()
                         .getVerifiedAccessToken(validationRequest.getAccessToken().getIdentifier(), false);
+                /*
+                 Check if the OAuth application is a fragment application. If that is not a fragment application,
+                 then getting the tenant domain from the token.
+                */
+                String appTenantDomain = IdentityTenantUtil.getTenantDomain(accessTokenDO.getTenantID());
+                if (OrganizationManagementUtil.isOrganization(appTenantDomain)) {
+                    ServiceProviderProperty[] serviceProviderProperties = OAuth2Util.getServiceProvider(
+                            accessTokenDO.getConsumerKey(), appTenantDomain).getSpProperties();
+                    if (!isFragmentApp(serviceProviderProperties)) {
+                        tenantDomain = appTenantDomain;
+                    }
+                }
                 boolean isCrossTenantTokenIntrospectionAllowed
                         = OAuthServerConfiguration.getInstance().isCrossTenantTokenIntrospectionAllowed();
                 if (!isCrossTenantTokenIntrospectionAllowed && accessTokenDO != null &&
@@ -536,6 +553,9 @@ private OAuth2IntrospectionResponseDTO validateAccessToken(OAuth2TokenValidation
                     LoggerUtils.triggerDiagnosticLogEvent(diagnosticLogBuilder);
                 }
                 return buildIntrospectionErrorResponse(e.getMessage());
+            } catch (OrganizationManagementException e) {
+                throw new IdentityOAuth2Exception("Error while checking whether the application tenant is an " +
+                        "organization.", e);
             }
 
             if (hasAccessTokenExpired(accessTokenDO)) {
@@ -684,6 +704,17 @@ private OAuth2IntrospectionResponseDTO validateAccessToken(OAuth2TokenValidation
         return introResp;
     }
 
+    private boolean isFragmentApp(ServiceProviderProperty[] serviceProviderProperties) {
+
+        if (serviceProviderProperties == null) {
+            return false;
+        }
+
+        return Arrays.stream(serviceProviderProperties).
+                anyMatch(property -> IS_FRAGMENT_APP.equals(property.getName()) &&
+                        Boolean.parseBoolean(property.getValue()));
+    }
+
     private String getAuthzUser(AccessTokenDO accessTokenDO) throws IdentityOAuth2Exception {
 
         AuthenticatedUser user = accessTokenDO.getAuthzUser();
diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/openidconnect/DefaultIDTokenBuilder.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/openidconnect/DefaultIDTokenBuilder.java
index 614539abd96..6beb79b8077 100644
--- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/openidconnect/DefaultIDTokenBuilder.java
+++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/openidconnect/DefaultIDTokenBuilder.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
+ * Copyright (c) 2017-2025, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
  *
  * WSO2 Inc. licenses this file to you under the Apache License,
  * Version 2.0 (the "License"); you may not use this file except
@@ -30,6 +30,7 @@
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.wso2.carbon.base.MultitenantConstants;
+import org.wso2.carbon.context.PrivilegedCarbonContext;
 import org.wso2.carbon.identity.application.authentication.framework.AuthenticationMethodNameTranslator;
 import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser;
 import org.wso2.carbon.identity.oauth.cache.AuthorizationGrantCache;
@@ -121,7 +122,7 @@ public String buildIDToken(OAuthTokenReqMessageContext tokenReqMsgCtxt,
         // Initialize OAuthAppDO using the client ID.
         OAuthAppDO oAuthAppDO;
         try {
-            oAuthAppDO = OAuth2Util.getAppInformationByClientId(clientId);
+            oAuthAppDO = OAuth2Util.getAppInformationByClientId(clientId, spTenantDomain);
         } catch (InvalidOAuthClientException e) {
             String error = "Error occurred while getting app information for client_id: " + clientId;
             throw new IdentityOAuth2Exception(error, e);
@@ -421,7 +422,16 @@ private JWTClaimsSet handleOIDCCustomClaims(OAuthTokenReqMessageContext tokReqMs
 
     private String getSigningTenantDomain(OAuthTokenReqMessageContext tokReqMsgCtx) {
         boolean isJWTSignedWithSPKey = OAuthServerConfiguration.getInstance().isJWTSignedWithSPKey();
-        if (isJWTSignedWithSPKey) {
+        String applicationResidentOrgId = PrivilegedCarbonContext.getThreadLocalCarbonContext()
+                .getApplicationResidentOrganizationId();
+        /*
+         If applicationResidentOrgId is not empty, then the request comes for an application which is
+         registered directly in the organization of the applicationResidentOrgId. In this case, the tenant domain
+         that needs to be signing the token should be the root tenant of the organization in applicationResidentOrgId.
+        */
+        if (StringUtils.isNotEmpty(applicationResidentOrgId)) {
+            return PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain();
+        } else if (isJWTSignedWithSPKey) {
             return (String) tokReqMsgCtx.getProperty(MultitenantConstants.TENANT_DOMAIN);
         } else {
             return tokReqMsgCtx.getAuthorizedUser().getTenantDomain();
diff --git a/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/OAuth2ServiceTest.java b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/OAuth2ServiceTest.java
index 424d6b6f793..41b100efe02 100644
--- a/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/OAuth2ServiceTest.java
+++ b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/OAuth2ServiceTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017-2024, WSO2 LLC. (http://www.wso2.com).
+ * Copyright (c) 2017-2025, WSO2 LLC. (http://www.wso2.com).
  *
  * WSO2 LLC. licenses this file to you under the Apache License,
  * Version 2.0 (the "License"); you may not use this file except
@@ -73,9 +73,11 @@
 import org.wso2.carbon.identity.oauth2.model.RefreshTokenValidationDataDO;
 import org.wso2.carbon.identity.oauth2.token.AccessTokenIssuer;
 import org.wso2.carbon.identity.oauth2.token.bindings.TokenBinding;
+import org.wso2.carbon.identity.oauth2.util.AuthzUtil;
 import org.wso2.carbon.identity.oauth2.util.OAuth2Util;
 import org.wso2.carbon.user.core.UserRealm;
 import org.wso2.carbon.user.core.UserStoreManager;
+import org.wso2.carbon.utils.multitenancy.MultitenantConstants;
 import org.wso2.carbon.utils.multitenancy.MultitenantUtils;
 
 import java.lang.reflect.Field;
@@ -667,7 +669,8 @@ public void testRevokeTokenByOAuthClientWithAccessToken() throws Exception {
         try (MockedStatic<OAuthComponentServiceHolder> oAuthComponentServiceHolder =
                      mockStatic(OAuthComponentServiceHolder.class);
              MockedStatic<OAuth2Util> oAuth2Util = mockStatic(OAuth2Util.class);
-             MockedStatic<OAuthUtil> oAuthUtil = mockStatic(OAuthUtil.class)) {
+             MockedStatic<OAuthUtil> oAuthUtil = mockStatic(OAuthUtil.class);
+             MockedStatic<IdentityTenantUtil> identityTenantUtil = mockStatic(IdentityTenantUtil.class)) {
             setUpRevokeToken(oAuthComponentServiceHolder, oAuth2Util, oAuthUtil);
             AccessTokenDO accessTokenDO = getAccessToken();
             TokenBinding tokenBinding = new TokenBinding();
@@ -680,9 +683,11 @@ public void testRevokeTokenByOAuthClientWithAccessToken() throws Exception {
             setPrivateField(oAuthTokenPersistenceFactory, "managementDAO", mockTokenManagementDAOImpl);
             AccessTokenDAO mockAccessTokenDAO = mock(AccessTokenDAO.class);
             setPrivateField(oAuthTokenPersistenceFactory, "tokenDAO", mockAccessTokenDAO);
+            identityTenantUtil.when(() -> IdentityTenantUtil.getTenantDomain(anyInt())).thenReturn(
+                    MultitenantConstants.SUPER_TENANT_DOMAIN_NAME);
 
             OAuthAppDO oAuthAppDO = new OAuthAppDO();
-            when(OAuth2Util.getAppInformationByClientId(anyString())).thenReturn(oAuthAppDO);
+            when(OAuth2Util.getAppInformationByClientId(anyString(), anyString())).thenReturn(oAuthAppDO);
 
             OAuthRevocationRequestDTO revokeRequestDTO = getOAuthRevocationRequestDTO();
             oAuth2Service.revokeTokenByOAuthClient(revokeRequestDTO);
@@ -710,14 +715,17 @@ public void testRevokeTokenByOAuthClientWithAccessTokenWithInvalidBinding() thro
         try (MockedStatic<OAuthComponentServiceHolder> oAuthComponentServiceHolder =
                      mockStatic(OAuthComponentServiceHolder.class);
              MockedStatic<OAuth2Util> oAuth2Util = mockStatic(OAuth2Util.class);
-             MockedStatic<OAuthUtil> oAuthUtil = mockStatic(OAuthUtil.class)) {
+             MockedStatic<OAuthUtil> oAuthUtil = mockStatic(OAuthUtil.class);
+             MockedStatic<IdentityTenantUtil> identityTenantUtil = mockStatic(IdentityTenantUtil.class)) {
             setUpRevokeToken(oAuthComponentServiceHolder, oAuth2Util, oAuthUtil);
             AccessTokenDO accessTokenDO = getAccessToken();
             when(OAuth2Util.findAccessToken(anyString(), anyBoolean())).thenReturn(accessTokenDO);
+            identityTenantUtil.when(() -> IdentityTenantUtil.getTenantDomain(anyInt())).thenReturn(
+                    MultitenantConstants.SUPER_TENANT_DOMAIN_NAME);
 
             OAuthAppDO oAuthAppDO = new OAuthAppDO();
             oAuthAppDO.setTokenBindingValidationEnabled(true);
-            when(OAuth2Util.getAppInformationByClientId(anyString())).thenReturn(oAuthAppDO);
+            when(OAuth2Util.getAppInformationByClientId(anyString(), anyString())).thenReturn(oAuthAppDO);
 
             OAuthRevocationRequestDTO revokeRequestDTO = getOAuthRevocationRequestDTO();
             OAuthRevocationResponseDTO oAuthRevocationResponseDTO = oAuth2Service
@@ -958,7 +966,10 @@ private void setUpRevokeToken(MockedStatic<OAuthComponentServiceHolder> oAuthCom
     @Test
     public void testGetOauthApplicationState() throws Exception {
 
-        try (MockedStatic<IdentityTenantUtil> identityTenantUtil = mockStatic(IdentityTenantUtil.class)) {
+        try (MockedStatic<IdentityTenantUtil> identityTenantUtil = mockStatic(IdentityTenantUtil.class);
+             MockedStatic<AuthzUtil> authzUtil = mockStatic(AuthzUtil.class)) {
+
+            authzUtil.when(AuthzUtil::isLegacyAuthzRuntime).thenReturn(false);
             String id = "clientId1";
             OAuthAppDO oAuthAppDO = new OAuthAppDO();
             oAuthAppDO.setState("ACTIVE");
@@ -979,6 +990,7 @@ public void testGetOauthApplicationState() throws Exception {
     public void testGetOauthApplicationStateWithIdentityOAuth2Exception() throws Exception {
 
         try (MockedStatic<IdentityTenantUtil> identityTenantUtil = mockStatic(IdentityTenantUtil.class)) {
+            identityTenantUtil.when(IdentityTenantUtil::getLoginTenantId).thenReturn(1);
             identityTenantUtil.when(() -> IdentityTenantUtil.getTenantId(anyString())).thenReturn(1);
             identityTenantUtil.when(() -> IdentityTenantUtil.getTenantDomain(1)).thenReturn("test.tenant");
 
@@ -998,6 +1010,7 @@ public void testGetOauthApplicationStateWithIdentityOAuth2Exception() throws Exc
     public void testGetOauthApplicationStateWithInvalidOAuthClientException() throws Exception {
 
         try (MockedStatic<IdentityTenantUtil> identityTenantUtil = mockStatic(IdentityTenantUtil.class)) {
+            identityTenantUtil.when(IdentityTenantUtil::getLoginTenantId).thenReturn(1);
             identityTenantUtil.when(() -> IdentityTenantUtil.getTenantId(anyString())).thenReturn(1);
             identityTenantUtil.when(() -> IdentityTenantUtil.getTenantDomain(1)).thenReturn("test.tenant");
 
diff --git a/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/client/authentication/BasicAuthClientAuthenticatorTest.java b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/client/authentication/BasicAuthClientAuthenticatorTest.java
index cda43492f2f..f60dbf7b1a7 100644
--- a/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/client/authentication/BasicAuthClientAuthenticatorTest.java
+++ b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/client/authentication/BasicAuthClientAuthenticatorTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
+ * Copyright (c) 2018-2025, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
  *
  * WSO2 Inc. licenses this file to you under the Apache License,
  * Version 2.0 (the "License"); you may not use this file except
@@ -115,7 +115,7 @@ public void testAuthenticateClient(String headerName, String headerValue, HashMa
             OAuthClientAuthnContext oAuthClientAuthnContext = (OAuthClientAuthnContext) oAuthClientAuthnContextObj;
             HttpServletRequest httpServletRequest = mock(HttpServletRequest.class);
 
-            oAuth2Util.when(() -> OAuth2Util.authenticateClient(anyString(), anyString())).thenReturn
+            oAuth2Util.when(() -> OAuth2Util.authenticateClient(anyString(), anyString(), anyString())).thenReturn
                     (isAuthenticated);
             when(httpServletRequest.getHeader(headerName)).thenReturn(headerValue);
             assertEquals(basicAuthClientAuthenticator.authenticateClient(httpServletRequest, bodyContent,
@@ -157,10 +157,10 @@ public void testAuthenticateClientExeption(String headerName, String headerValue
             HttpServletRequest httpServletRequest = mock(HttpServletRequest.class);
 
             if (exception instanceof IdentityOAuthAdminException) {
-                oAuth2Util.when(() -> OAuth2Util.authenticateClient(anyString(), anyString())).thenThrow(
+                oAuth2Util.when(() -> OAuth2Util.authenticateClient(anyString(), anyString(), anyString())).thenThrow(
                         (IdentityOAuthAdminException) exception);
             } else if (exception instanceof IdentityOAuth2Exception) {
-                oAuth2Util.when(() -> OAuth2Util.authenticateClient(anyString(), anyString())).thenThrow(
+                oAuth2Util.when(() -> OAuth2Util.authenticateClient(anyString(), anyString(), anyString())).thenThrow(
                         (IdentityOAuth2Exception) exception);
             }
 
diff --git a/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/client/authentication/OAuthClientAuthnServiceTest.java b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/client/authentication/OAuthClientAuthnServiceTest.java
index 3c8a67416d8..ac080c38b29 100644
--- a/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/client/authentication/OAuthClientAuthnServiceTest.java
+++ b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/client/authentication/OAuthClientAuthnServiceTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2018, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
+ * Copyright (c) 2018-2025, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -153,7 +153,7 @@ public void testAuthenticateClient(Map<String, String> headers, Map<String, List
             if (disableSampleAuthenticator) {
                 sampleClientAuthenticator.enabled = false;
             }
-            oAuth2Util.when(() -> OAuth2Util.authenticateClient(anyString(), anyString())).thenReturn
+            oAuth2Util.when(() -> OAuth2Util.authenticateClient(anyString(), anyString(), anyString())).thenReturn
                     (isBasicAuthenticated);
             HttpServletRequest httpServletRequest = mock(HttpServletRequest.class);
             setHeaders(httpServletRequest, headers);
diff --git a/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuerTest.java b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuerTest.java
index d0ff62f9808..7710576c7a9 100644
--- a/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuerTest.java
+++ b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/token/JWTTokenIssuerTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017-2024, WSO2 LLC. (http://www.wso2.com).
+ * Copyright (c) 2017-2025, WSO2 LLC. (http://www.wso2.com).
  *
  * WSO2 LLC. licenses this file to you under the Apache License,
  * Version 2.0 (the "License"); you may not use this file except
@@ -42,7 +42,9 @@
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 import org.wso2.carbon.base.CarbonBaseConstants;
+import org.wso2.carbon.context.PrivilegedCarbonContext;
 import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser;
+import org.wso2.carbon.identity.common.testng.WithCarbonHome;
 import org.wso2.carbon.identity.common.testng.WithH2Database;
 import org.wso2.carbon.identity.core.util.IdentityTenantUtil;
 import org.wso2.carbon.identity.core.util.IdentityUtil;
@@ -61,6 +63,7 @@
 import org.wso2.carbon.identity.oauth2.token.handlers.grant.AuthorizationGrantHandler;
 import org.wso2.carbon.identity.oauth2.util.OAuth2Util;
 import org.wso2.carbon.identity.openidconnect.CustomClaimsCallbackHandler;
+import org.wso2.carbon.utils.multitenancy.MultitenantConstants;
 
 import java.net.URI;
 import java.nio.charset.StandardCharsets;
@@ -98,6 +101,7 @@
 import static org.testng.Assert.fail;
 import static org.wso2.carbon.identity.openidconnect.util.TestUtils.getKeyStoreFromFile;
 
+@WithCarbonHome
 @WithH2Database(files = {"dbScripts/identity.sql", "dbScripts/insert_consumer_app.sql",
         "dbScripts/insert_local_idp.sql"})
 public class JWTTokenIssuerTest {
@@ -147,6 +151,10 @@ public class JWTTokenIssuerTest {
     @BeforeMethod
     public void setUp() throws Exception {
         initMocks(this);
+        System.setProperty(
+                CarbonBaseConstants.CARBON_HOME,
+                Paths.get(System.getProperty("user.dir"), "src", "test", "resources").toString()
+        );
         oAuthServerConfiguration = mockStatic(OAuthServerConfiguration.class);
         oAuthServerConfiguration.when(OAuthServerConfiguration::getInstance)
                 .thenReturn(this.mockOAuthServerConfiguration);
@@ -176,7 +184,11 @@ public Object[][] provideRequestScopes() {
     public void testBuildJWTTokenFromTokenMsgContext(String requestScopes[],
                                                      List<String> expectedJWTAudiences) throws Exception {
 
-        try (MockedStatic<OAuth2Util> oAuth2Util = mockStatic(OAuth2Util.class)) {
+        PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain("DUMMY_TENANT.COM");
+        try (MockedStatic<OAuth2Util> oAuth2Util = mockStatic(OAuth2Util.class);
+             MockedStatic<IdentityTenantUtil> identityTenantUtil = mockStatic(IdentityTenantUtil.class)) {
+
+            identityTenantUtil.when(() -> IdentityTenantUtil.getTenantId(anyString())).thenReturn(-1234);
             OAuth2AccessTokenReqDTO accessTokenReqDTO = new OAuth2AccessTokenReqDTO();
             accessTokenReqDTO.setGrantType(USER_ACCESS_TOKEN_GRANT_TYPE);
             accessTokenReqDTO.setClientId(DUMMY_CLIENT_ID);
@@ -231,7 +243,11 @@ public void testBuildJWTTokenFromTokenMsgContext(String requestScopes[],
     public void testBuildJWTTokenFromAuthzMsgContext(String requestScopes[],
                                                      List<String> expectedJWTAudiences) throws Exception {
 
-        try (MockedStatic<OAuth2Util> oAuth2Util = mockStatic(OAuth2Util.class)) {
+        PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain("DUMMY_TENANT.COM");
+        try (MockedStatic<OAuth2Util> oAuth2Util = mockStatic(OAuth2Util.class);
+        MockedStatic<IdentityTenantUtil> identityTenantUtil = mockStatic(IdentityTenantUtil.class)) {
+
+            identityTenantUtil.when(() -> IdentityTenantUtil.getTenantId(anyString())).thenReturn(-1234);
             OAuth2AuthorizeReqDTO authorizeReqDTO = new OAuth2AuthorizeReqDTO();
             OAuthAuthzReqMessageContext authzReqMessageContext = new OAuthAuthzReqMessageContext(authorizeReqDTO);
             authzReqMessageContext.setApprovedScope(requestScopes);
@@ -284,13 +300,15 @@ public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
     @Test(expectedExceptions = IdentityOAuth2Exception.class)
     public void testCreateJWTClaimSetForInvalidClient() throws Exception {
 
+        PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(
+                MultitenantConstants.SUPER_TENANT_DOMAIN_NAME);
         try (MockedStatic<OAuth2Util> oAuth2Util = mockStatic(OAuth2Util.class)) {
-            oAuth2Util.when(() -> OAuth2Util.getAppInformationByClientId(null))
+            oAuth2Util.when(() -> OAuth2Util.getAppInformationByClientId(anyString(), anyString()))
                     .thenThrow(new InvalidOAuthClientException("INVALID_CLIENT"));
             oAuth2Util.when(OAuth2Util::isTokenPersistenceEnabled).thenReturn(true);
             when(mockOAuthServerConfiguration.getSignatureAlgorithm()).thenReturn(SHA256_WITH_HMAC);
             JWTTokenIssuer jwtTokenIssuer = new JWTTokenIssuer();
-            jwtTokenIssuer.createJWTClaimSet(null, null, null);
+            jwtTokenIssuer.createJWTClaimSet(null, null, DUMMY_CLIENT_ID);
         }
     }
 
@@ -370,15 +388,19 @@ public void testCreateJWTClaimSet(Object authzReqMessageContext,
                                       String sub,
                                       long expectedExpiry, boolean ppidEnabled) throws Exception {
 
+        PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(
+                MultitenantConstants.SUPER_TENANT_DOMAIN_NAME);
         try (MockedStatic<OAuth2Util> oAuth2Util = mockStatic(OAuth2Util.class);
-             MockedStatic<IdentityUtil> identityUtil = mockStatic(IdentityUtil.class)) {
+             MockedStatic<IdentityTenantUtil> identityTenantUtil = mockStatic(IdentityTenantUtil.class)) {
+
+            identityTenantUtil.when(() -> IdentityTenantUtil.getTenantId(anyString())).thenReturn(-1234);
             OAuthAppDO appDO = spy(new OAuthAppDO());
             appDO.setSubjectType("pairwise");
             appDO.setSectorIdentifierURI(DUMMY_SECTOR_IDENTIFIER);
             appDO.setOauthConsumerKey(DUMMY_CLIENT_ID);
             mockGrantHandlers();
             mockCustomClaimsCallbackHandler();
-            oAuth2Util.when(() -> OAuth2Util.getAppInformationByClientId(anyString())).thenReturn(appDO);
+            oAuth2Util.when(() -> OAuth2Util.getAppInformationByClientId(anyString(), anyString())).thenReturn(appDO);
             oAuth2Util.when(OAuth2Util::getIDTokenIssuer).thenReturn(ID_TOKEN_ISSUER);
             oAuth2Util.when(() -> OAuth2Util.getIdTokenIssuer(anyString(), anyBoolean())).thenReturn(ID_TOKEN_ISSUER);
             oAuth2Util.when(() -> OAuth2Util.getOIDCAudience(anyString(), any())).thenReturn(Collections.singletonList
@@ -460,6 +482,8 @@ public void testSignJWTWithRSA(Object authzReqMessageContext,
                                    String sub,
                                    long expectedExpiry, boolean ppidEnabled) throws Exception {
 
+        PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(
+                MultitenantConstants.SUPER_TENANT_DOMAIN_NAME);
         try (MockedStatic<OAuth2Util> oAuth2Util = mockStatic(OAuth2Util.class, Mockito.CALLS_REAL_METHODS);
              MockedStatic<IdentityTenantUtil> identityTenantUtil = mockStatic(IdentityTenantUtil.class);
              MockedStatic<IdentityUtil> identityUtil = mockStatic(IdentityUtil.class)) {
@@ -470,7 +494,7 @@ public void testSignJWTWithRSA(Object authzReqMessageContext,
             mockCustomClaimsCallbackHandler();
             identityUtil.when(() -> IdentityUtil.getProperty(OAuthConstants.MTLS_HOSTNAME))
                     .thenReturn(DUMMY_MTLS_TOKEN_ENDPOINT);
-            oAuth2Util.when(() -> OAuth2Util.getAppInformationByClientId(anyString())).thenReturn(appDO);
+            oAuth2Util.when(() -> OAuth2Util.getAppInformationByClientId(anyString(), anyString())).thenReturn(appDO);
             oAuth2Util.when(OAuth2Util::isTokenPersistenceEnabled).thenReturn(true);
 
             System.setProperty(CarbonBaseConstants.CARBON_HOME,
@@ -780,7 +804,7 @@ private void prepareForBuildJWTToken(MockedStatic<OAuth2Util> oAuth2Util)
         OAuthAppDO appDO = spy(new OAuthAppDO());
         mockGrantHandlers();
         mockCustomClaimsCallbackHandler();
-        oAuth2Util.when(() -> OAuth2Util.getAppInformationByClientId(anyString())).thenReturn(appDO);
+        oAuth2Util.when(() -> OAuth2Util.getAppInformationByClientId(anyString(), anyString())).thenReturn(appDO);
         oAuth2Util.when(() -> OAuth2Util.getTenantDomain(anyInt())).thenReturn("super.wso2");
         oAuth2Util.when(OAuth2Util::isTokenPersistenceEnabled).thenReturn(true);
     }
@@ -822,6 +846,8 @@ public Map<String, Object> getAdditionalClaims(OAuthTokenReqMessageContext conte
     @Test
     public void testIssueSubjectToken() throws Exception {
 
+        PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(
+                MultitenantConstants.SUPER_TENANT_DOMAIN_NAME);
         when(mockOAuthServerConfiguration.getSignatureAlgorithm()).thenReturn(SHA256_WITH_RSA);
         try (MockedStatic<OAuth2Util> oAuth2Util = mockStatic(OAuth2Util.class, Mockito.CALLS_REAL_METHODS);
              MockedStatic<IdentityTenantUtil> identityTenantUtil = mockStatic(IdentityTenantUtil.class)) {
diff --git a/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/AbstractAuthorizationGrantHandlerTest.java b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/AbstractAuthorizationGrantHandlerTest.java
index b23e74675a9..ed4eaa75059 100644
--- a/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/AbstractAuthorizationGrantHandlerTest.java
+++ b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/AbstractAuthorizationGrantHandlerTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
+ * Copyright (c) 2017-2025, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
  *
  * WSO2 Inc. licenses this file to you under the Apache License,
  * Version 2.0 (the "License"); you may not use this file except
@@ -27,6 +27,8 @@
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
+import org.wso2.carbon.base.MultitenantConstants;
+import org.wso2.carbon.context.PrivilegedCarbonContext;
 import org.wso2.carbon.identity.action.execution.ActionExecutorService;
 import org.wso2.carbon.identity.action.execution.exception.ActionExecutionException;
 import org.wso2.carbon.identity.action.execution.model.ActionType;
@@ -74,6 +76,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyMap;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.mockStatic;
 import static org.mockito.Mockito.verify;
@@ -166,6 +169,9 @@ public void tearDown() {
              boolean dbEntryAvailable, String dbTokenState, boolean tokenLoggable, boolean isIDPIdColumnEnabled,
              boolean setBindingReference) throws Exception {
 
+        PrivilegedCarbonContext.startTenantFlow();
+        PrivilegedCarbonContext.getThreadLocalCarbonContext()
+                .setTenantDomain(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME);
         OAuth2ServiceComponentHolder.setIDPIdColumnEnabled(isIDPIdColumnEnabled);
 
         Map<String, AuthorizationGrantHandler> supportedGrantTypes = new HashMap<>();
@@ -189,7 +195,8 @@ public void tearDown() {
 
         // Mocking static methods using try-with-resources
         try (MockedStatic<IdentityUtil> identityUtil = mockStatic(IdentityUtil.class);
-             MockedStatic<OAuth2Util> oauth2Util = mockStatic(OAuth2Util.class)) {
+             MockedStatic<OAuth2Util> oauth2Util = mockStatic(OAuth2Util.class);
+             MockedStatic<IdentityTenantUtil> identityTenantUtil = mockStatic(IdentityTenantUtil.class)) {
 
             identityUtil.when(() -> IdentityUtil.getProperty(anyString()))
                     .thenReturn(Boolean.TRUE.toString());
@@ -201,13 +208,17 @@ public void tearDown() {
             OauthTokenIssuer oauthTokenIssuer = mock(JWTTokenIssuer.class);
             when(oauthTokenIssuer.getAccessTokenType()).thenReturn("jwt");
             oauth2Util.when(() -> OAuth2Util.getOAuthTokenIssuerForOAuthApp(clientId)).thenReturn(oauthTokenIssuer);
-            oauth2Util.when(() -> OAuth2Util.getAppInformationByClientId(clientId)).thenReturn(oAuthAppDO);
+            oauth2Util.when(() -> OAuth2Util.getAppInformationByClientId(eq(clientId), anyString())).
+                    thenReturn(oAuthAppDO);
 
+            identityTenantUtil.when(IdentityTenantUtil::getLoginTenantId).thenReturn(-1234);
             // Set allowed grant types (ensure PASSWORD_GRANT is allowed for renewal)
             OAuth2ServiceComponentHolder.setJwtRenewWithoutRevokeAllowedGrantTypes(
                     Collections.singletonList("password")); // This allows PASSWORD_GRANT
 
             OAuth2AccessTokenRespDTO tokenRespDTO = handler.issue(tokReqMsgCtx);
+        } finally {
+            PrivilegedCarbonContext.endTenantFlow();
         }
     }
 
@@ -258,6 +269,11 @@ public void testIssue(boolean cacheEnabled, boolean cacheEntryAvailable, long ca
                           boolean isIDPIdColumnEnabled)
             throws Exception {
 
+        PrivilegedCarbonContext.startTenantFlow();
+        PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(
+                MultitenantConstants.SUPER_TENANT_DOMAIN_NAME);
+        OAuthComponentServiceHolder.getInstance().setActionExecutorService(mockActionExecutionService);
+
         OAuth2ServiceComponentHolder.setIDPIdColumnEnabled(isIDPIdColumnEnabled);
 
         Map<String, AuthorizationGrantHandler> supportedGrantTypes = new HashMap<>();
@@ -273,6 +289,7 @@ public void testIssue(boolean cacheEnabled, boolean cacheEntryAvailable, long ca
 
         OAuth2AccessTokenRespDTO tokenRespDTO = handler.issue(tokReqMsgCtx);
         assertNotNull(tokenRespDTO.getAccessToken());
+        PrivilegedCarbonContext.endTenantFlow();
     }
 
     @DataProvider(name = "AuthorizeAccessDelegationDataProvider")
diff --git a/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/PasswordGrantHandlerTest.java b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/PasswordGrantHandlerTest.java
index f197e8bc3db..9d73fb44e7e 100644
--- a/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/PasswordGrantHandlerTest.java
+++ b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/PasswordGrantHandlerTest.java
@@ -20,9 +20,11 @@
 
 import org.apache.commons.logging.Log;
 import org.mockito.MockedStatic;
+import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
+import org.wso2.carbon.context.PrivilegedCarbonContext;
 import org.wso2.carbon.identity.application.authentication.framework.config.builder.FileBasedConfigurationBuilder;
 import org.wso2.carbon.identity.application.authentication.framework.config.model.AuthenticatorConfig;
 import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkUtils;
@@ -31,6 +33,7 @@
 import org.wso2.carbon.identity.application.common.model.ServiceProvider;
 import org.wso2.carbon.identity.application.common.model.ServiceProviderProperty;
 import org.wso2.carbon.identity.application.mgt.ApplicationManagementService;
+import org.wso2.carbon.identity.common.testng.WithCarbonHome;
 import org.wso2.carbon.identity.core.util.IdentityTenantUtil;
 import org.wso2.carbon.identity.core.util.IdentityUtil;
 import org.wso2.carbon.identity.multi.attribute.login.mgt.ResolvedUserResult;
@@ -70,6 +73,7 @@
 import static org.wso2.carbon.identity.application.authentication.framework.util.FrameworkConstants.SHOW_AUTHFAILURE_RESON_CONFIG;
 import static org.wso2.carbon.user.core.UserCoreConstants.PRIMARY_DEFAULT_DOMAIN_NAME;
 
+@WithCarbonHome
 public class PasswordGrantHandlerTest {
 
     private OAuthTokenReqMessageContext tokReqMsgCtx;
@@ -116,6 +120,15 @@ public void init() throws Exception {
 
         // Set the static field to the mock object
         logField.set(null, mockLog);
+
+        PrivilegedCarbonContext.startTenantFlow();
+        PrivilegedCarbonContext.getThreadLocalCarbonContext().setApplicationResidentOrganizationId(null);
+    }
+
+    @AfterMethod
+    public void tearDown() {
+
+        PrivilegedCarbonContext.endTenantFlow();
     }
 
     @DataProvider(name = "ValidateGrantDataProvider")
diff --git a/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/validators/TokenValidationHandlerTest.java b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/validators/TokenValidationHandlerTest.java
index b537478ab95..dce51dc02c1 100644
--- a/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/validators/TokenValidationHandlerTest.java
+++ b/components/org.wso2.carbon.identity.oauth/src/test/java/org/wso2/carbon/identity/oauth2/validators/TokenValidationHandlerTest.java
@@ -71,6 +71,7 @@
 import org.wso2.carbon.identity.openidconnect.util.TestUtils;
 import org.wso2.carbon.identity.organization.management.service.OrganizationManager;
 import org.wso2.carbon.identity.organization.management.service.util.OrganizationManagementConfigUtil;
+import org.wso2.carbon.identity.organization.management.service.util.OrganizationManagementUtil;
 import org.wso2.carbon.idp.mgt.IdentityProviderManager;
 import org.wso2.carbon.user.api.RealmConfiguration;
 import org.wso2.carbon.user.core.service.RealmService;
@@ -254,8 +255,12 @@ public void testBuildIntrospectionResponse(boolean isIDPIdColumnEnabled, String
                 OAuthServerConfiguration.class);
              MockedStatic<IdentityDatabaseUtil> identityDatabaseUtil = mockStatic(IdentityDatabaseUtil.class);
              MockedStatic<OAuth2ServiceComponentHolder> oAuth2ServiceComponentHolder =
-                     mockStatic(OAuth2ServiceComponentHolder.class);) {
+                     mockStatic(OAuth2ServiceComponentHolder.class);
+             MockedStatic<OrganizationManagementUtil> organizationManagementUtil =
+                     mockStatic(OrganizationManagementUtil.class)) {
 
+            organizationManagementUtil.when(() -> OrganizationManagementUtil.isOrganization(anyString())).
+                    thenReturn(false);
             OAuth2ServiceComponentHolder.setIDPIdColumnEnabled(isIDPIdColumnEnabled);
             mockRequiredObjects(oAuthServerConfiguration, identityDatabaseUtil);
             OAuth2ServiceComponentHolder oAuth2ServiceComponentHolderInstance =