无法使 Keycloak 客户端启动的客户端帐户链接正常工作

Unable to get Keycloak client initiated client account linking to work

启动客户端发起的帐户链接的请求失败。

控制台显示类型为 CLIENT_INITIATED_ACCOUNT_LINKING_ERROR 的警告,错误为:invalid_token.

url 是由 php 后端系统按照此处所述生成的:https://www.keycloak.org/docs/latest/server_development/#client-initiated-account-linking

还要确保在生成哈希时使用 UTF8 编码

该部分描述的所有先决条件都已满足。

我正在使用 Keycloak 15.0.2 和 Laravel 与 Socialite 一起对用户进行身份验证。

这就是哈希的生成方式。

$keycloack_user = Socialite::driver('keycloak')->user();
$bearerToken = $keycloack_user->token;
$tokenParts = explode(".", $bearerToken);
$tokenHeader = base64_decode($tokenParts[0]);
$tokenPayload = base64_decode($tokenParts[1]);
$jwtHeader = json_decode($tokenHeader);
$jwtPayload = json_decode($tokenPayload);

$client_id = $jwtPayload->azp;
$host = $jwtPayload->iss;

$session_state = $jwtPayload->session_state;

$nonce = Str::random(20);
$provider = "google";

$input = $nonce . $session_state . $client_id . $provider;
$utf8encoded = utf8_encode($input);
$hashed = hash('sha256', $utf8encoded);
$encoded = rtrim(strtr(base64_encode($hashed), '+/', '-_'), '=');

然后链接url构造如下:

$redirect_uri = urlencode(...);
$full_url = $host . "/broker/". $provider ."/link?client_id=". $client_id ."&redirect_uri=". $redirect_uri ."&nonce=". $nonce ."&hash=" . $encoded;

我目前正在测试我的本地机器,没有为任何应用程序使用 https。登录工作正常,在检查 JWT 令牌时,存在所需的角色映射:

"account": {
      "roles": [
        "manage-account",
        "manage-account-links",
        "view-profile"
      ]
    }

但是在访问 url 时它显示“无效请求”并且 Keycloak 控制台指示令牌无效。

更新:解决方案是return哈希方法的结果作为原始二进制数据

$hashed = hash('sha256', $utf8encoded, true);

我最近不得不处理相同的任务,但客户端是在 JavaScript 中实现的。我也被困了很长一段时间,直到我意识到 keycloak 期待编码的哈希值是多么不寻常。您需要考虑以下两点:

  • 在base64转换之前将哈希字符串编码为十六进制
  • + 替换为 -,将 / 替换为 _。除此之外,删除尾随 = 符号

下面是一个用 JS 编写的工作片段:

import sjcl from "sjcl";

hexToBase64(hexstring) {
        return btoa(hexstring.match(/\w{2}/g).map(function(a) {
            return String.fromCharCode(parseInt(a, 16));
        }).join(""));
    },

// Assume nonce, session_state, clientId, provider to be given
var data = nonce + session_state + clientId + provider;
var myBitArray = sjcl.hash.sha256.hash(data)
var hashedData = sjcl.codec.hex.fromBits(myBitArray)
var base64HashedData = this.hexToBase64(HashedData)
base64HashedData = base64HashedData.replaceAll('+','-').replaceAll('/','_').replaceAll('=','')

base64HashedData 就是您需要作为散列查询参数传递给 keycloak 的 link 端点的内容。