📝

KeycloakでのOpenID Connectお勉強メモ #3

2021/07/21に公開

はじめに

#2からのつづきです。
今回は以下の点について調べていきます。

  1. ログアウトのフロー

  2. ログアウトしないでトップに戻る

  3. ログアウトしないでトップに戻ってからの再ログイン

    • アクセストークンの有効期限内の場合

    • アクセストークンの有効期限が切れている場合

    • Cookieを削除した場合

  4. 再認証

検証環境は #1 を参照してください

1. ログアウトのフロー

ログアウトボタンを押すとRPとKeycloak両方ログアウトされます、こういうのを シングルログアウト(SLO) というみたいです。

このシングルログアウトですが、OpenID Connectの仕様はまだ正式ではなく Draft版だそうでmod-auth-openidcとKeycloakで連携しているシングルログアウトも独自なもののようです。

なので あんまり 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 Configurationend_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