【问题标题】:Sign out clients only in IdentityServer4仅在 IdentityServer4 中注销客户端
【发布时间】:2019-11-18 02:56:51
【问题描述】:

有没有办法使用 IdentityServer4 执行单点注销仅限客户端?使用 endsession 端点为客户端和 IdentityServer 执行单点注销没有问题。然而,我想做的是提供一种方法来强制所有客户端在下一次请求时重新进行身份验证(以获得新的声明),但不强制用户重新输入凭据。

用例是让用户编辑存储在声明中的个人资料信息。例如,他们的名字。当用户提交对此信息的更改时,需要让所有其他客户知道他们当前拥有的声明不再有效。这可以通过完全退出用户来实现,但是用户必须重新输入他们的 ID 和密码。

看流程,我想我要做的是直接执行内部EndSessionCallbackEndpoint,而不删除IdentityServer本身的身份验证cookie。

【问题讨论】:

  • 如果您查看快速入门,idsrv 主机只是一个 asp.net 应用程序,应该明确调用它的注销。您可以跳过该步骤,但继续注销视图。理论上应该有效。
  • 谢谢,@d_f,这就是我最终做的事情(见下文),但它对我来说只是“闻起来”不对劲。似乎应该有一种方法可以在对 endsession 端点的调用中表达这种缩进,而不是通过检测魔术 URL。
  • 而不是真正“闻起来”的“魔术网址”,因为它使用实体来实现其并非针对的目的,我会添加一个特殊的“魔术”参数。几乎一样,但气味少得多,恕我直言
  • 不过,我想这是我的问题:如何将该参数传递给 Logout 方法?据我所知,endsession 端点仅将注销 id 转发给 GET Logout 方法。
  • 好吧,目前EndSessionRequestValidator 不像TokenRequestValidatorValidatedEndSessionRequest 一样没有任何扩展点,所以唯一的替代方案就是使用state 参数而不重写所有内容。根据规范,它不假设任何验证,应按原样保留并发送回客户端。不知道气味会不会好很多,但可能有点:)

标签: identityserver4


【解决方案1】:

下面是一个有效的解决方案,但这真的感觉我在滥用系统。如果有人有提供预期功能的“标准”方式,请告诉我。

此解决方案将Logout 方法更改为“嗅探”魔术PostLogoutRedirectUri 并跳过删除本地身份验证cookie。显然不是特别可扩展或优雅。

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Logout(LogoutInputModel model)
{
    // build a model so the logged out page knows what to display
    var vm = await BuildLoggedOutViewModelAsync(model.LogoutId);

    // Special post-logout URL that should only log out clients, but keep the local authentication cookie.
    bool clientsOnly = !string.IsNullOrEmpty(vm.PostLogoutRedirectUri) && vm.PostLogoutRedirectUri.StartsWith("https://example.com/EditProfile");

    if (User?.Identity.IsAuthenticated == true && !clientsOnly)
    {
        // delete local authentication cookie
        await _signInManager.SignOutAsync();

        // raise the logout event
        await _events.RaiseAsync(new UserLogoutSuccessEvent(User.GetSubjectId(), User.GetDisplayName()));
    }

    // check if we need to trigger sign-out at an upstream identity provider
    if (vm.TriggerExternalSignout)
    {
        // build a return URL so the upstream provider will redirect back to us after the
        // user has logged out. this allows us to then complete our single sign-out processing.
        string url = Url.Action("Logout", new { logoutId = vm.LogoutId });

        // this triggers a redirect to the external provider for sign-out
        return SignOut(new AuthenticationProperties { RedirectUri = url }, vm.ExternalAuthenticationScheme);
    }

    return View("LoggedOut", vm);
}

【讨论】:

  • 请注意PostLogoutRedirectUri为空,以防客户端不提供id_token。
【解决方案2】:

这相当简单,并通过 IDS4 中的公共 API (IIdentityServerInteractionService) 公开。这是一个非常简化的步骤示例。这包括来自和反向通道的注销,因为两者都是由对SignOutIFrameUrl 的请求触发的。

首先,我们需要创建一个注销上下文并重定向到将执行注销的操作:

var signoutId = await _identityInteractionService.CreateLogoutContextAsync();
return RedirectToAction(nameof(ClientSignout), new { signoutId });

ClientSignout 操作:

var context = await _identityInteractionService.GetLogoutContextAsync(signoutId);
ViewBag.SignOutIframeUrl = context.SignOutIFrameUrl;
return View();

ClientSignout.cshtml:

<iframe src="@ViewBag.SignOutIframeUrl" id="SignOutIframe" frameborder="0" scrolling="no" style="display:none" />
//TODO: Detect client-side once iframe has loaded and then maybe redirect somewhere else?

这里的代码根本不涉及当前的身份验证会话,但另一种方法是强制刷新当前会话 ID,从而在任何具有会话监控的客户端中触发刷新。

ETA:我想得越多,我认为客户端会话监控方法是正确的。客户可以控制接下来要做什么。另一种选择是客户端定期调用 userinfo 端点以使用提供的访问令牌获取更新的声明。我们实际上使用了所有 3 种机制来获得良好的效果。

2020 年 10 月: 除此之外 - 我认为这些天我更倾向于使用 OIDC 反向通道规范:https://openid.net/specs/openid-connect-backchannel-1_0.html

我最近做了一个 PoC,后端代码可以为给定的一组用户和客户端结束客户端会话,而完全不涉及用户的浏览器。如果您想因政策更改或 IDP 中的“在所有地方退出”功能而强制重新进行身份验证,此功能非常强大。

但请注意,标准 identityserver4 代码需要调整才能使其正常工作,因为目前假定它只会用作正常注销流程的一部分。

【讨论】:

  • 这只是从原始快速入门中复制粘贴。 OP 在不复制和粘贴的情况下表现得更加优雅。当EndSessionRequestValidator 像其他请求验证器一样具有可扩展点时,他的解决方案可能被称为“完美”。
  • @d_f 我不确定您要表达什么观点。这是否没有解决如何仅显式退出客户端的原始问题?如果 OP 想要将其构建到保存表单的流程中,那么上面的内容可以很容易地适应它,而用户甚至都不知道。我仍然认为会话监控规范是一种更合适的处理方式。如果您执行 SignInAsync 并传递一个 AuthenticationProperties 对象并删除了session_id,那么 IDS4 将自动生成一个新对象并通知所有客户端刷新。
  • 我同意,会话监控方法会很好用。但这一点点改变了整个概念。如果我们保持原来的概念,OP的解决方案是可靠和简单的。
  • 我同意它会起作用,但显示的代码示例仅显示了如何不在本地注销,而不是现在实际执行客户端注销。即魔法正在“LoggedOut”视图中发生。
猜你喜欢
  • 2021-07-30
  • 2017-09-01
  • 1970-01-01
  • 2020-08-06
  • 1970-01-01
  • 2018-08-13
  • 1970-01-01
  • 1970-01-01
  • 2019-06-06
相关资源
最近更新 更多