如何配置ASP.NET WebApi以针对OpenID Connect服务器验证承载令牌



我正在编写一个从另一个服务接收POST的服务,该服务包括一个包含承载令牌的Authorization标头。这个令牌是从OpenIDConnect服务器独立获得的(Keycloft在我们的开发环境中,但不一定在生产环境中(。我们的服务不需要获得或发行代币;它只需要能够验证它们。

我们使用的是.NET Framework 4.8和自托管的ASP.NET WebApi(OWIN 4等(。

在配置方面,我们拥有的信息是:

  • OpenID Connect服务的URL,例如'http://keycloak:8080/auth/realms/demo/'
  • 客户端ID,例如"js-client">

我们的目的是从OpenID服务器的元数据端点动态获取颁发者公钥http://keycloak:8080/auth/realms/demo/.well-已知/openid配置'。目前我有这样的东西:

WebApp.Start(startOptions, builder => {
var config = ...
// ... Set up routes etc ...
config.Filters.Add(new HostAuthenticationFilter("Bearer"));
builder.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = "js-client",
Authority = "http://keycloak:8080/auth/realms/demo/",
RequireHttpsMetadata = false,
SignInAsAuthenticationType = "Bearer",
});
builder.UseWebApi(config);
}));

控制器动作看起来像:

[HttpGet]
[HttpPost]
[Authorize]
public IHttpActionResult Receive([FromBody] string dto) => Ok();

目前,它总是返回401 Unauthorized,并显示消息"为此授权已被拒绝"request’,而不管令牌的有效性如何。

Wireshark透露,我们的服务从未试图联系Keycloft服务器获取OIDC元数据,所以我猜授权处理程序甚至没有找到令牌。

我也看过UseJwtBearerAuthentication和UseOAuthAuthorizationServer,但它们似乎想要更多的信息,而不仅仅是OIDC端点(这并不奇怪(,或者它们需要自定义的提供程序实现。

这似乎不是一个不寻常的用例,我需要实现自己的验证器,所以我可能遗漏了什么?谷歌搜索中出现了数百个例子,这些例子似乎只与ASP.NET核心有关,或者不包括非交互式用例。

我通过检查OpenIdConnectAuthenticationMiddleware的源代码,在这方面取得了进展。

JwtBearer中间件处理颁发者的验证,但需要知道公钥。由于我需要避免直接配置,我需要向OIDC服务器请求它

这可以使用ConfigurationManager来完成,它应该为我们处理缓存等:

private JwtBearerAuthenticationOptions GetJwtBearerTokenAuthenticationOptions(string issuer, IConfigurationManager<OpenIdConnectConfiguration> configurationManager)
{
return new JwtBearerAuthenticationOptions
{
Realm = "demo",
TokenValidationParameters = new TokenValidationParameters
{
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
// ... etc ...
IssuerSigningKeyResolver = (token, securitytoken, kid, validationparameters) =>
configurationManager.GetConfigurationAsync(CancellationToken.None).GetAwaiter().GetResult().SigningKeys,
ValidIssuer = issuer.TrimEnd('/'),
}
};
}

(不幸的是,解析程序委托不能是异步的,所以我不能正确等待。(

ConfigurationManager可以这样构建(基于OpenIdConnectAuthenticationMiddleware的内部(:

private IConfigurationManager<OpenIdConnectConfiguration> GetOIDCConfigurationManager(string issuer)
{
var httpClient = new HttpClient(new WebRequestHandler());
httpClient.DefaultRequestHeaders.UserAgent.ParseAdd("Demo OpenIdConnect middleware");
httpClient.Timeout = TimeSpan.FromMinutes(1);
httpClient.MaxResponseContentBufferSize = 10485760L;
var httpRetriever = new HttpDocumentRetriever(httpClient) { RequireHttps = false };
return new ConfigurationManager<OpenIdConnectConfiguration>($"{issuer}.well-known/openid-configuration", new OpenIdConnectConfigurationRetriever(), httpRetriever);
}

这些可以按如下方式使用:

const string issuer = "http://keycloak:8080/auth/realms/demo/";
var configurationManager = GetOIDCConfigurationManager(issuer);
builder.UseJwtBearerAuthentication(GetJwtBearerTokenAuthenticationOptions(issuer, configurationManager));

这一切似乎都奏效了,尽管我很想知道是否有更简单的方法。。。?

显然,任何在生产中使用它的人都应该使用RequireHttps = true

最新更新