我正在开发一个身份验证服务器来提供令牌,用于使用 IdentityServer4 来使用我们的API。
我使用MongoDB作为数据库,我允许用户获取令牌,为了更安全,我使用自定义证书来加密令牌。
这就是我AuthenticationServer
的 Startup.cs 的样子:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
var cert = new X509Certificate2(Path.Combine(_environment.ContentRootPath, "cert", "whatever.pfx"), "whatever");
services.AddIdentityServer().AddSigningCredential(cert)
.AddInMemoryApiResources(Config.Config.GetApiResources());
services.AddTransient<IUserRepository, UserRepository>();
services.AddTransient<IClientStore, ClientStore>();
services.AddTransient<IProfileService, UserProfileService>();
services.AddTransient<IResourceOwnerPasswordValidator, UserResourceOwnerPasswordValidator>();
services.AddTransient<IPasswordHasher<User.Model.User>, PasswordHasher<User.Model.User>>();
}
如您所见,我可以自定义实现客户端身份验证和密码验证的接口。这很好。
然后我用生成的令牌保护另一个应用程序,我在那里定义它必须使用IdentityServerAuthetication
(localhost:5020是我的AuthenticationServer
所在的位置运行)
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseCors("CorsPolicy");
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = "http://localhost:5020",
RequireHttpsMetadata = false,
ApiName = "MyAPI",
RoleClaimType = JwtClaimTypes.Role
});
app.UseMvc();
}
一切正常,但如果我关闭了AuthenticationServer
,那么我从我正在保护的API中收到此错误:
System.InvalidOperationException:IDX10803:无法从:&#39; http://localhost:5020/.well-known/openid-configuration&#39;获取配置。 在Microsoft.IdentityModel.Protocols.ConfigurationManager`1.d__24.MoveNext() ---从抛出异常的先前位置开始的堆栈跟踪结束--- 在System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务) 在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务) 在Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.d__1.MoveNext() 失败:Microsoft.AspNetCore.Server.Kestrel [13]
所以看起来API似乎是去发现端点看看解密令牌的端点在哪里(我猜它会是userinfo_endpoint
)。
我的观点是:
也许我错过了完整的画面,我说的是愚蠢的事情,但我很乐意理解背后的概念。 提前致谢
答案 0 :(得分:1)
可以让发现端点关闭/不可用并仍然验证令牌。
您需要实现IConfigurationManager并将其传递给IdentityServerAuthenticationOptions中的JwtBearerOptions对象。
以下是一些示例代码:
public class OidcConfigurationManager : IConfigurationManager<OpenIdConnectConfiguration>
{
public OidcConfigurationManager()
{
SetConfiguration();
}
private OpenIdConnectConfiguration _config;
public Task<OpenIdConnectConfiguration> GetConfigurationAsync(CancellationToken cancel)
{
return Task.FromResult<OpenIdConnectConfiguration>(_config);
}
public void RequestRefresh()
{
}
private void SetConfiguration()
{
// Build config from JSON
var configJson =
@"{""issuer"":""http://localhost/id"",""jwks_uri"":""http://localhost/id/.well-known/openid-configuration/jwks"",""authorization_endpoint"":""http://localhost/id/connect/authorize"",""token_endpoint"":""http://localhost/id/connect/token"",""userinfo_endpoint"":""http://localhost/id/connect/userinfo"",""end_session_endpoint"":""http://localhost/id/connect/endsession"",""check_session_iframe"":""http://localhost/id/connect/checksession"",""revocation_endpoint"":""http://localhost/id/connect/revocation"",""introspection_endpoint"":""http://localhost/id/connect/introspect"",""frontchannel_logout_supported"":true,""frontchannel_logout_session_supported"":true,""scopes_supported"":[""openid"",""profile"",""api1"",""offline_access""],""claims_supported"":[""sub"",""name"",""family_name"",""given_name"",""middle_name"",""nickname"",""preferred_username"",""profile"",""picture"",""website"",""gender"",""birthdate"",""zoneinfo"",""locale"",""updated_at""],""grant_types_supported"":[""authorization_code"",""client_credentials"",""refresh_token"",""implicit"",""password""],""response_types_supported"":[""code"",""token"",""id_token"",""id_token token"",""code id_token"",""code token"",""code id_token token""],""response_modes_supported"":[""form_post"",""query"",""fragment""],""token_endpoint_auth_methods_supported"":[""client_secret_basic"",""client_secret_post""],""subject_types_supported"":[""public""],""id_token_signing_alg_values_supported"":[""RS256""],""code_challenge_methods_supported"":[""plain"",""S256""]}";
_config = new OpenIdConnectConfiguration(configJson);
// Add signing keys if not present in json above
_config.SigningKeys.Add(new X509SecurityKey(cert));
}
}
现在将该配置对象传递给IdentityServerAuthenticationOptions中的一些JwtBearerOptions(有点烦人,但这是我所知道的唯一方式)
var identityServerOptions = new IdentityServerAuthenticationOptions
{
Authority = "http://localhost:5020",
RequireHttpsMetadata = false,
ApiName = "MyAPI",
RoleClaimType = JwtClaimTypes.Role,
};
var jwtBearerOptions = new JwtBearerOptions() {ConfigurationManager = new OidcConfigurationManager()};
var combinedOptions = CombinedAuthenticationOptions.FromIdentityServerAuthenticationOptions(identityServerOptions);
combinedOptions.JwtBearerOptions = jwtBearerOptions;
app.UseIdentityServerAuthentication(combinedOptions);
现在,即使OIDC发现端点关闭,您的API也能够接收令牌并验证签名。
答案 1 :(得分:1)
API验证中间件在启动时下载发现文档的副本 - 然后(至少在默认情况下)每24小时下载一次。
如果签名验证失败,可能会重新触发下载(以适应计划外密钥翻转)。
您可以静态定义所有配置值 - 但是您将失去动态配置更新的所有好处。
如果您的发现端点不可用,那么整个令牌服务可能无法正常运行,这可能是一个更大的问题。
答案 2 :(得分:0)
IdentityServer需要X509证书的公钥来验证access_token
。它正在使用发现端点来获取该公钥,并且不时刷新已保存的公钥(因为公钥可能会更改)。
如果无法访问IdentityServer,则您的API无法保证access_token
有效。您可以增加access_token
验证请求结果的缓存。
我对IdentityServer4.AccessTokenValidation并不完全确定,但是使用IdentityServer3.AccessTokenValidation,您可以将ValidationMode
设置为Local
,因此只需下载一次公钥即可。