In a previous post I talked about an infinite redirect loop issue between an MVC application and Azure AD when performing sign in. In this post, we will look at another scenario which can lead to the same type of problem.

Background

Applications running an old version of OWIN middleware can run into this issue because of a known Katana bug. Due to a cookie mismanagement issue in the old version of OWIN, an asp.net application is not able to recognize an authenticated request from Azure AD so it keeps sending the request back to Azure AD for login leading to the infinite loop issue.

Error Message

Customer may see this error message in the browser:  “We couldn’t sign you in.  Please try again.”

How to recognize the Katana bug…

Capture a Fiddler trace and examine one of the later redirect frames back to the web application. Note in the screen shot below, the request in frame 58 contains multiple OpenIdConnect nonce cookies (red-circled). In a working scenario, we should only have one OpenIdConnect nonce cookie set at the beginning before authentication. After the request is successfully authenticated, this nonce cookie is destroyed and asp.net sets its own session cookie. Because of this bug, we see there is a build up of these nonce cookies.

Resolving the issue…

The problem has been fixed in ASP.NET core and in the new version of Katana Owin for ASP.NET.  To resolve this issue, you can upgrade your application to use ASP.NET Core. If you must continue stay on ASP.NET, perform the following:

  1. Update your application’s Microsoft.Owin.Host.SystemWeb package be at least version 3.1.0.0 and
  2. Modify your code to use one of the new cookie manager classes, for example something like the following:
app.UseCookieAuthentication(new CookieAuthenticationOptions 
{ 
    AuthenticationType = "Cookies", 
    CookieManager = new Microsoft.Owin.Host.SystemWeb.SystemWebChunkingCookieManager() 
});

or

app.UseCookieAuthentication(new CookieAuthenticationOptions() 
{ 
    CookieManager = new SystemWebCookieManager() 
});

Note: In some cases where the application is hosted under a virtual directory or Application instead of the root of the web site, the above solution may not work. See https://stackoverflow.com/questions/44397715/infinite-re-direct-loop-after-aad-authentication-when-redirect-is-specified and https://github.com/aspnet/AspNetKatana/issues/203 for more info. For instance, suppose you have the following environment

Root of web site: https://mysite – this runs under Application Pool 1

Application test2 under the root root: https://mysite/test2 – this runs under Application Pool 2

Your Asp.NET application is runs under https://mysite/tes2 Application with the following code:

        public void Configuration(IAppBuilder app)
        {
            // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=316888
            app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
            app.UseCookieAuthentication(new CookieAuthenticationOptions());
            app.UseOpenIdConnectAuthentication(
                new OpenIdConnectAuthenticationOptions
                {
                    // Sets the ClientId, authority, RedirectUri as obtained from web.config
                    ClientId = clientId,
                    Authority = authority,
                    RedirectUri = "https://mysite/test2",
                    // PostLogoutRedirectUri is the page that users will be redirected to after sign-out. In this case, it is using the home page
                    PostLogoutRedirectUri = redirectUri,
                    Scope = OpenIdConnectScope.OpenIdProfile,
                    // ResponseType is set to request the id_token - which contains basic information about the signed-in user
                    ResponseType = OpenIdConnectResponseType.IdToken,
                    // ValidateIssuer set to false to allow personal and work accounts from any organization to sign in to your application
                    // To only allow users from a single organizations, set ValidateIssuer to true and 'tenant' setting in web.config to the tenant name
                    // To allow users from only a list of specific organizations, set ValidateIssuer to true and use ValidIssuers parameter
                    
                   
                    // OpenIdConnectAuthenticationNotifications configures OWIN to send notification of failed authentications to OnAuthenticationFailed method
                    
                    Notifications = new OpenIdConnectAuthenticationNotifications
                    {
                        AuthenticationFailed = OnAuthenticationFailed
                    }
                    
                    
                }
            );
        }

And you are using the following code for trigger the Sign In flow:

        public void SignIn()
        {
            if (!Request.IsAuthenticated)
            {
                HttpContext.GetOwinContext().Authentication.Challenge(
                    new AuthenticationProperties { RedirectUri = "/" },
                    OpenIdConnectAuthenticationDefaults.AuthenticationType);
            }
        }

The above scenario can result in an authentication infinite loop with a build-up of multiple OpenIdConnect.nonce cookies as well. The difference here is that Asp.NET does not appear to set its authenticated session cookies. This particular problem can be resolved using the following code change to set the redirect URLs in both the OpenID Connect initialization code and the Challenge method (note the trailing slash in the redirect URL):

app.UseOpenIdConnectAuthentication(
                new OpenIdConnectAuthenticationOptions
                {
                    // Sets the ClientId, authority, RedirectUri as obtained from web.config
                    ClientId = clientId,
                    Authority = authority,
                    RedirectUri = "https://mysite/test2/",
                    // PostLogoutRedirectUri is the page that users will be redirected to after sign-out. In this case, it is using the home page
                    PostLogoutRedirectUri = redirectUri,
                    Scope = OpenIdConnectScope.OpenIdProfile,
...
        public void SignIn()
        {
            if (!Request.IsAuthenticated)
            {
                HttpContext.GetOwinContext().Authentication.Challenge(
                    new AuthenticationProperties { RedirectUri = "/test2/" },
                    OpenIdConnectAuthenticationDefaults.AuthenticationType);
            }
        }

 

References

https://varnerin.info/infinite-redirects-with-aspnet-owin-and-openid-connect/

Updated 11/16/2020 – add info regarding virtual Directory / Application

3 Thoughts to “Infinite sign in loop between MVC application and Azure AD”

  1. Rich Posada

    If you are using Visual Studio, go to the properties page for you project. Click on the “Web” tab on left, view the settings in the “Servers” section and be sure that the Project URL is referencing the HTTPS URL and not the HTTP URL. This is causing the cookie collision in this scenario.

  2. Kevin

    Thanks for posting this. I was experiencing the infinite loop and configuring the “SystemWebChunkingCookieManager” did the fix for me.

  3. […] Referred from : infinite redirect loop between Azure AD and MVC Asp.net application due to old version of OWIN | (aa… […]

Leave a Reply to Kevin Cancel reply