我正在开发一个通过 IdentityServer 4 身份验证服务器登录的项目。我们有多个单独托管的客户端应用程序可以访问它。
我们从连接到身份验证服务器的数据库中提取允许的客户端,每个客户端都会将 ClientID/Secret 注入到应用程序中。
客户端身份验证配置
public static void Configure(this WebApplicationBuilder builder)
{
builder.SetEnvironment();
builder.ConfigureClientDetails();
ConfigureAuthServer(builder);
//...
var anonymousPages = new string[] { "/", "/index", "/about" };
var authPages = new string[] { "/welcome" };
app.AddAuthorizationControl(anonymousPages, authPages);
app.UseAuthorization();
//...
app.Run();
}
private static void ConfigureAuthServer(WebApplicationBuilder builder)
{
builder.Services.AddAccessTokenManagement();
string[] scopes = {
"odata-api",
"openid",
"name",
"role",
"profile",
"offline_access"
};
builder.Services.AddAuthClient(AppEnvironment.AuthUrl, AppClient.ClientName,
AppClient.ClientID, AppClient.ClientSecret, scopes);
}
身份验证服务适配器(Git 子模块连接)
public static void AddAuthClient(this IServiceCollection services, string authServerUrl,
string clientUrl, string clientId, string clientSecret, string[] scopes)
{
Configuration.CookieName = "Cookies";
Configuration.AuthorityUrl = authServerUrl;
Configuration.ClientUrl = clientUrl;
Configuration.ClientId = new Guid(clientId);
Configuration.ClientSecret = clientSecret;
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddHttpClient();
services.AddAuthentication(options =>
{
options.DefaultScheme = Configuration.CookieName;
options.DefaultChallengeScheme = AUTHENTICATION_SCHEME;
})
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
{
options.Authority = authServerUrl;
options.TokenValidationParameters.ValidateAudience = false;
options.TokenValidationParameters.ValidTypes = new[] { "at+jwt" };
})
.AddCookie(Configuration.CookieName, options =>
{
options.ExpireTimeSpan = TimeSpan.FromHours(1);
options.SlidingExpiration = true;
})
.AddOpenIdConnect(AUTHENTICATION_SCHEME, options =>
{
options.SignInScheme = Configuration.CookieName;
options.Authority = Configuration.AuthorityUrl;
options.RequireHttpsMetadata = false;
options.ClientId = clientId;
options.ClientSecret = clientSecret;
options.SaveTokens = true;
options.ClaimActions.MapAll();
options.GetClaimsFromUserInfoEndpoint = true;
options.ResponseType = "code";
options.ResponseMode = "query";
options.Scope.Clear();
foreach (var scope in scopes)
{
options.Scope.Add(scope);
}
options.ClaimActions.Add(new JsonKeyClaimAction("name", "name", "name"));
options.ClaimActions.Add(new JsonKeyClaimAction("role", "role", "role"));
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role"
};
options.Events = new OpenIdConnectEvents
{
// ... Standard events with await Task.Yield()
}
}
为了运行这个,我同时运行两个解决方案。
身份验证:https://localhost:5000
客户端:https://localhost:7300
当我单击客户端上的登录按钮时,它成功重定向到身份验证服务器登录。输入并提交凭据后,将成功颁发令牌并成功登录身份验证服务器。
但是,由于响应消息通过
RedeemAuthorizationCodeAsync()
发送到 Backchannel,返回 400,因此重定向到客户端主页失败,并在 Microsoft.AspNetCore.Authentication.OpenIdConnect OpenIdConnectHandler
内的 Backchannel.SendAsync
方法中引发异常错误请求,因此用户永远不会对客户端进行身份验证,但它会完全通过身份验证服务器,您可以在那里执行正常操作。
我们有远程发布的身份验证服务器,它在那里工作正常,但对于用于开发和测试的本地连接,它不起作用。我尝试过调整客户端配置,并尝试针对远程服务器,但同样的错误仍然存在。
通过断点和检查日志进一步研究这一点,结果发现错误是由于客户端密钥值的验证而引发的。