3

In Sitecore 10.1.2, I am integrating with Keycloak in Sitecore CM using federation authenticator

Below is my code:

protected override void ProcessCore(IdentityProvidersArgs args)
{
    Assert.ArgumentNotNull(args, "args");
    IdentityProvider = this.GetIdentityProvider();
    var httphandler = new HttpClientHandler();
    httphandler.DefaultProxyCredentials = CredentialCache.DefaultCredentials;
    //httphandler.SslProtocols = SslProtocols.Ssl3 | SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12;
    httphandler.CheckCertificateRevocationList = true;
    httphandler.ServerCertificateCustomValidationCallback = (message, cert, chain, sslPolicyErrors) =>
    {
        return true;
    };
    var httpclient = new HttpClient();
    httpclient.DefaultRequestHeaders.TryAddWithoutValidation(Settings.GetSetting(KeycloakSettings.HeaderKey1, ""), Settings.GetSetting(KeycloakSettings.HeaderValue1, ""));
    httpclient.DefaultRequestHeaders.TryAddWithoutValidation(Settings.GetSetting(KeycloakSettings.HeaderKey2, ""), Settings.GetSetting(KeycloakSettings.HeaderValue2, ""));
    ServicePointManager.Expect100Continue = true;
    ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
    Log.Info("code entered ProcessCore", this);

    var options = new OpenIdConnectAuthenticationOptions
    {
        BackchannelHttpHandler = httphandler,
        //Backchannel = httpclient,
        RequireHttpsMetadata = false,
        MetadataAddress = MetadataAddress,
        ClientId = ClientId,
        ClientSecret = ClientSecret,
        Authority = Authority,
        RedirectUri = GetCallbackUrl(args),
        ResponseType = OpenIdConnectResponseType.Code,
        Scope = OpenIdScope,
        AuthenticationType = IdentityProvider.Name,
        RedeemCode = true,
        
        TokenValidationParameters = new TokenValidationParameters
        {
            NameClaimType = "name"
        },

        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();

                    // This exception should no longer be valid if we use inject KentorOwinCookieSaver middleware before OpenIdConnectAuthentication. However, we keep this code to safeguard against future.
                    if (notification.Exception.Message.Contains("IDX21323"))
                    {
                        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 System.Threading.Tasks.Task.CompletedTask;
                    }
                }

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

                return System.Threading.Tasks.Task.CompletedTask;
            },
            SecurityTokenValidated = OnSecurityTokenValidated,
            //AuthorizationCodeReceived = ProcessAuthorizationCodeReceived,
            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)
                {
                    // If signing out, add the id_token_hint
                    var idTokenClaim = notification.OwinContext.Authentication.User.FindFirst(idToken);

                    if (idTokenClaim != null)
                        notification.ProtocolMessage.IdTokenHint = idTokenClaim.Value;
                }

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

    // Sequence of this middleware matters. The KentorOwinCookieSave must comes before OpenIdConnectAuthentication.
    //args.App.UseKentorOwinCookieSaver();
    args.App.UseOpenIdConnectAuthentication(options);
}

and I am getting the below 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'. 

If I add args.App.UseKentorOwinCookieSaver(); then it works.

Not sure if I need to add this or there is some other issue.

1 Answer 1

1

The line args.App.UseKentorOwinCookieSaver(); is a workaround for a bug in Microsoft's Owin implementation for System.Web. The problem is that sometimes cookies are not saved correctly due to a conflict between Owin and System.Web libraries, as a result ASP.NET_SessionId and Owin-related cookies are not saved and it breaks authentication process.

The author of Kentor.OwinCookieSaver says that the workaround with UseKentorOwinCookieSaver() is outdated and recommends using approaches described on this Microsoft page.

I tried these options myself and two of them worked for me:

  • Ensure session is established prior to authentication - this works in my scenario if it is a front-end login page and I can make sure that ASP.NET session is created on the initial page load. However, it may not be feasible if you are just adding a new provider to the standard Sitecore CMS login page.
  • Disable the SessionStateModule - I do not think this is entirely possible with Sitecore because we can't change its internal functionality.
  • Reconfigure the CookieAuthenticationMiddleware to write directly to System.Web's cookie collection - this helped when I added the following line to OpenIdConnectAuthenticationOptions:
using Microsoft.Owin.Host.SystemWeb;

...
var options = new OpenIdConnectAuthenticationOptions
{
   ...
   CookieManager = new SystemWebCookieManager()
};

So if establishing a session on the login page is not an option, please try adding the line CookieManager = new SystemWebCookieManager() to OpenIdConnectAuthenticationOptions instead of using args.App.UseKentorOwinCookieSaver();.

1

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.