|
| 1 | +--- |
| 2 | +title: Troubleshoot ASP.NET OWIN and ASP.NET Core Authentication Sign-in Failures |
| 3 | +description: Helps you expose hidden error messages that can guide you toward resolving ASP.NET OWIN and ASP.NET Core authentication sign-in failures with Microsoft Entra ID. |
| 4 | +ms.reviewer: willfid, v-weizhu |
| 5 | +ms.service: entra-id |
| 6 | +ms.date: 07/02/2025 |
| 7 | +ms.custom: sap:Developing or Registering apps with Microsoft identity platform |
| 8 | +--- |
| 9 | +# Troubleshoot ASP.NET OWIN and ASP.NET Core authentication sign-in failures with Microsoft Entra ID |
| 10 | + |
| 11 | +When you develop an ASP.NET Open Web Interface for .NET (OWIN) or ASP.NET Core Authentication web application and integrate it with Microsoft Entra ID, you encounter some issues during the sign-in process without any error messages or hint about what the problem might be. This article doesn't focus on direct solutions to sign-in failures but aims to help you expose hidden error messages that can guide you toward resolving the issue. |
| 12 | + |
| 13 | +> [!NOTE] |
| 14 | +> This article assumes you use your own code to perform the authentication to Microsoft Entra ID. If you use the Azure App Services or Azure Function Apps authentication and authorization feature, this article doesn't apply to your scenario. |
| 15 | +
|
| 16 | +## Symptoms |
| 17 | + |
| 18 | +You might see some common sign-in failure behaviors as follows: |
| 19 | + |
| 20 | +- An infinite loop occurs between your web application and Microsoft Entra ID. |
| 21 | +- After signing in to Microsoft Entra ID, you're redirected to your web application as if you never signed in. |
| 22 | +- You enter an error page, but it doesn't provide useful error messages. |
| 23 | + |
| 24 | +## Expose hidden errors by using the OnAuthenticationFailed event |
| 25 | + |
| 26 | +To expose hidden errors during the sign-in process, use the `OnAuthenticationFailed` event. |
| 27 | + |
| 28 | +### For ASP.NET OWIN |
| 29 | + |
| 30 | +Ensure your code for the `AuthenticationFailed` event in the *Startup.Auth.cs* file follows this structure: |
| 31 | + |
| 32 | +```csharp |
| 33 | +public void ConfigureAuth(IAppBuilder app) |
| 34 | +{ |
| 35 | + app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType); |
| 36 | + |
| 37 | + app.UseCookieAuthentication(new CookieAuthenticationOptions()); |
| 38 | + |
| 39 | + app.UseOpenIdConnectAuthentication( |
| 40 | + new OpenIdConnectAuthenticationOptions |
| 41 | + { |
| 42 | + ResponseType = OpenIdConnectResponseType.CodeIdToken, |
| 43 | + ClientId = clientId, |
| 44 | + Authority = Authority, |
| 45 | + //... |
| 46 | +
|
| 47 | + Notifications = new OpenIdConnectAuthenticationNotifications() |
| 48 | + { |
| 49 | + // If there is a code in the OpenID Connect response, redeem it for an access token |
| 50 | + AuthorizationCodeReceived = (context) => |
| 51 | + { |
| 52 | + // ... |
| 53 | + }, |
| 54 | + |
| 55 | + // On Authentication Failed |
| 56 | + AuthenticationFailed = (context) => |
| 57 | + { |
| 58 | + String ErrorMessage = context.Exception.Message; |
| 59 | + String InnerErrorMessage = String.Empty; |
| 60 | + |
| 61 | + String RedirectError = String.Format("error_message={0}", ErrorMessage); |
| 62 | + |
| 63 | + if (context.Exception.InnerException != null) |
| 64 | + { |
| 65 | + InnerErrorMessage = context.Exception.InnerException.Message; |
| 66 | + RedirectError = String.Format("{0}&inner_error={1}", RedirectError, InnerErrorMessage); |
| 67 | + } |
| 68 | + |
| 69 | + // or you can just throw it |
| 70 | + // throw new Exception(RedirectError); |
| 71 | +
|
| 72 | + RedirectError = RedirectError.Replace("\r\n", " "); |
| 73 | + |
| 74 | + context.Response.Redirect("/?" + RedirectError); |
| 75 | + context.HandleResponse(); |
| 76 | + return Task.FromResult(0); |
| 77 | + } |
| 78 | + } |
| 79 | + |
| 80 | + }); |
| 81 | + |
| 82 | +// ... |
| 83 | +``` |
| 84 | + |
| 85 | +### For ASP.NET Core |
| 86 | + |
| 87 | +Ensure your code for the `AuthenticationFailed` event in the *Startup.cs* file follows this structure: |
| 88 | + |
| 89 | +```csharp |
| 90 | +public void ConfigureServices(IServiceCollection services) |
| 91 | +{ |
| 92 | + services.Configure<CookiePolicyOptions>(options => |
| 93 | + { |
| 94 | + // ... |
| 95 | + }); |
| 96 | + |
| 97 | + services.AddAuthentication(AzureADDefaults.AuthenticationScheme) |
| 98 | + .AddAzureAD(options => Configuration.Bind("AzureAd", options)); |
| 99 | + |
| 100 | + // ... |
| 101 | +
|
| 102 | + services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options => |
| 103 | + { |
| 104 | + options.Authority = options.Authority; |
| 105 | + |
| 106 | + // Token Validation |
| 107 | + options.TokenValidationParameters.IssuerValidator = AadIssuerValidator.ValidateAadIssuer; |
| 108 | + |
| 109 | + // Response type |
| 110 | + options.ResponseType = "id_token code"; |
| 111 | + |
| 112 | + // On Authorization Code Received |
| 113 | + options.Events.OnAuthorizationCodeReceived = async context => |
| 114 | + { |
| 115 | + // ... |
| 116 | + }; |
| 117 | + |
| 118 | + // On Authentication Failed |
| 119 | + options.Events.OnAuthenticationFailed = async context => |
| 120 | + { |
| 121 | + String ErrorMessage = context.Exception.Message; |
| 122 | + String InnerErrorMessage = String.Empty; |
| 123 | + |
| 124 | + String RedirectError = String.Format("?error_message={0}", ErrorMessage); |
| 125 | + |
| 126 | + if (context.Exception.InnerException != null) |
| 127 | + { |
| 128 | + InnerErrorMessage = context.Exception.InnerException.Message; |
| 129 | + RedirectError = String.Format("{0}&inner_error={1}", RedirectError, InnerErrorMessage); |
| 130 | + } |
| 131 | + |
| 132 | + // or you can just throw it |
| 133 | + // throw new Exception(RedirectError); |
| 134 | +
|
| 135 | + RedirectError = RedirectError.Replace("\r\n", " "); |
| 136 | + |
| 137 | + context.Response.Redirect(RedirectError); |
| 138 | + context.HandleResponse(); |
| 139 | + }; |
| 140 | + |
| 141 | + // ... |
| 142 | +``` |
| 143 | + |
| 144 | +You can modify this structure to send the error message to your logs or send it to a custom error page. At a minimum, the error message should be displayed in the browser's address bar. |
| 145 | + |
| 146 | +:::image type="content" source="media/asp-dot-net-open-web-interface-for-dot-net-core-authentication-sign-in-failures/error-message-in-address-bar.png" alt-text="Screenshot that shows the error message in the browser address bar."::: |
| 147 | + |
| 148 | +If there's an infinite loop, the error message should be visible in the Fiddler capture. |
| 149 | + |
| 150 | +:::image type="content" source="media/asp-dot-net-open-web-interface-for-dot-net-core-authentication-sign-in-failures/error-message-in-fiddler-capture.png" alt-text="Screenshot that shows the error message in the Fiddler capture."::: |
| 151 | + |
| 152 | +For more information about using Fiddler, see [Collect HTTPS traffic using Fiddler for Microsoft Entra ID apps](capture-https-traffic-fiddler-entra-id-app.md). |
| 153 | + |
| 154 | +## Microsoft Entra authentication and authorization error codes |
| 155 | + |
| 156 | +For a list of Microsoft Entra authentication and authorization errors, see [Microsoft Entra authentication and authorization error codes](/entra/identity-platform/reference-error-codes). |
| 157 | + |
| 158 | + |
| 159 | +[!INCLUDE [Azure Help Support](../../../includes/azure-help-support.md)] |
0 commit comments