Azure B2C / .NET - Handling a failed MFA verification flow

Giedrius Stasiulis 0 Reputation points
2024-06-12T05:50:33.9466667+00:00

I have a WPF application with the following client code to fetch a token from Azure B2C endpoint:


public static async Task<AuthenticationResult> AcquireTokenAsync(this IPublicClientApplication publicClientApp)
{
   try
   {
      var accounts = await publicClientApp.GetAccountsAsync();

      return await Task.Run(() =>
                publicClientApp
                    .AcquireTokenInteractive(apiScopes)
                    .WithAccount(accounts.FirstOrDefault())
                    .WithPrompt(Prompt.ForceLogin)
                    .ExecuteAsync());
   }
   catch (MsalClientException ex)
   {
       if (ex.ErrorCode.Equals("authentication_canceled"))
       {
           log.Info("Authentication was cancelled");
       }

       log.Error("Error in login sequence", ex);
       throw;
   }
}

Where publicClientApp is created as such:

app = PublicClientApplicationBuilder.Create(ClientId)
                        .WithB2CAuthority(Authority)
                        .Build();

I would like to be able to distinguish different "error code" situations, taking into account MFA. So far I have been able to capture when user cancels/closes the authentication window - which throws MsalClientException with an error code authentication_canceled.

Next I would like to capture the failed MFA verification, and by debugging the code I can see that after failing the verification enough times, the AcquireTokenInteractive throws:

Microsoft.Identity.Client.MsalClientException: 
The browser based authentication dialog failed to complete. Reason: The server encountered an unexpected condition that prevented it from fulfilling the request.
 

With an error code: authentication_ui_failed

Stack trace:

at Microsoft.Identity.Client.Platforms.net45.WebUI.<AcquireAuthorizationAsync>d__20.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.Identity.Client.Internal.Requests.InteractiveRequest.<AcquireAuthorizationAsync>d__10.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.Identity.Client.Internal.Requests.InteractiveRequest.<ExecuteAsync>d__9.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.Identity.Client.Internal.Requests.RequestBase.<RunAsync>d__14.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at Microsoft.Identity.Client.ApiConfig.Executors.PublicClientExecutor.<ExecuteAsync>d__2.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at MyProject.AzureB2CIntegration.PublicClientApplicationExtensions.<AcquireTokenAsync>d__4.MoveNext() in C:\Projects\My\Common\AzureB2CIntegration\PublicClientApplicationExtensions.cs:line 23

However, when inspecting the traffic on Fiddler, I get a more sensible error response from B2C:

status=500 Internal Server Error
error=server_error
error_description=AADB2C90151: User has exceeded the maximum number for retries for multi-factor authentication.

Question - is there a way to get a more detailed error information (like the one from Fiddler) from an exception that is thrown by AcquireTokenInteractive in such case?

.NET
.NET
Microsoft Technologies based on the .NET software framework.
3,565 questions
Azure
Azure
A cloud computing platform and infrastructure for building, deploying and managing applications and services through a worldwide network of Microsoft-managed datacenters.
1,059 questions
Microsoft Entra ID
Microsoft Entra ID
A Microsoft Entra identity service that provides identity management and access control capabilities. Replaces Azure Active Directory.
20,303 questions
{count} votes

3 answers

Sort by: Most helpful
  1. Shweta Mathur 29,031 Reputation points Microsoft Employee
    2024-06-17T06:38:13.0466667+00:00

    Hi @Giedrius Stasiulis ,

    Thanks for reaching out.

    I would like to be able to distinguish different "error code" situations, taking into account MFA. So far I have been able to capture when user cancels/closes the authentication window - which throws MsalClientException with an error code authentication_canceled****.

    The idea behind getting these errors is to catch them, and redirect the user where you want - for example sign-up or sign-in page, etc. The error is handled by the application based on the code sent back. For example, in an MVC app in the startup.auth.cs add the following to tell the application where to send the user.

    /*
            * Catch any failures received by the
    authentication middleware and handle appropriately
             */
            private Task
    OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage,
    OpenIdConnectAuthenticationOptions> notification)
            {
                notification.HandleResponse();
                // Handle the
    error code that Azure AD B2C throws when trying to reset a password from the
    login page 
                // because
    password reset is not supported by a "sign-up or sign-in policy"
                if (notification.ProtocolMessage.ErrorDescription != null &&
    notification.ProtocolMessage.ErrorDescription.Contains("AADB2C90118"))
                {
                    // If the
    user clicked the reset password link, redirect to the reset password route
                    notification.Response.Redirect("/Account/ResetPassword");
                }
                // Added to
    redirect to Home page due when pressing the Cancel button
                else if
    (notification.ProtocolMessage.Error == "access_denied" &&
    notification.ProtocolMessage.ErrorDescription.StartsWith("AADB2C90091"))
                {
                    notification.Response.Redirect("/Home/Index");
                 }
                else if
    (notification.Exception.Message == "access_denied")
                {
                    notification.Response.Redirect("/");
                }
                else
                {
                    notification.Response.Redirect("/Home/Error?message=" +
    notification.Exception.Message);
                }
                return Task.FromResult(0);
            }
    
    
    

    Reference of error codes - https://learn.microsoft.com/en-us/azure/active-directory-b2c/error-codes

    is there a way to get a more detailed error information (like the one from Fiddler) from an exception that is thrown by AcquireTokenInteractive in such case?

    You can use Application Insights to extract error details in your B2C application.

    Application Insights provides a way to track and analyze telemetry data from your application, including exceptions and other errors. You can use Application Insights to track exceptions and errors that occur in your B2C application, and then analyze the data to identify and fix issues.

    Reference - https://github.com/azure-ad-b2c/vscode-extension/blob/master/src/help/app-insights.md

    https://learn.microsoft.com/en-us/azure/active-directory-b2c/analytics-with-application-insights?pivots=b2c-custom-policy

    This will provide you details which user journey has invoked this error.

    Hope this will help.

    Thanks,

    Shweta

    Please remember to "Accept Answer" if answer helped you.

    0 comments No comments

  2. Giedrius Stasiulis 0 Reputation points
    2024-06-20T05:36:18.5066667+00:00

    Turns out behind the scenes I was using a Microsoft.Identity.Client 4.1.0 (due to it being wrapped around by another nuget, dohh). Once that got replaced with the most recent one, things started clicking as I expected.

    0 comments No comments

  3. Shweta Mathur 29,031 Reputation points Microsoft Employee
    2024-06-20T13:03:22.4933333+00:00

    @Giedrius Stasiulis

    I'm glad that you were able to resolve your issue and thank you for posting your solution so that others experiencing the same thing can easily reference this! Since the Microsoft Q&A community has a policy that "The question author cannot accept their own answer. They can only accept answers by others", I'll repost your solution in case you'd like to "Accept" the answer.

    User's image

    0 comments No comments