Skip to content

Commit 8cdfaa2

Browse files
Enable Authorization Code grant support for sub organization OAuth2 applications
1 parent 1a416de commit 8cdfaa2

File tree

13 files changed

+199
-28
lines changed

13 files changed

+199
-28
lines changed

components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/util/AuthzUtil.java

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import org.json.JSONException;
4444
import org.json.JSONObject;
4545
import org.owasp.encoder.Encode;
46+
import org.wso2.carbon.context.PrivilegedCarbonContext;
4647
import org.wso2.carbon.identity.application.authentication.framework.AuthenticationService;
4748
import org.wso2.carbon.identity.application.authentication.framework.AuthenticatorFlowStatus;
4849
import org.wso2.carbon.identity.application.authentication.framework.CommonAuthenticationHandler;
@@ -151,6 +152,7 @@
151152
import org.wso2.carbon.identity.openidconnect.OIDCRequestObjectUtil;
152153
import org.wso2.carbon.identity.openidconnect.model.RequestObject;
153154
import org.wso2.carbon.identity.openidconnect.model.RequestedClaim;
155+
import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementException;
154156
import org.wso2.carbon.utils.CarbonUtils;
155157
import org.wso2.carbon.utils.DiagnosticLog;
156158

@@ -2429,15 +2431,24 @@ public static void populateValidationResponseWithAppDetail(OAuthMessage oAuthMes
24292431

24302432
String clientId = oAuthMessage.getRequest().getParameter(CLIENT_ID);
24312433
try {
2432-
OAuthAppDO appDO = OAuth2Util.getAppInformationByClientId(clientId);
2434+
OAuthAppDO appDO;
2435+
String appResidentOrgId = PrivilegedCarbonContext.getThreadLocalCarbonContext()
2436+
.getApplicationResidentOrganizationId();
2437+
if (StringUtils.isNotEmpty(appResidentOrgId)) {
2438+
String appResidentTenantDomain = OAuth2ServiceComponentHolder.getInstance().getOrganizationManager()
2439+
.resolveTenantDomain(appResidentOrgId);
2440+
appDO = OAuth2Util.getAppInformationByClientId(clientId, appResidentTenantDomain);
2441+
} else {
2442+
appDO = OAuth2Util.getAppInformationByClientId(clientId);
2443+
}
24332444
if (Boolean.TRUE.equals(oAuthMessage.getRequest().getAttribute(OAuthConstants.PKCE_UNSUPPORTED_FLOW))) {
24342445
validationResponse.setPkceMandatory(false);
24352446
} else {
24362447
validationResponse.setPkceMandatory(appDO.isPkceMandatory());
24372448
}
24382449
validationResponse.setApplicationName(appDO.getApplicationName());
24392450
validationResponse.setPkceSupportPlain(appDO.isPkceSupportPlain());
2440-
} catch (InvalidOAuthClientException | IdentityOAuth2Exception e) {
2451+
} catch (InvalidOAuthClientException | IdentityOAuth2Exception | OrganizationManagementException e) {
24412452
throw new OAuthSystemException("Error while retrieving app information for client_id : " + clientId, e);
24422453
}
24432454
}
@@ -2834,7 +2845,8 @@ private static String getSpTenantDomain(String clientId) throws InvalidRequestEx
28342845
try {
28352846
// At this point we have verified that a valid app exists for the client_id. So we directly get the SP
28362847
// tenantDomain.
2837-
return OAuth2Util.getTenantDomainOfOauthApp(clientId);
2848+
String tenantDomain = IdentityTenantUtil.getTenantDomain(IdentityTenantUtil.getLoginTenantId());
2849+
return OAuth2Util.getTenantDomainOfOauthApp(clientId, tenantDomain);
28382850
} catch (InvalidOAuthClientException | IdentityOAuth2Exception e) {
28392851
throw new InvalidRequestException("Error retrieving Service Provider tenantDomain for client_id: "
28402852
+ clientId, OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ErrorCodes.OAuth2SubErrorCodes
@@ -2851,6 +2863,20 @@ private static String getLoginTenantDomain(OAuthMessage oAuthMessage, String cli
28512863

28522864
String loginTenantDomain =
28532865
oAuthMessage.getRequest().getParameter(FrameworkConstants.RequestParams.LOGIN_TENANT_DOMAIN);
2866+
String appResidentOrgId = PrivilegedCarbonContext.getThreadLocalCarbonContext()
2867+
.getApplicationResidentOrganizationId();
2868+
if (StringUtils.isNotEmpty(appResidentOrgId)) {
2869+
String appResidentTenantDomain;
2870+
try {
2871+
appResidentTenantDomain = OAuth2ServiceComponentHolder.getInstance().getOrganizationManager()
2872+
.resolveTenantDomain(appResidentOrgId);
2873+
} catch (OrganizationManagementException e) {
2874+
throw new InvalidRequestException("Error resolving tenant domain from organization id: "
2875+
+ appResidentOrgId, OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ErrorCodes.OAuth2SubErrorCodes
2876+
.UNEXPECTED_SERVER_ERROR);
2877+
}
2878+
return EndpointUtil.getSPTenantDomainFromClientId(clientId, appResidentTenantDomain);
2879+
}
28542880
if (StringUtils.isBlank(loginTenantDomain)) {
28552881
return EndpointUtil.getSPTenantDomainFromClientId(oAuthMessage.getClientId());
28562882
}

components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/util/EndpointUtil.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1544,6 +1544,29 @@ public static String getSPTenantDomainFromClientId(String clientId) {
15441544
}
15451545
}
15461546

1547+
/**
1548+
* This method retrieves the service provider tenant domain using the client ID.
1549+
* However, internally it uses the tenant present in the carbon context.
1550+
*
1551+
* @param clientId Client id of the application.
1552+
* @return Tenant domain of the service provider.
1553+
*/
1554+
public static String getSPTenantDomainFromClientId(String clientId, String tenantDomain) {
1555+
1556+
try {
1557+
OAuthAppDO oAuthAppDO = OAuth2Util.getAppInformationByClientId(clientId, tenantDomain);
1558+
return OAuth2Util.getTenantDomainOfOauthApp(oAuthAppDO);
1559+
} catch (IdentityOAuth2Exception e) {
1560+
log.error("Error while getting oauth app for client Id: " + clientId, e);
1561+
return MultitenantConstants.SUPER_TENANT_DOMAIN_NAME;
1562+
} catch (InvalidOAuthClientException e) {
1563+
if (log.isDebugEnabled()) {
1564+
log.debug("Error while getting oauth app for client Id: " + clientId, e);
1565+
}
1566+
return MultitenantConstants.SUPER_TENANT_DOMAIN_NAME;
1567+
}
1568+
}
1569+
15471570
/**
15481571
* Extract information related to the token request and exception and publish the event to listeners.
15491572
*

components/org.wso2.carbon.identity.oauth.endpoint/src/test/java/org/wso2/carbon/identity/oauth/endpoint/authz/OAuth2AuthzEndpointTest.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,8 @@ public void testAuthorize(Object flowStatusObject, String[] clientId, String ses
547547
MockedStatic<OAuthAdminServiceFactory> oAuthAdminServiceFactory =
548548
mockStatic(OAuthAdminServiceFactory.class);
549549
MockedStatic<OAuth2TokenValidatorServiceFactory> oAuth2TokenValidatorServiceFactory =
550-
mockStatic(OAuth2TokenValidatorServiceFactory.class);) {
550+
mockStatic(OAuth2TokenValidatorServiceFactory.class);
551+
MockedStatic<AuthzUtil> authzUtil = mockStatic(AuthzUtil.class)) {
551552
AuthenticatorFlowStatus flowStatus = (AuthenticatorFlowStatus) flowStatusObject;
552553

553554
Map<String, String[]> requestParams = new HashMap<>();
@@ -587,6 +588,7 @@ public void testAuthorize(Object flowStatusObject, String[] clientId, String ses
587588
.thenReturn(MultitenantConstants.SUPER_TENANT_ID);
588589
identityTenantUtil.when(IdentityTenantUtil::getLoginTenantId)
589590
.thenReturn(MultitenantConstants.SUPER_TENANT_ID);
591+
authzUtil.when(AuthzUtil::isLegacyAuthzRuntime).thenReturn(false);
590592
IdentityEventService eventServiceMock = mock(IdentityEventService.class);
591593
centralLogMgtServiceComponentHolder.when(
592594
CentralLogMgtServiceComponentHolder::getInstance)

components/org.wso2.carbon.identity.oauth.endpoint/src/test/java/org/wso2/carbon/identity/oauth/endpoint/par/OAuth2ParEndpointTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,7 @@ public void testPar(Object requestParamsObj, Object paramMapObj, Object oAuthCli
404404
oAuth2ServiceFactory.when(OAuth2ServiceFactory::getOAuth2Service).thenReturn(oAuth2Service);
405405
parAuthServiceFactory.when(ParAuthServiceFactory::getParAuthService).thenReturn(parAuthService);
406406
identityTenantUtil.when(IdentityTenantUtil::getLoginTenantId).thenReturn(-1234);
407+
identityTenantUtil.when(() -> IdentityTenantUtil.getTenantDomain(-1234)).thenReturn("carbon.super");
407408
identityTenantUtil.when(() -> IdentityTenantUtil.getTenantId("carbon.super")).thenReturn(-1234);
408409

409410
loggerUtils.when(LoggerUtils::isDiagnosticLogsEnabled).thenReturn(true);
@@ -412,7 +413,7 @@ public void testPar(Object requestParamsObj, Object paramMapObj, Object oAuthCli
412413

413414
if (Objects.equals(request.getParameter(OAuthConstants.OAuth20Params.RESPONSE_TYPE),
414415
RESPONSE_TYPE_CODE_ID_TOKEN)) {
415-
OAuthAppDO oauthAppDO = OAuth2Util.getAppInformationByClientId(CLIENT_ID_VALUE);
416+
OAuthAppDO oauthAppDO = OAuth2Util.getAppInformationByClientId(CLIENT_ID_VALUE, "carbon.super");
416417
oauthAppDO.setHybridFlowEnabled(true);
417418
oauthAppDO.setHybridFlowResponseType(RESPONSE_TYPE_CODE_ID_TOKEN);
418419
}

components/org.wso2.carbon.identity.oauth.endpoint/src/test/java/org/wso2/carbon/identity/oauth/endpoint/util/AuthzUtilTest.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,8 @@ public void testAuthorize(Object flowStatusObject, String[] clientId, String ses
490490
OAuthServerConfiguration.class);) {
491491
mockOAuthServerConfiguration(oAuthServerConfiguration);
492492
try (MockedStatic<OAuth2Util.OAuthURL> oAuthURL = mockStatic(OAuth2Util.OAuthURL.class);
493+
MockedStatic<OAuth2Util> oAuth2Util = mockStatic(OAuth2Util.class,
494+
Mockito.CALLS_REAL_METHODS);
493495
MockedStatic<IdentityTenantUtil> identityTenantUtil = mockStatic(IdentityTenantUtil.class);
494496
MockedStatic<LoggerUtils> loggerUtils = mockStatic(LoggerUtils.class);
495497
MockedStatic<CentralLogMgtServiceComponentHolder> centralLogMgtServiceComponentHolder =
@@ -537,6 +539,19 @@ public void testAuthorize(Object flowStatusObject, String[] clientId, String ses
537539
frameworkUtils.when(() -> FrameworkUtils.startTenantFlow(anyString())).thenAnswer(invocation -> null);
538540
frameworkUtils.when(FrameworkUtils::endTenantFlow).thenAnswer(invocation -> null);
539541

542+
OAuthAppDO appDO = new OAuthAppDO();
543+
appDO.setState("ACTIVE");
544+
AuthenticatedUser user = new AuthenticatedUser();
545+
user.setUserName(USERNAME);
546+
user.setTenantDomain(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME);
547+
appDO.setUser(user);
548+
if (expectedStatus == HttpServletResponse.SC_FOUND) {
549+
oAuth2Util.when(() -> OAuth2Util.getAppInformationByClientId(anyString(), anyString()))
550+
.thenReturn(appDO);
551+
} else {
552+
oAuth2Util.when(() -> OAuth2Util.getTenantDomainOfOauthApp(anyString(), anyString()))
553+
.thenReturn(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME);
554+
}
540555
loggerUtils.when(LoggerUtils::isDiagnosticLogsEnabled).thenReturn(diagnosticLogsEnabled);
541556
identityTenantUtil.when(() -> IdentityTenantUtil.getTenantDomain(anyInt()))
542557
.thenReturn(MultitenantConstants.SUPER_TENANT_DOMAIN_NAME);

components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/authz/handlers/util/ResponseTypeHandlerUtil.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,16 @@ public static AuthzCodeDO generateAuthorizationCode(OAuthAuthzReqMessageContext
294294
authorizationReqDTO.getConsumerKey(), authorizationCode, codeId,
295295
authorizationReqDTO.getPkceCodeChallenge(), authorizationReqDTO.getPkceCodeChallengeMethod());
296296
authzCodeDO.setRequestedActor(authorizationReqDTO.getRequestedActor());
297+
String appResidentOrganizationId = PrivilegedCarbonContext.getThreadLocalCarbonContext()
298+
.getApplicationResidentOrganizationId();
299+
if (StringUtils.isNotBlank(appResidentOrganizationId)) {
300+
if (log.isDebugEnabled()) {
301+
log.debug("Setting accessing organization id: " + appResidentOrganizationId +
302+
" and user resident organization in the authorization code data object.");
303+
}
304+
authzCodeDO.getAuthorizedUser().setAccessingOrganization(appResidentOrganizationId);
305+
authzCodeDO.getAuthorizedUser().setUserResidentOrganization(appResidentOrganizationId);
306+
}
297307
String appTenant = authorizationReqDTO.getTenantDomain();
298308
if (StringUtils.isNotEmpty(appTenant)) {
299309
OAuthTokenPersistenceFactory.getInstance().getAuthorizationCodeDAO()

components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/authz/validators/AbstractResponseTypeRequestValidator.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ public OAuth2ClientValidationResponseDTO validateClientInfo(HttpServletRequest r
104104
}
105105

106106
try {
107-
String appTenantDomain = OAuth2Util.getTenantDomainOfOauthApp(clientId);
107+
String tenantDomain = IdentityTenantUtil.getTenantDomain(IdentityTenantUtil.getLoginTenantId());
108+
String appTenantDomain = OAuth2Util.getTenantDomainOfOauthApp(clientId, tenantDomain);
108109
validateRequestTenantDomain(appTenantDomain);
109110
DiagnosticLog.DiagnosticLogBuilder diagnosticLogBuilder = null;
110111
if (LoggerUtils.isDiagnosticLogsEnabled()) {
@@ -125,7 +126,7 @@ public OAuth2ClientValidationResponseDTO validateClientInfo(HttpServletRequest r
125126
if (diagnosticLogBuilder != null) {
126127
diagnosticLogBuilder.inputParam(LogConstants.InputKeys.CLIENT_ID, clientId);
127128
}
128-
OAuthAppDO appDO = OAuth2Util.getAppInformationByClientId(clientId);
129+
OAuthAppDO appDO = OAuth2Util.getAppInformationByClientId(clientId, appTenantDomain);
129130
String appState = appDO.getState();
130131

131132
if (StringUtils.isEmpty(appState)) {

components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dao/AuthorizationCodeDAOImpl.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.apache.commons.lang.StringUtils;
2424
import org.apache.commons.logging.Log;
2525
import org.apache.commons.logging.LogFactory;
26+
import org.wso2.carbon.context.PrivilegedCarbonContext;
2627
import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser;
2728
import org.wso2.carbon.identity.application.common.IdentityApplicationManagementException;
2829
import org.wso2.carbon.identity.application.common.model.ServiceProvider;
@@ -38,6 +39,7 @@
3839
import org.wso2.carbon.identity.oauth2.model.AuthzCodeDO;
3940
import org.wso2.carbon.identity.oauth2.util.OAuth2TokenUtil;
4041
import org.wso2.carbon.identity.oauth2.util.OAuth2Util;
42+
import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementException;
4143

4244
import java.sql.Connection;
4345
import java.sql.PreparedStatement;
@@ -230,7 +232,14 @@ public AuthorizationCodeValidationResult validateAuthorizationCode(String consum
230232

231233
prepStmt = connection.prepareStatement(sql);
232234
prepStmt.setString(1, getPersistenceProcessor().getProcessedClientId(consumerKey));
233-
prepStmt.setInt(2, IdentityTenantUtil.getLoginTenantId());
235+
tenantId = IdentityTenantUtil.getLoginTenantId();
236+
String appResidentOrganizationId = PrivilegedCarbonContext.getThreadLocalCarbonContext()
237+
.getApplicationResidentOrganizationId();
238+
if (StringUtils.isNotEmpty(appResidentOrganizationId)) {
239+
tenantId = IdentityTenantUtil.getTenantId(OAuth2ServiceComponentHolder.getInstance()
240+
.getOrganizationManager().resolveTenantDomain(appResidentOrganizationId));
241+
}
242+
prepStmt.setInt(2, tenantId);
234243
//use hash value for search
235244
prepStmt.setString(3, getHashingPersistenceProcessor().getProcessedAuthzCode(authorizationKey));
236245
resultSet = prepStmt.executeQuery();
@@ -266,6 +275,12 @@ public AuthorizationCodeValidationResult validateAuthorizationCode(String consum
266275
"for client id " + consumerKey, e);
267276
}
268277
user.setAuthenticatedSubjectIdentifier(subjectIdentifier, serviceProvider);
278+
if (StringUtils.isNotEmpty(appResidentOrganizationId)) {
279+
String userOrganizationId = OAuth2ServiceComponentHolder.getInstance().getOrganizationManager()
280+
.resolveOrganizationId(tenantDomain);
281+
user.setAccessingOrganization(appResidentOrganizationId);
282+
user.setUserResidentOrganization(userOrganizationId);
283+
}
269284

270285
String tokenId = resultSet.getString(9);
271286
String tokenBindingReference = NONE;
@@ -291,6 +306,8 @@ public AuthorizationCodeValidationResult validateAuthorizationCode(String consum
291306

292307
} catch (SQLException | URLBuilderException e) {
293308
throw new IdentityOAuth2Exception("Error when validating an authorization code", e);
309+
} catch (OrganizationManagementException e) {
310+
throw new IdentityOAuth2Exception("Error occurred while resolving tenant id for organization.", e);
294311
} finally {
295312
IdentityDatabaseUtil.closeAllConnections(connection, resultSet, prepStmt);
296313
}

components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/token/handlers/grant/AuthorizationCodeGrantHandler.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ public boolean validateGrant(OAuthTokenReqMessageContext tokReqMsgCtx) throws Id
9292
// If redirect_uri was given in the authorization request,
9393
// token request should send matching redirect_uri value.
9494
validateCallbackUrlFromRequest(tokenReq.getCallbackURI(), authzCodeBean.getCallbackUrl());
95-
validatePKCECode(authzCodeBean, tokenReq.getPkceCodeVerifier());
95+
validatePKCECode(authzCodeBean, tokenReq.getPkceCodeVerifier(), tokenReq.getTenantDomain());
9696
validateRequestedActor(authzCodeBean, tokReqMsgCtx);
9797
setPropertiesForTokenGeneration(tokReqMsgCtx, tokenReq, authzCodeBean);
9898
} finally {
@@ -589,12 +589,12 @@ private void markAsExpired(AuthzCodeDO authzCodeBean) throws IdentityOAuth2Excep
589589
* @return true if PKCE is validated
590590
* @throws IdentityOAuth2Exception
591591
*/
592-
private boolean validatePKCECode(AuthzCodeDO authzCodeBean, String verificationCode)
592+
private boolean validatePKCECode(AuthzCodeDO authzCodeBean, String verificationCode, String tenantDomain)
593593
throws IdentityOAuth2Exception {
594594

595595
String pkceCodeChallenge = authzCodeBean.getPkceCodeChallenge();
596596
String pkceCodeChallengeMethod = authzCodeBean.getPkceCodeChallengeMethod();
597-
OAuthAppDO oAuthApp = getOAuthAppDO(authzCodeBean.getConsumerKey());
597+
OAuthAppDO oAuthApp = getOAuthAppDO(authzCodeBean.getConsumerKey(), tenantDomain);
598598
if (!validatePKCE(pkceCodeChallenge, verificationCode, pkceCodeChallengeMethod, oAuthApp)) {
599599
//possible malicious oAuthRequest
600600
log.warn("Failed PKCE Verification for oAuth 2.0 request");
@@ -653,9 +653,9 @@ private void revokeAuthorizationCode(AuthzCodeDO authzCodeBean) throws IdentityO
653653
}
654654
}
655655

656-
private OAuthAppDO getOAuthAppDO(String clientId) throws IdentityOAuth2Exception {
656+
private OAuthAppDO getOAuthAppDO(String clientId, String tenantDomain) throws IdentityOAuth2Exception {
657657
try {
658-
return OAuth2Util.getAppInformationByClientId(clientId);
658+
return OAuth2Util.getAppInformationByClientId(clientId, tenantDomain);
659659
} catch (InvalidOAuthClientException e) {
660660
throw new IdentityOAuth2Exception("Error while retrieving app information for client: " + clientId);
661661
}

0 commit comments

Comments
 (0)