今日やること
Keycloakアドベント 17日目は、OpenID Connectの認可コードフローをやってみたいと思います。Relying Partyを作って、認可コードフローでシングルサインオンをしてみましょう。
というか、認可コードフローってなんだっけ?
事前準備
Keycloakは2日目の記事などを参考にしてインストールしておいてください。テストユーザーはこの記事の中で作ります。
Relying Party (RP) を作る
OpenID Connectをやるなら、当然シングルサインオンするRelying Partyが必要なので、これから作っていきます。Relying Partyは何でもいいのですが、WebサーバーはおなじみのApache HTTPDにして、これにmod_auth_openidc
を入れてみます。
mod_auth_openidc
は、名前の通りApacheに入れるmod_xxx系で、httpd.conf
に設定するだけでRelying Party化することができ、Apache上のWebアプリケーションには、IDトークンとかアクセストークンとかがリクエストヘッダで渡ります。
環境
- Relying Party (RP) の環境
項目 | 値 |
---|---|
OS | CentOS 7.4 |
URL | https://172.26.22.25 |
クライアントID (client_id) | apache24 |
リダイレクトURI | https://172.26.22.25/private/callback |
7系のOSなら、rpm が yum レポジトリにあるのでインストールが簡単だと思います。また、雑な構築なのでURLはIPアドレスにしていますが、ちゃんとFQDNにしましょう。httpsになっているのは中の人の趣味の問題で、httpでもできます(OpenID Connectの仕様はhttps必須ですが)。
- OpenID Provider (OP) の環境
項目 | 値 |
---|---|
URL | https://172.26.22.5 |
エンドポイント | https://172.26.22.5/auth/realms/master/.well-known/openid-configuration |
Keycloakの環境です。こちらも雑な構築なのでIPアドレスですが、ちゃんとFQDNに。。httpsも以下同様。
Keycloakのエンドポイントは、URLにレルム名が入っている通り、レルム別になっています。つまり鍵とかアルゴリズムとかそういったモノは レルム単位に別々に設定できることになります。レルム(≒マルチテナント)という用途を考えると、まぁ当然かな、と。
必要なモノを手に入れる
とにかく動く環境があればいいや、ならyumで入ります。
$ yum install mod_auth_openidc
でも、最新版が使いたい! というわがままな人の場合は GitHub からソースか RPM を取得する必要があります。
本体(ダウンロード先)
- ソースコード:https://github.com/pingidentity/mod_auth_openidc
- RPM:https://github.com/pingidentity/mod_auth_openidc/releases/
この記事執筆時点(2017年11月)では、最新安定版は 2.3.2 みたいなので、これを使ってソースからビルドしてインストールすることにします。
依存ライブラリ
yumで入りますが、cjose だけ yum レポジトリにないので個別にダウンロードします。mod_auth_openidc
ダウンロードページと同じ場所にあります。この記事執筆時点(2017年12月)では v.2.3.0 のページからダウンロードしろ、だそうです。
https://github.com/pingidentity/mod_auth_openidc/releases/v2.3.0
ソースビルドしてインストールする
- 依存ライブラリを yum とか rpm で突っ込む
$ yum install automake
$ yum install curl curl-devel
$ yum install jansson jansson-devel
$ rpm -ivh cjose-0.5.1-1.el7.centos.x86_64.rpm
- configureする
Apacheのmod系インストールのお約束ですが、aprとapr-utilはApache HTTPDをインストールしたファイルを参照する必要があります。いわゆるpkgconfigです。Apache HTTPDをyumでインストールした場合は不要ですが、ソースビルドで入れた場合は、pkgconfigのパスを環境変数PKG_CONFIG_PATH
で設定する必要があります。pkgconfigは (Apacheインストールディレクトリ)/lib/pkgconfig
にあります。また、configure
のときにも apxs
のパスを--with-apxs2
で指定する必要があります。(パスは(Apacheインストールディレクトリ)/bin/apxs
) 。
$ ./autogen.sh
$ export PKG_CONFIG_PATH=/opt/apache-2.4/lib/pkgconfig
$ ./configure --with-apxs2=/opt/apache-2.4/bin/apxs
$ make && make install
うまくいくと、(Apacheインストールディレクトリ)/modules/mod_auth_openidc.so
が生成されます。
設定する
Keycloakに設定する
Keycloak管理コンソールに管理者でログインして、設定します。
「クライアントID」は Relying Party の設定で書いた apache24
にして、「クライアントプロトコル」はopenid-connect
を選択して「保存」を押します。
デフォルトの設定がちょっと残念なので、少し変更しておきます。「アクセスタイプ」をpublic
→ confidential
に変えておきます。また、「有効なリダイレクトURI」には Relying Party の設定で書いたURLを設定します。
「保存」した後、「クレデンシャル」を選択して、「クライアント認証」を「Client ID & Secret」を選択し、「シークレット」の値をコピーして控えておきます。この値がクライアントシークレットになります。
mod_auth_openidc(Apache)を設定する
httpd.conf
に Relying Party の設定します。実際はhttpd.conf
とはファイルを分けて、Include
するようにしますが。
LoadModule auth_openidc_module modules/mod_auth_openidc.so
OIDCProviderMetadataURL https://172.26.22.5/auth/realms/master/.well-known/openid-configuration
OIDCClientID apache24
OIDCClientSecret f6757521-f4e9-4f5e-9ed1-0bf2a6d06cfe
OIDCResponseType code
OIDCScope "openid"
OIDCSSLValidateServer Off
OIDCProviderTokenEndpointAuth client_secret_basic
OIDCRedirectURI https://172.26.22.25/private/callback
OIDCCryptoPassphrase passphrase
OIDCPreservePost On
<Location /private>
AuthType openid-connect
Require valid-user
</Location>
<Location /public>
OIDCUnAuthAction pass
AuthType openid-connect
Require valid-user
</Location>
パラメータを説明しますと..
パラメータ名 | 説明 |
---|---|
OIDCProviderMetadataURL | OpenID Provider(OP)の.well-knownのURL |
OIDCClientID | クライアントID |
OIDCClientSecret | クライアントシークレット。さっきメモっておいたやつ。 |
OIDCResponseType | response_typeの値。認可コードフローなので code を指定する。 |
OIDCScope | スコープ。OpenID Connectを使いたいのでopenid を指定する。他のスコープはとりあえず指定しない |
OIDCSSLValidateServer | OpenID Provider(OP)にアクセスするとき、SSL証明書を検証するか? 今回はテスト環境なので、自己署名証明書(いわゆるオレオレ証明書)なので、検証しないにしておく。本番環境ではちゃんとOnにして検証するようにしましょう。 |
OIDCProviderTokenEndpointAuth | クライアント認証のやり方。クライアントID&シークレットを指定。 |
OIDCRedirectURI | リダイレクトURI |
OIDCCryptoPassphrase | なんだこれは? 多分IDトークンの暗号化かな |
OIDCPreservePost | POSTのときリクエストパラメータを保存しておくか? とりあえず無視しておいていい。 |
他に重要な設定は、どのURLをOpenID Connectによる認証を有効にするか、です。今回は、/private
配下を、要OpenID Connect認証にします。
<Location /private>
AuthType openid-connect
Require valid-user
</Location>
もう一つ重要なことは、リダイレクトURIはOpenID Connectの認証が有効になっているパス配下でないとダメだ、です。今回は /private
配下に対してOpenID Connect認証を有効にしているので、https://172.26.22.5/private/xxxxx
でないとダメ、ということです。xxxxx
の部分は、今回は callback
というパスにしていますが、このURLには何かコンテンツを置いておく必要はありません。逆に言うと、mod_auth_openidcがリクエストをフックしちゃうので、このURLにコンテンツは置けません。それと、スクリーンショットには載せませんが、パスワードの設定をお忘れなく。
これで設定は全部完了です。Apacheを再起動しておきましょう。
テストユーザーを作っておく
じゃあ早速シングルサインオンを… ユーザーを作っていませんでした。もう一回Keycloakに管理者でログインして「ユーザー」の「ユーザーの追加」からユーザーを作成します。設定する項目はなんでもいいのですが、必要最小限の項目+「姓」「名」を設定しておきます。
やってみる
これで設定は全部完了です(2度目)。ではOpenID Connectでシングルサインオンしましょう。まずは Relying Party (https://172.26.22.25/private/headers.pl
)にアクセスすれば、後は勝手に進みます。
やったぜ。ちなみに headers.pl はリクエストヘッダを表示するCGIです。phpinfo()
でもいいです。mod_auth_openidcの場合、IDトークンは、というかIDトークンのClaim
は OIDC_CLAIM_xxx
という名前のヘッダに入ります。アクセストークンは OIDC_access_token
です。
何かおかしいぞ
よく見ると、何か変ですね。
-
scope=openid
しか指定していないのに、姓名(name
)と姓(family_name
)と名(last_name
)が入っている - というか、同意画面が出なかったような
今一度、Keycloakのクライアントの設定を見てみると..
同意画面を出してみる
「同意が必要」を有効にした後、気を取り直して、もう一度やってみます。
同意画面はでました、が... 明らかに要求していないスコープを返そうとしています(名、氏名、Eメール、姓)。また role_uma_authorization
って何だ?
スコープを設定する
クライアント(mod_auth_openidc
)が要求するスコープに応じて、Keycloakが返す値が変わるようにしてみます。今はopenid
だけなので、「ユーザー名」だけになるようにします。Keycloakのスコープのデフォルト設定は、何か変な感じです。ちゃんとスコープを設定してみましょう。「クライアント」から「スコープ」を見てみます。
ロールは出なくなりましたが、スコープの調整ができてないです。今度は「クライアント」から「マッパー」を見てみましょう。「full name
」(姓名)だけ設定を見てみます。
うーん、ダメみたいですね... どうもKeycloakはユーザー属性を含めるかどうかは、同意の有無・IDトークン・アクセストークン・UserInfoですべて別々になっているようです。
IDトークンの内容(Claim)を変えてみる
今度はIDトークンの内容(Claim)を調整してみましょう。「クライアント」>「マッパー」の画面に「IDトークンに追加」という項目があるので、これで調整できそうです。username
だけ有効にして、他を無効にしてみてやってみましょう。(下の図はgiven name
だけですが、username
以外をすべて無効にする)
普通にIDトークンに「姓」「名」が入っているように見えます。本当はどうなっているのか、HTTP通信を覗いて見ましょう。
IDトークンの内容を見てみる
というか、この地点で気づいたのですが、エンドポイントの送信先が https ではなく http になっていますね… 見なかったことにして(無理)、先に進みます。Relying Party(RP) → Keycloak(OP) への通信を tcpdump します。
tcpdump -i any port 80 -w 1.pac -s 0
- リクエスト
POST /auth/realms/master/protocol/openid-connect/token HTTP/1.1
Authorization: Basic YXBhY2hlMjQ6ZjY3NTc1MjEtZjRlOS00ZjVlLTllZDEtMGJmMmE2ZDA2Y2Zl
User-Agent: mod_auth_openidc
Host: 172.26.22.5
Accept: */*
Content-Type: application/x-www-form-urlencoded
Content-Length: 251
grant_type=authorization_code&code=uss.BVNJp6jf36eljWCjnWVjDIw3CAt04GBPPVJ2H_2uFW0.79e07c02-fe25-4829-9f37-c40a0a24f838.9115397f-1a68-444c-bdc4-1f5ac372a1f6&redirect_uri=https%3A%2F%2F172.26.22.25%2Fprivate%2Fcallback&state=Uwkp9CRo0vtNSrSVIQOSLP0PIpU
- レスポンス(※JSONは見やすいように整形していますが、実際は1行です)
HTTP/1.1 200 OK
Date: Mon, 30 Oct 2017 02:05:09 GMT
Server: WildFly/11
X-Powered-By: Undertow/1
Content-Type: application/json
Content-Length: 3249
{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJWa1hGdXFRYldjUnJzbEduZGwzYWtnMkxsdjk0dzlZWHl0aW1qM24tX25JIn0.eyJqdGkiOiJlNjBjOGNlMS05MjcyLTQ3YTQtYjFlMi02NjIyZjU0YjdjMTEiLCJleHAiOjE1MDkzMjkxNjksIm5iZiI6MCwiaWF0IjoxNTA5MzI5MTA5LCJpc3MiOiJodHRwOi8vMTcyLjI2LjIyLjUvYXV0aC9yZWFsbXMvbWFzdGVyIiwiYXVkIjoiYXBhY2hlMjQiLCJzdWIiOiI5YjUzNTIwMC1lZDRjLTQ0NjEtYjM4My1jMTI5ZmY1NzUxMDAiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJhcGFjaGUyNCIsIm5vbmNlIjoiZkhNNXNndWtiZlI0TW1TN3g1R2E2LUdsSU1UZ0hIeVQzTUxvVGMzNWh4WSIsImF1dGhfdGltZSI6MTUwOTMyOTEwOSwic2Vzc2lvbl9zdGF0ZSI6Ijc5ZTA3YzAyLWZlMjUtNDgyOS05ZjM3LWM0MGEwYTI0ZjgzOCIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOltdLCJyZXNvdXJjZV9hY2Nlc3MiOnt9LCJuYW1lIjoidTExMSB0ZXN0IiwicHJlZmVycmVkX3VzZXJuYW1lIjoidTExMSIsImdpdmVuX25hbWUiOiJ1MTExIiwiZmFtaWx5X25hbWUiOiJ0ZXN0In0.M5kBlL7_kVXAjW5nOFyRlfP7EqPq2XbM1MGdw6-cqfOTbgU2rwFHIx99YZ6HSAlyf2ezBuVgKfsJPC74SNZ8EkDHTAw6fYnMAyo2wwKX4HOuqPZ6tjjUKL9hPn4rkzN8TdOFD1teCTTvkG_RbtOvJYs4LaLjTpLWKsS6RzxVmqD3woKzedKM9q-lx8mMJgNnmQl7867Pr7ACPMCxBb_6XILO2w2v6kXP0LCiNwHePPYpmIawnwf7uizhXF1KmLgwZKbZ5gsGni-Bzm7Kh3N8vF3bBOQF-NxNhkgZ73GKqZWY04i3kgJmPsy7s8wFBAEZ2pC2BLOwPx_rLCCctBMKSA",
"expires_in": 60,
"refresh_expires_in": 1800,
"refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJWa1hGdXFRYldjUnJzbEduZGwzYWtnMkxsdjk0dzlZWHl0aW1qM24tX25JIn0.eyJqdGkiOiIwMmU0OGZkZi1hM2JlLTQ1ZTQtOTdlOC1hNjU0MjY5MDFmYzUiLCJleHAiOjE1MDkzMzA5MDksIm5iZiI6MCwiaWF0IjoxNTA5MzI5MTA5LCJpc3MiOiJodHRwOi8vMTcyLjI2LjIyLjUvYXV0aC9yZWFsbXMvbWFzdGVyIiwiYXVkIjoiYXBhY2hlMjQiLCJzdWIiOiI5YjUzNTIwMC1lZDRjLTQ0NjEtYjM4My1jMTI5ZmY1NzUxMDAiLCJ0eXAiOiJSZWZyZXNoIiwiYXpwIjoiYXBhY2hlMjQiLCJub25jZSI6ImZITTVzZ3VrYmZSNE1tUzd4NUdhNi1HbElNVGdISHlUM01Mb1RjMzVoeFkiLCJhdXRoX3RpbWUiOjAsInNlc3Npb25fc3RhdGUiOiI3OWUwN2MwMi1mZTI1LTQ4MjktOWYzNy1jNDBhMGEyNGY4MzgiLCJyZXNvdXJjZV9hY2Nlc3MiOnt9fQ.SyzqS-2iQczTJCfxzfwXop1yfcDRFU-btCSm0kKheRx4rydIYpCPT1nB_NBmvjh6YZrZTJBykDMkio_VfxX5KoRLHGc_lmPfy0mKnEVDhC4xgXDQrVnu5pQa3_JztpzWZ49mYbjX9aMX4XD6e-qlneg6yf-9MzRrTXFZHFFpSPAEBSMwFW1K5zS6QuYf3iYNeaoeF46xsvhSXS535zWBymx9na0AZu4k_crlDKSHOWQ1fMYpRyKb9LXeciuAOzkeun7aHqtRyj3wSk1N3UGSoZk7CXc1tSZKL5AslJSsVjDI-P4UhjDJ4F7r-NY19MYcKwQCsOObmxZXtRj56t8Mww",
"token_type": "bearer",
"id_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJWa1hGdXFRYldjUnJzbEduZGwzYWtnMkxsdjk0dzlZWHl0aW1qM24tX25JIn0.eyJqdGkiOiJjNzUwMGU0ZC01OWYwLTQ5NzYtYmU0MS1jMjdiZjI5MTA1ZjYiLCJleHAiOjE1MDkzMjkxNjksIm5iZiI6MCwiaWF0IjoxNTA5MzI5MTA5LCJpc3MiOiJodHRwOi8vMTcyLjI2LjIyLjUvYXV0aC9yZWFsbXMvbWFzdGVyIiwiYXVkIjoiYXBhY2hlMjQiLCJzdWIiOiI5YjUzNTIwMC1lZDRjLTQ0NjEtYjM4My1jMTI5ZmY1NzUxMDAiLCJ0eXAiOiJJRCIsImF6cCI6ImFwYWNoZTI0Iiwibm9uY2UiOiJmSE01c2d1a2JmUjRNbVM3eDVHYTYtR2xJTVRnSEh5VDNNTG9UYzM1aHhZIiwiYXV0aF90aW1lIjoxNTA5MzI5MTA5LCJzZXNzaW9uX3N0YXRlIjoiNzllMDdjMDItZmUyNS00ODI5LTlmMzctYzQwYTBhMjRmODM4IiwiYWNyIjoiMSIsInByZWZlcnJlZF91c2VybmFtZSI6InUxMTEifQ.W0gBnz2m0HbuSlYPD4VV24UEfC8BMtDoRl3Rikh6KcYw8QXf9O51KSXhx8xdS9kuAepRfCVAhP3ckk5TdPLCk8fESTWy6s1UaC4i4AcpFpF7-ZtZCkRtpk5Ehd7x-SLg204vBsC5C84Kct68AZU1yqsblKWSlhzoxXNnR2k1D6gqLHYWiaqZO2BG14EYy_BSNhEbhWQEF5Gw8MQwl6BQ7WsZ1lF-ox6Xg6kDYuHmKkSxXo-IxjIpiE-r5z8NuwpUla_I1pp0dvUGXTCn6uJwp08tJ6V5gCjcWdoJKQiXJT9Cafe63GJwPb0T7IQWfdqKJuBLACnwB1NU0_TMbXZQXQ",
"not-before-policy": 0,
"session_state": "79e07c02-fe25-4829-9f37-c40a0a24f838"
}
- IDトークン(Claim)のデコード(※見やすいように整形しています)
{
"jti": "c7500e4d-59f0-4976-be41-c27bf29105f6",
"exp": 1509329169,
"nbf": 0,
"iat": 1509329109,
"iss": "http://172.26.22.5/auth/realms/master",
"aud": "apache24",
"sub": "9b535200-ed4c-4461-b383-c129ff575100",
"typ": "ID",
"azp": "apache24",
"nonce": "fHM5sgukbfR4MmS7x5Ga6-GlIMTgHHyT3MLoTc35hxY",
"auth_time": 1509329109,
"session_state": "79e07c02-fe25-4829-9f37-c40a0a24f838",
"acr": "1",
"preferred_username": "u111"
}
ユーザーID(sub
)とユーザー名(preferred_usename
)だけで、ちゃんと姓名は入っていませんでした。
ユーザー情報エンドポイントの通信内容を見てみる
実はmod_auth_openidc
は、「⑥ユーザー情報エンドポイント」にも(あれば)アクセスして追加のユーザー情報を取得しに行きます。同じようにHTTP通信の中を見てみましょう。
- リクエスト
リクエストヘッダ:Authorization: Bearer
にアクセストークンを付与して送信しています。
GET /auth/realms/master/protocol/openid-connect/userinfo HTTP/1.1
User-Agent: mod_auth_openidc
Host: 172.26.22.5
Accept: */*
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJWa1hGdXFRYldjUnJzbEduZGwzYWtnMkxsdjk0dzlZWHl0aW1qM24tX25JIn0.eyJqdGkiOiJlNjBjOGNlMS05MjcyLTQ3YTQtYjFlMi02NjIyZjU0YjdjMTEiLCJleHAiOjE1MDkzMjkxNjksIm5iZiI6MCwiaWF0IjoxNTA5MzI5MTA5LCJpc3MiOiJodHRwOi8vMTcyLjI2LjIyLjUvYXV0aC9yZWFsbXMvbWFzdGVyIiwiYXVkIjoiYXBhY2hlMjQiLCJzdWIiOiI5YjUzNTIwMC1lZDRjLTQ0NjEtYjM4My1jMTI5ZmY1NzUxMDAiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJhcGFjaGUyNCIsIm5vbmNlIjoiZkhNNXNndWtiZlI0TW1TN3g1R2E2LUdsSU1UZ0hIeVQzTUxvVGMzNWh4WSIsImF1dGhfdGltZSI6MTUwOTMyOTEwOSwic2Vzc2lvbl9zdGF0ZSI6Ijc5ZTA3YzAyLWZlMjUtNDgyOS05ZjM3LWM0MGEwYTI0ZjgzOCIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOltdLCJyZXNvdXJjZV9hY2Nlc3MiOnt9LCJuYW1lIjoidTExMSB0ZXN0IiwicHJlZmVycmVkX3VzZXJuYW1lIjoidTExMSIsImdpdmVuX25hbWUiOiJ1MTExIiwiZmFtaWx5X25hbWUiOiJ0ZXN0In0.M5kBlL7_kVXAjW5nOFyRlfP7EqPq2XbM1MGdw6-cqfOTbgU2rwFHIx99YZ6HSAlyf2ezBuVgKfsJPC74SNZ8EkDHTAw6fYnMAyo2wwKX4HOuqPZ6tjjUKL9hPn4rkzN8TdOFD1teCTTvkG_RbtOvJYs4LaLjTpLWKsS6RzxVmqD3woKzedKM9q-lx8mMJgNnmQl7867Pr7ACPMCxBb_6XILO2w2v6kXP0LCiNwHePPYpmIawnwf7uizhXF1KmLgwZKbZ5gsGni-Bzm7Kh3N8vF3bBOQF-NxNhkgZ73GKqZWY04i3kgJmPsy7s8wFBAEZ2pC2BLOwPx_rLCCctBMKSA
- レスポンス(※JSON部分は、見やすいように整形しています)
HTTP/1.1 200 OK
Date: Mon, 30 Oct 2017 02:05:09 GMT
Server: WildFly/11
Cache-Control: no-cache
X-Powered-By: Undertow/1
Content-Type: application/json
Content-Length: 115
{
"sub": "9b535200-ed4c-4461-b383-c129ff575100",
"preferred_username": "u111",
"given_name": "u111",
"family_name": "test"
}
こっちのアクセスで追加のユーザー情報を取得していました。
ユーザー属性をカスタムしてみる
Keycloak 定義済みだけではなく、勝手に定義したユーザー属性をIDトークンやユーザー情報エンドポイントの公開情報に入れてみましょう。
「名前」(属性名)はなんでもいいですが、今回はhoge
で。同意の有無はどちらでもいいです。「マッパータイプ」は色々あってよく分かりませんが、なんとなく「User Attribute
」を選択。「トークンクレーム名」にはhoge
、「ユーザー属性」はfuga
にしておきます。あとは「IDトークンに追加」「UserInfo②追加」を有効にしておきます。
ユーザーにも取得元属性であるfuga
を追加しましょう。「ユーザー」からu111
を選択して「属性」タブを選択します。
この画面で key
に fuga
、「値」に「ふが」を入力して、「追加」を押して保存します。
ちゃんと属性 hoge
にユーザー属性 fuga
の値「ふが」が入っていました。IDトークンの中身も見てみましょう。(見やすいように整形しています)
{
"jti": "d86d78d7-7c99-440a-95d0-bcbd9d53aa69",
"exp": 1509332783,
"nbf": 0,
"iat": 1509332723,
"iss": "http://172.26.22.5/auth/realms/master",
"aud": "apache24",
"sub": "9b535200-ed4c-4461-b383-c129ff575100",
"typ": "ID",
"azp": "apache24",
"nonce": "V1tIvqOpONeSSRfwsKIhJj7UFoaSxQNaELM6WDd5euI",
"auth_time": 1509332723,
"session_state": "fb659484-cb1d-4565-9239-dae3337bb4f5",
"acr": "1",
"hoge": "ふが",
"preferred_username": "u111"
}
ちゃんと入っていました。
まとめ
Keycloakはクライアントからのスコープの要求とは関係なく、何を返すかはKeycloakの設定のみで決定してしまうようです。このことはIDトークンとユーザー情報エンドポイントの公開情報の両方とも同じようです。また、クライアントやスコープ単位に「同意が必要」の設定があるように、ユーザー同意自体を求めなかったり、スコープによっては同意を求めないで、Keycloakは属性をRelying Partyに渡してしまうようです。クライアントの要求していないスコープを返そうとするのは悪いわけではないのですが、少なくともスコープ単位で同意を求めない、という設定をするのはあまりよくないように思います。同意とは、ユーザーが自分自身の情報をRelying Partyに渡すか渡さないかを、ユーザー自身に決定させるためのものですから。ただ、少なくとも、Keycloakはかなり細かく設定ができることが分かりました。
一方、IDトークンやユーザー情報エンドポイントの公開情報は、各属性単位に設定できるようです。1つ問題になるのが、Relying Partyがユーザー情報を取得するとき、IDトークンに入れてもらうのか、ユーザー情報エンドポイントから取得するのか、どちらにすべきかということです。OpenID Connectの仕様(RFC)ではどちらでもOKなので、やりやすい方を選択、ということになるでしょう。OpenID Connectが世間に出てきたはじめのうちは、IDトークンに必要な属性を入れてもらうほうが主流でした。おそらく、Relying Partyの実装の負担を減らしたい、OpenID Provider(OP)がまだユーザー情報エンドポイントを未実装、という事情があったのだと思います。ただ、現在はユーザー情報エンドポイントから取得するのが主流になっているように思います。これは、リフレッシュトークンがあるからだと思います。
話題としては、クライアント認証の方法やアクセストークンの内容、リフレッシュトークン、認証認可リクエストでの細かいパラメータなどがあるのですが、さすがにこの記事のページが長すぎるので、今日はこの辺にしておきます。