Keycloak 反向通道注销

Posted

技术标签:

【中文标题】Keycloak 反向通道注销【英文标题】:Keycloak Backchannel logout 【发布时间】:2018-10-13 01:50:52 【问题描述】:

我试图从我的 Web 应用程序中了解反向通道注销的流程,但我发现文档令人困惑。我有一个在 JBoss EAP 中运行的 JEE 应用程序,带有 Java Servlet Filter Adapter(由于某些技术原因,我不能使用 EAP 适配器)。 documentation for logging out 说:

您可以通过多种方式退出 Web 应用程序。对于 Java EE servlet 容器,可以调用 HttpServletRequest.logout()。为了 其他浏览器应用程序,您可以将浏览器重定向到 http://auth-server/auth/realms/realm-name/protocol/openid-connect/logout?redirect_uri=encodedRedirectUri,如果您与浏览器进行 SSO 会话,则会将您注销。

Admin URL configuration 的文档说:

例如,反向通道注销的工作方式是: 1.用户从一个应用程序发出注销请求 2.应用程序向Keycloak发送注销请求 3. Keycloak服务器使用户会话失效 4. Keycloak 服务器然后使用与会话关联的管理 url 向应用程序发送反向通道请求 5. 当应用程序收到注销请求时,它会使相应的 HTTP 会话失效

所以根据我的理解,要么:

    调用 HttpServletRequest.logout() 应该向 Keycloak 发送请求 到http://auth-server/auth/realms/realm-name/protocol/openid-connect/logout?redirect_uri=encodedRedirectUri 的 GET 应该以某种方式检测到 clientId(来自重定向 URI?)并向适当的反向通道发送请求

这两个选项似乎都不适合我。在这两种情况下,我都不会收到从 Keycloak 到我的 adminUrl 的回调。此外,在调用 Request.logout() 后,我仍然在 Keycloak Admin 中看到相同数量的活动会话。根据this SO post,它似乎可以工作,但我不确定我是否缺少配置或类似的东西。

我尝试使用 access_token 将 GET 发送到注销端点,但这也没有任何区别。

我对本文档有什么误解?我应该如何编写注销代码?

【问题讨论】:

【参考方案1】:

普通注销和后台注销是完全不同的两件事。

普通注销的工作原理是将浏览器重定向到您在 (2) 中提到的 URL。用户被 Keycloak 中的浏览器会话识别,因此浏览器重定向至关重要(甚至不需要提供 redirect_uri)。

我们在 Tomcat 中通过在我们的应用程序中调用以下代码来实现这一点,该代码本身会触发重定向(当然,使用 keycloak.json 作为配置):

Object keycloakAttr = request.getAttribute(KeycloakSecurityContext.class.getName());
if (keycloakAttr != null && keycloakAttr instanceof RefreshableKeycloakSecurityContext) 

  // code inspired by org.keycloak.adapters.tomcat.AbstractKeycloakAuthenticatorValve.logoutInternal
  RefreshableKeycloakSecurityContext ksc = (RefreshableKeycloakSecurityContext)keycloakAttr;
  KeycloakDeployment deployment = ksc.getDeployment();
  ksc.logout(deployment);

  // Since a CatalinaSessionTokenStore is used as token store in Tomcat
  // tokenStore.logout() is not necessary (???)

  request.removeAttribute(KeycloakSecurityContext.class.getName());
  request.setUserPrincipal(null);

反向通道注销只有在存在 多个依赖方(在 OIDC 中)或 Keycloak 发挥作用的“客户”时才有意义。它确保同一领域(对于同一用户)中的客户端的客户端会话不会“幸存”注销。

执行上述注销后,Keycloak 检查是否存在与 Keycloak 会话关联的客户端会话,并将反向通道注销请求发送到相应的客户端(在同一领域!),因此此过程根本不涉及浏览器会话.

为了使这项工作(对于正确安装了相应 Keycloak 适配器的 Node.js 和 Tomcat),我们只需要在 Keycloak 中正确配置客户端的 Admin URL。注意:它默认为“/”,但对于 Tomcat,必须包含 webapp 上下文,例如https://myapp.com/mywebapp/

【讨论】:

我假设 Keycloak 当前的反向通道注销实现是专有的并且需要 Keycloak 适配器之一才能工作?换句话说,这不是 OIDC 反向通道注销规范草案的实现参见openid.net/specs/openid-connect-backchannel-1_0.html 哦,是的,应该的。请参阅keycloak.org/docs/latest/securing_apps/…。它说“这是基于 RFC7523 规范”。 (tools.ietf.org/html/rfc7523) @Simon 有没有办法从自定义身份验证器或所需操作中注销所有用户会话?我正在尝试在更改密码后注销用户会话【参考方案2】:

你没有使用 Keycloak 的适配器之一吗?我遇到了完全相同的问题。

事实证明,keycloaks 自己的适配器在将代码交换为令牌时会在其 POST 中向 protocol/openid-connect/token 发送 client_session_state

除非在token POST 中提供了这样的参数,否则不会调用 AdminURL。去搞清楚。添加该参数,它就会开始工作!

查看相关keycloak forum post

【讨论】:

以上是关于Keycloak 反向通道注销的主要内容,如果未能解决你的问题,请参考以下文章

Keycloak 及其不同的适配器是不是实现了 Openid Connect Backchannel 注销规范

Keycloak 注销不会结束会话

Keycloak注销请求不会注销用户

如何在基于 Keycloak/Spring 的应用程序中实现单点注销?

注销重定向时出现 Keycloak CORS 问题

带有弹簧靴的keycloak openid单次注销