question

ManishKumarGupta-2357 avatar image
0 Votes"
ManishKumarGupta-2357 asked Bruce-SqlWork answered

Samesite=strict with openidconnect SSO login using adfs

Hi,

My asp.net application is working fine with cookie attribute value SameSite=none, however when i am setting it to SameSite=strict getting below error
IDX10311: RequireNonce is 'true' (default) but validationContext.Nonce is null. A nonce cannot be validated. If you don't need to check the nonce, set OpenIdConnectProtocolValidator

I need to set the strict value once the authentication is successful or even before that my code snippet is as below

CookieManager.cs
public static void SetCookies(HttpContextBase context)
{
var request = context.Request;
var response = context.Response;

         var holder = CookieManagerModule.AddCurrentValuesToContext(context, false);
         if (holder == null)
         {
             return;
         }

         var names = holder.AllKeys;
         var values = new string[names.Length];
         holder.CopyTo(values, 0);

         var allData = CookieManagerModule.Encode(SystemServices.ArrayEncode(new string[] { SystemServices.ArrayEncode(names), SystemServices.ArrayEncode(values) }));

         for (var i = 0; i < CookieManagerModule.MaxCookieSpan; i++)
         {
             var name = CookieManagerModule.NamePrefix + i.ToString();

             if ((i * CookieManagerModule.MaxDataLength) < allData.Length)
             {
                 var data = allData.Substring(
                     (i * CookieManagerModule.MaxDataLength),
                     Math.Min((i + 1) * CookieManagerModule.MaxDataLength, allData.Length) - (i * CookieManagerModule.MaxDataLength)
                     );

                 response.Cookies.Remove(name);
                 string currentUserAgent = HttpContext.Current.Request.UserAgent;
                    

                 var cookie = new HttpCookie(name, data)
                 {
                     Path = "/",
                     HttpOnly = true,
                     SameSite = SameSiteMode.Strict,
                     Secure = true
                 };
                 response.Cookies.Add(cookie);
             }
             else if (Array.IndexOf(request.Cookies.AllKeys, name) >= 0)
             {
                 response.Cookies.Remove(name);

                 // add an expired cookie
                 var cookie = new HttpCookie(name, String.Empty)
                 {
                     Path = "/",
                     HttpOnly = true,
                     SameSite = SameSiteMode.Strict,
                     Secure = true,
                     Expires = CookieManagerModule.CookieExpiration                        
                 };
                 response.Cookies.Add(cookie);
             }
         }

         // A few notes from before:
         // IE 7 & 8 will not be able to download data (such as report exports or pdf reports) over SSL if not caching responses (http://support.microsoft.com/kb/323308). 
         // Also don't want to cache response cookie information in javascript or regular HTML because cookie contains employee specific information.
         // Disable response caching due to the cookie header (http://blogs.msdn.com/b/friis/archive/2011/08/30/don-t-let-your-cookie-being-cached-by-accident.aspx).
         response.Cache.SetCacheability(HttpCacheability.NoCache);
     }

startup.cs

private void ConfigureOpenIdConnect(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(OpenIdConnectAuthenticationContext.AuthenticationType);

         string applicationBaseUrl = PortalApplication.SSOConfiguration.ApplicationBaseUrl;

         if (!applicationBaseUrl.EndsWith("/"))
         {
             applicationBaseUrl = applicationBaseUrl + "/";
         }

         app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
         {
             ClientId = PortalApplication.SSOConfiguration.OpenIdSettings.OidcClientId,
             ClientSecret = PortalApplication.SSOConfiguration.OpenIdSettings.OidcClientSecret,
             RedirectUri = applicationBaseUrl + SSOResponse.Path,
             MetadataAddress = PortalApplication.SSOConfiguration.OpenIdSettings.OidcMetadataAddressUrl,
             CookieManager = new PortalCookieManager(),
             AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Passive,
             PostLogoutRedirectUri = applicationBaseUrl + "./SSOLogoutComplete.aspx",
                
             TokenValidationParameters = new TokenValidationParameters
             {
                 ValidateAudience = true,
                 ValidateIssuer = true
             },

             Notifications = new OpenIdConnectAuthenticationNotifications
             {                    
                 SecurityTokenValidated = async (notification) =>
                 {
                     IOwinContext context = notification.OwinContext;
                     ClaimsIdentity claimsIdentity = notification.AuthenticationTicket.Identity;
                     Claim identityClaim = claimsIdentity.FindFirst(PortalApplication.SSOConfiguration.NameClaimType);

                     SSOCredential credential = SSOCredential.CreateFromAuthenticatedIdentity(identityClaim != null ? identityClaim.Value : claimsIdentity.Name, claimsIdentity.IsAuthenticated);
                     var identity = new ExternalIdentity(PortalHttpApplication.Host, credential);

                     if (identity != null)
                     {
                         AuthenticationException ex;

                         string oidcToken = notification.ProtocolMessage.IdToken;

                         if (Authentication.TryAuthenticate(identity, out ex))
                         {
                             PortalHttpApplication.WriteTrace(
                                 this,
                                 PortalTraces.Authentication,
                                 DiagnosticSeverityLevel.Informational,
                                 APISoftware.Culture.Customer.FormatString("SignIn (SSO) {0}", identity.Name));

                             DateTimeOffset? expiry = notification.AuthenticationTicket.Properties.ExpiresUtc;

                             if (PortalApplication.SSOConfiguration.TimeoutOverride.HasValue)
                             {
                                 expiry = DateTime.UtcNow.AddMinutes(PortalApplication.SSOConfiguration
                                     .TimeoutOverride.Value.TotalMinutes);
                             }

                             if (expiry != null)
                             {
                                 var configuration = notification.Options.ConfigurationManager.GetConfigurationAsync(notification.Request.CallCancelled).Result;
                                 string domainHint = null;
                                 notification.AuthenticationTicket.Properties.Dictionary.TryGetValue(OpenIdConnectAuthenticationContext.DomainHintKey, out domainHint);

                                 var authenticationContext = new OpenIdConnectAuthenticationContext(
                                     configuration.TokenEndpoint,
                                     notification.Options.ClientId,
                                     notification.Options.ClientSecret,
                                     notification.Options.RedirectUri,
                                     domainHint,
                                     notification.ProtocolMessage.IdToken);

                                 await authenticationContext.GetRefreshTokenAsync(notification.ProtocolMessage.Code);
                                 Authentication.SetUser(identity, expiry.Value.UtcDateTime, authenticationContext.SerializeToToken());

                                 ISystemPrincipal systemPrincipal = new SystemPrincipal(identity);
                                 HttpContext.Current.User = systemPrincipal;
                                 Thread.CurrentPrincipal = systemPrincipal;

                                 return;
                             }
                         }

                         PortalHttpApplication.WriteAlert(this, new Exception(APISoftware.Culture.Customer.FormatString("Failed attempt to Sign-In (SSO) {0}", identity.Name), ex));

                         if (!String.IsNullOrWhiteSpace(oidcToken))
                         {
                             OpenIdConnectAuthenticationContext.Logout(notification.ProtocolMessage.IdToken, "SSOError.aspx?errorCode=403", context);
                         }
                     }
                 },
                 AuthenticationFailed = (notification) =>
                 {
                     PortalHttpApplication.WriteAlert(
                         this,
                         new Exception("Failed attempt to SignIn (SSO)", notification.Exception));

                     Authentication.SignOut();
                     notification.Response.Redirect(SSOError.Path + "?errorCode=401");
                     return Task.FromResult(0);
                 },

                 RedirectToIdentityProvider = (notification) =>
                 {
                     if (notification.ProtocolMessage.RequestType == Microsoft.IdentityModel.Protocols.OpenIdConnectRequestType.LogoutRequest)
                     {
                         string idTokenHint = String.Empty;

                         notification.OwinContext.Authentication.AuthenticationResponseRevoke.Properties.Dictionary.TryGetValue(
                             OpenIdConnectAuthenticationContext.IdTokenHintKey,
                             out idTokenHint);

                         if (idTokenHint != null)
                         {
                             notification.ProtocolMessage.IdTokenHint = idTokenHint;
                         }
                     }

                     IDictionary<string, string> propertiesDictionary = null;

                     if (notification.OwinContext.Authentication.AuthenticationResponseChallenge != null
                         && notification.OwinContext.Authentication.AuthenticationResponseChallenge.Properties != null)
                     {
                         propertiesDictionary = notification.OwinContext.Authentication.AuthenticationResponseChallenge.Properties.Dictionary;
                     }
                     else if (notification.OwinContext.Authentication.AuthenticationResponseRevoke != null
                         && notification.OwinContext.Authentication.AuthenticationResponseRevoke.Properties != null)
                     {
                         propertiesDictionary = notification.OwinContext.Authentication.AuthenticationResponseRevoke.Properties.Dictionary;
                     }

                     if (propertiesDictionary != null && propertiesDictionary.ContainsKey(OpenIdConnectAuthenticationContext.DomainHintKey))
                     {
                         notification.ProtocolMessage.DomainHint = propertiesDictionary[OpenIdConnectAuthenticationContext.DomainHintKey];
                     }

                     return Task.FromResult(0);
                 },
             },
         });
     }


Please suggest me how to use or bypass samesite=strict to avoid exception.
i am using Microsoft.Owin.Security.OpenIdConnect v3.1.0.0


dotnet-csharpdotnet-aspnet-generaldotnet-aspnet-webformsdotnet-aspnet-webpagesadfs-to-aad-migration
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

1 Answer

Bruce-SqlWork avatar image
1 Vote"
Bruce-SqlWork answered

Nonce is a validation feature. A value is encrypted and the key is stored in a http only cookie. On the redirect back, if same site strict is set, the cookie is not included, so validation fails.

If you want to set same site strict you need to turn off nonce validation, or write your own validation that does not require a cookie.

5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.