Skip to content

Commit dcff15b

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

File tree

14 files changed

+224
-33
lines changed

14 files changed

+224
-33
lines changed

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

Lines changed: 52 additions & 7 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;
@@ -51,6 +52,7 @@
5152
import org.wso2.carbon.identity.application.authentication.framework.config.model.graph.js.JsLogger;
5253
import org.wso2.carbon.identity.application.authentication.framework.context.AuthHistory;
5354
import org.wso2.carbon.identity.application.authentication.framework.context.SessionContext;
55+
import org.wso2.carbon.identity.application.authentication.framework.exception.FrameworkException;
5456
import org.wso2.carbon.identity.application.authentication.framework.exception.UserIdNotFoundException;
5557
import org.wso2.carbon.identity.application.authentication.framework.exception.auth.service.AuthServiceClientException;
5658
import org.wso2.carbon.identity.application.authentication.framework.exception.auth.service.AuthServiceException;
@@ -151,6 +153,8 @@
151153
import org.wso2.carbon.identity.openidconnect.OIDCRequestObjectUtil;
152154
import org.wso2.carbon.identity.openidconnect.model.RequestObject;
153155
import org.wso2.carbon.identity.openidconnect.model.RequestedClaim;
156+
import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementException;
157+
import org.wso2.carbon.identity.organization.management.service.util.OrganizationManagementUtil;
154158
import org.wso2.carbon.utils.CarbonUtils;
155159
import org.wso2.carbon.utils.DiagnosticLog;
156160

@@ -1523,7 +1527,7 @@ private static void addToAuthenticationResultDetailsToOAuthMessage(OAuthMessage
15231527
addMappedRemoteClaimsToSessionCache(oAuthMessage, authnResult);
15241528
}
15251529

1526-
private static void updateAuthTimeInSessionDataCacheEntry(OAuthMessage oAuthMessage) {
1530+
private static void updateAuthTimeInSessionDataCacheEntry(OAuthMessage oAuthMessage) throws OAuthSystemException {
15271531

15281532
String commonAuthIdCookieValue = getCommonAuthCookieString(oAuthMessage.getRequest());
15291533
long authTime = getAuthenticatedTimeFromCommonAuthCookieValue(commonAuthIdCookieValue,
@@ -2429,15 +2433,23 @@ public static void populateValidationResponseWithAppDetail(OAuthMessage oAuthMes
24292433

24302434
String clientId = oAuthMessage.getRequest().getParameter(CLIENT_ID);
24312435
try {
2432-
OAuthAppDO appDO = OAuth2Util.getAppInformationByClientId(clientId);
2436+
OAuthAppDO appDO;
2437+
String appResidentOrgId = PrivilegedCarbonContext.getThreadLocalCarbonContext()
2438+
.getApplicationResidentOrganizationId();
2439+
if (StringUtils.isNotEmpty(appResidentOrgId)) {
2440+
String appResidentTenantDomain = FrameworkUtils.resolveAppResidentTenantDomain(appResidentOrgId);
2441+
appDO = OAuth2Util.getAppInformationByClientId(clientId, appResidentTenantDomain);
2442+
} else {
2443+
appDO = OAuth2Util.getAppInformationByClientId(clientId);
2444+
}
24332445
if (Boolean.TRUE.equals(oAuthMessage.getRequest().getAttribute(OAuthConstants.PKCE_UNSUPPORTED_FLOW))) {
24342446
validationResponse.setPkceMandatory(false);
24352447
} else {
24362448
validationResponse.setPkceMandatory(appDO.isPkceMandatory());
24372449
}
24382450
validationResponse.setApplicationName(appDO.getApplicationName());
24392451
validationResponse.setPkceSupportPlain(appDO.isPkceSupportPlain());
2440-
} catch (InvalidOAuthClientException | IdentityOAuth2Exception e) {
2452+
} catch (InvalidOAuthClientException | IdentityOAuth2Exception | FrameworkException e) {
24412453
throw new OAuthSystemException("Error while retrieving app information for client_id : " + clientId, e);
24422454
}
24432455
}
@@ -2834,7 +2846,8 @@ private static String getSpTenantDomain(String clientId) throws InvalidRequestEx
28342846
try {
28352847
// At this point we have verified that a valid app exists for the client_id. So we directly get the SP
28362848
// tenantDomain.
2837-
return OAuth2Util.getTenantDomainOfOauthApp(clientId);
2849+
String tenantDomain = IdentityTenantUtil.getTenantDomain(IdentityTenantUtil.getLoginTenantId());
2850+
return OAuth2Util.getTenantDomainOfOauthApp(clientId, tenantDomain);
28382851
} catch (InvalidOAuthClientException | IdentityOAuth2Exception e) {
28392852
throw new InvalidRequestException("Error retrieving Service Provider tenantDomain for client_id: "
28402853
+ clientId, OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ErrorCodes.OAuth2SubErrorCodes
@@ -2851,6 +2864,19 @@ private static String getLoginTenantDomain(OAuthMessage oAuthMessage, String cli
28512864

28522865
String loginTenantDomain =
28532866
oAuthMessage.getRequest().getParameter(FrameworkConstants.RequestParams.LOGIN_TENANT_DOMAIN);
2867+
String appResidentOrgId = PrivilegedCarbonContext.getThreadLocalCarbonContext()
2868+
.getApplicationResidentOrganizationId();
2869+
if (StringUtils.isNotEmpty(appResidentOrgId)) {
2870+
String appResidentTenantDomain;
2871+
try {
2872+
appResidentTenantDomain = FrameworkUtils.resolveAppResidentTenantDomain(appResidentOrgId);
2873+
} catch (FrameworkException e) {
2874+
throw new InvalidRequestException("Error resolving tenant domain from organization id: "
2875+
+ appResidentOrgId, OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ErrorCodes.OAuth2SubErrorCodes
2876+
.INVALID_REQUEST);
2877+
}
2878+
return EndpointUtil.getSPTenantDomainFromClientId(clientId, appResidentTenantDomain);
2879+
}
28542880
if (StringUtils.isBlank(loginTenantDomain)) {
28552881
return EndpointUtil.getSPTenantDomainFromClientId(oAuthMessage.getClientId());
28562882
}
@@ -4351,10 +4377,29 @@ private static String appendAuthenticatedIDPs(SessionDataCacheEntry sessionDataC
43514377
* @param resultFromLogin The session context.
43524378
* @param cookieValue The cookie string which contains the commonAuthId value.
43534379
*/
4354-
private static void associateAuthenticationHistory(SessionDataCacheEntry resultFromLogin, String cookieValue) {
4380+
private static void associateAuthenticationHistory(SessionDataCacheEntry resultFromLogin, String cookieValue)
4381+
throws OAuthSystemException {
43554382

4356-
SessionContext sessionContext = getSessionContext(cookieValue,
4357-
resultFromLogin.getoAuth2Parameters().getLoginTenantDomain());
4383+
String tenantDomain = resultFromLogin.getoAuth2Parameters().getLoginTenantDomain();
4384+
/*
4385+
If the app is created in an organization, get the tenant domain of the primary organization since the
4386+
session is stored against the primary organization.
4387+
*/
4388+
try {
4389+
String appTenantDomain = OAuth2Util.getTenantDomainOfOauthApp(
4390+
resultFromLogin.getoAuth2Parameters().getClientId(), tenantDomain);
4391+
if (OrganizationManagementUtil.isOrganization(appTenantDomain)) {
4392+
String appOrgId = OAuth2ServiceComponentHolder.getInstance().getOrganizationManager()
4393+
.resolveOrganizationId(appTenantDomain);
4394+
String primaryOrgId = OAuth2ServiceComponentHolder.getInstance().getOrganizationManager()
4395+
.getPrimaryOrganizationId(appOrgId);
4396+
tenantDomain = OAuth2ServiceComponentHolder.getInstance().getOrganizationManager()
4397+
.resolveTenantDomain(primaryOrgId);
4398+
}
4399+
} catch (OrganizationManagementException | IdentityOAuth2Exception | InvalidOAuthClientException e) {
4400+
throw new OAuthSystemException(e);
4401+
}
4402+
SessionContext sessionContext = getSessionContext(cookieValue, tenantDomain);
43584403
if (sessionContext != null && sessionContext.getSessionAuthHistory() != null
43594404
&& sessionContext.getSessionAuthHistory().getHistory() != null) {
43604405
List<String> authMethods = new ArrayList<>();

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 and the tenant domain.
1549+
*
1550+
* @param clientId Client id of the application.
1551+
* @param tenantDomain Tenant domain to which the client ID belongs.
1552+
* @return tenantDomain 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
}

0 commit comments

Comments
 (0)