如何将IsActiveAsync的失败信息传递给用户和客户端?

4
在我的 IdentityServer4 项目中,我实现了 IProfileService。在使用 Implicit 和 Hybrid 流时,人类用户通过登录网页成功验证后,IsActiveAsync 方法会被调用多次。
我注意到当 context.Caller 是以下值之一时,它会被调用:
  • AuthorizeEndpoint(带有用户的声明)
  • AuthorizationCodeValidation(带有用户的声明)
  • AccessTokenValidation(不带用户的声明)
由于一个错误,我的代码设置了 context.IsActive = false。当这种情况发生时,用于访问登录页面的 Web 浏览器会被重定向回登录页面,而没有任何错误消息或原因信息。用户会困惑为什么他们成功验证但仍提示重新登录。也没有添加任何新的查询字符串参数。 IdentityServer4 日志会显示原因:

[23:16:40 Information] IdentityServer4.ResponseHandling.AuthorizeInteractionResponseGenerator Showing login: User is not active

现在,假设我的 IsActive = false 代码并不是一个错误,而实际上是按设计(例如,在不同 OAuth/OpenIDConnect HTTP 请求之间,用户帐户确实已被禁用),那么我该如何确保向用户和/或客户端软件呈现此消息?
1个回答

3

经过一些调查,我决定更新答案。

首先,context.IsActive用于指示是否应执行GetProfileDataAsync

“问题”在于客户端启动多个调用ProfileService以检索信息的单独调用。这些分离的调用由客户端启动,并在用户登录后进行。

对于直接响应,您可以在Login方法中添加代码。当用户不活动时,您可以用错误消息作出回应。我认为这是检查的最佳位置,因为它将防止很多操作。

以下片段取自IdentityServer的一个示例。它处理Login请求。

[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);
        },
    }
}

这可能是您正在寻找的内容。

但是,如果有一些情况导致在用户已经成功通过“登录”操作进行身份验证之后,“IsActive”指示为false呢? - Dai
@Dai 这个回答解决了你的问题吗?还是有什么遗漏的地方需要补充呢? - user4864425
感谢您查阅:我目前已转向另一个项目,实际上还没有开始解决这个问题,但在我回到那个项目之前,我会尝试您的方法并观察其效果再标记为已接受,但我仍会点赞您 :) - Dai

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接