keycloak >= 24 && 25.0.4 >= keycloak-js >= 24 の組み合わせには致命的なバグがある

OIDC client library のバグで時間を溶かしたので記録しておきます。

# UPDATE

keycloak-js 25.0.4 でついに修正が入りました。これで安心してupgradeできます。よかったね〜
https://github.com/keycloak/keycloak/commit/a95894dbadfb8b9d973bc52f13e331ac3d8dafd2

しかしrelease noteにはありません。なぜ〜〜
https://github.com/keycloak/keycloak/releases
https://github.com/keycloak/keycloak/releases/tag/25.0.4


# Issue
keycloak 23 を 25.0.2 にしたら突然 OIDC Clientのアプリが認証で無限 redirect ループするようになった。

# 症状
- keycloak-js を最新の 25.0.2 にしたら、一部のクライアントは直ったが一部は直らない
- 同じ状態でも発生したりしなかったりする
    - local環境で再現できないので手間取った
- keycloakの debug logを出してもエラーは出ていない
- keycloak-jsの方はすぐredirectしてしまうので調査困難


# 原因
- keycloak-js おそらく 24 から 25.0.2 にはバグがある
- access_token を問題なく取得した後、「こんなんいらん！」と投げ捨てるバグ w

# Overview
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24737/fdffc6fa-bb32-9bf6-d26b-0e403a360476.png)


# details
問題のコードは 2022/1/13 からずっと変わっていない
https://github.com/keycloak/keycloak/commit/dea123169f7

```js
dea123169f7 adapters/oidc/js/src/main/js/keycloak.js (Jon Koops            2022-01-13 08:28:30 +0100  805)
      if (useNonce && ((kc.tokenParsed && kc.tokenParsed.nonce != oauth.storedNonce) ||
dea123169f7 adapters/oidc/js/src/main/js/keycloak.js (Jon Koops            2022-01-13 08:28:30 +0100  806)                 (kc.refreshTokenParsed && 
      kc.refreshTokenParsed.nonce != oauth.storedNonce) ||
dea123169f7 adapters/oidc/js/src/main/js/keycloak.js (Jon Koops            2022-01-13 08:28:30 +0100  807)                 (kc.idTokenParsed && kc.idTokenParsed.nonce != oauth.storedNonce))) {
```


問題のきっかけ
**keycloak 24 から access_token の nonce が消えた**

> Sat Feb 17 19:35:39 2024 +0100
> JS adapter incorrectly checked the "nonce" in the access token and which was removed in Keycloak 24. 

そのコミット
> dea15e25da Only add the nonce claim to the ID Token (mapper for backwards compatibility) Closes #26893
> https://github.com/keycloak/keycloak/pull/27588

真の原因
keycloak 24 の変更により、 `access_token.nonce` の検証をしている `keycloak-js` がこけるようになった

# Solutoin
2024-02-08 に直ったもの(5ヶ月経ってるのに 最新 v25.0.2 でもまだ取り込まれていない)
25.0.3で直るのかどうかはわかってない。

https://github.com/keycloak/keycloak/commit/a95894dbad
https://github.com/keycloak/keycloak/issues/26651

> a95894dbadf js/libs/keycloak-js/src/keycloak.js      (Grzegorz Grzybek     2024-02-08 13:19:32 +0100  805)             if (useNonce && (kc.idTokenParsed && kc.idTokenParsed.nonce != oauth.storedNonce)) {

これを node_modules に無理やり当てればいい。

```diff
-            if (useNonce && ((kc.tokenParsed && kc.tokenParsed.nonce != oauth.storedNonce) ||
-                (kc.refreshTokenParsed && kc.refreshTokenParsed.nonce != oauth.storedNonce) ||
-                (kc.idTokenParsed && kc.idTokenParsed.nonce != oauth.storedNonce))) {

+            if (useNonce && (kc.idTokenParsed && kc.idTokenParsed.nonce != oauth.storedNonce)) {
```

# 学び
- すぐ redirect する場合は localStorage に log を入れると便利
- 怪しいところにこういうのを入れて、chrome debug tool > Application > localStorage で確認する
```js
function saveLog() {
    const fieldname = `tokenParsedNonce-${new Date().toISOString()}`;
    localStorage.setItem(fieldname, "保存したい文字列");
  }
}
saveLog()
```

- vue router はただの被疑者
    - redirectするときにhashが無限に増えていく問題があったので vue routerとの相性が悪いんじゃないかと調べたらこういうissueがあった
        - https://github.com/keycloak/keycloak/issues/14742
    - vue router周りの大改造をして時間ロス。結果意味なし。でもこの方が安定しそうで結果的にはよかったと思う。
    - vue routerを動かす前に `keycloak.init()` するといいらしい。確かに。

```js
export const keycloak = new Keycloak({
  realm: "[REDACTED]",
  url: "[REDACTED]",
  clientId: "[REDACTED]",
});

const kc_options = {
  onLoad: "login-required",
  checkLoginIframe: false,
  flow: "standard",
};

await keycloak.init(kc_options);

app.use(createPinia());
app.use(router);
```

- token取れて `authSuccess()` 呼んでればここは大丈夫やろ、という油断
    - まさか `authSuccess()` で token 投げ捨てるとは思わなかった

http status=200 でここ通ればだいたい ok やろ・・
```js
            if (req.status == 200) {
                var tokenResponse = JSON.parse(req.responseText);
         -->    authSuccess(tokenResponse['access_token'], tokenResponse['refresh_token'], tokenResponse['id_token'], kc.flow === 'standard');
```
> https://github.com/keycloak/keycloak/blob/a95894dbadfb8b9d973bc52f13e331ac3d8dafd2/js/libs/keycloak-js/src/keycloak.js#L788

よく見たらレスポンスのvalidationしてないのに `authSuccess()` してた。これは関数名がミスリーディング..



# 疑問
100%コケそうなバグなのに、一貫性がないのが謎。keycloak >= 24 がたまにaccess tokenにnonceを入れるバグでもあるのん？？


# 終わりに
みんな！最新バージョンじゃなくてもバグはあるぞ！
どうせなら誰も見つけてないときに見つけて PR 送りたかった！