IDX21323 from B2C to app
I have retrofitted a WebForms app with AAD B2C authentication. This has been in production for about 3.5 weeks. Some users have reported that on Sign-in they are prompted for mfa twice (they must send the verification code and verify twice) which didn't occur in test. After investigating the issue, I discovered that error IDX21323 is being thrown on redirect from B2C to the app in these instances. The following code is recovering from the error, but it is redirecting to B2C and subsequently triggering the second mfa verification. I'm looking for a way to prevent the error from occurring in the first place or to prevent the second mfa verification.
If notification.Exception.Message.Contains("IDX21323") Then
notification.OwinContext.Authentication.Challenge()
Return Task.FromResult(True)
End If
The full error is: IDX21323: RequireNonce is 'True'. OpenIdConnectProtocolValidationContext.Nonce was null, OpenIdConnectProtocol.ValidatedIdToken.Payload.Nonce was not null. The nonce cannot be validated. If you don't need to check the nonce, set OpenIdConnectProtocolValidator.RequireNonce to 'false'. Note if a 'nonce' is found it will be evaluated.
Here is the startup.auth code:
Partial Public Class Startup
'App config settings
Private Shared ReadOnly clientId As String = ConfigurationManager.AppSettings("ida:ClientId")
Private Shared ReadOnly aadInstance As String = ConfigurationManager.AppSettings("ida:AadInstance")
Private Shared ReadOnly tenant As String = ConfigurationManager.AppSettings("ida:Tenant")
Public Shared ReadOnly userProfileSignUpRedirectUri As String = ConfigurationManager.AppSettings("ida:UserProfileSignUpRedirectUri")
Public Shared ReadOnly vendorSignUpRedirectUri As String = ConfigurationManager.AppSettings("ida:VendorSignUpRedirectUri")
Public Shared ReadOnly signInRedirectUri As String = ConfigurationManager.AppSettings("ida:SignInRedirectUri")
Public Shared ReadOnly profileEditRedirectUri As String = ConfigurationManager.AppSettings("ida:ProfileEditRedirectUri")
Private Shared ReadOnly postLogoutRedirectUri As String = ConfigurationManager.AppSettings("ida:PostLogoutRedirectUri")
'B2C policy identifiers
Public Shared UserProfileSignUpPolicyId As String = ConfigurationManager.AppSettings("ida:UserProfileSignUpPolicyId")
Public Shared VendorSignUpPolicyId As String = ConfigurationManager.AppSettings("ida:VendorSignUpPolicyId")
Public Shared SignInPolicyId As String = ConfigurationManager.AppSettings("ida:SignInPolicyId")
Public Shared ProfileEditPolicyId As String = ConfigurationManager.AppSettings("ida:ProfileEditPolicyId")
'Public Shared emailValue As String
Public Sub ConfigureAuth(app As IAppBuilder)
Try
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType)
app.UseCookieAuthentication(New CookieAuthenticationOptions() With {
.CookieSameSite = SameSiteMode.None,
.CookieSecure = CookieSecureOption.Always
})
'Configure OpenID Connect middleware for each policy
app.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(UserProfileSignUpPolicyId, userProfileSignUpRedirectUri, userProfileSignUpRedirectUri))
app.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(VendorSignUpPolicyId, vendorSignUpRedirectUri, vendorSignUpRedirectUri))
app.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(ProfileEditPolicyId, profileEditRedirectUri, profileEditRedirectUri))
app.UseOpenIdConnectAuthentication(CreateOptionsFromPolicy(SignInPolicyId, signInRedirectUri, postLogoutRedirectUri))
Catch ex As Exception
'WriteErrorToLog(ex.ToString, HttpContext.Current.Session("LoggedInUser"), "B2C Configure Auth", Reflection.MethodBase.GetCurrentMethod().Name)
LogMessage(Me.ToString(), System.Reflection.MethodBase.GetCurrentMethod().Name, "Exception", ex.ToString())
'HttpContext.Current.Response.Redirect($"../errAppError.aspx")
End Try
End Sub
Private Function AuthenticationFailed(notification As AuthenticationFailedNotification(Of OpenIdConnectMessage, OpenIdConnectAuthenticationOptions)) As Task
Dim emailAddress As String = ""
Try
Dim authenticationProperties As AuthenticationProperties = notification?.OwinContext?.Authentication?.AuthenticationResponseChallenge?.Properties
If authenticationProperties IsNot Nothing AndAlso authenticationProperties.Dictionary.ContainsKey("useremail") Then
emailAddress = authenticationProperties.Dictionary("useremail")
End If
If notification Is Nothing Then
LogMessage(Me.ToString(), System.Reflection.MethodBase.GetCurrentMethod().Name, "Informational", "Email: '" + emailAddress + "': MS Owin.Security returned an error, but the notification argument is null.")
Return Task.FromResult(0)
End If
LogMessage(Me.ToString(), System.Reflection.MethodBase.GetCurrentMethod().Name, "Informational", "Email: '" + emailAddress + "': B2C AuthenticationFailed: " + notification.Exception.ToString())
notification.HandleResponse()
If notification.Exception.Message.Contains("IDX21323") Then
notification.OwinContext.Authentication.Challenge()
Return Task.FromResult(True)
End If
If notification.Exception.Message.Contains("AADB2C90091") Then
If notification.Options.AuthenticationType.Contains("ProfileEdit") Then
notification.Response.Redirect($"../UserProfile.aspx")
Else
notification.Response.Redirect($"../NoAccess.aspx?MSG=9&MsgContent=Authentication Error: Signup/Signin Aborted")
End If
Else
HttpContext.Current.GetOwinContext().Authentication.SignOut(CookieAuthenticationDefaults.AuthenticationType)
HttpContext.Current.Session("LoginErrors") = "Authentication Error: SignUp/SignIn could not be completed. Please close the browser before trying again."
notification.Response.Redirect($"../NoAccess.aspx?MSG=9&MsgContent=Authentication Error: SignUp/SignIn could not be completed. Please close the browser before trying again.")
End If
Catch ex As Exception
LogMessage(Me.ToString(), System.Reflection.MethodBase.GetCurrentMethod().Name, "Exception", "Email: '" + emailAddress + "': " + ex.ToString())
notification.Response.Redirect($"../NoAccess.aspx?MSG=9&MsgContent=Authentication Error: SignUp/SignIn could not be completed. Please close the browser before trying again.")
End Try
Return Task.FromResult(0)
End Function
Private Function CreateOptionsFromPolicy(policy As String, redirectUri As String, logoutUri As String) As OpenIdConnectAuthenticationOptions
' For each policy, give OWIN the policy-specific metadata address, And
' set the authentication type to the id of the policy
'These are standard OpenID Connect parameters, with values pulled from web.config
Dim options As New OpenIdConnectAuthenticationOptions With {
.MetadataAddress = String.Format(aadInstance, tenant, policy),
.AuthenticationType = policy,
.ClientId = clientId,
.RedirectUri = redirectUri,
.PostLogoutRedirectUri = logoutUri,
.Notifications = New OpenIdConnectAuthenticationNotifications With {
.AuthenticationFailed = AddressOf AuthenticationFailed,
.RedirectToIdentityProvider = Function(notification)
Dim authenticationProperties As AuthenticationProperties = notification?.OwinContext?.Authentication?.AuthenticationResponseChallenge?.Properties
If authenticationProperties IsNot Nothing AndAlso authenticationProperties.Dictionary.ContainsKey("useremail") Then
Dim additionalParamValue As String = authenticationProperties.Dictionary("useremail")
'Create an instance of AuthenticationProperties and add the additional parameter
If Not String.IsNullOrEmpty(additionalParamValue) Then
notification.ProtocolMessage.Parameters.Add("signInName", additionalParamValue)
End If
End If
Return Task.FromResult(0)
End Function
},
.Scope = OpenIdConnectScope.OpenId,
.ResponseType = OpenIdConnectResponseType.IdToken,
.TokenValidationParameters = New TokenValidationParameters With {
.NameClaimType = "name"
},
.CookieManager = New SameSiteCookieManager(New SystemWebCookieManager())
}
Return options
End Function
End Class