在后端前面使用 Keycloak Gatekeeper API

Use Keycloak Gatekeeper in front of backend API

在 DOMAIN 上运行的单页应用程序 (SPA) 对 DOMAIN/graphql 的调用被重新路由到后端。前端和后端都通过 Keycloak Gatekeeper 实例进行保护。

想法是前端和后端共享 kc-access 令牌。

现在,访问令牌在后端 Gatekeeper 中过期。如果 SPA 在浏览器中刷新,前端将重新路由到 Keycloak,并且需要新的访问令牌。但是,如果没有刷新,则 POST 对 DOMAIN/graphql 的请求会在令牌过期时失败并返回 307 状态代码。浏览器不知道如何处理这个。浏览器记录给出 "{"error":"RESTEASY003065: Cannot consume content type"}"。如果 POST 的 content-type header 被删除,错误是 "no client_id provided",而 client_id 包含在查询字符串中。

将 POST 请求重定向到 Keycloak 可能不是最佳解决方案。如果后端刷新它的访问令牌本身,则更清洁。

这是我们通过向后端的 Gatekeeper 添加 session 状态存储来尝试的。我们正在使用以下配置:

- --discovery-url=DISCOVERY_URL
- --client-id=CLIENT_ID
- --client-secret=****
- --enable-refresh-tokens=true
- --encryption-key=0123456789012345
- --store-url=boltdb:///boltdb
- --listen=0.0.0.0:3001
- --verbose=true
- --redirection-url=REDIRECTION_URL
- --upstream-url=http://127.0.0.1:3000

这确实在 Gatekeeper 中创建了一个 /boltdb 文件,但它似乎没有被使用,因为该文件没有改变。

后端的 Gatekeeper 提供以下日志记录:

|1.5716729131430433e+09|debug|keycloak-gatekeeper/session.go:51|found the user identity|{"id": "b5b659cd-148e-4f23-bf2f-28e6f207f6c7", "name": "piet", "email": "", "roles": "offline_access,dashboard_viewer,uma_authorization,account:manage-account,account:manage-account-links,account:view-profile", "groups": ""}|
|1.5716729131462774e+09|info|keycloak-gatekeeper/middleware.go:154|accces token for user has expired, attemping to refresh the token|{"client_ip": "****", "email": ""}|
|1.5716729131463811e+09|error|keycloak-gatekeeper/middleware.go:161|unable to find a refresh token for user|{"client_ip": "**", "email": "", "error": "no session state found"}|

所以我们是 "unable to find a refresh token for user" 因为根据日志记录有 "no session state found"。

有人知道如何启用令牌刷新吗?

看起来设计不太好。 Keycloak Gatekeeper 使用授权代码流,这不是您所发现的 SPA 的最佳流程(在 SPA 案例中读取 Gatekeeper 提供的用户身份似乎非常 hackish)。

SPA 将代码流与 PKCE 或隐式流结合使用,并且这些流使用静默令牌更新(而不是刷新令牌)。恕我直言,最好的选择是在前端 (SPA) 和后端使用相同的客户端 ID(例如 API)。但是前端将受到带有 PKCE 的代码流的保护,并且它将处理自己的令牌更新。 Gatekeeper 仅保护后端(+ --no-redirects 设置对 API 保护有意义)

通过在前端的 Gatekeeper 中使用相同的加密密钥设置 enable-refresh-tokens=true,设计工作正常。

用户检索前端并重定向到 Keycloak。在那里获得授权码。此授权代码由前端 Gatekeeper 交换,用于获取放置在前端 cookie 中的访问和刷新令牌。当使用过期的访问令牌调用后端时,刷新令牌将被解密并用于获取新的访问令牌。

刷新令牌可能会过期或失效。当返回 401 时,前端应刷新页面,以便将用户重定向到 Keycloak。

更安全的方法是不将令牌存储在前端 cookie 中,而是存储在共享存储中。