ASP.NET Core 2 Jwt Auth with Azure B2C // IDX10500: 签名验证失败。没有提供安全密钥来验证签名
ASP.NET Core 2 Jwt Auth with Azure B2C // IDX10500: Signature validation failed. No security keys were provided to validate the signature
我遇到了 Jwt 身份验证问题。
我有一个 ASP.NET Core 2 WepApi,它也服务于我的 SPA 应用程序(它是一个 Vue-App)SPA 应用程序通过 Microsoft 的 MSAL.js 库从 Azure B2C 获取令牌。
当我点击需要授权的 WebApi 时,出现以下错误:
info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler[1]
Failed to validate the token [MyTokenHere]
Microsoft.IdentityModel.Tokens.SecurityTokenInvalidSignatureException: IDX10500: Signature validation failed. No security keys were provided to validate the signature.
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(String token, TokenValidationParameters validationParameters)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(String token, TokenValidationParameters validationParameters, SecurityToken& validatedToken)
at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.<HandleAuthenticateAsync>d__6.MoveNext()
info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler[7]
Bearer was not authenticated. Failure message: IDX10500: Signature validation failed. No security keys were provided to validate the signature.
在浏览器中我收到 401
GET http://localhost:51420/api/values 401 (Unauthorized)
我在此处提供的示例应用程序中遇到了同样的问题An ASP.NET Core web API with Azure AD B2C(使用他们的 tanant)
这是我的 Startup.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.SpaServices.Webpack;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using VueTemplate.SignalR;
namespace VueTemplate
{
public class Startup
{
public string Authority { get; set; } = "https://login.microsoftonline.com/tfp/[MyB2CTenant]/[MyPolicy]/v2.0/";
public string ClientId { get; set; } = [MyApplicationId];
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddSignalR();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => new JwtBearerOptions() {
Authority = Authority,
Audience = ClientId,
Events = new JwtBearerEvents() { OnAuthenticationFailed = AuthenticationFailed, }
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions() {
HotModuleReplacement = true
});
}
else {
app.UseExceptionHandler("/Home/Error"); // TODO Create Error page
}
app.UseAuthentication();
app.UseStaticFiles();
app.UseSignalR(routes => {
routes.MapHub<ChatHub>("Hub/Chat");
});
app.UseMvc(routes => {
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}"
);
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" }
);
});
}
private Task AuthenticationFailed(AuthenticationFailedContext arg)
{
// For debugging purposes only!
var s = $"AuthenticationFailed: {arg.Exception.Message}";
arg.Response.ContentLength = s.Length;
arg.Response.Body.Write(Encoding.UTF8.GetBytes(s), 0, s.Length);
return Task.FromResult(0);
}
}
}
ValuesController.cs
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace VueTemplate.Controllers
{
[Authorize]
[Route("api/[controller]/")]
public class ValuesController : Controller
{
[HttpGet]
public IActionResult Get() {
return Ok(new int[] { 1, 2, 3, 4 });
}
}
}
有什么想法吗?我必须提供安全密钥吗?我在 Azure B2C 的什么地方可以找到它?
我找到了解决问题的方法。
配置AddJwtBearer()
方法的正确方法是使用已经提供的选项对象,而不是创建一个新的。
差:
.AddJwtBearer(option => new JwtBearerOptions // <--- Evil
{
Authority = string.Format("https://login.microsoftonline.com/tfp/{0}/{1}/v2.0/",
Configuration["Authentication:AzureAd:Tenant"], Configuration["Authentication:AzureAd:Policy"]),
Audience = Configuration["Authentication:AzureAd:ClientId"],
Events = new JwtBearerEvents
{
OnAuthenticationFailed = AuthenticationFailed
},
});
好:
.AddJwtBearer(options => {
options.Authority = string.Format("https://login.microsoftonline.com/tfp/{0}/{1}/v2.0/", Configuration["Authentication:AzureAd:Tenant"], Configuration["Authentication:AzureAd:Policy"]);
options.Audience = Configuration["Authentication:AzureAd:ClientId"];
options.Events = new JwtBearerEvents {
OnAuthenticationFailed = AuthenticationFailed
};
});
我遇到了 Jwt 身份验证问题。
我有一个 ASP.NET Core 2 WepApi,它也服务于我的 SPA 应用程序(它是一个 Vue-App)SPA 应用程序通过 Microsoft 的 MSAL.js 库从 Azure B2C 获取令牌。
当我点击需要授权的 WebApi 时,出现以下错误:
info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler[1]
Failed to validate the token [MyTokenHere]
Microsoft.IdentityModel.Tokens.SecurityTokenInvalidSignatureException: IDX10500: Signature validation failed. No security keys were provided to validate the signature.
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(String token, TokenValidationParameters validationParameters)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateToken(String token, TokenValidationParameters validationParameters, SecurityToken& validatedToken)
at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.<HandleAuthenticateAsync>d__6.MoveNext()
info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler[7]
Bearer was not authenticated. Failure message: IDX10500: Signature validation failed. No security keys were provided to validate the signature.
在浏览器中我收到 401
GET http://localhost:51420/api/values 401 (Unauthorized)
我在此处提供的示例应用程序中遇到了同样的问题An ASP.NET Core web API with Azure AD B2C(使用他们的 tanant)
这是我的 Startup.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.SpaServices.Webpack;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using VueTemplate.SignalR;
namespace VueTemplate
{
public class Startup
{
public string Authority { get; set; } = "https://login.microsoftonline.com/tfp/[MyB2CTenant]/[MyPolicy]/v2.0/";
public string ClientId { get; set; } = [MyApplicationId];
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddSignalR();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => new JwtBearerOptions() {
Authority = Authority,
Audience = ClientId,
Events = new JwtBearerEvents() { OnAuthenticationFailed = AuthenticationFailed, }
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseWebpackDevMiddleware(new WebpackDevMiddlewareOptions() {
HotModuleReplacement = true
});
}
else {
app.UseExceptionHandler("/Home/Error"); // TODO Create Error page
}
app.UseAuthentication();
app.UseStaticFiles();
app.UseSignalR(routes => {
routes.MapHub<ChatHub>("Hub/Chat");
});
app.UseMvc(routes => {
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}"
);
routes.MapSpaFallbackRoute(
name: "spa-fallback",
defaults: new { controller = "Home", action = "Index" }
);
});
}
private Task AuthenticationFailed(AuthenticationFailedContext arg)
{
// For debugging purposes only!
var s = $"AuthenticationFailed: {arg.Exception.Message}";
arg.Response.ContentLength = s.Length;
arg.Response.Body.Write(Encoding.UTF8.GetBytes(s), 0, s.Length);
return Task.FromResult(0);
}
}
}
ValuesController.cs
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace VueTemplate.Controllers
{
[Authorize]
[Route("api/[controller]/")]
public class ValuesController : Controller
{
[HttpGet]
public IActionResult Get() {
return Ok(new int[] { 1, 2, 3, 4 });
}
}
}
有什么想法吗?我必须提供安全密钥吗?我在 Azure B2C 的什么地方可以找到它?
我找到了解决问题的方法。
配置AddJwtBearer()
方法的正确方法是使用已经提供的选项对象,而不是创建一个新的。
差:
.AddJwtBearer(option => new JwtBearerOptions // <--- Evil
{
Authority = string.Format("https://login.microsoftonline.com/tfp/{0}/{1}/v2.0/",
Configuration["Authentication:AzureAd:Tenant"], Configuration["Authentication:AzureAd:Policy"]),
Audience = Configuration["Authentication:AzureAd:ClientId"],
Events = new JwtBearerEvents
{
OnAuthenticationFailed = AuthenticationFailed
},
});
好:
.AddJwtBearer(options => {
options.Authority = string.Format("https://login.microsoftonline.com/tfp/{0}/{1}/v2.0/", Configuration["Authentication:AzureAd:Tenant"], Configuration["Authentication:AzureAd:Policy"]);
options.Audience = Configuration["Authentication:AzureAd:ClientId"];
options.Events = new JwtBearerEvents {
OnAuthenticationFailed = AuthenticationFailed
};
});