Edit

Share via


Infinite sign-in loop between ASP.NET application and Microsoft Entra ID

This article provides solutions to an issue where an ASP.NET application experiences an infinite redirect loop during signing in with Microsoft Entra ID.

Symptoms

An ASP.NET application running an earlier version of Open Web Interface for .NET (OWIN) middleware fails to recognize an authenticated request from Microsoft Entra ID. It keeps sending the request back to Microsoft Entra ID for signing in, leading to the infinite loop issue. The following error message might be displayed in the browser:

We couldn't sign you in. Please try again.

Cause

This issue occurs due to a cookie mismanagement issue (a known Katana bug) in the earlier version of OWIN.

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 following screenshot, the request in frame 58 contains multiple OpenIdConnect.nonce cookies (red-circled). In a working scenario, you 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, you see a buildup of these nonce cookies.

Screenshot that shows multiple OpenIdConnect nonce cookies.

Solution 1: Upgrade to ASP.NET Core

The issue is resolved in ASP.NET Core and a later version of Katana OWIN for ASP.NET. To resolve this issue, upgrade your application to use ASP.NET Core.

If you must continue to use ASP.NET, perform the following actions:

  • Update your application's Microsoft.Owin.Host.SystemWeb package to version 3.1.0.0 or later.

  • Modify your code to use one of the new cookie manager classes, for example:

    app.UseCookieAuthentication(new CookieAuthenticationOptions 
    { 
        AuthenticationType = "Cookies", 
        CookieManager = new Microsoft.Owin.Host.SystemWeb.SystemWebChunkingCookieManager() 
    });
    

    or

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

Solution 2: Correct the redirect URL

In some cases where the application is hosted under a virtual directory or an application instead of the root of the web site, solution 1 might not work. For more information, see Infinite re-direct loop after AAD Authentication when redirect is specified and Microsoft Account OAuth2 sign-on fails when redirect URL is not under the website root.

For example, suppose you have the following environment:

  • The root of a web site: https://mysite – This site runs under Application Pool 1.

  • An application test2 under the root: https://mysite/test2 – This application runs under Application Pool 2.

  • Your ASP.NET application runs under the test2 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
                        }
    
                    }
                );
            }
    
  • You use the following code to trigger the sign-in flow:

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

This scenario can result in an authentication infinite loop with a buildup of multiple OpenIdConnect.nonce cookies. The difference is that ASP.NET doesn't appear to set its authenticated session cookies. To resolve the issue in such scenario, set the redirect URLs in 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

Contact us for help

If you have questions or need help, create a support request, or ask Azure community support. You can also submit product feedback to Azure feedback community.