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:
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" />
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);
}
}
});
}
}