skip to Main Content

I have a legacy ASP.NET 4.8 MVC application which is being migrated to use Azure AD Authentication using the Microsoft.Owin framework. The application configuration works fine on my local machine. However, I encounter issues when running it on the production server.

Production environment proxy Setup: The application is hosted on an IIS server behind a proxy. SSL is terminated at the proxy, and HTTP requests are forwarded to the IIS server.

When I browse protected pages on the production server, I am correctly redirected to the login.microsoftonline.com login page and can log in successfully. However, after logging in, the .AspNet.Cookies authentication cookie is not set on the production server, resulting in a redirect loop between the application and the Azure AD /authorize endpoint.

I am assuming there is a problem issuing ".AspNet.Cookies" cookie as I don’t see that in my browser. How can this be confirmed? or traced?

This is how it is configured in startup.cs class. I have followed some online article to implement this & working fine on my local.

public void Configuration(IAppBuilder app)
{
    // Configure Auth0 parameters
    string auth0Domain = ConfigurationManager.AppSettings["auth0:Domain"];
    string auth0ClientId = ConfigurationManager.AppSettings["auth0:ClientId"];
    string auth0RedirectUri = ConfigurationManager.AppSettings["auth0:RedirectUri"];
    string auth0PostLogoutRedirectUri = ConfigurationManager.AppSettings["auth0:PostLogoutRedirectUri"];

    // Set Cookies as default authentication type
    app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
        LoginPath = new PathString("/Account/Login"),
        CookieSameSite = Microsoft.Owin.SameSiteMode.None,
        // More information on why the CookieManager needs to be set can be found here: 
        // https://github.com/aspnet/AspNetKatana/wiki/System.Web-response-cookie-integration-issues
        CookieManager = new SameSiteCookieManager(new SystemWebCookieManager())
    });


    // Configure Auth0 authentication
    app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
    {
        AuthenticationType = "Auth0",

        Authority = auth0Domain,

        ClientId = auth0ClientId,

        RedirectUri = auth0RedirectUri,
        PostLogoutRedirectUri = auth0PostLogoutRedirectUri,
        ResponseType = OpenIdConnectResponseType.CodeIdToken,
        Scope = "openid profile email",

        SignInAsAuthenticationType = CookieAuthenticationDefaults.AuthenticationType,

        TokenValidationParameters = new TokenValidationParameters
        {
            NameClaimType = "name"
        },

        // More information on why the CookieManager needs to be set can be found here: 
        // https://docs.microsoft.com/en-us/aspnet/samesite/owin-samesite
        CookieManager = new SameSiteCookieManager(new SystemWebCookieManager()),

        Notifications = new OpenIdConnectAuthenticationNotifications
        {
            AuthorizationCodeReceived = async n =>
            {
                var client = new HttpClient();
                var tokenResponse = await client.RequestAuthorizationCodeTokenAsync(new AuthorizationCodeTokenRequest
                {
                    Code = n.Code,
                    Address = "https://login.microsoftonline.com/2d55c52b-2865-45c0-b174-fef7eed8e331/oauth2/v2.0/token",
                    ClientId = auth0ClientId,
                    ClientSecret = "WLT8Q~KKe2S5k~XrzUsz-XJUiRPdgaiBAx1uYcnr",
                    RedirectUri = auth0RedirectUri,
                });

                if (tokenResponse.IsError)
                    throw new Exception(tokenResponse.Error);

                var response = await client.GetUserInfoAsync(new UserInfoRequest
                {
                    Token = tokenResponse.AccessToken,
                    Address = "https://graph.microsoft.com/oidc/userinfo",
                });

                if (response.IsError)
                    throw new Exception(response.Error);

                n.AuthenticationTicket.Identity.AddClaims(response.Claims);
            },
            RedirectToIdentityProvider = notification =>
            {
                if (notification.ProtocolMessage.RequestType == OpenIdConnectRequestType.Authentication)
                {

                }
                if (notification.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
                {
                    var logoutUri = $"https://{auth0Domain}/v2/logout?client_id={auth0ClientId}";

                    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;
                        }
                        logoutUri += $"&returnTo={Uri.EscapeDataString(postLogoutUri)}";
                    }

                    notification.Response.Redirect(logoutUri);
                    notification.HandleResponse();
                }
                return Task.FromResult(0);
            }
        }
    });
}

How can I ensure the .AspNet.Cookies cookie is set correctly on the production server?

Are there additional configurations needed to handle SSL termination at the proxy?

Edit: added web.config file

<?xml version="1.0" encoding="utf-8"?>
<!--
  For more information on how to configure your ASP.NET application, please visit
  https://go.microsoft.com/fwlink/?LinkId=301880
  -->
<configuration>
  <configSections>
    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=4.4.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
    <section name="sdx.logging" type="SDX.Logging.SDXLoggingConfiguration, SDX.System" />
    <section name="sdx.services" type="SDX.Services.SDXServicesConfiguration, SDX.Services" />
    <section name="sdx.resources" type="SDX.Resources.SDXResourcesConfiguration, SDX.Resources" />
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
  </configSections>
  
  <appSettings>
    <add key="webpages:Version" value="3.0.0.0" />
    <add key="webpages:Enabled" value="true" />
    <add key="PreserveLoginUrl" value="true" />
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
    
    <add key="auth0:Domain" value="https://login.microsoftonline.com/xxxx/v2.0" />
    <add key="auth0:ClientId" value="f14563a5c4" />
    <add key="auth0:RedirectUri" value="http://localhost:49141" />
    <add key="auth0:PostLogoutRedirectUri" value="http://localhost:49141" />
  </appSettings>
  <connectionStrings>
  </connectionStrings>
  <log4net debug="true">
  <appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
    <file value="logslog.txt" />
    <appendToFile value="true" />
    <rollingStyle value="Size" />
    <maxSizeRollBackups value="10" />
    <maximumFileSize value="10MB" />
    <datePattern value="yyyyMMdd" />
    <layout type="log4net.Layout.PatternLayout">
      <conversionPattern value="%date [%thread] %-5level %logger [%ndc] – %message%newline" />
    </layout>
  </appender>

  <root>
    <level value="DEBUG" />
    <appender-ref ref="RollingLogFileAppender" />
  </root>
</log4net>
  <!--
      maxLogAgeDays = How many days of logs to keep. Max=7.
      traceLevel = 
        Error: Output error-handling messages.
        Warning: Output warnings and error-handling messages.
        Info: Output informational messages, warnings, and error-handling messages.
        Verbose: Output all debugging and tracing messages.
    -->
  <sdx.logging traceLevel="Verbose" cleanupInterval="1.00:00:00" logFilename="C:SourceCodesVSTSLogs%dweb.log">
    <cleanupDirectories>
      <add name="C:SourceCodesVSTSLogs" />
    </cleanupDirectories>
  </sdx.logging>
  
  <system.web>
    <compilation debug="true" targetFramework="4.8" />
    <httpRuntime targetFramework="4.8" />
  </system.web>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Antlr3.Runtime" publicKeyToken="eb42632606e9261f" />
        <bindingRedirect oldVersion="0.0.0.0-3.5.0.2" newVersion="3.5.0.2" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Web.Infrastructure" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="0.0.0.0-2.0.1.0" newVersion="2.0.1.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" />
        <bindingRedirect oldVersion="0.0.0.0-12.0.0.0" newVersion="12.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Optimization" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="1.1.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="0.0.0.0-1.6.5135.21930" newVersion="1.6.5135.21930" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-5.2.9.0" newVersion="5.2.9.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.IdentityModel.Logging" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-7.5.2.0" newVersion="7.5.2.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.IdentityModel.Tokens" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-7.5.2.0" newVersion="7.5.2.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.IdentityModel.JsonWebTokens" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-7.5.2.0" newVersion="7.5.2.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.0.1.2" newVersion="4.0.1.2" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.2.0.1" newVersion="4.2.0.1" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Text.Encodings.Web" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.0.3.0" newVersion="4.0.3.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.IdentityModel.Protocols" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-7.5.2.0" newVersion="7.5.2.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.IdentityModel.Protocols.OpenIdConnect" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-7.5.2.0" newVersion="7.5.2.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.IdentityModel.Tokens.Jwt" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-7.5.2.0" newVersion="7.5.2.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Text.Json" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Bcl.AsyncInterfaces" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-8.0.0.0" newVersion="8.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.2.2.0" newVersion="4.2.2.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Owin.Security.OAuth" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.2.2.0" newVersion="4.2.2.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.2.2.0" newVersion="4.2.2.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Owin.Security.Cookies" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.2.2.0" newVersion="4.2.2.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
  <system.codedom>
    <compilers>
      <compiler language="c#;cs;csharp" extension=".cs" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.CSharpCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:default /nowarn:1659;1699;1701" />
      <compiler language="vb;vbs;visualbasic;vbscript" extension=".vb" type="Microsoft.CodeDom.Providers.DotNetCompilerPlatform.VBCodeProvider, Microsoft.CodeDom.Providers.DotNetCompilerPlatform, Version=2.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" warningLevel="4" compilerOptions="/langversion:default /nowarn:41008 /define:_MYTYPE=&quot;Web&quot; /optionInfer+" />
    </compilers>
  </system.codedom>
  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework" />
  </entityFramework>
  <system.serviceModel>
    <behaviors>
      <endpointBehaviors>
        <behavior name="ServiceEndpointBehavior">
          <dataContractSerializer maxItemsInObjectGraph="2147483647" />
          <clientCredentials>
            <serviceCertificate>
              <authentication certificateValidationMode="None" />
            </serviceCertificate>
          </clientCredentials>
        </behavior>
        <behavior name="MyServiceBehavior">
          <enableWebScript />
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="false" />
    <bindings>
      <netTcpBinding>
        <binding name="NetTcpBinding" closeTimeout="00:10:00" openTimeout="00:10:00" receiveTimeout="00:10:00" sendTimeout="00:10:00" transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions" hostNameComparisonMode="StrongWildcard" listenBacklog="10" maxBufferPoolSize="2147483647" maxBufferSize="2147483647" maxConnections="10" maxReceivedMessageSize="2147483647">
          <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
          <reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" />
          <security mode="None">
            <message clientCredentialType="None" />
          </security>
        </binding>
      </netTcpBinding>
    </bindings>
    <client>
      <endpoint name="NetTcpBinding_IMySampleAppService" address="net.tcp://localhost:8585/MySampleAppService" binding="netTcpBinding" bindingConfiguration="NetTcpBinding" contract="SDX.GCPS.Models.IMySampleAppService" behaviorConfiguration="ServiceEndpointBehavior">
        <identity>
          <servicePrincipalName value="hostname/localhost" />
        </identity>
      </endpoint>
    </client>
  </system.serviceModel>
</configuration>

Edit: 07/09/2024
below is the samesite cookie error I see in chrome.
Cookie Error Image

Code added in global.asax

protected void Application_BeginRequest(object sender, EventArgs e)
{
    HttpApplication application = sender as HttpApplication;
    if (application != null)
    {
        var userAgent = application.Context.Request.UserAgent;
        application.Response.AddOnSendingHeaders(context =>
        {
            var cookies = context.Response.Cookies;
            for (var i = 0; i < cookies.Count; i++)
            {
                var cookie = cookies[i];

                if (cookie.Name.ToLower().Contains("nonce"))
                    cookie.SameSite = SameSiteMode.None;                        
            }
        });
    }
}

2

Answers


  1. Configure proxy settings in Web.config:

    <configuration>
      <system.net>
        <defaultProxy>
          <proxy autoDetect="true" />
        </defaultProxy>
      </system.net>
    </configuration>
    

    Custom middleware for HTTP headers forwarding:

    using System.Threading.Tasks;
    using System.Web;
    
    public class ForwardHeadersMiddleware : OwinMiddleware
    {
        public ForwardHeadersMiddleware(OwinMiddleware next) : base(next) { }
    
        public override async Task Invoke(IOwinContext context)
        {
            var request = context.Request;
            var response = context.Response;
    
            // Forward the headers
            var xForwardedFor = request.Headers["X-Forwarded-For"];
            var xForwardedProto = request.Headers["X-Forwarded-Proto"];
            var xForwardedHost = request.Headers["X-Forwarded-Host"];
    
            if (!string.IsNullOrEmpty(xForwardedFor))
            {
                response.Headers.Set("X-Forwarded-For", xForwardedFor);
            }
    
            if (!string.IsNullOrEmpty(xForwardedProto))
            {
                response.Headers.Set("X-Forwarded-Proto", xForwardedProto);
            }
    
            if (!string.IsNullOrEmpty(xForwardedHost))
            {
                response.Headers.Set("X-Forwarded-Host", xForwardedHost);
            }
    
            await Next.Invoke(context);
        }
    }
    

    A complex approach (OwinForwardedHeaderMiddleware): https://github.com/thehoneymad/OwinForwardedHeaderMiddleware/tree/master

    Login or Signup to reply.
  2. Try adding the following configuration in web.config. According to https://techcommunity.microsoft.com/t5/iis-support-blog/changes-in-samesite-cookie-in-asp-net-core-and-how-it-impacts/ba-p/1150771 these settings are required even though you have set sameSiteMode to None in OpenIdConfiguration

    <system.web>
      <httpCookies requireSSL="true" sameSite="None" />
      <authentication>
        <forms requireSSL="true" cookieSameSite="None" />
      </authentication>
      <sessionState cookieSameSite="None" />
    </system.web>
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search