【问题标题】:Can't sign out from Identity Service using endsession API无法使用 endsession API 从身份服务中注销
【发布时间】:2021-10-23 14:48:16
【问题描述】:

我正在执行一个 GET 到

GET https://localhost:44301/connect/endsession?id_token_hint=eyJhbGciO...GzHCPw

the docs for EndSession endpoint 建议的那样。

它似乎(在某种程度上)有效,因为我在重定向到的方法中的断点上遇到了问题。

[HttpGet("logout")]
public async Task<IActionResult> LogOut(
    [FromQuery] string id_token_hint,
    [FromQuery] string post_logout_redirect_uri,
    [FromQuery] string session,
    [FromQuery] string logoutId)
{ 
  LogoutRequest context = await InteractionService
    .GetLogoutContextAsync(logoutId);
  ...
}

在这里,我在logoutId 中获得了一个值(除非我跳过传递身份令牌,导致我null),而其他变量未设置,保持为null。起初,我很高兴看到 context 不是 null。然而,我很快就知道它的设置很糟糕,尽管关注了stuff that work

我可以看到客户的姓名和 ID(这似乎是正确的)。但是,除了数组Parameters 之外,其他所有内容都是null,它包含零个元素。

我确保传入 identity 令牌,而不是 access 令牌。我还尝试了带有所有参数 described in the docs 的完整版本(尝试在我的配置和其他配置中提到的各种重定向 URL)。然而,同样的(错误)行为随之而来。

获取 https://localhost:44301/connect/endsession
?id_token_hint=eyJhbGciO...GzHCPw
&post_logout_redirect_uri=https://get_the_duck.off
&session=1337

由于我得到了突破性的打击并获得了交互服务可解析的logoutId 的一些价值,我觉得它已正确连接(这是预期的,因为这样的安全性按预期工作)。然而,我的应用程序似乎是一个跟踪者,只是不会让他们离开,可以这么说。我怀疑,文档没有提到一些微小的细节(或者在我不理解的公式中模糊了)。 (谷歌搜索没有给出任何我认为相关的内容。)

努力证明(包括一堆关于安全性的博客,并非专门针对注销)。

【问题讨论】:

  • 您是将用户的浏览器重定向到该 URL 还是以其他方式调用它?
  • @mackie 用户的浏览器对问题中的端点执行 GET 请求,提供 ID 令牌。然后,IDS4 通过反向通道将调用路由到我的安全控制器(仍然使用 IDP)的方法,将 logoutId 传递给我,我在交互服务中将其用于 GetLogoutContextAsync(logoutId)。这样,我获得了一个注销上下文。但是,该上下文似乎缺少数据(客户端的 ID 除外,这是正确的)。
  • @mackie 我试过SingOutAsync(scheme),其中方案是一堆不同的选项:Identity.Applicationidsrvidsrv.exernal 和其他一些选项。但是,令牌可以重新用于访问,并且用户似乎没有退出。如果注销上下文中没有任何引用用户 GUID 的内容,那么哪种方法有意义。
  • 当您说“令牌”时,您是指颁发给客户端应用程序的访问令牌吗?退出不会撤销任何访问令牌,因为它们本质上不能被撤销。如果您创建逻辑来执行此操作或显式使用客户端应用程序中的撤销端点,则可以清除刷新令牌/引用令牌。如果您指的是 auth cookie,则删除不受后端支持的 cookie 不会使其值无效,因此如果有人设法复制它,它仍然可以重复使用。
  • @mackie 抱歉不准确。我的意思是 SignOutAsync(...) 执行之后,两个 cookie(名为 idsrv.session.AspNetCore.Identity:application)还在那里。当我调用 SignInAsync(...) 时会出现这些,并且我希望当我注销用户时它们会消失。浏览器中的令牌会被 Angular 应用程序删除,但是当它调用 /connect/authorize 时,会在不传递任何凭据的情况下发出一个新令牌。如果我手动删除 cookie,则会在发出任何令牌之前请求凭据。我断定用户没有退出。

标签: c# asp.net-identity identityserver4 asp.net-core-3.1


【解决方案1】:

为什么需要调用那个端点?涉及许多 cookie/会话,最简单的方法是在 ASP.NET Core 中使用:

[HttpPost]
[ValidateAntiForgeryToken]
public async Task Logout()
{
    await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
    await HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme);

    //Important, this method should never return anything.
}

这通常适用于我从我的 ASP.NET Core 客户端进行完全注销。

此外,最好只接受使用 HTTP POST 而不是 GET 的注销。

【讨论】:

  • 我不完全确定我是否做对了。我会回答这个问题,但在我看来这没有任何意义。为什么我调用 /connect/endsession?因为文档是这样说的。为什么要获取?一样的答案。为什么我的LogOut(...) 方法上有GET?因为这是 Identity Server 调用的,所以他们不会请求 POST。为什么要经历它?因为除非我在该方法中手动注销,否则 cookie 仍然存在。为什么不直接删除 cookie?我需要记录有关操作 的信息,当我指定架构时,它现在崩溃给我 500。 cookie 没有被删除。
  • 客户端中的 OpenIDConnect 和 Cookie 身份验证处理程序通常会为您处理所有这些。请查看 fiddler 之类的工具,查看是什么请求创建了 500 错误,并在日志中查看错误包含的内容。您是否设置了 LogoutPath 和 SignedOutRedirectUri 参数?你的创业班是什么样的?
  • 嗯,注销路径设置为我在问题中显示的端点。这就是 Identity Server 将客户端应用程序向 /connect/endsession 发出的请求反向引导的方式。我觉得我们在这里谈论的是不同的设置。我正在尝试关注我在问题中链接到的那个。也许对我们来说不一样,因为我们确实有 SPA 拨打电话?
  • 你知道 LogOutPath 的真正用途吗?它不是你想的那样。看到这个问题stackoverflow.com/questions/52709492/…
  • 我正在查看该答案(以及其他一些答案)。令我印象深刻的是,我有AddAuthentication(),然后是.AddIdentityServerAuthentication("default",...),而其他示例有AddCookie(...) 和/或AddOpenIdConnect(...)。我已经尝试了您建议的方法(包括一堆其他可用的方案)。没有任何东西会删除设置的会话 cookie。
【解决方案2】:

如果您查看官方quickstart,您会看到他们建议将初始Get 组合在一起,然后是Post,中间有一个可选提示。而最终的Post 方法implementation 通过调用

await HttpContext.SignOutAsync();

【讨论】:

  • 在链接的示例中,有一部分基于方法BuildLoggedOutViewModelAsync(string logoutId) 中的上下文创建模型。在那里,有一段await _interaction.GetLogoutContextAsync(logoutId)。问题是当我访问它时,我正确地看到了客户端的 ID 和名称其余字段是 null。所以我无法注销,因为应用程序认为我的用户没有经过身份验证。但是会话 cookie 在那里,我可以使用访问令牌访问 API。对我来说毫无意义...调用HttpContext.SignOutAsync() 无法注销。
  • 请仔细阅读并检查 mackie 提出的问题。如果您无权访问用户,这意味着托管 Identityserver 的 ASP.NET 应用程序无法将调用视为经过身份验证。一个可能的原因是在不发送 cookie 时调用 iframe 中的注销或任何类似的跨域调用。您必须确保(使用浏览器控制台)对 identityserver 的调用包含 cookie。
  • 也许我误解了这里的命名。 cookie sent 到底是什么?根据文档,注销应该是带有身份令牌的 GET,返回 URL 和状态。我们将如何基于此传递 cookie?在标题中作为承载者?我想我错过了一些东西。我们在这里谈论的是 SPA,而不是 MVC 应用程序,对吧?
  • 那个 GET 只是来自浏览器的一个 http 请求,而 Identityserver 中的注销页面只是一个 MVC 应用程序中的一个页面,所以除了令牌和状态等任何特定数据之外,它假设它自己的 cookie 嵌入到浏览器的请求中。该 cookie 是应用程序执行注销所需的主要内容,而令牌主要用于验证目的,可能很容易被忽略。
  • 我认为造成混淆的主要原因是我们正在做 SPA 而不是 MVC 应用程序。它使事情变得相当复杂。我认为这在我的问题中很明显和/或在这两种情况下都可以互换适用。但我意识到事实并非如此。 :)
猜你喜欢
  • 2013-08-28
  • 2020-04-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-13
  • 2015-05-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多