.netazureazure-active-directoryopenid-connectmicrosoft-entra-id

The required field 'nonce' is missing from the credential. Ensure that you have all the necessary parameters for the login request. [Azure Open ID]


I'm trying to implement Azure AD authentication in a .NET 4.8 project that is developed on the NopCommerce 3.8 framework.

But I'm getting this error when the callback happens:

OpenIdConnectMessage.Error was not null, indicating an error.
Error: 'invalid_request'. Error_Description (may be empty): 'AADSTS90014: The required field 'nonce' is missing from the credential. Ensure that you have all the necessary parameters for the login request.
Trace ID: ef6427f0b00
Correlation ID: 0311700e-cadc-506e764
Timestamp: 2024-08-29 03:51:08Z'.
Error_Uri (may be empty): 'https://login.microsoftonline.com/error?code=90014'

This is my Azure configuration:

enter image description here

enter image description here

And this is my code:

using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Owin;
using Microsoft.Owin.Host.SystemWeb;
using Microsoft.Owin.Logging;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.OpenIdConnect;
using Owin;
using System;
using System.Configuration;
using System.Data.Entity.Core.Common.CommandTrees.ExpressionBuilder;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Web;
using System.Web.Helpers;

[assembly: OwinStartup(typeof(Nop.Web.OwinStartup))]

namespace Nop.Web
{
    public class OwinStartup
    {
        public void Configuration(IAppBuilder app)
        {
            app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

            //app.UseCookieAuthentication(new CookieAuthenticationOptions());
            app.UseCookieAuthentication(new CookieAuthenticationOptions()
            {
                CookieSameSite = Microsoft.Owin.SameSiteMode.None,
                CookieSecure = CookieSecureOption.Always,
                CookieHttpOnly = true,
                CookieManager =new Microsoft.Owin.Host.SystemWeb.SystemWebCookieManager()
            });

            AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;

            var openIdOptions = new OpenIdConnectAuthenticationOptions
            {
                ClientId = ConfigurationManager.AppSettings["ClientId"],
                Authority = ConfigurationManager.AppSettings["Authority"],
                PostLogoutRedirectUri = ConfigurationManager.AppSettings["RedirectUri"],
                RedirectUri = ConfigurationManager.AppSettings["RedirectUri"],
                Scope = "openid email profile offline_access",
                ClientSecret = ConfigurationManager.AppSettings["ClientSecret"],
                ResponseType = OpenIdConnectResponseType.IdToken,
                TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = false,
                },
                ProtocolValidator = new OpenIdConnectProtocolValidator
                {
                    RequireNonce = false
                },
                Notifications = new OpenIdConnectAuthenticationNotifications
                {
                    AuthorizationCodeReceived = async (context) =>
                    {
                        var claim = ClaimsPrincipal.Current;
                        var code = context.Code;

                        string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;

                    },
                    AuthenticationFailed = (context) =>
                    {
                        context.HandleResponse();
                        context.Response.Redirect("/Error?message=" + context.Exception.Message);
                        return Task.FromResult(0);
                    }
                }
            };

            app.UseOpenIdConnectAuthentication(openIdOptions);
        }
    }
}

Login controller

public partial class CustomerController : BasePublicController
        {
    
    
     [NopHttpsRequirement(SslRequirement.Yes)]
     //available even when a store is closed
     [StoreClosed(true)]
     //available even when navigation is not allowed
     [PublicStoreAllowNavigation(true)]
     public ActionResult Login(bool? checkoutAsGuest)
     {
    
         if (!Request.IsAuthenticated)
         { 
             var redirectUri = ConfigurationManager.AppSettings["RedirectUri"];
             HttpContext.GetOwinContext().Authentication.Challenge(new AuthenticationProperties
             {
                 //RedirectUri = Url.Action("ExternalLoginCallback", "Account")
                  RedirectUri = redirectUri
             }, OpenIdConnectAuthenticationDefaults.AuthenticationType);
         }
         //return View();
    
         var model = new LoginModel();
         model.UsernamesEnabled = _customerSettings.UsernamesEnabled;
         model.CheckoutAsGuest = checkoutAsGuest.GetValueOrDefault();
         model.DisplayCaptcha = _captchaSettings.Enabled && _captchaSettings.ShowOnLoginPage;
         return View(model);
     }      
    
    
     [AllowAnonymous]
     [Route("signin-oidc")]
     public ActionResult ExternalLoginCallback()
     {
         // This will return the result of the authentication
         var authResult = HttpContext.GetOwinContext().Authentication.AuthenticateAsync("ExternalCookie").Result;
    
         if (authResult != null)
         {
             // Extract the claims from the external login
             var claimsIdentity = authResult.Identity;
    
             // Optionally, you can extract specific claims like name, email, etc.
             var userName = claimsIdentity.FindFirst(ClaimTypes.Name)?.Value;
             var email = claimsIdentity.FindFirst(ClaimTypes.Email)?.Value;
    
             // Sign the user in with a new identity based on the claims from the external login
             var identity = new ClaimsIdentity(claimsIdentity.Claims, "ApplicationCookie");
             HttpContext.GetOwinContext().Authentication.SignIn(new AuthenticationProperties { IsPersistent = true }, identity);
    
             // Redirect to the home page or wherever you want the user to go after a successful login
             return RedirectToAction("Index", "Home");
         }
    
         // If we got here, something went wrong with the authentication process
         return RedirectToAction("Login", "Account");
     }
    
    }

Web Config

  <add key="owin:AutomaticAppStartup" value="true" />
  <add key="ClientId" value="a403fdgdfg-gdfgdfg-897e-c3d" />
  <add key="Authority" value="https://login.microsoftonline.com/33dffdf8-dere-4079-afa8-7567hghf8f5fba/v2.0" /> 
  <add key="RedirectUri" value="https://localhost:44396/signin-oidc" />
  <add key="PostLogoutRedirectUri" value="https://localhost:44396/" />
  <add key="ClientSecret" value="PaR8Q~IU1-K3TnyBshBYNyXRqjzOmlwGT6CAtca3" />

Solution

  • This is the working code.

    public class OwinStartup
     {
         public void Configuration(IAppBuilder app)
         {
    
             app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
             app.UseKentorOwinCookieSaver();
    
             app.UseCookieAuthentication(new CookieAuthenticationOptions()
             {
                 CookieSameSite = Microsoft.Owin.SameSiteMode.None,
                 CookieSecure = CookieSecureOption.Always,
                 CookieHttpOnly = true,
                 CookieManager = new Microsoft.Owin.Host.SystemWeb.SystemWebCookieManager()
             });
    
             AntiForgeryConfig.UniqueClaimTypeIdentifier = "preferred_username"; //ClaimTypes.NameIdentifier;
             app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
             {
                 ClientId = ConfigurationManager.AppSettings["ClientId"],
                 Authority = ConfigurationManager.AppSettings["Authority"],
                 RedirectUri = ConfigurationManager.AppSettings["RedirectUri"],
                 ResponseType = OpenIdConnectResponseType.IdToken,//Code,//IdToken,
                 PostLogoutRedirectUri = ConfigurationManager.AppSettings["RedirectUri"],
                 Scope = "openid email profile offline_access",
                 TokenValidationParameters = new TokenValidationParameters
                 {
                     ValidateIssuer = true
                 },
                 Notifications = new OpenIdConnectAuthenticationNotifications
                 {
                     AuthenticationFailed = context =>
                     {
                         context.HandleResponse();
                         context.Response.Redirect("/Home/Error?message=" + context.Exception.Message);
                         return Task.FromResult(0);
                     }
                 }
             });    
         }
     }