oauth2.0 ASP.NET Core 6 Web API授权在访问令牌中缺少签名密钥

ubof19bj  于 2023-10-15  发布在  .NET
关注(0)|答案(1)|浏览(161)

如何配置具有OAuth2授权的ASP.NET Core 6 Web API以使用openid系统的jwks端点来验证访问令牌?
当我“手动”验证声明时,我的API可以很好地与oauth2一起工作,但失败了http 401未经授权的响应头WwwAuthenticate:
Bearer error=“invalid_token”,error_description=“未找到签名密钥”
我使用Microsoft.AspNetCore.Authentication.JwtBearer v6.0.20。我认为问题在于ID令牌有一个签名密钥ID,但在Access令牌上为null:

根据拥有openid的团队(我在公司环境中),系统符合“pingfederate”,并且通过设计签名密钥不存在。
他们建议我使用jwks端点来验证访问令牌,但我不知道如何在.NET中配置它
我的程序.main -为简洁起见被截断:

var builder = WebApplication.CreateBuilder(args);
var config = builder.Configuration;

...
builder.Services.AddSwaggerGen(opt =>
{
    opt.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
    {
        Type = SecuritySchemeType.OAuth2,
        Flows = new OpenApiOAuthFlows
        {
            AuthorizationCode = new OpenApiOAuthFlow
            {
                AuthorizationUrl = new Uri($"{config.GetValue<string>("authority")}/authorization"),
                TokenUrl = new Uri($"{config.GetValue<string>("authority")}/token")
            }
        }
    });
    opt.AddSecurityRequirement(new OpenApiSecurityRequirement
    {
        {
            new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference
                {
                    Id = "oauth2",
                    Type = ReferenceType.SecurityScheme
                }
            }, new string[]{ }
        }
    });
});

builder.Services.AddAuthentication(authOpt =>
{
    authOpt.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
    .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, opt =>
    {
        opt.Authority = config.GetValue<string>("authority");
        opt.MetadataAddress = $"{config.GetValue<string>("authority")}/.well-known/openid-configuration";
        opt.Audience = "myapp";
    });
    
builder.Services.AddAuthorization(auth =>
{
    auth.AddPolicy("canread", pol => pol.RequireClaim("scope", "read:weather"));
});

var app = builder.Build();

app.UseSwagger();
// configure swagger ui auth option
app.UseSwaggerUI(opt =>
{
    opt.OAuthAppName("my app");
    opt.OAuthClientId(config.GetValue<string>("clientid"));
    opt.OAuthClientSecret(config.GetValue<string>("clientsecret"));
    opt.OAuthAdditionalQueryStringParams(new Dictionary<string, string> { { "audience", "myapp" } });
    opt.OAuthScopeSeparator(" ");
    opt.OAuthUsePkce();
});

...
// custom middleware to inspect access token is passed
app.UseMiddleware<MessageInspector>();
app.UseAuthentication();
app.UseAuthorization();

...

如果我使用AuthorizeAttribute,它会因为签名密钥丢失错误而失败,但是如果我“手动”验证声明,它会正常工作:

//[Authorize]
//[Authorize("canread", AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[HttpGet(Name = "GetWeatherForecast")]
public ActionResult<IEnumerable<WeatherForecast>> Get()
{
    var tokenStr = HttpContext.Request.Headers.Authorization.FirstOrDefault();
    var handler = new JwtSecurityTokenHandler();
    // auth token is in form "Bearer abcdefg1234567890..."
    var jwt = handler.ReadJwtToken(tokenStr.Remove(0, 7));

    if (!jwt.Claims.Where(x => x.Type == "scope" && x.Value == "read:weather").Any()) { return StatusCode(401); }

    return Ok(Enumerable.Range(1, 5).Select(index => new WeatherForecast
    {
        Date = DateTime.Now.AddDays(index),
        TemperatureC = Random.Shared.Next(-20, 55),
        Summary = Summaries[Random.Shared.Next(Summaries.Length)]
    })
    .ToArray());
}

我可以看到访问令牌是以“承载xxx”格式传递的

hjqgdpho

hjqgdpho1#

我遵循这个solution并使用IdentityModel.AspNetCore.OAuth2Introspection pkg
唯一的区别是我不能使用AddJwtBearer,因为它给了我一个InvalidOperationException
方案已存在:承载
然后使用AddOAuth2Introspection,一切都像魔法一样工作:

.AddOAuth2Introspection(OAuth2IntrospectionDefaults.AuthenticationScheme, opt =>
{
    opt.Authority = "***";
    opt.ClientId = "***";
    opt.ClientSecret = "***";
});

相关问题