KeycloakでのOpenID Connectお勉強メモ #3
はじめに
#2からのつづきです。
今回は以下の点について調べていきます。
-
ログアウトのフロー
-
ログアウトしないでトップに戻る
-
ログアウトしないでトップに戻ってからの再ログイン
-
アクセストークンの有効期限内の場合
-
アクセストークンの有効期限が切れている場合
-
Cookieを削除した場合
-
-
再認証
検証環境は #1 を参照してください
1. ログアウトのフロー
ログアウトボタンを押すとRPとKeycloak両方ログアウトされます、こういうのを シングルログアウト(SLO) というみたいです。
このシングルログアウトですが、OpenID Connectの仕様はまだ正式ではなく Draft版だそうでmod-auth-openidcとKeycloakで連携しているシングルログアウトも独自なもののようです。
- OpenID Connect Front-Channel Logout 1.0
- OpenID Connect Back-Channel Logout 1.0
- 参考 : Keycloakのシングル・ログアウト(SLO)についてのまとめ
- 参考 : 9. How do I logout users?
なので あんまり OpenID Connect のお勉強にならないかもしれませんが簡単に見てみます。
Logout RP
- シングルログアウトは
mod-auth-openidc
のリダイレクトURIにlogout
パラメータをつけて投げるところから始まる - logoutパラメータの値はログアウト完了後に表示する画面を指定する
-
mod-auth-openidc
はRP側のログアウト処理(セッションの削除)を行った後 Keycloakのログアウトエンドポイントにリダイレクトするレスポンス(302 Found)を返す
Logoutボタンのクリックアクション
/secure
? logout = http://test-oidc-rp:8081/loggedout.html
Logout Keycloak
-
End-UserはRPからのリダイレクトレスポンスを受けてKeycloakのログアウトエンドポイントに飛んでいく
-
Keycloakのログアウトエンドポイントは OpenID Configuration の
end_session_endpoint
に定義されている -
ログアウトエンドポイントのパラメータ
post_logout_redirect_uri
にログアウト完了後のリダイレクト先が付いている -
また、
id_token_hint
パラメータでログアウトするユーザーの情報を渡している -
Keycloakは対象ユーザーのログアウト処理(セッション削除)して
post_logout_redirect_uri
にリダイレクトするレスポンス(302 Fount)を返す -
End-Userはそのままリダイレクト先に飛んでいき ログアウト画面 に遷移する
まとめ
- ログアウトは
mod-auth-openidc
のリダイレクトURIにlogout
パラメータをつけて投げるだけでシングルログアウトされる、ただし、この連携は OpenID Connect の仕様ではないようだ - Keycloakのログアウトエンドポイントは OpenID Configuration の
end_session_endpoint
に定義されているものなので OpenID Connect の仕様 に則っている
2. ログアウトしないでトップに戻る
単にTOPページに戻るだけっすね。
3. ログアウトしないでトップに戻ってからの再ログイン
ログアウトしないでトップに戻ってから再度ログインしたときのフローを見てみます
アクセストークンの有効期限内の場合
トップに戻ってすぐにまたログインするとKeycloakへの問い合わせは発生していません。
これは取得したトークンがまだ有効だということをRPだけで確認しているからですね。mod-auth-openidcがアクセストークンの有効期限をチェックしてまだ有効だということで通しているようです。
- この場合、いちいちKeycloakに問い合わせるコストが無いというメリットがあるが、Keycloak側からトークンを失効させてもRPには伝わらないというデメリットもある
- このデメリットの対策としてトークンの有効期限を1分と短くしているんだろう
アクセストークンの有効期限が切れている場合
アクセストークンの有効期限は1分なのでトークンが失効した頃を見計らってログインしてみると画面にYour Access token has expiredとなっています。(そう表示されるように作っている)
アクセストークンの有効期限が切れていても mod_auth_openidc
は何もしないようです。
なので、手動でアクセストークンを更新する Refresh Access token ボタンを作ってこのボタンからアクセストークンを更新するフローを見てみました。
memo :
mod_auth_openidc
でアクセストークンを自動的に更新する便利設定が見つからず(そんな機能はないかも) 手動で更新する方法にしました。mod_auth_openidc
でアクセストークンを更新するhookはこちらを参照 Access Tokens and Refresh Tokens
Refresh Access token
ボタンをクリックするとアクセストークンを更新します、このとき、既に保持しているもう一つのトークン「リフレッシュトークン」を使っています。この手順は OpenID Connect の仕様です。
OpenID Connect Core 1.0 - 12. Using Refresh Tokens
Refresh Access tokenボタンのクリックアクション
/secure
? refresh = http://test-oidc-rp:8081/secure/welcome.php
& access_token = $access_token
まとめ
-
アクセストークンの再取得はリフレッシュトークンを使う
-
つまりアカウントクレデンシャルを使った認可コード取得は行わない
-
アカウントクレデンシャル(パスワード)を極力通信しないところがポイント
-
ボタンクリックしたら全てリダイレクトしてやるのでユーザー観点で手間はない
Cookieを削除した場合
アクセストークンがなくなったらどうなるのか?
アクセストークンはRP側のセッションCookieに紐付いてRP側で持っているようなので、このCookieを削除したらアクセストークンをロストすることになるだろう。
やってみたらこんなフローになった。
Point-1
一旦ログインが完了した状態ではEnd-User側は以下の情報を保持している。
RPとKeycloakそれぞれのセッションクッキーを持っていてログインしている状態。
Cookie | 対象 |
---|---|
mod_auth_openidc_session | RP |
AUTH_SESSION_ID_LEGACY | Keycloak |
KEYCLOAK_IDENTITY_LEGACY | Keycloak |
KEYCLOAK_SESSION_KEGACY | Keycloak |
この状態から ブラウザに保管されているRP用のセッションクッキー(mod_auth_openidc_session)を削除すると、RPはKeycloakから発行されたアクセストークンがどれかわからなくなる。
そんな状態でRPにアクセスするとmod-auth-openidcはアクセストークンが無いためトークンをもらうためにKeycloakにリダイレクトする。
Point-2
- End-UserはKeycloakからもらったセッショントークンは持っているのでKeycloakにリダイレクトする際にKeycloak用のCookieがついていく
- KeycloakはCookieからセッションが有効である(認証済み)と判断し、即座に認可コードを発行する(ログイン画面は表示しない)
以降
以降は通常のフローでアクセストークンを取得する。
まとめ
- リフレッシュトークンも失効した場合など、RP側で保持するアクセストークンがなくなった場合は認可コード再取得するフローになる
- Keycloak側でセッションが残っていればアカウントクレデンシャル(パスワード)を通信することはない
- Keycloak側のセッションもタイムアウトしていればログイン画面を表示する
ちなみに、有効期限設定はこうなっています。
項目 | 設定項目 | 値 | |
---|---|---|---|
アクセストークンの有効期限 | Keycloak : Access Token Lifespan | 1分 | JWTに格納される |
リフレッシュトークンの有効期限 | Keycloak : SSO Session Idle | 30分 | JWTに格納される |
Keycloakセッションの有効期限 | Keycloak : SSO Session Max | 10時間 | リフレッシュトークン発行後に強制的に無効になる時間 |
RPセッションの有効期限 | mod-auth-openidc : OIDCSessionInactivityTimeout | 5分 | 無操作のとき |
4. 再認証
ログイン後 重要な操作をする際に、再度ユーザー確認したい場合があります。そんなときはOpenID Connect的にはどうするのかやってみました。
OpenID Connect では ユーザーの認証をする際は まず **認可エンドポイント(Authorization Endpoint) ** にアクセスしますが この時指定する prompt
というパラメータで 再認証 の指定をすることができます。
以下は 3.1.2.1. Authentication Request の抜粋
prompt
none
Authorization Server はいかなる認証および同意 UI をも表示してはならない (MUST NOT). End-User が認証済でない場合, Client が要求する Claim 取得に十分な事前同意を取得済でない場合, またはリクエストを処理するために必要な何らかの条件を満たさない場合には, エラーが返される. 典型的なエラーコードは
login_required
,interaction_required
であり, その他のコードは Section 3.1.2.6 で定義されている. これは既存の認証と同意の両方, またはいずれかを確認する方法として使用できる.login
Authorization Server は End-User を再認証するべきである (SHOULD). 再認証が不可能な場合はエラーを返す (MUST). 典型的なエラーコードは
login_required
である.consent
Authorization Server は Client にレスポンスを返す前に End-User に同意を要求するべきである (SHOULD). 同意要求が不可能な場合はエラーを返す (MUST). 典型的なエラーコードは
consent_required
である.select_account
Authorization Server は End-User にアカウント選択を促すべきである (SHOULD). この prompt 値は, End-User が Authorization Server 上に複数アカウントを持っているとき, 現在のセッションに紐づくアカウントの中から一つを選択することを可能にする. End-User によるアカウント選択が不可能な場合はエラーを返す (MUST). 典型的なエラーコードは
account_selection_required
である.
というわけで、ログイン後に表示する Welcome 画面に setting
というボタンを追加して このボタンがクリックされたときに遷移するsetting画面は再認証が必要である とした場合は必ずログイン画面を表示してアクセストークンを取得します。
settingボタンのクリックアクション
http://test-oidc-keycloak:8080/auth/realms/master/protocol/openid-connect/auth
? response_type=code
& scope=openid
& client_id=test
& redirect_uri=http://test-oidc-rp:8081/secure/setting/setting-content.html
& prompt=login
おつかれさまでした
OpenID Connect 完全に理解した?
Discussion