OpenID Connect JWKs Endpoint とid_token署名検証方法 メモ


## JWKsエンドポイント

* `id_token`の署名を検証するための公開鍵の`modulus`(絶対値)と`exponent`(指数値)を返却するエンドポイント
  * JSON Web Key (JWK)：署名鍵を表現するためのJSONデータ構造



### リクエスト

* Keycloak例

```http
GET /auth/realms/master/protocol/openid-connect/certs HTTP/1.1
Host: localhost:8080
```



### レスポンス

* Keycloak例

```json
{
    "keys": [
        {
            "kid": "QSiaBQwkRZ9vqJgGv2byfTppJyeJT9mkxENgbxttoT8",
            "kty": "RSA",
            "alg": "RS256",
            "use": "sig",
            "n": "jKv0hkM...b9VvYIJ9hG8xN6zOQ",
            "e": "AQAB",
            "x5c": [
                "MIIC.....DyPe2E="
            ],
            "x5t": "9MMflCR5h-VY2D4HZx_kZ_L64XM",
            "x5t#S256": "DOiv2PKlM9QyI97gyUGl_95EULW9kyrv1ocAbxn1Q6M"
        },
        {
            "kid": "oYNqQTimc0dJfz7JFKNCwqgWC5Fp26eidiNcva6Nfn8",
            "kty": "RSA",
            "alg": "RS256",
            "use": "enc",
            "n": "mJNKSuJ...69NGCQ",
            "e": "AQAB",
            "x5c": [
                "MIICmzCCAYMCBgF8.../S2ZUocM="
            ],
            "x5t": "s7q4K84XS_OT4kRhwY-dPBGDeQU",
            "x5t#S256": "NbJVyOEgN_P5tJ0AO8bKX8vK2Loaw1PSQrlSS3YMhc4"
        }
    ]
}
```



#### パラメータ

| パラメーター名 | 説明                                                         |
| :------------- | :----------------------------------------------------------- |
| keys           | JWK配列                                                      |
| kid            | 鍵識別子<br />ID TokenのHeaderに含まれているKey ID。 署名に利用した秘密鍵の対になる公開鍵を判定するために利用。 |
| kty            | RSAやEC等の暗号アルゴリズムファミリー                        |
| use            | 公開鍵の用途                                                 |
| alg            | 署名検証アルゴリズム                                         |
| x5c            | PKIX証明書からなるJSON 配列<br />それぞれの証明書文字列は base64 エンコードされた値で表現される※base64url エンコードではない<br />当該JWK の鍵を含む PKIX 証明書は先頭の証明書である必要がある |
| x5t            | DER エンコードされた X.509 証明書の SHA-1 thumbprintの base64url エンコード値<br />当該証明書に含まれる鍵は当該 JWK の示す公開鍵と一致する |
| x5t#S256       | DER エンコードされた X.509 証明書の SHA-256 thumbprint の base64url エンコード値<br />当該証明書に含まれる鍵は当該 JWK の示す公開鍵と一致する |
| n              | modulus<br />公開鍵を復元するための公開鍵の絶対値            |
| e              | exponent<br />公開鍵を復元するための指数値                   |



## `id_token`署名検証の流れ

1. `id_token`を取得
2. `id_token`をパース
3. `id_token`から`alg`、`kid`を取得
4. JWKsエンドポイントへリクエストして公開鍵を取得
5. 4で取得したkeys配列にある`kid`と3で取得した`kid`が一致するものを取得
6. `kid`と対応する`n`と`e`も取得
7. `n`と`e`、`alg`を利用して公開鍵を作成
7. 7で作成した鍵を使用して`id_token`を検証



* `PyJWT`を用いて`id_token`検証を行う例

```python
import jwt
from jwt import PyJWKClient
def get_payload_from_id_token(id_token):
    # Keycloak JWKs Endpoint URL
    url = f"{KEYCLOAK_BASE_URL_CONTAINER_NAME}auth/realms/{KEYCLOAK_REALM_NAME}/protocol/openid-connect/certs"
    # JWKs Endpoint Client 
    jwks_client = PyJWKClient(url)
    # Signing Key
    signing_key = jwks_client.get_signing_key_from_jwt(id_token)
    # Decoding id_token by Signing Key
    payload = jwt.decode(id_token, signing_key.key, algorithms=["RS256"],audience=APP_CLIENT_ID)
    return payload
```





## 参考情報

* [JSON Web Key (JWK)](https://openid-foundation-japan.github.io/rfc7517.ja.html)
* [KeyCloak decoding with public_key in python](https://stackoverflow.com/questions/59764606/keycloak-decoding-with-public-key-in-python)