2

In my sitecore 9.1.1 Instance I've almost successfully setup Federated Authentication. The problem I'm currently trying to solve is the following:

The Login Button authenticates my user via Keycloak and my custom ExternalUserBuilder class gets called just fine. The Logout Button deletes the user sesson in Keycloak. But when I login once again with the same browser then my custom ExternalUserBuild Code doesn't get called again. The user is authenticated against Keycloak.

Opening an incognito Browser and click the login calls the ExternalUserBuilder class as it used to.

So as long as I'm using the same browser it's not working properly.

Any idea what I missed?

This is my code:

config

 <pipelines>
      <owin.identityProviders>
        <processor type="ST.Foundation.OKC.Identity.Pipelines.KeycloakIdentityProvider, ST.Foundation.OKC" resolve="true" />
      </owin.identityProviders>
      <!-- 
        Pipeline to handle OpenIdConnectAuthenticationNotification AuthorizationCodeReceived event / callback. 
     -->
      <Keycloak.AuthorizationCodeReceived>
        <processor type="ST.Foundation.OKC.Identity.Pipelines.AuthorizationCodeReceived.MapClaims, ST.Foundation.OKC" resolve="true" />
      </Keycloak.AuthorizationCodeReceived>
    </pipelines>


    <federatedAuthentication type="Sitecore.Owin.Authentication.Configuration.FederatedAuthenticationConfiguration, Sitecore.Owin.Authentication">
      <identityProvidersPerSites hint="list:AddIdentityProvidersPerSites">
        
        <mapEntry name="Keycloak" type="Sitecore.Owin.Authentication.Collections.IdentityProvidersPerSitesMapEntry, Sitecore.Owin.Authentication" resolve="true">
          <sites hint="list">
            <site>modulkatalog</site>
            <site>swk-privatkunden</site>
            <site>shared-site</site>
          </sites>


          <identityProviders hint="list:AddIdentityProvider">
            <identityProvider ref="federatedAuthentication/identityProviders/identityProvider[@id='Keycloak']" />
          </identityProviders>


          <externalUserBuilder type="ST.Foundation.OKC.Identity.Provider.UserBuilder, ST.Foundation.OKC" resolve="true">
            <param desc="isPersistentUser">false</param>
          </externalUserBuilder>
        </mapEntry>

      </identityProvidersPerSites>


      <identityProviders hint="list:AddIdentityProvider">
        <identityProvider id="Keycloak" type="Sitecore.Owin.Authentication.Configuration.DefaultIdentityProvider, Sitecore.Owin.Authentication" >
          <param desc="name">Keycloak</param>
          <param desc="domainManager" type="Sitecore.Abstractions.BaseDomainManager" resolve="true" />
          <caption>Log in with Keycloak</caption>
          <domain>extranet</domain>
          <triggerExternalSignOut>true</triggerExternalSignOut>
          <transformations hint="list:AddTransformation">
            <!-- IDP Transformation -->
            <transformation name="Idp Claim" type="Sitecore.Owin.Authentication.Services.SetIdpClaimTransform, Sitecore.Owin.Authentication" />
            <!-- Name Identifier Claim-->
            <transformation name ="Name identifier claim" type="Sitecore.Owin.Authentication.Services.DefaultTransformation, Sitecore.Owin.Authentication">
              <sources hint="raw:AddSource">
                <claim name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" />
              </sources>
              <targets hint="raw:AddTarget">
                <claim name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" />
              </targets>
              <keepSource>true</keepSource>
            </transformation>
            <transformation name="map role to idp" type="Sitecore.Owin.Authentication.Services.DefaultTransformation, Sitecore.Owin.Authentication">
              <sources hint="raw:AddSource">
                <claim name="idp" value="Keycloak" />
              </sources>
              <targets hint="raw:AddTarget">
                <claim name="http://schemas.microsoft.com/ws/2008/06/identity/claims/role" value="extranet\OKC" />
              </targets>
              <keepSource>true</keepSource>
            </transformation>
          </transformations>
        </identityProvider>
      </identityProviders>
      <!--List of all shared transformations-->
      <sharedTransformations>
        <transformation name ="Shared Name identifier claim" type="Sitecore.Owin.Authentication.Services.DefaultTransformation, Sitecore.Owin.Authentication">
          <sources hint="raw:AddSource">
            <claim name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name" />
          </sources>
          <targets hint="raw:AddTarget">
            <claim name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" />
          </targets>
          <keepSource>true</keepSource>
        </transformation>
      </sharedTransformations>
      <!--Property mappings initializer-->

      <propertyInitializer type='Sitecore.Owin.Authentication.Services.PropertyInitializer, Sitecore.Owin.Authentication'>
        <maps hint='list'>
          <map name='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/name' />
              <target name='FullName' />
            </data>
          </map>          
          <map name='emailclaim' 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/emailaddress' />
              <target name='Email' />
            </data>
          </map>        
          </maps>
        </propertyInitializer>

    </federatedAuthentication>

KeycloakIdentityProvider.cs

  var options = new OpenIdConnectAuthenticationOptions(authenticationType)
            {
                AuthenticationMode = AuthenticationMode.Passive,
                RedirectUri = GetCallbackUrl(),
               // PostLogoutRedirectUri = _logoutUri,
                ClientId = _clientId,
                Authority = _issuer,
                ResponseType = OpenIdConnectResponseType.CodeIdToken,  
                AuthenticationType = GetAuthenticationType(), 
                UseTokenLifetime = false, 
                CookieManager = CookieManager,
                Scope = _scope,             
                TokenValidationParameters = new TokenValidationParameters
                {
                    NameClaimType = "name",
                    
                },
                

                Notifications = new OpenIdConnectAuthenticationNotifications
                {
                    AuthenticationFailed = (AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> notification) =>
                    {
                      
                        if (notification.Exception != null)
                        {
                            Log.Info($"Keycloak authorization fail: "+ notification.Exception.Message, this);


                            notification.HandleResponse();

                            if (notification.Exception.Message.Contains("IDX21323")|| notification.Exception.Message.Contains("IDX20803"))
                            {
                                notification.HandleResponse();
                                /* This line of code is the key to solve error 
                               IDX21323: RequireNonce is '[PII is hidden]'. OpenIdConnectProtocolValidationContext.Nonce was null, OpenIdConnectProtocol.ValidatedIdToken.Payload.Nonce was not null. 
                               The nonce cannot be validated. If you don't need to check the nonce, set OpenIdConnectProtocolValidator.RequireNonce to 'false'. Note if a 'nonce' is found it will be evaluated.
                               */
                                notification.OwinContext.Authentication.Challenge();
                                return Task.CompletedTask;
                            }                           
                            
                        }


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


                        return Task.CompletedTask;
                    },                   
                    MessageReceived = n =>
                    {                        
                        Log.Info("messagereceived errordescription=" + n.ProtocolMessage.ErrorDescription, this);
                        return Task.CompletedTask;
                    },
                    RedirectToIdentityProvider = notification =>
                    {
                        if (notification.ProtocolMessage.RequestType == OpenIdConnectRequestType.Authentication)
                        {
                            //PKCE Implementation
                            return Task.CompletedTask;
                        }

                        if (notification.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
                        {
                                                                 
                            var revokeProperties = notification.OwinContext.Authentication.AuthenticationResponseRevoke?.Properties?.Dictionary;
                            var postLogoutUri = notification.ProtocolMessage.PostLogoutRedirectUri;
                            if (!string.IsNullOrEmpty(postLogoutUri))
                            {
                                if (postLogoutUri.StartsWith("/"))
                                {
                                    // transform to absolute
                                    var request = notification.Request;
                                    postLogoutUri = request.Scheme + "://" + request.Host + request.PathBase + postLogoutUri;
                                }
                            }                           

                            var nonce = string.Empty;
                            if (revokeProperties != null && revokeProperties.ContainsKey("nonce"))
                            {
                                //nonce is always empty on the first login and login 
                                //not empty on the second login and then logout without starting with a new inkognito browser
                                nonce = $"?ReturnUrl={postLogoutUri}&nonce={revokeProperties["nonce"]}";
                            }
                        
                            notification.ProtocolMessage.PostLogoutRedirectUri = postLogoutUri;
                           
                            return Task.FromResult(0);

                        }
                    

                        return Task.CompletedTask;
                    },
                    AuthorizationCodeReceived = ProcessAuthorizationCodeReceived,
                }

            };
                                   
            args.App.UseOpenIdConnectAuthentication(options);
        }

LogoutController

   var authenticationTypes = new string[] {
                                                        DefaultAuthenticationTypes.ApplicationCookie,
                                                        DefaultAuthenticationTypes.ExternalCookie,
            OpenIdConnectAuthenticationDefaults.AuthenticationType,
                                                        "Keycloak"};
            HttpContext.GetOwinContext().Authentication.SignOut(new AuthenticationProperties { RedirectUri = returnUrl }, authenticationTypes);

           
            AuthenticationManager.Logout();
            
2
  • Can you try the login/logout on a incognito window once ?
    – Abhay Dhar
    Jun 16 at 14:34
  • As I've mentioned, I did that. And it is only working there as well, wenn I close the window after the 1st login/logout and open a new one again for the 2nd login. Than the custom externalUserBuilder get's called also on the 2nd login Jun 17 at 5:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.