KeycloakでのOpenID Connectお勉強メモ #2
はじめに
#1 からのつづきです。前回はこのフローを作りました。
今回はこの内容を詳しく見ていきます。
フロー図

Flow_OIDC
参考文献
調べていくにあたり以下の資料を見ながら進めて行きます。
- OpenID Connect Core 1.0 日本語訳
- OpenID Connect Basic Client Implementer's Guide 1.0 日本語訳
- OpenID Connect Discovery 1.0
OpenID Connect フローには種類があるということを知る
OpenID Connect Basic Client Implementer's Guide 1.0 日本語訳 - 2. Protocol Elementsに書いてあるように OpenID Connect には以下の種類があります。
- Authorization Code Flow
- Implicit Flow
- Hybrid Flow
ここでは Authorization Code Flow (最も一般的な方法) でやっています。
まずはこれを見ときます。
OpenID Connect Basic Client Implementer's Guide 1.0 日本語訳 - 2.1. Code Flow
Code Flow は以下のステップからなる.
- Client は必要なリクエストパラメータを含んだ Authentication Request を構築する.
- Client は Authorization Server にリクエストを送信する.
- Authorization Server は End-User を認証する.
- Authorization Server は End-User の Consent/Authorization を取得する.
- Authorization Server は End-User を
codeとともに Client に戻す.- Client は
codeを Token Endpoint に送信し, Access Token と ID Token を受け取る.- Client はそれらのトークンを検証し, End-User の Subject Identifier を取得する.
おおざっぱに構成を理解する

-page-1
End-User
ブラウザを操作する人
Relying Party (RP)
OpenID Connect を利用する側
- 
Apache + mod-auth-openidc 
- 
PHP 公式のDockerコンテナを使う - apache2が入っている
- libapache2-mod-auth-openidcも入れます
- 詳細は Dockerfile 参照
 
- 
libapache2-mod-auth-openidc - Apacheのモジュール(アドオンみたいなもの?)でこれを付けるとApacheがOpenID Connectで話せるようになる便利なもの
- Protected Resource を定義するだけでOPに問い合わせてくれます
- 今回の場合 /secure以下のリソースを Protected Resource にしてこのリソースにリクエストがあれば Keycloak に問い合わせするようになります。
 
- 
サイトの構成は以下の通り / ├── index.html --- TOP 画面 ├── loggedout.html --- ログアウト後の画面 └── secure (Protedted Resource) └── welcome.php --- ログイン後の画面
OpenID Provider (OP)
OpenID Connect を提供する側
- 
Keycloak 
- 
操作者(End-User)の Account(アカウント) を保持する - Account は UserInfoとCredentialで構成される- UserInfo - 名前とかメールアドレスとか
- Credential - パスワード
 
 
- Account は 
- 
RP-Apache(mod-auth-openidc) からリダイレクトされてEnd-userの認証認可を行う 
- 
RP-Apache(mod-auth-openidc) からのリクエストでEnd-userのUserInfoを提供する 
画面フロー
- 
- ブラウザから index.htmlにアクセスしてTOP画面を表示する
 
- ブラウザから 
- 
- 
Login with Keycloakをクリックすると/secure/welcome.phpに遷移する
 
- 
- 
- が、しかし /secureは mod-auth-openidc が管理する Protected Resource なのである
 
- が、しかし 
- 
- mod-auth-openidc はすかさず Keycloak にリダイレクトする
 
- 
- Keycloak はログイン画面を表示する
 
- 
- 無事ログインできたら  Welcome画面( /secure/welcome.php)を画面に表示する
 
- 無事ログインできたら  Welcome画面( 
- 
- welcome画面で Logoutをクリックするとログアウト処理をして ログアウト完了画面に遷移する
 
- welcome画面で 
- 
- ログアウト完了画面で Goto TopをクリックするとTOP画面に戻る
 
- ログアウト完了画面で 
memo : 今回は 7) 8)の詳細は調査していません、次の記事で...

Screen_Diagram
Keycloakのエンドポイント
フロー図に書いているエンドポイント
Configuration Endpoint
- OpenID Connect Discovery 1.0 - 4. Obtaining OpenID Provider Configuration Information
- OpenID Configuration (Keycloakが実装しているエンドポイント情報)
Authorization Endpoint(認可エンドポイント)
- OpenID Connect Core 1.0 - 3.1.2. Authorization Endpoint
- 認証画面出してID/パスワードを検証するところ
Authenticate Endpoint
- End-Userが提示したCredentialを検証するエンドポイント
- ここで Credential の検証OKとなるとAuthorization Code(認可コード) を発行して End-User に返す
- OpenID Connectで明確に定義されたエンドポイントではなく、Authorization Endpointが提供する機能の一部という扱い
- 
OpenID Connect Core 1.0 - 3.1.2.3. Authorization Server Authenticates End-User あたりの説明が参考になる
- ↑に書いてあるとおり End-User の認証方式については規定範疇外 となっていて認証のやり方の細かい方法は書いていないです。このへんが OpenID Connect と FIDO は補完関係 ということなんですね。
 
Token Endpoint(トークンエンドポイント)
- OpenID Connect Core 1.0 - 3.1.3. Token Endpoint
- Authorization Code(認可コード) を受け取って検証してOKであれば Access Token(アクセストークン) , ID Token(IDトークン) を発行して RP に返す
jwks_uri(証明書エンドポイント)
- OpenID Connect Discovery 1.0 - 3. OpenID Provider Metadata
- JWK Set を公開するエンドポイント
- 要はJWTの署名を検証するための公開鍵リポジトリということみたい
UserInfo Endpoint(ユーザー情報エンドポイント)
- OpenID Connect Core 1.0 - 5.3.1. UserInfo Request
- Access Token(アクセストークン) をパラメータで受けてユーザー情報を返す
フローの詳細
0. Start
ログインを開始するまでのフロー
- ブラウザ(End-user)が RP の 管理するリソース index.htmlにアクセスする

-page-2
- 
index.htmlは ログインする前に表示する画面(Protected Resouce ではない)なので、そのまま200 OKでレスポンスを返す

test-oidc-rp_1.png
1. Login
Keycloakのログイン画面を表示するまでのフロー
- 
Login with Keycloakをクリックすると、./secure/welcome.phpに遷移(GET)する

-page-2_1
- 
リクエストを受けたRPの mod-auth-openidcは 認証が必要なリソースであることを検知するmemo : 認可が必要なリソースの定義は auth_openidc.confの<Location /secure>でされている
- 
mod-auth-openidcはまだアクセストークンを持っていないため、Keycloakからアクセストークンを取得するフローを開始するmemo : アクセストークンの取得フローが openid-connectであることはauth_openidc.confで設定されている
- 
mod-auth-openidcはまずKeyCloakからOpenID Configuration を取得するmemo : OpenID Configurationのエンドポイントは auth_openidc.confで定義されている

-page-3
- 
mod-auth-openidcは End-user へレスポンス 302 Found(リダイレクト) を返す
レスポンスは以下の通り
| パラメータ | 値 | 説明 | 取得元 | 
|---|---|---|---|
| Location | /protocol/openid-connect/auth | 認可エンドポイント にリダイレクトすることを指定する | OpenID Configurationのauthorization_endpoint | 
| response_type | code | トークン発行手順がcodeであることを示す | auth_openidc.confのOIDCResponseType | 
| scope | openid | ID トークンの発行を要求することを示す | auth_openidc.confのOIDCClientID | 
| client_id | test | KeyCloakで設定されているクライアント | auth_openidc.confのOIDCScope | 
| redirect_uri | http://test-oidc-rp:8081/secure | Keycoakで認証が完了した後にリダイレクトさせてほしい場所。 ここからはKeycloakの制御になって操作を行うがその後にRPに戻ってきてほしいんでここで指定しておく(*1) | auth_openidc.confのOIDCRedirectURI | 
memo : state/nonceもついているけど省略
- 
End-user は レスポンスを受け取り、KeyCloak の 認可エンドポイント にすっ飛んで行く GET /auth/realms/master/protocol/openid-connect/auth ?response_type=code &scope=openid &client_id=test &state=28bO9uWjEJxcWw-W-k4J2VEmXYw &redirect_uri=http%3A%2F%2Ftest-oidc-rp%3A8081%2Fsecure &nonce=0Q3u5PmRIscEEgnIUeRiT9D8fI7HmwTQFd2dZ8Pk7aI

-page-4
- 
Keycloak は ログイン画面を返す memo : Cookieが大量についてくるけど省略 

-page-5

Sign_in_to_Keycloak
2. Get Authorization Code
Username/Passwordを取得してから**Authorization Code(認可コード)**を取得するまでのフロー
memo
操作者がログイン画面でUsername/Passwordを入力して Sign In ボタンをクリックするところから始まります。
ここから先がゴチャゴチャしているんですがフローはノンストップなので操作者にとっては一瞬です。
- 
ログイン画面でUsername/Passwordを入力して Sign Inすると End-user は KeyCloak の Authenticate Endpoint に POST するmemo : クエリパラメータでめっちゃ色々ついているけど省略 - 
Formデータ Form item 値 説明 username hoge ユーザー名 password xxx パスワード 
 
- 

-page-6
- Keycloak は usernameとpasswordを検証して認証OKとなれば End-user へレスポンス 302 Found(リダイレクト) を返す
memo : ここに 認可コード が入っている
| パラメータ | 値 | 説明 | 
|---|---|---|
| Location | http://test-oidc-rp:8081/secure | リダイレクト先 (*1)で指定されたもの | 
| code | 050ab57b-64e1-410a-・・・ | 認可コード(Authorization Code) | 
- End-user は リダイレクトのレスポンスを受けたので、そのままリダイレクト先に飛んでいく

-page-7
3. Get Access Token & ID Token
認可コードを取得してから Access Token(アクセストークン) と ID Token(IDトークン) を取得するまでのフロー
- 
mod-auth-openidcは Keycloak の トークンエンドポイント にPOSTする- 
このとき Authorization ヘッダに Basic を設定する ヘッダ 値 説明 Authorization Basic dGVz... Basic認証であることを示す 
 ↑の値なんですが Basic dGVzdDozM2I3MzJjNS0wMDZjLTQxNjMtOWM3Yy01NzVhNjBhMmEzNDg=これは Basic認証であることを意味していて、Basic 以降のBase64をデコードすると test:33b732c5-006c-4163-9c7c-575a60a2a348となっています。 要はこの値で接続してくるクライアントを認証しています。→9. Client Authentication クライアントIDとシークレットは auth_openidc.conf の OIDCClientID,OIDCClientSecretです。
- 
Form Form item 値 説明 grant_type authorization_code 3.1.3.1. Token Request code 050ab57b-64e1-410a-・・・ 先程取得した 認可コード redirect_uri http://test-oidc-rp:8081/secure RPからOPに直接リクエストするバックチャネル通信なんでOPがリダイレクトのレスポンスしてくるわけではないと思うのですが Validation するため必須。 
 3.1.3.2. Token Request Validation
 >redirect_uriパラメータの値が, 最初の Authorization Request に含まれていたredirect_uriパラメータの値と同一であることを確認
 ということみたい。
 
- 

-page-8
- 
Keycloak は Authorizationヘッダの値とか認可コードを検証してOKであれば mod-auth-openidcにレスポンス を返す。
 レスポンスはJSON形式で**Access Token(アクセストークン) と ID Token(IDトークン)**が含まれている。Key Value 説明 access_token JWT形式のデータ アクセストークン expires_in 60 アクセストークンの有効期限(60秒=1分) refresh_expires JWT形式のデータ リフレッシュトークン refresh_expires_in 1800 リフレッシュトークンの有効期限(1800秒=30分) token_type Bearer トークンタイプ id_token JWT形式のデータ IDトークン 

-page-9
アクセストークンとは何か
リソースにアクセスするときの許可証。OAuthのそれと同じ。
IDトークンとは何か
ユーザーをちゃんと認証したよ、という証明書みたいなもの。ユーザー名とかの属性も入っている。
詳細は以下のサイトを見たほうがいい
- 
続けざまに KeyCloak の jwks_url(証明書エンドポイント) から証明書をGETする。 - 
証明書 = アクセストークン、IDトークンのJWTの署名を検証するために使う公開鍵 
- 
ここに入っている公開鍵でJWTを検証することでアクセストークン、IDトークンが改ざんされていないことを確認する memo : jwks_url は OpenID Configuration に入っている 
  
 -page-10
- 
- 
UserInfo Endpoint(ユーザー情報エンドポイント) からユーザー情報をGETする。 
- 
ユーザー情報 = UsernameとかEmailとか - ユーザー情報エンドポイントにアクセスする際のAuthorizationヘッダはBearerで、先程取得した アクセストークン を指定する
 memo : UserInfo Endpoint は OpenID Configuration に入っている 

-page-11
- ここまででmod-auth-openidcは必要な情報を全て取得したことになる。End-userには一番最初にアクセスを要求されたURLに遷移するリダイレクトのレスポンスを返す。
| パラメータ | 値 | 説明 | 
|---|---|---|
| Location | http://test-oidc-rp:8081/secure/welcome.php | リダイレクト先 | 

-page-12
4. Resource access
リダイレクト先に遷移する。リクエストを受けた mod-auth-openidc はアクセスを許可しHTMLを返す。
note :
mod-auth-openidcは Cookie mod_auth_openidc_session にアクセストークンを紐付けていてアクセストークンの有効性を確認しているようです。mod_auth_openidc_sessionを削除するとKeycloakに問い合わせて再度アクセストークンをもらってきているのでしょう。

-page-13

おつかれさまでした
よくわからないことがまだあるなぁ...
- ログアウトの動作 (Logout)
- ログインしたままTOPに戻って再度ログインしたときの動作(Return to top wthile logged in)
- アクセストークン/IDトークンの有効期限が切れたときの動作
蛇足(データサンプル)
OpenID Configuration
{
  "issuer": "http://test-oidc-keycloak:8080/auth/realms/master",
  "authorization_endpoint": "http://test-oidc-keycloak:8080/auth/realms/master/protocol/openid-connect/auth",
  "token_endpoint": "http://test-oidc-keycloak:8080/auth/realms/master/protocol/openid-connect/token",
  "introspection_endpoint": "http://test-oidc-keycloak:8080/auth/realms/master/protocol/openid-connect/token/introspect",
  "userinfo_endpoint": "http://test-oidc-keycloak:8080/auth/realms/master/protocol/openid-connect/userinfo",
  "end_session_endpoint": "http://test-oidc-keycloak:8080/auth/realms/master/protocol/openid-connect/logout",
  "jwks_uri": "http://test-oidc-keycloak:8080/auth/realms/master/protocol/openid-connect/certs",
  "check_session_iframe": "http://test-oidc-keycloak:8080/auth/realms/master/protocol/openid-connect/login-status-iframe.html",
  "grant_types_supported": [
    "authorization_code",
    "implicit",
    "refresh_token",
    "password",
    "client_credentials",
    "urn:ietf:params:oauth:grant-type:device_code",
    "urn:openid:params:grant-type:ciba"
  ],
  "response_types_supported": [
    "code",
    "none",
    "id_token",
    "token",
    "id_token token",
    "code id_token",
    "code token",
    "code id_token token"
  ],
  "subject_types_supported": [
    "public",
    "pairwise"
  ],
  "id_token_signing_alg_values_supported": [
    "PS384",
    "ES384",
    "RS384",
    "HS256",
    "HS512",
    "ES256",
    "RS256",
    "HS384",
    "ES512",
    "PS256",
    "PS512",
    "RS512"
  ],
  "id_token_encryption_alg_values_supported": [
    "RSA-OAEP",
    "RSA-OAEP-256",
    "RSA1_5"
  ],
  "id_token_encryption_enc_values_supported": [
    "A256GCM",
    "A192GCM",
    "A128GCM",
    "A128CBC-HS256",
    "A192CBC-HS384",
    "A256CBC-HS512"
  ],
  "userinfo_signing_alg_values_supported": [
    "PS384",
    "ES384",
    "RS384",
    "HS256",
    "HS512",
    "ES256",
    "RS256",
    "HS384",
    "ES512",
    "PS256",
    "PS512",
    "RS512",
    "none"
  ],
  "request_object_signing_alg_values_supported": [
    "PS384",
    "ES384",
    "RS384",
    "HS256",
    "HS512",
    "ES256",
    "RS256",
    "HS384",
    "ES512",
    "PS256",
    "PS512",
    "RS512",
    "none"
  ],
  "response_modes_supported": [
    "query",
    "fragment",
    "form_post"
  ],
  "registration_endpoint": "http://test-oidc-keycloak:8080/auth/realms/master/clients-registrations/openid-connect",
  "token_endpoint_auth_methods_supported": [
    "private_key_jwt",
    "client_secret_basic",
    "client_secret_post",
    "tls_client_auth",
    "client_secret_jwt"
  ],
  "token_endpoint_auth_signing_alg_values_supported": [
    "PS384",
    "ES384",
    "RS384",
    "HS256",
    "HS512",
    "ES256",
    "RS256",
    "HS384",
    "ES512",
    "PS256",
    "PS512",
    "RS512"
  ],
  "introspection_endpoint_auth_methods_supported": [
    "private_key_jwt",
    "client_secret_basic",
    "client_secret_post",
    "tls_client_auth",
    "client_secret_jwt"
  ],
  "introspection_endpoint_auth_signing_alg_values_supported": [
    "PS384",
    "ES384",
    "RS384",
    "HS256",
    "HS512",
    "ES256",
    "RS256",
    "HS384",
    "ES512",
    "PS256",
    "PS512",
    "RS512"
  ],
  "claims_supported": [
    "aud",
    "sub",
    "iss",
    "auth_time",
    "name",
    "given_name",
    "family_name",
    "preferred_username",
    "email",
    "acr"
  ],
  "claim_types_supported": [
    "normal"
  ],
  "claims_parameter_supported": true,
  "scopes_supported": [
    "openid",
    "email",
    "roles",
    "profile",
    "web-origins",
    "offline_access",
    "address",
    "microprofile-jwt",
    "phone"
  ],
  "request_parameter_supported": true,
  "request_uri_parameter_supported": true,
  "require_request_uri_registration": true,
  "code_challenge_methods_supported": [
    "plain",
    "S256"
  ],
  "tls_client_certificate_bound_access_tokens": true,
  "revocation_endpoint": "http://test-oidc-keycloak:8080/auth/realms/master/protocol/openid-connect/revoke",
  "revocation_endpoint_auth_methods_supported": [
    "private_key_jwt",
    "client_secret_basic",
    "client_secret_post",
    "tls_client_auth",
    "client_secret_jwt"
  ],
  "revocation_endpoint_auth_signing_alg_values_supported": [
    "PS384",
    "ES384",
    "RS384",
    "HS256",
    "HS512",
    "ES256",
    "RS256",
    "HS384",
    "ES512",
    "PS256",
    "PS512",
    "RS512"
  ],
  "backchannel_logout_supported": true,
  "backchannel_logout_session_supported": true,
  "device_authorization_endpoint": "http://test-oidc-keycloak:8080/auth/realms/master/protocol/openid-connect/auth/device",
  "backchannel_token_delivery_modes_supported": [
    "poll"
  ],
  "backchannel_authentication_endpoint": "http://test-oidc-keycloak:8080/auth/realms/master/protocol/openid-connect/ext/ciba/auth"
}
Access Token
https://jwt.io/ でデコードできる
JWT
eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJwSmdoalAxUVJLZ1lEWnFJVzNKV1dETnI3eUc5aEJiVGk4VV94SXh0Tmc4In0.eyJleHAiOjE2MjQ3NDc2MzIsImlhdCI6MTYyNDc0NzU3MiwiYXV0aF90aW1lIjoxNjI0NzQ3NTcxLCJqdGkiOiI2ZmU0NjkwNy1iZTFjLTQ2YWUtODlhYi03ZGIxMjI4YTJlMDYiLCJpc3MiOiJodHRwOi8vdGVzdC1vaWRjLWtleWNsb2FrOjgwODAvYXV0aC9yZWFsbXMvbWFzdGVyIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6ImFjMDg0OGU4LTUzZTMtNDQ4My1hNzM5LWQ3NjIzMTdhZjQ0NCIsInR5cCI6IkJlYXJlciIsImF6cCI6InRlc3QiLCJub25jZSI6IlVudkUtWjhwUS13MmxPSUVyd2F2WDF4dEo4eXlCNlhhV0l4cjRMQmxiZzAiLCJzZXNzaW9uX3N0YXRlIjoiZDk2OTQyNzctMDFhNy00OWRjLWJlMDktNTRhMzg3MDY2ZDkxIiwiYWNyIjoiMSIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJkZWZhdWx0LXJvbGVzLW1hc3RlciIsIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6Im9wZW5pZCBlbWFpbCBwcm9maWxlIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJob2dlIn0.ETQpOLqEPvTSBfwts85YwDMen8tw8ZUeF21w5LqdBaVquS7DuNcQpI-A_4RM3OR2L3XuwIfMYLk3AmGObZz_4bY7d1yfeBJYNwXXS1A6VJDQU3kZdGr9VZPAUoHcoMD6R4EZbG4nCl_idJ0b_bg4WEeY5IjdNLjcxrZZ4fax0TpuvBIoRJmnOIKMsKg5W7HyJYZ8nqKxFw1a3aGV2sy5tqeyAYordAO7_T4pgt-WpH73IJFqT7mNXorEnNEROqZQR7FAqU4Rs2mFBynAN1Sq_ICB8avYzF9QkLrUmud7A5oFvCjVi2WWHNxhhkM6x7Gv7gjqwlsaEofp9awkjqfFew
Decoded-Payload
{
  "exp": 1625894396,
  "iat": 1625894336,
  "auth_time": 1625894336,
  "jti": "193cba26-dcba-4704-9d39-31f88ee8339b",
  "iss": "http://test-oidc-keycloak:8080/auth/realms/master",
  "aud": "account",
  "sub": "9e9e8079-e8c4-4eaa-baaf-bcd22707d9a7",
  "typ": "Bearer",
  "azp": "test",
  "nonce": "B-3iE7nRNtsVs4D0r7YZw88-PPwBsQDoDKpOxrN3o9o",
  "session_state": "ef353a37-809a-4945-b3f2-0d04d6d01e32",
  "acr": "1",
  "realm_access": {
    "roles": [
      "offline_access",
      "uma_authorization"
    ]
  },
  "resource_access": {
    "account": {
      "roles": [
        "manage-account",
        "manage-account-links",
        "view-profile"
      ]
    }
  },
  "scope": "openid profile email",
  "email_verified": false,
  "preferred_username": "hoge",
  "email": "aa@aa.com"
}
ID Token
JWT
eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJwSmdoalAxUVJLZ1lEWnFJVzNKV1dETnI3eUc5aEJiVGk4VV94SXh0Tmc4In0.eyJleHAiOjE2MjQ3NDc2MzIsImlhdCI6MTYyNDc0NzU3MiwiYXV0aF90aW1lIjoxNjI0NzQ3NTcxLCJqdGkiOiJjYjI3YmY1MC0zYmZjLTQxN2QtOWU1NS1jMzc4M2UyODFiMzEiLCJpc3MiOiJodHRwOi8vdGVzdC1vaWRjLWtleWNsb2FrOjgwODAvYXV0aC9yZWFsbXMvbWFzdGVyIiwiYXVkIjoidGVzdCIsInN1YiI6ImFjMDg0OGU4LTUzZTMtNDQ4My1hNzM5LWQ3NjIzMTdhZjQ0NCIsInR5cCI6IklEIiwiYXpwIjoidGVzdCIsIm5vbmNlIjoiVW52RS1aOHBRLXcybE9JRXJ3YXZYMXh0Sjh5eUI2WGFXSXhyNExCbGJnMCIsInNlc3Npb25fc3RhdGUiOiJkOTY5NDI3Ny0wMWE3LTQ5ZGMtYmUwOS01NGEzODcwNjZkOTEiLCJhdF9oYXNoIjoiUlZadkNhaEdCYWk2TlVydkVKam9mZyIsImFjciI6IjEiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInByZWZlcnJlZF91c2VybmFtZSI6ImhvZ2UifQ.rMcNKLdfd64W0r9QIPIlXVIiyTi6MUhmAQX2H_Bp6eviEecRoPYHxawG5fXZS2iNKAzDL5OjHbGl_Yzxkjr2UPBB7UWfgVIcjfD3BiOQO-V4haa8O3gGPMx0Xm1LD6r5QVV6eARm5_FTvpO8xwmgVzW0KXRfn9M-HyofE4NEjsMQTAel8TY_U7H1KJsqTezEZRfHrtU7EwyO0OVRvfVXp58F9Ly5GtXNrMDlekQWdaTA222_Rk6Tm6h3KNgwUKwbigtBwMDK19ZXeJ94_sFcbHfKcbgnFhnzEYyZ-3rSM9SKtPh0JUlordUOU7FUQVqKxviOgwrj7Wqi-2kekBZnxg
Decoded JSON
{
  "exp": 1624747632,
  "iat": 1624747572,
  "auth_time": 1624747571,
  "jti": "cb27bf50-3bfc-417d-9e55-c3783e281b31",
  "iss": "http://test-oidc-keycloak:8080/auth/realms/master",
  "aud": "test",
  "sub": "ac0848e8-53e3-4483-a739-d762317af444",
  "typ": "ID",
  "azp": "test",
  "nonce": "UnvE-Z8pQ-w2lOIErwavX1xtJ8yyB6XaWIxr4LBlbg0",
  "session_state": "d9694277-01a7-49dc-be09-54a387066d91",
  "at_hash": "RVZvCahGBai6NUrvEJjofg",
  "acr": "1",
  "email_verified": false,
  "preferred_username": "hoge"
}
jwks_uri Keys
{
  "keys": [
    {
      "kid": "pJghjP1QRKgYDZqIW3JWWDNr7yG9hBbTi8U_xIxtNg8",
      "kty": "RSA",
      "alg": "RS256",
      "use": "sig",
      "n": "zSlLHc3Znj4mpnGyyxr32XgT-cCP4YPUCGxQ4s7yuplhAeePO2vUUytlx0oekP1M8xVa4fFsmv1VNkbj8iF6k_gmyRJAoBjA6CEiV7p42-nQcwSV6i_V-iyXKejKoH7PKAQZmhUsz21fNEWhpprRL3s3iGQS5EQpdJf_ACIbSWAvU2GxczFUxTReVGBSjwWrtqdC1N-qvyVFoD_1o1HKD0wGGIxJS3Bt4qvYKuxrpXb_5iaK1lSYXj8r2nwd2tgs7qe1WgTFDpvh_ncLPIkNf08OLsH7CxoDs-sPQvbKgufsU1vTFhh0fXi6Z1pheO1mTnncs6KgLy_nlC7JskPM2Q",
      "e": "AQAB",
      "x5c": [
        "MIICmzCCAYMCBgF6KFytNzANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0ZXIwHhcNMjEwNjIwMDczODIwWhcNMzEwNjIwMDc0MDAwWjARMQ8wDQYDVQQDDAZtYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDNKUsdzdmePiamcbLLGvfZeBP5wI/hg9QIbFDizvK6mWEB5487a9RTK2XHSh6Q/UzzFVrh8Wya/VU2RuPyIXqT+CbJEkCgGMDoISJXunjb6dBzBJXqL9X6LJcp6Mqgfs8oBBmaFSzPbV80RaGmmtEvezeIZBLkRCl0l/8AIhtJYC9TYbFzMVTFNF5UYFKPBau2p0LU36q/JUWgP/WjUcoPTAYYjElLcG3iq9gq7Guldv/mJorWVJhePyvafB3a2Czup7VaBMUOm+H+dws8iQ1/Tw4uwfsLGgOz6w9C9sqC5+xTW9MWGHR9eLpnWmF47WZOedyzoqAvL+eULsmyQ8zZAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAIqK00VqPXHVl8gpybKsyyZCnz+w3HoFO+7c4pVtWHy7eREPdmY4pgyqWKyY4064J/ZJ7TK8JNs6tQf1/LZUmp6fRMXQVRJglJyGz/J4ZBg7kqmzxi9kroNRhcStMidYjEkUHhpTK3dIOfUZ4vaf/OfN3WE8JzsXG5Jxz7DY9wN3oUCeZf6/ju2Lb8gIKAq+7wmxhmkZEZNU21Rx9S6bz4mYMSFaEQ7ldg3y14doiCZYCeVSJSoq74uOdAv+kxdo/FsnGZT8GRKsvCwQMSYAe/l5ccHVceFK3Isxp2EZVNwD/6ytjMkUgUTibakJAGVJMouRZStiy2n2srQ2egDjKhA="
      ],
      "x5t": "HaBzJVfpnNV6jtIK9ZtDn0F7mYA",
      "x5t#S256": "VPN7LyGrZXA_gb6WBOkk-4pUWuzxxctuKAWtdm5mYzk"
    }
  ]
}
Userinfo
{
	"sub": "9e9e8079-e8c4-4eaa-baaf-bcd22707d9a7",
	"email_verified": false,
	"preferred_username": "hoge",
	"email": "aa@aa.com"
}



Discussion