3

I am using Sitecore 10.1 and I want to do federation authentication on the CM server. When I am clicking on "Log in with keycloak" button it is redirecting to Keycloak URL and after entering my username and password, it is coming back to the Sitecore login page. enter image description here

But it is showing the error message "You do not have access to the system. If you think this is wrong, please contact the system administrator."

enter image description here

Below is OnSecurityTokenValidated, I am getting IsAuthenticated true and getting claims as well.

enter image description here

Below is my code:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:role="http://www.sitecore.net/xmlconfig/role/"
               xmlns:env="http://www.sitecore.net/xmlconfig/env/" xmlns:zone="http://www.sitecore.net/xmlconfig/zone/">
    <sitecore role:require="ContentManagement or Standalone">
        <pipelines>
            <owin.identityProviders>
                <processor type="XXX.KeycloakIdentityProvider, XXX.Website" resolve="true" />
            </owin.identityProviders>
        </pipelines>
        <federatedAuthentication type="Sitecore.Owin.Authentication.Configuration.FederatedAuthenticationConfiguration, Sitecore.Owin.Authentication">
            <identityProvidersPerSites hint="list:AddIdentityProvidersPerSites">
                <!-- Defines a list of providers assigned to all sites. -->
                <mapEntry name="all sites" type="Sitecore.Owin.Authentication.Collections.IdentityProvidersPerSitesMapEntry, Sitecore.Owin.Authentication" resolve="true">
                    <sites hint="list">
                        <site>regexp:.*</site>
                    </sites>
                    <identityProviders hint="list:AddIdentityProvider">
                        <identityProvider ref="federatedAuthentication/identityProviders/identityProvider[@id='keycloak']" />
                    </identityProviders>
                    <externalUserBuilder type="Sitecore.Owin.Authentication.Services.DefaultExternalUserBuilder, Sitecore.Owin.Authentication" resolve="true">
                        <IsPersistentUser>false</IsPersistentUser>
                    </externalUserBuilder>
                </mapEntry>
            </identityProvidersPerSites>

            <identityProviders>
                <identityProvider id="keycloak" type="Sitecore.Owin.Authentication.Configuration.DefaultIdentityProvider, Sitecore.Owin.Authentication">
                    <param desc="name">$(id)</param>
                    <param desc="domainManager" type="Sitecore.Abstractions.BaseDomainManager" resolve="true" />
                    <caption>Log in with keycloak</caption>
                    <icon>/sitecore/shell/themes/standard/Custom/24x24/msazure.png</icon>
                    <domain>sitecore</domain>
                    <enabled>true</enabled>
                    <transformations hint="list:AddTransformation">
                        <transformation name="Idp Claim" type="Sitecore.Owin.Authentication.Services.SetIdpClaimTransform, Sitecore.Owin.Authentication" />
                        <transformation name="set id_token claim" type="Sitecore.Owin.Authentication.Services.SaveIdTokenInClaim, Sitecore.Owin.Authentication" />
                        <transformation type="Sitecore.Owin.Authentication.Services.DefaultTransformation, Sitecore.Owin.Authentication">
                            <sources hint="raw:AddSource">
                                <claim name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"  />
                            </sources>
                            <targets hint="raw:AddTarget">
                                <claim name="Email"  />
                            </targets>
                            <keepSource>false</keepSource>
                        </transformation>
                    </transformations>
                </identityProvider>
            </identityProviders>
            <propertyInitializer type="Sitecore.Owin.Authentication.Services.PropertyInitializer, Sitecore.Owin.Authentication">
                <maps hint="list">
                    <map name="set SSO FullName" type="Sitecore.Owin.Authentication.Services.DefaultClaimToPropertyMapper, Sitecore.Owin.Authentication" resolve="true" patch:source="Project.NAC.AzureAD.config">
                        <data hint="raw:AddData">
                            <source name="full_name" />
                            <target name="FullName" />
                        </data>
                    </map>
                    <map name="Given Name" type="Sitecore.Owin.Authentication.Services.DefaultClaimToPropertyMapper, Sitecore.Owin.Authentication" resolve="true">
                        <data hint="raw:AddData">
                            <source name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname" />
                            <target name="Name" />
                        </data>
                    </map>
                </maps>
            </propertyInitializer>
        </federatedAuthentication>
    </sitecore>
</configuration>
 public class KeycloakIdentityProvider : IdentityProvidersProcessor
    {
        protected override string IdentityProviderName => "keycloak";
        private string ClientId => Settings.GetSetting(KeycloakSettings.ClientId, "");
        private string ClientSecret => Settings.GetSetting(KeycloakSettings.ClientSecret, "");
        private string Authority => Settings.GetSetting(KeycloakSettings.Authority, "");
        private string OAuthRedirectUri => Settings.GetSetting(KeycloakSettings.OAuthRedirectUri, "");
        private string MetadataAddress => Settings.GetSetting(KeycloakSettings.MetadataAddress, "");

        private readonly string idToken = "id_token";
        private readonly string accessDeniedRelativePath = "/custom/errorpages/Forbidden.aspx";

        public KeycloakIdentityProvider(FederatedAuthenticationConfiguration federatedAuthenticationConfiguration, Microsoft.Owin.Infrastructure.ICookieManager cookieManager, BaseSettings settings) : base(federatedAuthenticationConfiguration, cookieManager, settings)
        {
        }

        protected IdentityProvider IdentityProvider { get; set; }

        protected override void ProcessCore(IdentityProvidersArgs args)
        {
            Assert.ArgumentNotNull(args, "args");
            IdentityProvider = this.GetIdentityProvider();
            var identityProvider = this.GetIdentityProvider();
            var saveSigninToken = identityProvider.TriggerExternalSignOut;
            var options = new OpenIdConnectAuthenticationOptions
            {
                RequireHttpsMetadata = false,
                MetadataAddress = MetadataAddress,
                ClientId = ClientId,
                ClientSecret = ClientSecret,
                Authority = Authority,
                RedirectUri = GetCallbackUrl(args),
                ResponseType = OpenIdConnectResponseType.Code,
                Scope = OpenIdConnectScope.OpenIdProfile + " " + OpenIdConnectScope.OfflineAccess,
                AuthenticationType = IdentityProvider.Name,
                RedeemCode = true,
                TokenValidationParameters = new TokenValidationParameters
                {
                    SaveSigninToken = saveSigninToken
                },

                Notifications = new OpenIdConnectAuthenticationNotifications
                {
                    AuthenticationFailed = (AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification) =>
                    {
                        Log.Info("code entered the authentication failed event", this);
                        if (notification.Exception != null)
                        {
                            Log.Info($"Keycloak authorization fail with exception.\n {notification.Exception.Message}", this);

                            notification.HandleResponse();
                            if (notification.Exception.Message.Contains("IDX21323"))
                            {
                                notification.HandleResponse();
                                notification.OwinContext.Authentication.Challenge();
                                return System.Threading.Tasks.Task.CompletedTask;
                            }
                        }

                        notification.HandleResponse();
                        notification.Response.Redirect(accessDeniedRelativePath);

                        return System.Threading.Tasks.Task.CompletedTask;
                    },
                    SecurityTokenValidated = OnSecurityTokenValidated,
                    RedirectToIdentityProvider = notification =>
                    {
                        Log.Info("code entered the RedirectToIdentityProvider event", this);
                        if (notification.ProtocolMessage.RequestType == OpenIdConnectRequestType.Authentication)
                        {
                            notification.ProtocolMessage.SetParameter("kc_idp_hint", "saml");
                        }
                        if (notification.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
                        {
                            var idTokenClaim = notification.OwinContext.Authentication.User.FindFirst(idToken);
                            if (idTokenClaim != null)
                                notification.ProtocolMessage.IdTokenHint = idTokenClaim.Value;
                        }

                        return System.Threading.Tasks.Task.CompletedTask;
                    }
                }
            };

            args.App.UseKentorOwinCookieSaver();
            args.App.UseOpenIdConnectAuthentication(options);
        }
        private System.Threading.Tasks.Task OnSecurityTokenValidated(SecurityTokenValidatedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification)
        {
            var identityProvider = this.GetIdentityProvider();
            var identity = notification.AuthenticationTicket.Identity;

            foreach (var current in identityProvider.Transformations)
            {
                current.Transform(identity, new TransformationContext(this.FederatedAuthenticationConfiguration, identityProvider));
            }
            return System.Threading.Tasks.Task.CompletedTask;
        }

        private string GetCallbackUrl(IdentityProvidersArgs args)
        {
            string settingValue = Settings.GetSetting("Sitecore.CM.LoadBalanceDNS");
            var hostName = string.IsNullOrEmpty(settingValue) ? HttpContext.Current.Request.Url.AbsoluteUri : settingValue;
            return string.Concat(hostName, "/", OAuthRedirectUri);
        }

        private void HandleAuthorizationError(AuthorizationCodeReceivedNotification notification)
        {
            // Log Error
            notification.HandleResponse();
            notification.Response.Redirect(accessDeniedRelativePath);
        }
    }

1 Answer 1

2

The error You do not have access to the system. If you think this is wrong, please contact the system administrator is a Sitecore error which means that the user who tries to log in does not have any roles allowing access to Sitecore CMS.

As your externalUserBuilder is configured to create virtual users, their roles should be mapped from claims during login. The recommended approach is to assign groups/roles in the external identity provider (Keycloak) and map them to Sitecore roles in the config. Here is an example of role mapping config:

<transformation name="Role transformation" type="Sitecore.Owin.Authentication.Services.DefaultTransformation, Sitecore.Owin.Authentication">
     <sources hint="raw:AddSource">
          <claim name="groups" value="{Group name from claim}" />
     </sources>
     <targets hint="raw:AddTarget">
          <claim name="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" value="sitecore\Sitecore Client Authoring" />
     </targets>
     <keepSource>true</keepSource>
</transformation>

Just replace "groups" with your claim name and insert your group as the value. Sitecore role "sitecore\Sitecore Client Authoring" can be replaced with any standard or custom Sitecore role. You can also have multiple <transformation> config sections for different roles.

3

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.