diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/config/OAuthServerConfiguration.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/config/OAuthServerConfiguration.java index 6ccffbb41e..d4a94097c1 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/config/OAuthServerConfiguration.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/config/OAuthServerConfiguration.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 @@ -233,6 +233,8 @@ public class OAuthServerConfiguration { private List supportedIdTokenEncryptionMethods = new ArrayList<>(); private String userInfoJWTSignatureAlgorithm = "SHA256withRSA"; private boolean userInfoMultiValueSupportEnabled = true; + private boolean userInfoInternalPrefixedRolesClaimAllowed = true; + private String authContextTTL = "15L"; // property added to fix IDENTITY-4551 in backward compatible manner private boolean useMultiValueSeparatorForAuthContextToken = true; @@ -1575,6 +1577,16 @@ public boolean getUserInfoMultiValueSupportEnabled() { return userInfoMultiValueSupportEnabled; } + /** + * Returns whether Internal prefix should be appended for roles claim of the userinfo response. + * + * @return True if Internal prefix value should be appended for the role claim of userinfo response. + */ + public boolean getUserInfoInternalPrefixedRolesClaimAllowed() { + + return userInfoInternalPrefixedRolesClaimAllowed; + } + public String getConsumerDialectURI() { return consumerDialectURI; } @@ -3503,6 +3515,15 @@ private void parseOpenIDConnectConfig(OMElement oauthConfigElem) { userInfoMultiValueSupportEnabled = Boolean.parseBoolean( userInfoMultiValueSupportEnabledElem.getText().trim()); } + + OMElement userInfoInternalPrefixedRolesClaim = openIDConnectConfigElem + .getFirstChildWithName(getQNameWithIdentityNS(ConfigElements + .OPENID_CONNECT_USERINFO_INTERNAL_PREFIXED_ROLE_CLAIM_ALLOWED)); + if (userInfoInternalPrefixedRolesClaim != null) { + userInfoInternalPrefixedRolesClaimAllowed = + Boolean.parseBoolean(userInfoInternalPrefixedRolesClaim.getText().trim()); + } + if (openIDConnectConfigElem.getFirstChildWithName( getQNameWithIdentityNS(ConfigElements.OPENID_CONNECT_SIGN_JWT_WITH_SP_KEY)) != null) { isJWTSignedWithSPKey = Boolean.parseBoolean(openIDConnectConfigElem.getFirstChildWithName( @@ -4132,6 +4153,8 @@ private class ConfigElements { public static final String OPENID_CONNECT_USERINFO_JWT_SIGNATURE_ALGORITHM = "UserInfoJWTSignatureAlgorithm"; public static final String OPENID_CONNECT_USERINFO_MULTI_VALUE_SUPPORT_ENABLED = "UserInfoMultiValueSupportEnabled"; + public static final String OPENID_CONNECT_USERINFO_INTERNAL_PREFIXED_ROLE_CLAIM_ALLOWED = + "UserInfoInternalPrefixedRolesClaimAllowed"; public static final String OPENID_CONNECT_SIGN_JWT_WITH_SP_KEY = "SignJWTWithSPKey"; public static final String OPENID_CONNECT_IDTOKEN_CUSTOM_CLAIM_CALLBACK_HANDLER = "IDTokenCustomClaimsCallBackHandler"; diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/openidconnect/AbstractUserInfoResponseBuilder.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/openidconnect/AbstractUserInfoResponseBuilder.java index b3dcf43013..26bffdfb83 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/openidconnect/AbstractUserInfoResponseBuilder.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/openidconnect/AbstractUserInfoResponseBuilder.java @@ -25,10 +25,12 @@ import org.wso2.carbon.identity.application.common.model.ServiceProvider; import org.wso2.carbon.identity.application.common.util.IdentityApplicationConstants; import org.wso2.carbon.identity.application.mgt.ApplicationManagementService; +import org.wso2.carbon.identity.core.util.IdentityUtil; import org.wso2.carbon.identity.oauth.cache.AuthorizationGrantCache; import org.wso2.carbon.identity.oauth.cache.AuthorizationGrantCacheEntry; import org.wso2.carbon.identity.oauth.cache.AuthorizationGrantCacheKey; import org.wso2.carbon.identity.oauth.common.exception.InvalidOAuthClientException; +import org.wso2.carbon.identity.oauth.config.OAuthServerConfiguration; import org.wso2.carbon.identity.oauth.dao.OAuthAppDO; import org.wso2.carbon.identity.oauth.user.UserInfoEndpointException; import org.wso2.carbon.identity.oauth.user.UserInfoResponseBuilder; @@ -40,6 +42,8 @@ import org.wso2.carbon.identity.oauth2.util.OAuth2Util; import org.wso2.carbon.identity.openidconnect.internal.OpenIDConnectServiceComponentHolder; import org.wso2.carbon.identity.openidconnect.model.RequestedClaim; +import org.wso2.carbon.user.core.UserCoreConstants; +import org.wso2.carbon.user.core.util.UserCoreUtil; import java.util.ArrayList; import java.util.HashMap; @@ -50,6 +54,7 @@ import static org.apache.commons.collections.CollectionUtils.isNotEmpty; import static org.apache.commons.lang.StringUtils.isNotEmpty; import static org.wso2.carbon.identity.oauth.common.OAuthConstants.OAuth20Params.USERINFO; +import static org.wso2.carbon.identity.oauth.common.OAuthConstants.OIDCClaims.ROLES; /** * Abstract user info response builder. @@ -76,6 +81,9 @@ public String getResponseString(OAuth2TokenValidationResponseDTO tokenResponse) Map userClaims = retrieveUserClaims(tokenResponse); Map filteredUserClaims = filterOIDCClaims(tokenResponse, clientId, spTenantDomain, userClaims); + // Handle roles claim. + handleRolesClaim(filteredUserClaims); + // Handle subject claim. String subjectClaim = getSubjectClaim(userClaims, clientId, spTenantDomain, tokenResponse); subjectClaim = getOIDCSubjectClaim(clientId, spTenantDomain, subjectClaim); @@ -84,6 +92,29 @@ public String getResponseString(OAuth2TokenValidationResponseDTO tokenResponse) return buildResponse(tokenResponse, spTenantDomain, filteredUserClaims); } + private void handleRolesClaim(Map filteredUserClaims) { + + // This check is added for the backward compatibility of userinfo response. + if (OAuthServerConfiguration.getInstance().getUserInfoInternalPrefixedRolesClaimAllowed()) { + return; + } + + if (!(filteredUserClaims.get(ROLES) instanceof String[])) { + return; + } + String[] roles = (String[]) filteredUserClaims.get(ROLES); + if (roles == null) { + return; + } + for (int i = 0; i < roles.length; i++) { + String role = roles[i]; + if (UserCoreConstants.INTERNAL_DOMAIN.equalsIgnoreCase(IdentityUtil.extractDomainFromName(role))) { + String domainRemovedRole = UserCoreUtil.removeDomainFromName(role); + roles[i] = domainRemovedRole; + } + } + } + private String getOIDCSubjectClaim(String clientId, String spTenantDomain, String subjectClaim) throws UserInfoEndpointException {