在我的IdentityServer4
项目中,我实现了IProfileService
。在使用隐式和混合流时,人类用户已成功通过登录网页进行身份验证之后,多次调用IsActiveAsync
方法。
我注意到context.Caller
是以下值之一时被调用:
AuthorizeEndpoint
(带有用户的声明)AuthorizationCodeValidation
(带有用户的声明)AccessTokenValidation
(无用户要求)由于一个错误,我的代码集context.IsActive = false
-发生这种情况时,用于访问登录页面的Web浏览器仅重定向回登录页面,没有错误消息或原因信息。用户会感到困惑,为什么他们已成功通过身份验证,但提示再次登录。也没有添加新的查询字符串参数。
IdentityServer4
日志确实显示了原因:
[23:16:40信息]
IdentityServer4.ResponseHandling.AuthorizeInteractionResponseGenerator
显示登录信息:用户未激活
现在,假设我的IsActive = false
代码不是错误,而是实际上是设计错误(例如,由于用户的帐户实际上在不同的OAuth / OpenIDConnect HTTP请求之间的微秒内被禁用),在哪种情况下我如何确保此消息显示给用户和/或客户端软件?
答案 0 :(得分:1)
经过一番调查,我决定更新答案。
首先,context.IsActive
用于指示是否应执行GetProfileDataAsync
。
“问题”是客户端开始多次调用ProfileService来检索信息。这些单独的调用由客户端发起,并在用户登录后跟随。
对于直接响应,您可以将代码添加到Login方法中。当用户不活跃时,您可以使用错误消息进行响应。我认为这是检查的最佳位置,因为它将阻止很多操作。
以下片段摘自IdentityServer的示例之一。它处理登录请求。
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginInputModel model, string button)
{
if (ModelState.IsValid)
{
var result = await _signInManager.PasswordSignInAsync(model.Username, model.Password, model.RememberLogin, lockoutOnFailure: true);
if (result.Succeeded)
{
var user = await _userManager.FindByNameAsync(model.Username);
// Assume user has property IsActive for this example.
// You can implement this anyway you like.
if (user.IsActive)
{
...
}
}
ModelState.AddModelError("", AccountOptions.InvalidCredentialsErrorMessage);
}
// something went wrong, show form with error
var vm = await BuildLoginViewModelAsync(model);
return View(vm);
}
您可以使用ModelState.AddModelError
添加任何您喜欢的消息。
现在假设用户登录并在以后被停用,则ProfileService将不会返回声明。相反,它将导致出现401:用户未激活。
然后,客户端将引发异常,因为请求必须成功:HttpResponseMessage.EnsureSuccessStatusCode
。
幸运的是,有一个处理异常的选项。那就是实现oidc事件。这只是一个简单的例子:
services.AddOpenIdConnect("oidc", "Open Id connect", options =>
{
options.Events = new OpenIdConnectEvents()
{
// When a user is not active this will result in a 401
OnAuthenticationFailed = (context) =>
{
// Clear the exception, otherwise it is re-thrown after this event.
context.HandleResponse();
// Handle the exception, e.g. redirect to an error page.
context.Response.Redirect($"LoginError/?message={context.Exception.Message}");
return Task.FromResult(0);
},
}
}
这可能是您想要的。