org.wso2.carbon.identity.oauth2.util.ClaimsUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.wso2.carbon.identity.oauth2.util.ClaimsUtil.java

Source

/*
 * Copyright (c) 2018, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * WSO2 Inc. licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.wso2.carbon.identity.oauth2.util;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.opensaml.saml2.core.Assertion;
import org.opensaml.saml2.core.Attribute;
import org.opensaml.saml2.core.AttributeStatement;
import org.opensaml.xml.XMLObject;
import org.w3c.dom.Element;
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.ClaimConfig;
import org.wso2.carbon.identity.application.common.model.ClaimMapping;
import org.wso2.carbon.identity.application.common.model.IdentityProvider;
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.base.IdentityConstants;
import org.wso2.carbon.identity.base.IdentityException;
import org.wso2.carbon.identity.claim.metadata.mgt.ClaimMetadataHandler;
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.config.OAuthServerConfiguration;
import org.wso2.carbon.identity.oauth2.IdentityOAuth2Exception;
import org.wso2.carbon.identity.oauth2.dto.OAuth2AccessTokenRespDTO;
import org.wso2.carbon.identity.oauth2.internal.OAuth2ServiceComponentHolder;
import org.wso2.carbon.identity.oauth2.token.OAuthTokenReqMessageContext;
import org.wso2.carbon.identity.openidconnect.OIDCConstants;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

public class ClaimsUtil {

    private static Log log = LogFactory.getLog(ClaimsUtil.class);

    private final static String INBOUND_AUTH2_TYPE = "oauth2";
    private final static String SP_DIALECT = "http://wso2.org/oidc/claim";

    public static boolean isInLocalDialect(Map<String, String> attributes) {
        Iterator<String> iterator = attributes.keySet().iterator();
        if (iterator.hasNext()) {
            return iterator.next().startsWith("http://wso2.org/claims/");
        }
        return false;
    }

    public static Map<String, String> convertFederatedClaimsToLocalDialect(Map<String, String> remoteClaims,
            ClaimMapping[] idPClaimMappings, String tenantDomain) {

        if (log.isDebugEnabled()) {
            StringBuilder claimUris = new StringBuilder();
            for (String key : remoteClaims.keySet()) {
                claimUris.append(key).append(",");
            }
            log.debug("Converting federated user claims to local dialect. Converting claim urls: "
                    + claimUris.toString());
        }

        if (idPClaimMappings != null && idPClaimMappings.length > 0) {
            Map<String, String> localToIdPClaimMap;
            localToIdPClaimMap = FrameworkUtils.getClaimMappings(idPClaimMappings, true);
            Map<String, String> defaultValuesForClaims = loadDefaultValuesForClaims(idPClaimMappings);

            // Loop remote claims and map to local claims
            Map<String, String> convertedClaims = mapRemoteClaimsToLocalClaims(remoteClaims, localToIdPClaimMap,
                    defaultValuesForClaims);
            if (log.isDebugEnabled()) {
                StringBuilder claimUris = new StringBuilder();
                for (String key : convertedClaims.keySet()) {
                    claimUris.append(key).append(",");
                }
                log.debug("Converted federated user claims to local dialect. Converting claim urls: "
                        + claimUris.toString());
            }
            return convertedClaims;
        } else {
            // If idp claim mappings are not configured, return original claims.
            return remoteClaims;
        }

    }

    /**
     * To get the relevant Service Provider.
     *
     * @param requestMsgCtx Token Request Message Context.
     * @return Relevant Service Provider.
     * @throws IdentityApplicationManagementException Identity Application Management Exception.
     */
    private static ServiceProvider getServiceProvider(OAuthTokenReqMessageContext requestMsgCtx)
            throws IdentityApplicationManagementException {

        String spTenantDomain = requestMsgCtx.getOauth2AccessTokenReqDTO().getTenantDomain();
        if (StringUtils.isBlank(spTenantDomain)) {
            spTenantDomain = MultitenantConstants.SUPER_TENANT_DOMAIN_NAME;
        }
        ApplicationManagementService applicationMgtService = OAuth2ServiceComponentHolder
                .getApplicationMgtService();
        String spName = applicationMgtService.getServiceProviderNameByClientId(
                requestMsgCtx.getOauth2AccessTokenReqDTO().getClientId(), INBOUND_AUTH2_TYPE, spTenantDomain);
        return applicationMgtService.getApplicationExcludingFileBasedSPs(spName, spTenantDomain);
    }

    public static Map<String, String> convertClaimsToOIDCDialect(OAuthTokenReqMessageContext requestMsgCtx,
            Map<String, String> userClaims) throws IdentityApplicationManagementException, IdentityException {

        Map<String, String> mappedAppClaims = new HashMap<>();

        if (log.isDebugEnabled()) {
            StringBuilder claimUris = new StringBuilder();
            for (String key : userClaims.keySet()) {
                claimUris.append(key).append(",");
            }
            log.debug("Converting user claims from local dialect to OIDC dialect for user: "
                    + requestMsgCtx.getAuthorizedUser() + ", client id:"
                    + requestMsgCtx.getOauth2AccessTokenReqDTO().getClientId() + ", converting claim urls: "
                    + claimUris.toString());
        }

        String spTenantDomain = requestMsgCtx.getOauth2AccessTokenReqDTO().getTenantDomain();
        if (StringUtils.isBlank(spTenantDomain)) {
            spTenantDomain = MultitenantConstants.SUPER_TENANT_DOMAIN_NAME;
        }

        ApplicationManagementService applicationMgtService = OAuth2ServiceComponentHolder
                .getApplicationMgtService();
        String spName = applicationMgtService.getServiceProviderNameByClientId(
                requestMsgCtx.getOauth2AccessTokenReqDTO().getClientId(), INBOUND_AUTH2_TYPE, spTenantDomain);
        ServiceProvider serviceProvider = applicationMgtService.getApplicationExcludingFileBasedSPs(spName,
                spTenantDomain);
        if (serviceProvider == null) {
            return mappedAppClaims;
        }
        ClaimMapping[] spClaimMappings = serviceProvider.getClaimConfig().getClaimMappings();
        if (spClaimMappings == null || !(spClaimMappings.length > 0)) {
            spClaimMappings = new ClaimMapping[0];
        }

        List<String> requestedLocalClaims = new ArrayList<>();
        for (ClaimMapping mapping : spClaimMappings) {
            if (mapping.isRequested()) {
                requestedLocalClaims.add(mapping.getLocalClaim().getClaimUri());
            }
        }
        if (log.isDebugEnabled()) {
            log.debug("Requested number of local claims: " + requestedLocalClaims.size());
        }

        Map<String, String> spToLocalClaimMappings = ClaimMetadataHandler.getInstance()
                .getMappingsMapFromOtherDialectToCarbon(SP_DIALECT, null, spTenantDomain, false);

        for (Map.Entry<String, String> oidcToLocalClaimMapping : spToLocalClaimMappings.entrySet()) {
            String value = userClaims.get(oidcToLocalClaimMapping.getValue());
            if (value != null && requestedLocalClaims.contains(oidcToLocalClaimMapping.getValue())) {
                mappedAppClaims.put(oidcToLocalClaimMapping.getKey(), value);
                if (log.isDebugEnabled()
                        && IdentityUtil.isTokenLoggable(IdentityConstants.IdentityTokens.USER_CLAIMS)) {
                    log.debug("Mapped claim: key -  " + oidcToLocalClaimMapping.getKey() + " value -" + value);
                }
            }
        }

        if (log.isDebugEnabled()) {
            StringBuilder claimUris = new StringBuilder();
            for (String key : mappedAppClaims.keySet()) {
                claimUris.append(key).append(",");
            }
            log.debug("Converted user claims from local dialect to OIDC dialect for user: "
                    + requestMsgCtx.getAuthorizedUser() + ", client id:"
                    + requestMsgCtx.getOauth2AccessTokenReqDTO().getClientId() + ", converted claim urls: "
                    + claimUris.toString());
        }

        return mappedAppClaims;
    }

    /**
     * Handle claims from identity provider based on claim configurations.
     *
     * @param identityProvider Identity Provider
     * @param attributes       Relevant Claims coming from IDP
     * @param tenantDomain     Tenant Domain.
     * @param tokenReqMsgCtx   Token request message context.
     * @return mapped local claims.
     * @throws IdentityOAuth2Exception Identity Oauth2 Exception.
     */
    public static Map<String, String> handleClaimMapping(IdentityProvider identityProvider,
            Map<String, String> attributes, String tenantDomain, OAuthTokenReqMessageContext tokenReqMsgCtx)
            throws IdentityException, IdentityApplicationManagementException {

        boolean proxyUserAttributes = !OAuthServerConfiguration.getInstance()
                .isConvertOriginalClaimsFromAssertionsToOIDCDialect();

        if (proxyUserAttributes) {
            setHasNonOIDCClaimsProperty(tokenReqMsgCtx);
            return attributes;
        }

        ClaimMapping[] idPClaimMappings = identityProvider.getClaimConfig().getClaimMappings();
        Map<String, String> claimsAfterIdpMapping;
        Map<String, String> claimsAfterSPMapping = new HashMap<>();
        ServiceProvider serviceProvider = getServiceProvider(tokenReqMsgCtx);

        if (ArrayUtils.isNotEmpty(idPClaimMappings)) {
            if (log.isDebugEnabled()) {
                log.debug(
                        "Claim mappings exist for identity provider " + identityProvider.getIdentityProviderName());
            }
            claimsAfterIdpMapping = handleClaimsForIDP(attributes, tenantDomain, identityProvider, false,
                    idPClaimMappings);
            if (isUserClaimsInTokenLoggable()) {
                if (log.isDebugEnabled()) {
                    log.debug("Claims of user : " + tokenReqMsgCtx.getAuthorizedUser() + " after IDP "
                            + " claim mapping " + claimsAfterIdpMapping.toString());
                }
            }
            if (isSPRequestedClaimsExist(tokenReqMsgCtx)) {
                claimsAfterSPMapping = ClaimsUtil.convertClaimsToOIDCDialect(tokenReqMsgCtx, claimsAfterIdpMapping);
                claimsAfterSPMapping = handleUnMappedClaims(tokenReqMsgCtx, attributes, claimsAfterSPMapping,
                        idPClaimMappings);
            } else {
                if (isUserClaimsInTokenLoggable()) {
                    if (log.isDebugEnabled()) {
                        log.debug("IDP claims exists, SP claims does not exist, for the identity provider "
                                + identityProvider.getIdentityProviderName() + ", service provider "
                                + serviceProvider.getApplicationName() + ", hence cannot do claim mapping");
                    }
                }
            }
        } else {
            claimsAfterIdpMapping = attributes;

            if (isUserClaimsInTokenLoggable()) {
                if (log.isDebugEnabled()) {
                    log.debug("IDP claims do not exist for, identity provider, "
                            + identityProvider.getIdentityProviderName()
                            + ", hence directly copying custom claims, " + claimsAfterIdpMapping.toString());
                }
            }
            if (isSPRequestedClaimsExist(tokenReqMsgCtx)) {
                claimsAfterSPMapping = ClaimsUtil.convertClaimsToOIDCDialect(tokenReqMsgCtx, claimsAfterIdpMapping);
                if (isUserClaimsInTokenLoggable()) {
                    if (log.isDebugEnabled()) {
                        log.debug("IDP claims do not exist but SP Claim mappings exists for, identity provider, "
                                + identityProvider.getIdentityProviderName() + ", and Service Provider, "
                                + serviceProvider.getApplicationName() + ", claims after SP mapping, "
                                + claimsAfterSPMapping.toString());
                    }
                }
                claimsAfterSPMapping = handleUnMappedClaims(tokenReqMsgCtx, attributes, claimsAfterSPMapping,
                        idPClaimMappings);
            } else {
                setHasNonOIDCClaimsProperty(tokenReqMsgCtx);
                claimsAfterSPMapping = attributes;
                if (isUserClaimsInTokenLoggable()) {
                    if (log.isDebugEnabled()) {
                        log.debug("IDP claims and SP Claim mappings do not exists for, identity provider, "
                                + identityProvider.getIdentityProviderName() + ", and Service Provider, "
                                + serviceProvider.getApplicationName() + ", hence claims are proxied, "
                                + claimsAfterSPMapping.toString());
                    }
                }
            }
        }
        return claimsAfterSPMapping;
    }

    /**
     * To check whether user claims in token is loggable.
     *
     * @return true if the user claims in token is loggable, otherwise false.
     */
    private static boolean isUserClaimsInTokenLoggable() {
        return IdentityUtil.isTokenLoggable(IdentityConstants.IdentityTokens.USER_CLAIMS);
    }

    /**
     * To set HasNonOIDCClaims property to true.
     *
     * @param tokenReqMsgCtx Token request message context.
     */
    private static void setHasNonOIDCClaimsProperty(OAuthTokenReqMessageContext tokenReqMsgCtx) {
        tokenReqMsgCtx.addProperty(OIDCConstants.HAS_NON_OIDC_CLAIMS, true);
    }

    /**
     * To check whether requested claims exist in the relevant Service Provider.
     *
     * @param tokenReqMsgCtx Token request message context.
     * @return true if requested claim mappings exist in SP side.
     * @throws IdentityApplicationManagementException Identity application management exception.
     */
    private static boolean isSPRequestedClaimsExist(OAuthTokenReqMessageContext tokenReqMsgCtx)
            throws IdentityApplicationManagementException {

        boolean isSPClaimMappingExist = false;
        ServiceProvider serviceProvider = getServiceProvider(tokenReqMsgCtx);
        ClaimConfig claimConfig = serviceProvider.getClaimConfig();
        ClaimMapping[] claimMappings = claimConfig.getClaimMappings();
        if (claimMappings != null) {
            for (ClaimMapping claimMapping : claimMappings) {
                if (claimMapping.isRequested()) {
                    isSPClaimMappingExist = true;
                    break;
                }
            }
        }
        if (isSPClaimMappingExist && log.isDebugEnabled()) {
            log.debug("Service provider " + serviceProvider.getApplicationName() + " has requested claim mappings");
        }
        return isSPClaimMappingExist;
    }

    /**
     * This method handles unmapped claims.
     *
     * @param tokenReqMsgCtx             Token request message context.
     * @param userAttributes             User Attributes.
     * @param claimsAfterIDPandSPMapping Claims after IDP and SP mapping.
     * @param idPClaimMappings           IDP Claim mappings.
     * @return the final mapping after handling unmapped claims.
     * @throws IdentityApplicationManagementException Identity Application Management Exception.
     */
    private static Map<String, String> handleUnMappedClaims(OAuthTokenReqMessageContext tokenReqMsgCtx,
            Map<String, String> userAttributes, Map<String, String> claimsAfterIDPandSPMapping,
            ClaimMapping[] idPClaimMappings) throws IdentityApplicationManagementException {

        boolean isAddUnmappedUserAttributes = OAuthServerConfiguration.getInstance().isAddUnmappedUserAttributes();
        if (isAddUnmappedUserAttributes) {
            claimsAfterIDPandSPMapping = addMissingClaims(tokenReqMsgCtx, userAttributes,
                    claimsAfterIDPandSPMapping, idPClaimMappings);
            setHasNonOIDCClaimsProperty(tokenReqMsgCtx);
            if (isUserClaimsInTokenLoggable()) {
                if (log.isDebugEnabled()) {
                    log.debug("AddUnMappedAttributes is set to true in identity level, hence OIDC claims "
                            + "after conversion, for the user : " + tokenReqMsgCtx.getAuthorizedUser() + ", "
                            + claimsAfterIDPandSPMapping.toString());
                }
            }
        } else {
            if (isUserClaimsInTokenLoggable()) {
                if (log.isDebugEnabled()) {
                    log.debug("AddUnMappedAttributes is set to false in identity level, hence OIDC claims "
                            + "after conversion, for the user : " + tokenReqMsgCtx.getAuthorizedUser() + ", "
                            + claimsAfterIDPandSPMapping.toString());
                }
            }
        }
        return claimsAfterIDPandSPMapping;
    }

    /**
     * To add the missing claims that are missed in IDP and SP mapping.
     *
     * @param tokenReqMsgCtx             Token request message context.
     * @param userAttributes                 Attributes received from IDP.
     * @param claimsAfterIDPandSPMapping Claims.
     * @param idPClaimMappings           IDP Claim mappings.
     * @return Final claim map with all the claims received from the IDP.
     * @throws IdentityApplicationManagementException Identity Application Management Exception.
     */
    private static Map<String, String> addMissingClaims(OAuthTokenReqMessageContext tokenReqMsgCtx,
            Map<String, String> userAttributes, Map<String, String> claimsAfterIDPandSPMapping,
            ClaimMapping[] idPClaimMappings) throws IdentityApplicationManagementException {

        boolean isUserClaimsLoggable = isUserClaimsInTokenLoggable();
        ServiceProvider serviceProvider = getServiceProvider(tokenReqMsgCtx);
        ClaimConfig serviceProviderClaimConfig = serviceProvider.getClaimConfig();
        AuthenticatedUser authenticatedUser = tokenReqMsgCtx.getAuthorizedUser();

        userAttributes.forEach((key, value) -> {
            boolean foundMatching = false;
            String localClaimUri = null;

            // If IDP Claim mapping is not empty.
            if (ArrayUtils.isNotEmpty(idPClaimMappings)) {
                // Go through the claim mappings to identify the missed attributes in IDP level claim mapping.
                for (ClaimMapping claimMapping : idPClaimMappings) {
                    if (claimMapping.getRemoteClaim().getClaimUri().equals(key)) {
                        localClaimUri = claimMapping.getLocalClaim().getClaimUri();
                        foundMatching = true;
                        break;
                    }
                }
                // If the relevant attribute is not mapped in IDP, add that.
                if (!foundMatching) {
                    if (isUserClaimsLoggable) {
                        if (log.isDebugEnabled()) {
                            log.debug("IDP Claim mapping does not exist for " + key + ", hence adding value "
                                    + value + " for the user : " + authenticatedUser);
                        }
                    }
                    claimsAfterIDPandSPMapping.put(key, value);
                } else {
                    // If the relevant attribute has mapping in IDP level, check for SP level mapping.
                    foundMatching = false;
                    ClaimMapping[] spClaimMapping = serviceProviderClaimConfig.getClaimMappings();
                    for (ClaimMapping claimMapping : spClaimMapping) {
                        if (claimMapping.getLocalClaim().getClaimUri().equals(localClaimUri)
                                && claimMapping.isRequested()) {
                            foundMatching = true;
                            break;
                        }
                    }
                    // If the relevant attribute has IDP level mapping but not SP level mapping, add it.
                    if (!foundMatching) {
                        if (isUserClaimsLoggable) {
                            if (log.isDebugEnabled()) {
                                log.debug("IDP Claim mapping exist, but SP Claim mapping does not exist for " + key
                                        + ", hence adding value " + value + " for the user : " + authenticatedUser);
                            }
                        }
                        claimsAfterIDPandSPMapping.put(key, value);
                    }
                }
            } else {
                // If the IDP level mapping is not there, all the claims coming from IDP are assumed to be local claim.
                ClaimMapping[] spClaimMapping = serviceProviderClaimConfig.getClaimMappings();
                for (ClaimMapping claimMapping : spClaimMapping) {
                    if (claimMapping.getLocalClaim().getClaimUri().equals(key) && claimMapping.isRequested()) {
                        foundMatching = true;
                        break;
                    }
                }
                // If the attribute does not have the specific mapping in SP level, add the mapping.
                if (!foundMatching) {
                    if (isUserClaimsLoggable) {
                        if (log.isDebugEnabled()) {
                            log.debug("SP Claim mapping does not exist for " + key + ", hence adding value " + value
                                    + " for the user : " + authenticatedUser);
                        }
                    }
                    claimsAfterIDPandSPMapping.put(key, value);
                }
            }
        });
        if (isUserClaimsLoggable) {
            if (log.isDebugEnabled()) {
                log.debug("Final set of claims for the user : " + authenticatedUser + ": "
                        + claimsAfterIDPandSPMapping.toString());
            }
        }
        return claimsAfterIDPandSPMapping;
    }

    /**
     * To check whether relevant identity provider is resident identity provider.
     *
     * @param identityProvider Specific Identity Provider
     * @return true if the specific identity provider resident identity provider, unless false.
     */
    public static boolean isResidentIdp(IdentityProvider identityProvider) {
        return IdentityApplicationConstants.RESIDENT_IDP_RESERVED_NAME
                .equals(identityProvider.getIdentityProviderName());
    }

    public static Map<String, String> mapRemoteClaimsToLocalClaims(Map<String, String> remoteClaims,
            Map<String, String> localToIdPClaimMap, Map<String, String> defaultValuesForClaims) {

        Map<String, String> localUnfilteredClaims = new HashMap<>();
        for (Map.Entry<String, String> entry : localToIdPClaimMap.entrySet()) {
            String localClaimURI = entry.getKey();
            String claimValue = remoteClaims.get(localToIdPClaimMap.get(localClaimURI));
            if (StringUtils.isEmpty(claimValue)) {
                claimValue = defaultValuesForClaims.get(localClaimURI);
            }
            if (!StringUtils.isEmpty(claimValue)) {
                localUnfilteredClaims.put(localClaimURI, claimValue);
            }
        }

        return localUnfilteredClaims;
    }

    public static Map<String, String> loadDefaultValuesForClaims(ClaimMapping[] idPClaimMappings) {

        Map<String, String> defaultValuesForClaims = new HashMap<>();
        for (ClaimMapping claimMapping : idPClaimMappings) {
            String defaultValue = claimMapping.getDefaultValue();
            if (defaultValue != null && !defaultValue.isEmpty()) {
                defaultValuesForClaims.put(claimMapping.getLocalClaim().getClaimUri(), defaultValue);
            }
        }
        return defaultValuesForClaims;
    }

    public static Map<String, String> extractClaimsFromAssertion(OAuthTokenReqMessageContext tokReqMsgCtx,
            OAuth2AccessTokenRespDTO responseDTO, Assertion assertion, String userAttributeSeparator) {

        Map<String, String> attributes = new HashMap<>();

        List<AttributeStatement> attributeStatementList = assertion.getAttributeStatements();
        if (CollectionUtils.isNotEmpty(attributeStatementList)) {
            for (AttributeStatement statement : attributeStatementList) {
                List<Attribute> attributesList = statement.getAttributes();
                for (Attribute attribute : attributesList) {
                    List<XMLObject> values = attribute.getAttributeValues();
                    String attributeValues = null;
                    if (values != null) {
                        for (int i = 0; i < values.size(); i++) {
                            Element value = attribute.getAttributeValues().get(i).getDOM();
                            String attributeValue = value.getTextContent();
                            if (log.isDebugEnabled()) {
                                log.debug("Attribute: " + attribute.getName() + ", Value: " + attributeValue);
                            }
                            if (StringUtils.isBlank(attributeValues)) {
                                attributeValues = attributeValue;
                            } else {
                                attributeValues += userAttributeSeparator + attributeValue;
                            }
                            attributes.put(attribute.getName(), attributeValues);
                        }
                    }
                }
            }
        } else {
            if (log.isDebugEnabled()) {
                log.debug("No AttributeStatement found in the request for client with id: "
                        + tokReqMsgCtx.getOauth2AccessTokenReqDTO().getClientId());
            }
        }

        return attributes;

    }

    /**
     * This method handles the claim from an non- resident IDP.
     *
     * @param attributes        Relevant User Attributes.
     * @param tenantDomain      Tenant Domain.
     * @param identityProvider  Identity Provider.
     * @param localClaimDialect Local Claim Dialect.
     * @param idPClaimMappings  IDP Claim Mappings.
     * @return claims from IDP
     */
    public static Map<String, String> handleClaimsForIDP(Map<String, String> attributes, String tenantDomain,
            IdentityProvider identityProvider, boolean localClaimDialect, ClaimMapping[] idPClaimMappings) {

        Map<String, String> localClaims;
        if (localClaimDialect) {
            localClaims = handleLocalClaims(attributes, identityProvider);
        } else {
            if (idPClaimMappings.length > 0) {
                localClaims = ClaimsUtil.convertFederatedClaimsToLocalDialect(attributes, idPClaimMappings,
                        tenantDomain);
                if (log.isDebugEnabled()) {
                    log.debug("IDP claims dialect is not local. Converted claims for identity provider: "
                            + identityProvider.getIdentityProviderName());
                }
            } else {
                localClaims = handleLocalClaims(attributes, identityProvider);
            }
        }
        return localClaims;
    }

    /**
     * This method handles the claim from resident IDP
     *
     * @param attributes       Relevant User Attributes.
     * @param identityProvider Identity Provider
     * @return Claims from IDP
     */
    public static Map<String, String> handleClaimsForResidentIDP(Map<String, String> attributes,
            IdentityProvider identityProvider) {

        boolean localClaimDialect;
        Map<String, String> localClaims = new HashMap<>();
        localClaimDialect = identityProvider.getClaimConfig().isLocalClaimDialect();
        if (localClaimDialect) {
            localClaims = handleLocalClaims(attributes, identityProvider);
        } else {
            if (ClaimsUtil.isInLocalDialect(attributes)) {
                localClaims = attributes;
                if (log.isDebugEnabled()) {
                    log.debug("IDP claims dialect is not local. But claims are in local dialect "
                            + "for identity provider: " + identityProvider.getIdentityProviderName()
                            + ". Using attributes in assertion as the IDP claims.");
                }
            } else {
                if (log.isDebugEnabled()) {
                    log.debug("IDP claims dialect is not local. These claims are not handled for "
                            + "identity provider: " + identityProvider.getIdentityProviderName());
                }
            }

        }
        return localClaims;
    }

    /**
     * This method is responsible for adding user attributes to cache.
     * @param tokenRespDTO Token response.
     * @param msgCtx Request message context.
     * @param userAttributes Relevant user attributes.
     */
    public static void addUserAttributesToCache(OAuth2AccessTokenRespDTO tokenRespDTO,
            OAuthTokenReqMessageContext msgCtx, Map<ClaimMapping, String> userAttributes) {

        AuthorizationGrantCacheKey authorizationGrantCacheKey = new AuthorizationGrantCacheKey(
                tokenRespDTO.getAccessToken());
        AuthorizationGrantCacheEntry authorizationGrantCacheEntry = new AuthorizationGrantCacheEntry(
                userAttributes);
        authorizationGrantCacheEntry
                .setSubjectClaim(msgCtx.getAuthorizedUser().getAuthenticatedSubjectIdentifier());

        Object hasNonOIDCClaimsProperty = msgCtx.getProperty(OIDCConstants.HAS_NON_OIDC_CLAIMS);
        if (hasNonOIDCClaimsProperty != null) {
            authorizationGrantCacheEntry.setHasNonOIDCClaims((Boolean) hasNonOIDCClaimsProperty);
        } else {
            authorizationGrantCacheEntry.setHasNonOIDCClaims(false);
        }

        if (StringUtils.isNotBlank(tokenRespDTO.getTokenId())) {
            authorizationGrantCacheEntry.setTokenId(tokenRespDTO.getTokenId());
        }

        long validityPeriod = TimeUnit.MILLISECONDS.toNanos(tokenRespDTO.getExpiresInMillis());
        authorizationGrantCacheEntry.setValidityPeriod(validityPeriod);
        AuthorizationGrantCache.getInstance().addToCacheByToken(authorizationGrantCacheKey,
                authorizationGrantCacheEntry);
    }

    /**
     * This method is responsible for checking whether particular claims from IDP are in local claim format.
     * @param attributes Relevant User attributes.
     * @param identityProvider Identity Provider.
     * @return relevant local claims.
     */
    private static Map<String, String> handleLocalClaims(Map<String, String> attributes,
            IdentityProvider identityProvider) {

        Map<String, String> localClaims = new HashMap<>();
        if (ClaimsUtil.isInLocalDialect(attributes)) {
            localClaims = attributes;
            if (log.isDebugEnabled()) {
                log.debug("Claims are in local dialect for identity provider: "
                        + identityProvider.getIdentityProviderName()
                        + ". Using attributes in assertion as the IDP claims.");
            }
        } else {
            if (log.isDebugEnabled()) {
                log.debug("Claims are not in local dialect for identity provider: "
                        + identityProvider.getIdentityProviderName()
                        + ". Not considering attributes in assertion.");
            }
        }
        return localClaims;
    }
}