diff --git a/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/authz/OAuth2AuthzEndpoint.java b/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/authz/OAuth2AuthzEndpoint.java index 58f004c7fd9..07cdc75a723 100644 --- a/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/authz/OAuth2AuthzEndpoint.java +++ b/components/org.wso2.carbon.identity.oauth.endpoint/src/main/java/org/wso2/carbon/identity/oauth/endpoint/authz/OAuth2AuthzEndpoint.java @@ -421,6 +421,37 @@ private void addFederatedTokensToSessionCache(OAuthMessage oAuthMessage, } } + /** + * Add unfiltered federated user claims to session cache. + * + * @param oAuthMessage The OAuthMessage with the session data cache entry. + * @param authenticationResult The authentication result of authorization call. + */ + private void addUnfilteredFederatedUserClaimsToSessionCache(OAuthMessage oAuthMessage, + AuthenticationResult authenticationResult) { + + if (!(authenticationResult.getProperty(FrameworkConstants.UNFILTERED_LOCAL_CLAIM_VALUES) instanceof Map)) { + return; + } + Map unfilteredFederatedUserClaims = (Map) authenticationResult + .getProperty(FrameworkConstants.UNFILTERED_LOCAL_CLAIM_VALUES); + + SessionDataCacheEntry sessionDataCacheEntry = oAuthMessage.getSessionDataCacheEntry(); + if (sessionDataCacheEntry == null || unfilteredFederatedUserClaims.isEmpty()) { + return; + } + Map unfilteredFederatedUserAttributes = new HashMap<>(); + unfilteredFederatedUserClaims.forEach( + (key, value) -> unfilteredFederatedUserAttributes.put(ClaimMapping.build(key, key, null, + false), value)); + sessionDataCacheEntry.setUnfilteredFederatedUserClaims(unfilteredFederatedUserAttributes); + if (log.isDebugEnabled() && authenticationResult.getSubject() != null) { + log.debug("Added the unfiltered federated user claims to the session data cache. " + + "Session context identifier: " + sessionDataCacheEntry.getSessionContextIdentifier() + + " for the user: " + authenticationResult.getSubject().getLoggableMaskedUserId()); + } + } + /** * This method creates a list of FederatedTokenDO objects from the list of FederatedToken objects. * @@ -1389,6 +1420,7 @@ private void addToAuthenticationResultDetailsToOAuthMessage(OAuthMessage oAuthMe authnResult.getProperty(FrameworkConstants.AnalyticsAttributes.SESSION_ID)); // Adding federated tokens come with the authentication result of the authorization call. addFederatedTokensToSessionCache(oAuthMessage, authnResult); + addUnfilteredFederatedUserClaimsToSessionCache(oAuthMessage, authnResult); } private void updateAuthTimeInSessionDataCacheEntry(OAuthMessage oAuthMessage) { @@ -2143,6 +2175,11 @@ private void addUserAttributesToOAuthMessage(OAuthMessage oAuthMessage, String c authorizationGrantCacheEntry.setRequestObjectFlow(isRequestObjectFlow); authorizationGrantCacheEntry.setFederatedTokens(sessionDataCacheEntry.getFederatedTokens()); sessionDataCacheEntry.setFederatedTokens(null); + Map unfilteredFederatedUserAttributes = sessionDataCacheEntry. + getUnfilteredFederatedUserAttributes(); + if (unfilteredFederatedUserAttributes != null) { + authorizationGrantCacheEntry.setUnfilteredFederatedUserAttributes(unfilteredFederatedUserAttributes); + } oAuthMessage.setAuthorizationGrantCacheEntry(authorizationGrantCacheEntry); } @@ -3785,6 +3822,7 @@ private OAuth2AuthorizeReqDTO buildAuthRequest(OAuth2Parameters oauth2Params, Se authzReqDTO.setState(oauth2Params.getState()); authzReqDTO.setHttpServletRequestWrapper(new HttpServletRequestWrapper(request)); authzReqDTO.setRequestedSubjectId(oauth2Params.getRequestedSubjectId()); + authzReqDTO.setUnfilteredFederatedUserAttributes(sessionDataCacheEntry.getUnfilteredFederatedUserAttributes()); if (sessionDataCacheEntry.getParamMap() != null && sessionDataCacheEntry.getParamMap().get(OAuthConstants .AMR) != null) { @@ -4520,6 +4558,10 @@ private void addUserAttributesToCache(SessionDataCacheEntry sessionDataCacheEntr DeviceAuthorizationGrantCacheKey cacheKey = new DeviceAuthorizationGrantCacheKey(deviceCode); DeviceAuthorizationGrantCacheEntry cacheEntry = new DeviceAuthorizationGrantCacheEntry(sessionDataCacheEntry.getLoggedInUser().getUserAttributes()); + if (sessionDataCacheEntry.getUnfilteredFederatedUserAttributes() != null) { + cacheEntry.setUnfilteredFederatedUserAttributes(sessionDataCacheEntry + .getUnfilteredFederatedUserAttributes()); + } DeviceAuthorizationGrantCache.getInstance().addToCache(cacheKey, cacheEntry); } diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/cache/AuthorizationGrantCacheEntry.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/cache/AuthorizationGrantCacheEntry.java index 41e81160253..6718c472f26 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/cache/AuthorizationGrantCacheEntry.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/cache/AuthorizationGrantCacheEntry.java @@ -66,6 +66,8 @@ public class AuthorizationGrantCacheEntry extends CacheEntry { private boolean hasNonOIDCClaims; + private Map unfilteredFederatedUserAttributes; + /* OIDC sub claim. This should be formatted based on the Service Provider configurations to append userStoreDomain and tenantDomain. @@ -390,4 +392,15 @@ public void setPreIssueAccessTokenActionsExecuted(boolean preIssueAccessTokenAct isPreIssueAccessTokenActionsExecuted = preIssueAccessTokenActionsExecuted; } + + public Map getUnfilteredFederatedUserAttributes() { + + return unfilteredFederatedUserAttributes; + } + + public void setUnfilteredFederatedUserAttributes( + Map unfilteredFederatedUserAttributes) { + + this.unfilteredFederatedUserAttributes = unfilteredFederatedUserAttributes; + } } diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/cache/SessionDataCacheEntry.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/cache/SessionDataCacheEntry.java index b2febaedfb5..35f9343a4fb 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/cache/SessionDataCacheEntry.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth/cache/SessionDataCacheEntry.java @@ -19,6 +19,7 @@ package org.wso2.carbon.identity.oauth.cache; import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; +import org.wso2.carbon.identity.application.common.model.ClaimMapping; import org.wso2.carbon.identity.oauth2.authz.OAuthAuthzReqMessageContext; import org.wso2.carbon.identity.oauth2.model.FederatedTokenDO; import org.wso2.carbon.identity.oauth2.model.OAuth2Parameters; @@ -53,6 +54,7 @@ public class SessionDataCacheEntry extends CacheEntry { private Map endpointParams = new HashMap<>(); private List federatedTokens; + private Map unfilteredFederatedUserAttributes; public OAuthAuthzReqMessageContext getAuthzReqMsgCtx() { return authzReqMsgCtx; @@ -172,4 +174,14 @@ public void setFederatedTokens(List federatedTokens) { this.federatedTokens = federatedTokens; } + + public Map getUnfilteredFederatedUserAttributes() { + + return unfilteredFederatedUserAttributes; + } + + public void setUnfilteredFederatedUserClaims(Map unfilteredFederatedUserAttributes) { + + this.unfilteredFederatedUserAttributes = unfilteredFederatedUserAttributes; + } } diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/authz/handlers/TokenResponseTypeHandler.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/authz/handlers/TokenResponseTypeHandler.java index c2563ab165a..fca16d1a087 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/authz/handlers/TokenResponseTypeHandler.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/authz/handlers/TokenResponseTypeHandler.java @@ -554,6 +554,11 @@ private void addUserAttributesToCache(String accessToken, authorizationGrantCacheEntry.setMaxAge(authorizeReqDTO.getMaxAge()); } + if (authorizeReqDTO.getUnfilteredFederatedUserAttributes() != null) { + authorizationGrantCacheEntry.setUnfilteredFederatedUserAttributes( + authorizeReqDTO.getUnfilteredFederatedUserAttributes()); + } + ClaimMapping key = new ClaimMapping(); Claim claimOfKey = new Claim(); claimOfKey.setClaimUri(OAuth2Util.SUB); diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/authz/handlers/util/ResponseTypeHandlerUtil.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/authz/handlers/util/ResponseTypeHandlerUtil.java index d124e392b62..2362830746e 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/authz/handlers/util/ResponseTypeHandlerUtil.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/authz/handlers/util/ResponseTypeHandlerUtil.java @@ -487,6 +487,11 @@ private static void addUserAttributesToCache(String accessToken, OAuthAuthzReqMe userAttributes.put(key, sub); } + if (authorizeReqDTO.getUnfilteredFederatedUserAttributes() != null) { + authorizationGrantCacheEntry.setUnfilteredFederatedUserAttributes( + authorizeReqDTO.getUnfilteredFederatedUserAttributes()); + } + authorizationGrantCacheEntry .setValidityPeriod(TimeUnit.MILLISECONDS.toNanos(accessTokenDO.getValidityPeriodInMillis())); AuthorizationGrantCache.getInstance().addToCacheByToken(authorizationGrantCacheKey, diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/device/cache/DeviceAuthorizationGrantCacheEntry.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/device/cache/DeviceAuthorizationGrantCacheEntry.java index 9e6b022940e..0f48ff8733e 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/device/cache/DeviceAuthorizationGrantCacheEntry.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/device/cache/DeviceAuthorizationGrantCacheEntry.java @@ -31,12 +31,20 @@ public class DeviceAuthorizationGrantCacheEntry extends CacheEntry { private static final long serialVersionUID = -3043225645166013281L; private Map userAttributes; + private Map unfilteredFederatedUserAttributes; public DeviceAuthorizationGrantCacheEntry(Map userAttributes) { this.userAttributes = userAttributes; } + public DeviceAuthorizationGrantCacheEntry(Map userAttributes, + Map unfilteredFederatedUserAttributes) { + + this.userAttributes = userAttributes; + this.unfilteredFederatedUserAttributes = unfilteredFederatedUserAttributes; + } + /** * Return user attributes of cache entry. * @@ -56,4 +64,15 @@ public void setUserAttributes(Map userAttributes) { this.userAttributes = userAttributes; } + + public Map getUnfilteredFederatedUserAttributes() { + + return unfilteredFederatedUserAttributes; + } + + public void setUnfilteredFederatedUserAttributes( + Map unfilteredFederatedUserAttributes) { + + this.unfilteredFederatedUserAttributes = unfilteredFederatedUserAttributes; + } } diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dto/OAuth2AuthorizeReqDTO.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dto/OAuth2AuthorizeReqDTO.java index e3db5c13814..fa21d91e16f 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dto/OAuth2AuthorizeReqDTO.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/oauth2/dto/OAuth2AuthorizeReqDTO.java @@ -19,10 +19,12 @@ package org.wso2.carbon.identity.oauth2.dto; import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; +import org.wso2.carbon.identity.application.common.model.ClaimMapping; import org.wso2.carbon.identity.oauth2.model.HttpRequestHeader; import org.wso2.carbon.identity.openidconnect.model.RequestObject; import java.util.LinkedHashSet; +import java.util.Map; import java.util.Properties; import javax.servlet.http.Cookie; @@ -61,6 +63,7 @@ public class OAuth2AuthorizeReqDTO { private boolean isRequestObjectFlow; private String state; private String requestedSubjectId; + private Map unfilteredFederatedUserAttributes; public String getRequestedSubjectId() { @@ -303,4 +306,15 @@ public void setHttpServletRequestWrapper(HttpServletRequestWrapper httpServletRe this.httpServletRequestWrapper = httpServletRequestWrapper; } + + public Map getUnfilteredFederatedUserAttributes() { + + return unfilteredFederatedUserAttributes; + } + + public void setUnfilteredFederatedUserAttributes( + Map unfilteredFederatedUserAttributes) { + + this.unfilteredFederatedUserAttributes = unfilteredFederatedUserAttributes; + } } 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 16f539f4ec4..2376ce8dc72 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 @@ -621,7 +621,13 @@ private Optional getAuthzGrantCacheEntryFromDevice DeviceAuthorizationGrantCache.getInstance().getValueFromCache(deviceCodeCacheKey); if (cacheEntry != null) { Map userAttributes = cacheEntry.getUserAttributes(); - return Optional.of(new AuthorizationGrantCacheEntry(userAttributes)); + AuthorizationGrantCacheEntry authorizationGrantCacheEntry = + new AuthorizationGrantCacheEntry(userAttributes); + if (cacheEntry.getUnfilteredFederatedUserAttributes() != null) { + authorizationGrantCacheEntry.setUnfilteredFederatedUserAttributes(cacheEntry + .getUnfilteredFederatedUserAttributes()); + } + return Optional.of(authorizationGrantCacheEntry); } return Optional.empty(); } diff --git a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/openidconnect/JWTAccessTokenOIDCClaimsHandler.java b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/openidconnect/JWTAccessTokenOIDCClaimsHandler.java index 9c91b7c8a6d..c4301576345 100644 --- a/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/openidconnect/JWTAccessTokenOIDCClaimsHandler.java +++ b/components/org.wso2.carbon.identity.oauth/src/main/java/org/wso2/carbon/identity/openidconnect/JWTAccessTokenOIDCClaimsHandler.java @@ -20,27 +20,46 @@ import com.nimbusds.jwt.JWTClaimsSet; import net.minidev.json.JSONArray; +import org.apache.commons.collections.MapUtils; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.apache.oltu.oauth2.common.exception.OAuthSystemException; +import org.wso2.carbon.CarbonConstants; import org.wso2.carbon.base.MultitenantConstants; import org.wso2.carbon.identity.application.authentication.framework.model.AuthenticatedUser; import org.wso2.carbon.identity.application.authentication.framework.util.FrameworkUtils; import org.wso2.carbon.identity.application.common.IdentityApplicationManagementException; +import org.wso2.carbon.identity.application.common.model.ClaimMapping; import org.wso2.carbon.identity.application.common.model.ServiceProvider; import org.wso2.carbon.identity.application.mgt.ApplicationManagementService; +import org.wso2.carbon.identity.base.IdentityConstants; import org.wso2.carbon.identity.base.IdentityException; import org.wso2.carbon.identity.claim.metadata.mgt.ClaimMetadataHandler; import org.wso2.carbon.identity.claim.metadata.mgt.exception.ClaimMetadataException; import org.wso2.carbon.identity.core.util.IdentityTenantUtil; +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.OAuthConstants; 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.oauth2.IdentityOAuth2Exception; import org.wso2.carbon.identity.oauth2.authz.OAuthAuthzReqMessageContext; import org.wso2.carbon.identity.oauth2.dao.OAuthTokenPersistenceFactory; +import org.wso2.carbon.identity.oauth2.device.cache.DeviceAuthorizationGrantCache; +import org.wso2.carbon.identity.oauth2.device.cache.DeviceAuthorizationGrantCacheEntry; +import org.wso2.carbon.identity.oauth2.device.cache.DeviceAuthorizationGrantCacheKey; +import org.wso2.carbon.identity.oauth2.dto.OAuth2AuthorizeReqDTO; import org.wso2.carbon.identity.oauth2.internal.OAuth2ServiceComponentHolder; +import org.wso2.carbon.identity.oauth2.model.RefreshTokenValidationDataDO; +import org.wso2.carbon.identity.oauth2.token.AccessTokenIssuer; import org.wso2.carbon.identity.oauth2.token.OAuthTokenReqMessageContext; +import org.wso2.carbon.identity.oauth2.token.OauthTokenIssuer; +import org.wso2.carbon.identity.oauth2.token.handlers.grant.RefreshGrantHandler; +import org.wso2.carbon.identity.oauth2.util.AuthzUtil; import org.wso2.carbon.identity.oauth2.util.OAuth2Util; import org.wso2.carbon.identity.openidconnect.internal.OpenIDConnectServiceComponentHolder; import org.wso2.carbon.identity.organization.management.service.exception.OrganizationManagementException; @@ -48,6 +67,7 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -55,8 +75,13 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; +import static org.apache.commons.collections.MapUtils.isEmpty; +import static org.apache.commons.collections.MapUtils.isNotEmpty; +import static org.wso2.carbon.identity.oauth.common.OAuthConstants.ACCESS_TOKEN; +import static org.wso2.carbon.identity.oauth.common.OAuthConstants.AUTHZ_CODE; import static org.wso2.carbon.identity.oauth.common.OAuthConstants.OIDCClaims.ADDRESS; import static org.wso2.carbon.identity.oauth.common.OAuthConstants.OIDCClaims.GROUPS; +import static org.wso2.carbon.identity.oauth2.device.constants.Constants.DEVICE_CODE; /** * A class that provides OIDC claims for JWT access tokens. @@ -66,21 +91,14 @@ public class JWTAccessTokenOIDCClaimsHandler implements CustomClaimsCallbackHand private static final Log log = LogFactory.getLog(JWTAccessTokenOIDCClaimsHandler.class); private static final String OAUTH2 = "oauth2"; + private static final String OIDC_DIALECT = "http://wso2.org/oidc/claim"; @Override public JWTClaimsSet handleCustomClaims(JWTClaimsSet.Builder builder, OAuthTokenReqMessageContext request) throws IdentityOAuth2Exception { - String clientId = request.getOauth2AccessTokenReqDTO().getClientId(); - String spTenantDomain = getServiceProviderTenantDomain(request); - AuthenticatedUser authenticatedUser = request.getAuthorizedUser(); - - Map claims = getAccessTokenUserClaims(authenticatedUser, clientId, spTenantDomain); - if (claims == null || claims.isEmpty()) { - return builder.build(); - } - Map filteredClaims = handleClaimsFormat(claims, clientId, spTenantDomain); - return setClaimsToJwtClaimSet(builder, filteredClaims); + Map claims = getUserClaimsInOIDCDialect(request); + return setClaimsToJwtClaimSet(builder, claims); } @Override @@ -91,63 +109,519 @@ public JWTClaimsSet handleCustomClaims(JWTClaimsSet.Builder builder, OAuthAuthzR Handling the user attributes for the access token. There is no requirement of the consent to manage user attributes for the access token. */ - String clientId = request.getAuthorizationReqDTO().getConsumerKey(); - String spTenantDomain = getServiceProviderTenantDomain(request); - AuthenticatedUser authenticatedUser = request.getAuthorizationReqDTO().getUser(); + Map claims = getUserClaimsInOIDCDialect(request); + return setClaimsToJwtClaimSet(builder, claims); + } + + /** + * Get response map. + * + * @param requestMsgCtx Token request message context + * @return Mapped claimed + * @throws OAuthSystemException + */ + private Map getUserClaimsInOIDCDialect(OAuthTokenReqMessageContext requestMsgCtx) + throws IdentityOAuth2Exception { - Map claims = getAccessTokenUserClaims(authenticatedUser, clientId, spTenantDomain); - if (claims == null || claims.isEmpty()) { - return builder.build(); + Map userClaimsInOIDCDialect; + + Map userAttributes = getCachedUserAttributes(requestMsgCtx, false); + if ((userAttributes.isEmpty() || isOrganizationSwitchGrantType(requestMsgCtx)) + && (isLocalUser(requestMsgCtx.getAuthorizedUser()) + || isOrganizationSsoUserSwitchingOrganization(requestMsgCtx.getAuthorizedUser()))) { + if (log.isDebugEnabled()) { + log.debug("User attributes not found in cache against the access token or authorization code. " + + "Retrieving claims for local user: " + requestMsgCtx.getAuthorizedUser() + " from userstore."); + } + if (!StringUtils.equals(requestMsgCtx.getAuthorizedUser().getUserResidentOrganization(), + requestMsgCtx.getAuthorizedUser().getAccessingOrganization()) && + !CarbonConstants.ENABLE_LEGACY_AUTHZ_RUNTIME && + StringUtils.isNotEmpty(AuthzUtil.getUserIdOfAssociatedUser(requestMsgCtx.getAuthorizedUser()))) { + requestMsgCtx.getAuthorizedUser().setSharedUserId(AuthzUtil.getUserIdOfAssociatedUser(requestMsgCtx + .getAuthorizedUser())); + requestMsgCtx.getAuthorizedUser().setUserSharedOrganizationId(requestMsgCtx.getAuthorizedUser() + .getAccessingOrganization()); + } + // Get claim in oidc dialect from user store. + userClaimsInOIDCDialect = retrieveClaimsForLocalUser(requestMsgCtx); + } else { + // Get claim map from the cached attributes + userClaimsInOIDCDialect = getOIDCClaimsFromUserAttributes(userAttributes, requestMsgCtx); + Map federatedUserAttributes = getCachedUserAttributes(requestMsgCtx, true); + Map federatedUserClaimsInOIDCDialect = + getOIDCClaimsFromFederatedUserAttributes(federatedUserAttributes, requestMsgCtx); + userClaimsInOIDCDialect.putAll(federatedUserClaimsInOIDCDialect); + } + + Object hasNonOIDCClaimsProperty = requestMsgCtx.getProperty(OIDCConstants.HAS_NON_OIDC_CLAIMS); + if (isPreserverClaimUrisInAssertion(requestMsgCtx) || (hasNonOIDCClaimsProperty != null + && (Boolean) hasNonOIDCClaimsProperty)) { + return userClaimsInOIDCDialect; + } else { + return filterClaims(userClaimsInOIDCDialect, requestMsgCtx); } - Map filteredClaims = handleClaimsFormat(claims, clientId, spTenantDomain); - return setClaimsToJwtClaimSet(builder, filteredClaims); } - private Map getAccessTokenUserClaims(AuthenticatedUser authenticatedUser, String clientId, - String spTenantDomain) - throws IdentityOAuth2Exception { + private Map filterClaims(Map userClaimsInOIDCDialect, + OAuthTokenReqMessageContext requestMsgCtx) throws IdentityOAuth2Exception { + String spTenantDomain = getServiceProviderTenantDomain(requestMsgCtx); + String clientId = requestMsgCtx.getOauth2AccessTokenReqDTO().getClientId(); // Get allowed access token claims. List allowedClaims = getAccessTokenClaims(clientId, spTenantDomain); if (allowedClaims.isEmpty()) { return new HashMap<>(); } + Map claims = allowedClaims.stream() + .filter(userClaimsInOIDCDialect::containsKey) + .collect(Collectors.toMap(claim -> claim, userClaimsInOIDCDialect::get)); + return handleClaimsFormat(claims, clientId, spTenantDomain); + } - // Get OIDC to Local claim mappings. - Map oidcToLocalClaimMappings = getOIDCToLocalClaimMappings(spTenantDomain); - if (oidcToLocalClaimMappings.isEmpty()) { + private Map filterClaims(Map userClaimsInOIDCDialect, + OAuthAuthzReqMessageContext authzReqMessageContext) + throws IdentityOAuth2Exception { + + String spTenantDomain = getServiceProviderTenantDomain(authzReqMessageContext); + String clientId = authzReqMessageContext.getAuthorizationReqDTO().getConsumerKey(); + // Get allowed access token claims. + List allowedClaims = getAccessTokenClaims(clientId, spTenantDomain); + if (allowedClaims.isEmpty()) { return new HashMap<>(); } - List localClaimURIs = allowedClaims.stream().map(oidcToLocalClaimMappings::get).filter(Objects::nonNull) - .collect(Collectors.toList()); + Map claims = allowedClaims.stream().filter(userClaimsInOIDCDialect::containsKey) + .collect(Collectors.toMap(claim -> claim, userClaimsInOIDCDialect::get)); + return handleClaimsFormat(claims, clientId, spTenantDomain); + } + + private Map retrieveClaimsForLocalUser(OAuthTokenReqMessageContext requestMsgCtx) + throws IdentityOAuth2Exception { + try { - return getUserClaimsFromUserStore(authenticatedUser, clientId, spTenantDomain, localClaimURIs); + String spTenantDomain = getServiceProviderTenantDomain(requestMsgCtx); + String clientId = requestMsgCtx.getOauth2AccessTokenReqDTO().getClientId(); + AuthenticatedUser authenticatedUser = requestMsgCtx.getAuthorizedUser(); + + return getUserClaimsInOIDCDialect(spTenantDomain, clientId, authenticatedUser); } catch (UserStoreException | IdentityApplicationManagementException | IdentityException | OrganizationManagementException e) { if (FrameworkUtils.isContinueOnClaimHandlingErrorAllowed()) { - log.error("Error occurred while getting claims for user: " + authenticatedUser + + log.error("Error occurred while getting claims for user: " + requestMsgCtx.getAuthorizedUser() + " from userstore.", e); } else { throw new IdentityOAuth2Exception("Error occurred while getting claims for user: " + - authenticatedUser + " from userstore.", e); + requestMsgCtx.getAuthorizedUser() + " from userstore.", e); } } - return null; + return new HashMap<>(); } /** - * This method retrieves user claims from the user store. + * Get oidc claims mapping. * - * @param authenticatedUser Authenticated user. - * @param clientId Client Id. - * @param spTenantDomain SP tenant domain. - * @param claimURIList List of claim URIs. - * @return Map of user claims. + * @param userAttributes User attributes. + * @param requestMsgCtx Request Context. + * @return User attributes Map. + */ + private Map getOIDCClaimsFromUserAttributes(Map userAttributes, + OAuthTokenReqMessageContext requestMsgCtx) + throws IdentityOAuth2Exception { + + String spTenantDomain = getServiceProviderTenantDomain(requestMsgCtx); + Map claims = new HashMap<>(); + if (isNotEmpty(userAttributes)) { + for (Map.Entry entry : userAttributes.entrySet()) { + claims.put(entry.getKey().getRemoteClaim().getClaimUri(), entry.getValue().toString()); + } + } + return OIDCClaimUtil.getMergedUserClaimsInOIDCDialect(spTenantDomain, claims); + } + + /** + * Get oidc claims mapping. + * + * @param federatedUserAttributed User attributes. + * @param requestMsgCtx Request Context. + * @return User attributes Map. + */ + private Map getOIDCClaimsFromFederatedUserAttributes(Map federatedUserAttributed, OAuthTokenReqMessageContext requestMsgCtx) + throws IdentityOAuth2Exception { + + String spTenantDomain = getServiceProviderTenantDomain(requestMsgCtx); + // Retrieve OIDC to Local Claim Mappings. + Map oidcToLocalClaimMappings = null; + try { + oidcToLocalClaimMappings = ClaimMetadataHandler.getInstance() + .getMappingsMapFromOtherDialectToCarbon(OIDC_DIALECT, null, spTenantDomain, false); + } catch (ClaimMetadataException e) { + throw new IdentityOAuth2Exception("Error while retrieving OIDC to Local claim mappings.", e); + } + // Get user claims in OIDC dialect. + Map userClaimsInOidcDialect = new HashMap<>(); + if (MapUtils.isNotEmpty(federatedUserAttributed)) { + for (Map.Entry userAttribute : federatedUserAttributed.entrySet()) { + ClaimMapping claimMapping = userAttribute.getKey(); + String claimValue = userAttribute.getValue(); + if (oidcToLocalClaimMappings.containsValue(claimMapping.getLocalClaim().getClaimUri())) { + String localClaimURI = claimMapping.getLocalClaim().getClaimUri(); + String oidcClaimUri = oidcToLocalClaimMappings.entrySet().stream() + .filter(entry -> entry.getValue().equals(localClaimURI)) + .map(Map.Entry::getKey) + .findFirst() + .orElse(null); + + if (oidcClaimUri != null) { + userClaimsInOidcDialect.put(oidcClaimUri, claimValue.toString()); + if (log.isDebugEnabled() && + IdentityUtil.isTokenLoggable(IdentityConstants.IdentityTokens.USER_CLAIMS)) { + log.debug("Mapped claim: key - " + oidcClaimUri + " value - " + claimValue); + } + } + } + } + } + return OIDCClaimUtil.getMergedUserClaimsInOIDCDialect(spTenantDomain, userClaimsInOidcDialect); + } + + private Map getUserClaimsInOIDCDialect(OAuthAuthzReqMessageContext authzReqMessageContext) + throws IdentityOAuth2Exception { + + Map userClaimsInOIDCDialect; + Map userAttributes = + getUserAttributesCachedAgainstToken(getAccessToken(authzReqMessageContext), false); + + if (isEmpty(userAttributes)) { + if (isLocalUser(authzReqMessageContext)) { + if (log.isDebugEnabled()) { + log.debug("User attributes not found in cache. Trying to retrieve attribute for " + + "local user: " + authzReqMessageContext.getAuthorizationReqDTO().getUser()); + } + + userClaimsInOIDCDialect = retrieveClaimsForLocalUser(authzReqMessageContext); + } else { + if (log.isDebugEnabled()) { + log.debug("User attributes not found in cache. Trying to retrieve attribute for federated " + + "user: " + authzReqMessageContext.getAuthorizationReqDTO().getUser()); + } + userClaimsInOIDCDialect = retrieveClaimsForFederatedUser(authzReqMessageContext); + } + } else { + userClaimsInOIDCDialect = getOIDCClaimMapFromUserAttributes(userAttributes); + Map unfilteredUserAttributes = + getUserAttributesCachedAgainstToken(getAccessToken(authzReqMessageContext), true); + Map federatedUserClaimsInOIDCDialect = + getUserClaimsInOIDCDialectFromFederatedUserAttributed(authzReqMessageContext + .getAuthorizationReqDTO().getTenantDomain(), unfilteredUserAttributes); + userClaimsInOIDCDialect.putAll(federatedUserClaimsInOIDCDialect); + } + return filterClaims(userClaimsInOIDCDialect, authzReqMessageContext); + } + + /** + * This method retrieves the user attributes cached against the access token or the authorization code. + * Currently, this is supported for the code grant and the refresh grant. + * + * @param requestMsgCtx The context of the OAuth token request containing necessary properties. + * @param fetchFederatedUserAttributes Flag to indicate whether to fetch federated user attributes. + * @return A map of cached user attributes against the code or the access token. + * @throws IdentityOAuth2Exception If an error occurs while selecting the OAuth2 token issuer. + */ + private Map getCachedUserAttributes(OAuthTokenReqMessageContext requestMsgCtx, + boolean fetchFederatedUserAttributes) + throws IdentityOAuth2Exception { + + Map userAttributes = getUserAttributesCachedAgainstAuthorizationCode( + getAuthorizationCode(requestMsgCtx), fetchFederatedUserAttributes); + if (log.isDebugEnabled()) { + log.debug("Retrieving claims cached against authorization_code for user: " + + requestMsgCtx.getAuthorizedUser()); + } + if (isEmpty(userAttributes)) { + if (log.isDebugEnabled()) { + log.debug("No claims cached against the authorization_code for user: " + requestMsgCtx. + getAuthorizedUser() + ". Retrieving claims cached against the access_token."); + } + userAttributes = getUserAttributesCachedAgainstToken(getAccessToken(requestMsgCtx), + fetchFederatedUserAttributes); + if (log.isDebugEnabled()) { + log.debug("Retrieving claims cached against access_token for user: " + + requestMsgCtx.getAuthorizedUser()); + } + } + // Check for claims cached against the device code. + if (isEmpty(userAttributes)) { + if (log.isDebugEnabled()) { + log.debug("No claims cached against the access_token for user: " + + requestMsgCtx.getAuthorizedUser() + ". Retrieving claims cached against the device code."); + } + userAttributes = getUserAttributesCachedAgainstDeviceCode(getDeviceCode(requestMsgCtx), + fetchFederatedUserAttributes); + } + /* When building the jwt token, we cannot add it to authorization cache, as we save entries against, access + token. Hence if it is added against authenticated user object.*/ + if (isEmpty(userAttributes)) { + if (log.isDebugEnabled()) { + log.debug("No claims found in authorization cache. Retrieving claims from attributes of user : " + + requestMsgCtx.getAuthorizedUser()); + } + AuthenticatedUser user = requestMsgCtx.getAuthorizedUser(); + userAttributes = user != null ? user.getUserAttributes() : null; + } + // In the refresh flow, we need to follow the same way to get the claims. + if (isEmpty(userAttributes)) { + if (log.isDebugEnabled()) { + log.debug("No claims found in user in user attributes for user : " + requestMsgCtx.getAuthorizedUser()); + } + + /* + The purpose of this segment is retrieving the user attributes at the refresh grant while the caches + are disabled. The code/token acts as the key in cache layer while access token hash acts as the key for + entries in the persistence layer(SessionStore). + At this point, the token indicated by RefreshGrantHandler.PREV_ACCESS_TOKEN is no longer + present in the caches or the persistence layer because a new access token has already been generated + and added to the cache with new token references. However, RefreshGrantHandler.PREV_ACCESS_TOKEN cannot + yet be replaced with the new access token since the refresh token has not been generated, and + the new token is not yet considered "previous" by definition. + */ + String latestAccessTokenHash = getLatestAccessTokenHash(requestMsgCtx); + if (StringUtils.isNotBlank(latestAccessTokenHash)) { + userAttributes = getUserAttributesCachedAgainstToken(latestAccessTokenHash, + fetchFederatedUserAttributes); + } + + Object previousAccessTokenObject = requestMsgCtx.getProperty(RefreshGrantHandler.PREV_ACCESS_TOKEN); + + if (previousAccessTokenObject != null) { + if (log.isDebugEnabled()) { + log.debug("Retrieving claims from previous access token of user : " + requestMsgCtx + .getAuthorizedUser()); + } + RefreshTokenValidationDataDO refreshTokenValidationDataDO = + (RefreshTokenValidationDataDO) previousAccessTokenObject; + + // This segment is retrieving the user attributes at the refresh grant while the caches are enabled. + if (isEmpty(userAttributes)) { + userAttributes = getUserAttributesCachedAgainstToken(refreshTokenValidationDataDO.getAccessToken(), + fetchFederatedUserAttributes); + } + requestMsgCtx.addProperty(OIDCConstants.HAS_NON_OIDC_CLAIMS, + isTokenHasCustomUserClaims(refreshTokenValidationDataDO)); + } + } + return userAttributes; + } + + private Map getUserAttributesCachedAgainstAuthorizationCode(String authorizationCode, + boolean fetchFederatedUserAttr) { + + Map userAttributes = Collections.emptyMap(); + if (authorizationCode != null) { + // Get the cached user claims against the authorization code if any. + userAttributes = getUserAttributesFromCacheUsingCode(authorizationCode, fetchFederatedUserAttr); + } + return userAttributes; + } + + private Map getUserAttributesCachedAgainstDeviceCode(String deviceCode, + boolean fetchFederatedUserAttributes) { + + if (StringUtils.isEmpty(deviceCode)) { + return Collections.emptyMap(); + } + DeviceAuthorizationGrantCacheKey cacheKey = new DeviceAuthorizationGrantCacheKey(deviceCode); + DeviceAuthorizationGrantCacheEntry cacheEntry = + DeviceAuthorizationGrantCache.getInstance().getValueFromCache(cacheKey); + if (fetchFederatedUserAttributes) { + return cacheEntry == null ? Collections.emptyMap() : cacheEntry.getUnfilteredFederatedUserAttributes(); + } + return cacheEntry == null ? Collections.emptyMap() : cacheEntry.getUserAttributes(); + } + + /** + * Get user attributes cached against the authorization code. + * + * @param authorizationCode Authorization Code + * @return User attributes cached against the authorization code + */ + private Map getUserAttributesFromCacheUsingCode(String authorizationCode, + boolean fetchFederatedUserAttributes) { + if (log.isDebugEnabled()) { + if (IdentityUtil.isTokenLoggable(IdentityConstants.IdentityTokens.AUTHORIZATION_CODE)) { + log.debug("Retrieving user attributes cached against authorization code: " + authorizationCode); + } else { + log.debug("Retrieving user attributes cached against authorization code."); + } + } + + AuthorizationGrantCacheKey cacheKey = new AuthorizationGrantCacheKey(authorizationCode); + AuthorizationGrantCacheEntry cacheEntry = + AuthorizationGrantCache.getInstance().getValueFromCacheByCode(cacheKey); + if (fetchFederatedUserAttributes) { + return cacheEntry == null ? new HashMap<>() : cacheEntry.getUnfilteredFederatedUserAttributes(); + } + return cacheEntry == null ? new HashMap<>() : cacheEntry.getUserAttributes(); + } + + private Map getUserAttributesCachedAgainstToken(String accessToken, + boolean fetchFederatedUserAttributes) { + Map userAttributes = Collections.emptyMap(); + if (accessToken != null) { + // get the user claims cached against the access token if any + userAttributes = getUserAttributesFromCacheUsingToken(accessToken, fetchFederatedUserAttributes); + } + return userAttributes; + } + + private Map retrieveClaimsForLocalUser(OAuthAuthzReqMessageContext authzReqMessageContext) + throws IdentityOAuth2Exception { + + try { + String spTenantDomain = getServiceProviderTenantDomain(authzReqMessageContext); + String clientId = authzReqMessageContext.getAuthorizationReqDTO().getConsumerKey(); + AuthenticatedUser authenticatedUser = authzReqMessageContext.getAuthorizationReqDTO().getUser(); + + return getUserClaimsInOIDCDialect(spTenantDomain, clientId, authenticatedUser); + } catch (UserStoreException | IdentityApplicationManagementException | IdentityException | + OrganizationManagementException e) { + if (FrameworkUtils.isContinueOnClaimHandlingErrorAllowed()) { + log.error("Error occurred while getting claims for user " + + authzReqMessageContext.getAuthorizationReqDTO().getUser(), e); + } else { + throw new IdentityOAuth2Exception("Error occurred while getting claims for user " + + authzReqMessageContext.getAuthorizationReqDTO().getUser(), e); + } + } + return new HashMap<>(); + } + + /** + * Retrieve the claim set of the AuthenticatedUser from the OAuthAuthzReqMessageContext. + * + * @param authzReqMessageContext OAuthAuthzReqMessageContext. + * @return Map of user attributes. + */ + private Map retrieveClaimsForFederatedUser(OAuthAuthzReqMessageContext authzReqMessageContext) + throws IdentityOAuth2Exception { + + OAuth2AuthorizeReqDTO oAuth2AuthorizeReqDTO = authzReqMessageContext.getAuthorizationReqDTO(); + Map userClaimsMappedToOIDCDialect = new HashMap<>(); + + if (oAuth2AuthorizeReqDTO == null) { + if (log.isDebugEnabled()) { + log.debug("OAuth2AuthorizeReqDTO is NULL for federated user: " + + authzReqMessageContext.getAuthorizationReqDTO().getUser()); + } + return userClaimsMappedToOIDCDialect; + } + AuthenticatedUser authenticatedUser = oAuth2AuthorizeReqDTO.getUser(); + if (authenticatedUser == null) { + if (log.isDebugEnabled()) { + log.debug("Authenticated User is not available in the request"); + } + return userClaimsMappedToOIDCDialect; + } + Map userAttributes = authenticatedUser.getUserAttributes(); + Map unfilteredFederatedUserAttributes = + oAuth2AuthorizeReqDTO.getUnfilteredFederatedUserAttributes(); + userClaimsMappedToOIDCDialect = getOIDCClaimMapFromUserAttributes(userAttributes); + Map federatedUserClaimsMappedToOIDCDialect = + getUserClaimsInOIDCDialectFromFederatedUserAttributed(authzReqMessageContext.getAuthorizationReqDTO() + .getTenantDomain(), unfilteredFederatedUserAttributes); + userClaimsMappedToOIDCDialect.putAll(federatedUserClaimsMappedToOIDCDialect); + return userClaimsMappedToOIDCDialect; + } + + /** + * Get claims map. + * + * @param userAttributes User Attributes + * @return User attribute map + */ + private Map getOIDCClaimMapFromUserAttributes(Map userAttributes) { + + Map claims = new HashMap<>(); + if (isNotEmpty(userAttributes)) { + for (Map.Entry entry : userAttributes.entrySet()) { + claims.put(entry.getKey().getRemoteClaim().getClaimUri(), entry.getValue()); + } + } + return claims; + } + + private static Map getUserClaimsInOIDCDialectFromFederatedUserAttributed(String spTenantDomain, + Map + unfilteredUserAttributed) + throws IdentityOAuth2Exception { + + // Retrieve OIDC to Local Claim Mappings. + Map oidcToLocalClaimMappings = null; + try { + oidcToLocalClaimMappings = ClaimMetadataHandler.getInstance() + .getMappingsMapFromOtherDialectToCarbon(OIDC_DIALECT, null, spTenantDomain, false); + } catch (ClaimMetadataException e) { + throw new IdentityOAuth2Exception("Error while retrieving OIDC to Local claim mappings.", e); + } + // Get user claims in OIDC dialect. + Map userClaimsInOidcDialect = new HashMap<>(); + if (MapUtils.isNotEmpty(unfilteredUserAttributed)) { + for (Map.Entry userAttribute : unfilteredUserAttributed.entrySet()) { + ClaimMapping claimMapping = userAttribute.getKey(); + String claimValue = userAttribute.getValue(); + if (oidcToLocalClaimMappings.containsValue(claimMapping.getLocalClaim().getClaimUri())) { + String localClaimURI = claimMapping.getLocalClaim().getClaimUri(); + String oidcClaimUri = oidcToLocalClaimMappings.entrySet().stream() + .filter(entry -> entry.getValue().equals(localClaimURI)) + .map(Map.Entry::getKey) + .findFirst() + .orElse(null); + + if (oidcClaimUri != null) { + userClaimsInOidcDialect.put(oidcClaimUri, claimValue); + if (log.isDebugEnabled() && + IdentityUtil.isTokenLoggable(IdentityConstants.IdentityTokens.USER_CLAIMS)) { + log.debug("Mapped claim: key - " + oidcClaimUri + " value - " + claimValue); + } + } + } + } + } + return userClaimsInOidcDialect; + } + + /** + * Get user claims in OIDC claim dialect. + * + * @param oidcToLocalClaimMappings OIDC dialect to Local dialect claim mappings + * @param userClaims User claims in local dialect + * @return Map of user claim values in OIDC dialect. */ - private Map getUserClaimsFromUserStore(AuthenticatedUser authenticatedUser, String clientId, - String spTenantDomain, List claimURIList) - throws IdentityApplicationManagementException, UserStoreException, OrganizationManagementException, - IdentityException { + private static Map getUserClaimsInOidcDialect(Map oidcToLocalClaimMappings, + Map userClaims) { + + Map userClaimsInOidcDialect = new HashMap<>(); + if (MapUtils.isNotEmpty(userClaims)) { + // Map<"email", "http://wso2.org/claims/emailaddress"> + for (Map.Entry claimMapping : oidcToLocalClaimMappings.entrySet()) { + String claimValue = userClaims.get(claimMapping.getValue()); + if (claimValue != null) { + String oidcClaimUri = claimMapping.getKey(); + userClaimsInOidcDialect.put(oidcClaimUri, claimValue); + if (log.isDebugEnabled() && + IdentityUtil.isTokenLoggable(IdentityConstants.IdentityTokens.USER_CLAIMS)) { + log.debug("Mapped claim: key - " + oidcClaimUri + " value - " + claimValue); + } + } + } + } + + return userClaimsInOidcDialect; + } + + private Map getUserClaimsInOIDCDialect(String spTenantDomain, String clientId, + AuthenticatedUser authenticatedUser) + throws IdentityApplicationManagementException, IdentityException, UserStoreException, + OrganizationManagementException { Map userClaimsMappedToOIDCDialect = new HashMap<>(); ServiceProvider serviceProvider = getServiceProvider(spTenantDomain, clientId); @@ -156,9 +630,157 @@ private Map getUserClaimsFromUserStore(AuthenticatedUser authent spTenantDomain + ". Returning empty claim map for user."); return userClaimsMappedToOIDCDialect; } - return OIDCClaimUtil.getUserClaimsInOIDCDialect(serviceProvider, authenticatedUser, claimURIList); + + List allowedClaims = getAccessTokenClaims(clientId, spTenantDomain); + if (allowedClaims.isEmpty()) { + return new HashMap<>(); + } + Map oidcToLocalClaimMappings = getOIDCToLocalClaimMappings(spTenantDomain); + if (oidcToLocalClaimMappings.isEmpty()) { + return new HashMap<>(); + } + List localClaimURIs = allowedClaims.stream().map(oidcToLocalClaimMappings::get).filter(Objects::nonNull) + .collect(Collectors.toList()); + return OIDCClaimUtil.getUserClaimsInOIDCDialect(serviceProvider, authenticatedUser, localClaimURIs); } + /** + * Get claims map. + * + * @param userAttributes User Attributes + * @return User attribute map + */ + private Map getClaimMapFromUserAttributes(Map userAttributes) { + + Map claims = new HashMap<>(); + if (isNotEmpty(userAttributes)) { + for (Map.Entry entry : userAttributes.entrySet()) { + claims.put(entry.getKey().getRemoteClaim().getClaimUri(), entry.getValue()); + } + } + return claims; + } + + /** + * Get user attribute cached against the access token. + * + * @param accessToken Access token + * @return User attributes cached against the access token + */ + private Map getUserAttributesFromCacheUsingToken(String accessToken, + boolean fetchFederatedUserAttributes) { + + if (log.isDebugEnabled()) { + if (IdentityUtil.isTokenLoggable(IdentityConstants.IdentityTokens.ACCESS_TOKEN)) { + log.debug("Retrieving user attributes cached against access token: " + accessToken); + } else { + log.debug("Retrieving user attributes cached against access token."); + } + } + + AuthorizationGrantCacheKey cacheKey = new AuthorizationGrantCacheKey(accessToken); + AuthorizationGrantCacheEntry cacheEntry = AuthorizationGrantCache.getInstance() + .getValueFromCacheByToken(cacheKey); + if (fetchFederatedUserAttributes) { + return cacheEntry == null ? new HashMap<>() : cacheEntry.getUnfilteredFederatedUserAttributes(); + } + return cacheEntry == null ? new HashMap<>() : cacheEntry.getUserAttributes(); + } + + private String getAuthorizationCode(OAuthTokenReqMessageContext requestMsgCtx) { + return (String) requestMsgCtx.getProperty(AUTHZ_CODE); + } + + private String getAccessToken(OAuthAuthzReqMessageContext authzReqMessageContext) { + return (String) authzReqMessageContext.getProperty(ACCESS_TOKEN); + } + + private String getAccessToken(OAuthTokenReqMessageContext requestMsgCtx) { + return (String) requestMsgCtx.getProperty(ACCESS_TOKEN); + } + + private String getDeviceCode(OAuthTokenReqMessageContext requestMsgCtx) { + + return (String) requestMsgCtx.getProperty(DEVICE_CODE); + } + + private boolean isLocalUser(AuthenticatedUser authenticatedUser) { + + return !authenticatedUser.isFederatedUser(); + } + + private boolean isLocalUser(OAuthAuthzReqMessageContext authzReqMessageContext) { + + return !authzReqMessageContext.getAuthorizationReqDTO().getUser().isFederatedUser(); + } + + /** + * The access token hash acts as the key for entries in the SessionStore. + * This method retrieves the access token hash for OAuthConstants.ACCESS_TOKEN from the properties + * of OAuthTokenReqMessageContext treating it as the latest access token. It determines the type + * of access token (opaque or JWT) via the OAuth token issuer and obtains the access token hash accordingly. + * This method is useful for retrieving access tokens when the cache is disabled and + * SessionStore persistence is employed. + * + * @param oAuthTokenReqMessageContext The context of the OAuth token request containing necessary properties. + * @return The hash of the latest access token if available and valid, otherwise null. + * @throws IdentityOAuth2Exception If an error occurs while selecting the OAuth2 token issuer. + */ + private String getLatestAccessTokenHash(OAuthTokenReqMessageContext oAuthTokenReqMessageContext) + throws IdentityOAuth2Exception { + + // The OAuthConstants.ACCESS_TOKEN is considered as the latest access token. + Object latestAccessTokenObj = getAccessToken(oAuthTokenReqMessageContext); + if (latestAccessTokenObj != null && StringUtils.isNotBlank(latestAccessTokenObj.toString())) { + + Object oAuthAppDOObj = oAuthTokenReqMessageContext.getProperty(AccessTokenIssuer.OAUTH_APP_DO); + + if (oAuthAppDOObj != null) { + try { + OAuthAppDO oAuthAppDO = (OAuthAppDO) oAuthAppDOObj; + OauthTokenIssuer tokenIssuer = OAuth2Util.getOAuthTokenIssuerForOAuthApp(oAuthAppDO); + if (tokenIssuer != null) { + String latestAccessToken = latestAccessTokenObj.toString(); + try { + return tokenIssuer.getAccessTokenHash(latestAccessToken); + } catch (OAuthSystemException e) { + throw new IdentityOAuth2Exception("Error occurred while generating the access token hash " + + "at user attribute retrieval", e); + } + } + } catch (ClassCastException e) { + log.error("Error occurred while generating the access token hash at user attribute " + + "retrieval", e); + + } + } + } + return null; + } + + /** + * To check whether a token has custom user claims. + * + * @param refreshTokenValidationDataDO RefreshTokenValidationDataDO. + * @return true if the token user attributes has non OIDC claims. + */ + private boolean isTokenHasCustomUserClaims(RefreshTokenValidationDataDO refreshTokenValidationDataDO) { + + if (refreshTokenValidationDataDO.getAccessToken() == null) { + return false; + } + AuthorizationGrantCacheKey cacheKey = new AuthorizationGrantCacheKey( + refreshTokenValidationDataDO.getAccessToken()); + AuthorizationGrantCacheEntry cacheEntry = AuthorizationGrantCache.getInstance() + .getValueFromCacheByToken(cacheKey); + boolean hasNonOIDCClaims = cacheEntry != null && cacheEntry.isHasNonOIDCClaims(); + + if (log.isDebugEnabled()) { + log.debug("hasNonOIDCClaims is set to " + hasNonOIDCClaims + " for the access token of the user : " + + refreshTokenValidationDataDO.getAuthorizedUser()); + } + return cacheEntry != null && cacheEntry.isHasNonOIDCClaims(); + } /** * This method retrieves OIDC to Local claim mappings. @@ -327,4 +949,32 @@ private String getServiceProviderTenantDomain(OAuthAuthzReqMessageContext reques return spTenantDomain; } + /** + * Check whether an organization SSO user is trying to switch the organization. + * + * @param authorizedUser authorized user from the token request. + * @return true if an organization SSO user is trying to switch the organization. + */ + private boolean isOrganizationSsoUserSwitchingOrganization(AuthenticatedUser authorizedUser) { + + String accessingOrganization = authorizedUser.getAccessingOrganization(); + String userResidentOrganization = authorizedUser.getUserResidentOrganization(); + /* A federated user with resident organization is considered as an organization SSO user. When the accessing + organization is different to the resident organization, it means the user is trying to switch the + organization. */ + return authorizedUser.isFederatedUser() && userResidentOrganization != null && !userResidentOrganization.equals + (accessingOrganization); + } + + private boolean isOrganizationSwitchGrantType(OAuthTokenReqMessageContext requestMsgCtx) { + + return StringUtils.equals(requestMsgCtx.getOauth2AccessTokenReqDTO().getGrantType(), + OAuthConstants.GrantTypes.ORGANIZATION_SWITCH); + } + + private boolean isPreserverClaimUrisInAssertion(OAuthTokenReqMessageContext requestMsgCtx) { + + return !OAuthServerConfiguration.getInstance().isConvertOriginalClaimsFromAssertionsToOIDCDialect() && + requestMsgCtx.getAuthorizedUser().isFederatedUser(); + } }