本記事では、KeycloakでOIDCを使いシングルサインオンを実行してみようと思います。
認可にはいくつかの流れ(フロー)があり、今回はその中でも特にセキュアである認可コードフローでシングルサインオンを実現してみようと思います。
認可コードフローとは
認可コードフローとは、クライアントID・クライアントシークレットを安全に保存できるアプリ(コンフィデンシャルクライアント)に適したフローです。
ユーザを介さずクライアントとOP(認証基盤)間でのみアクセストークンの受け渡しをするため、アプリケーションユーザにアクセストークンが流出するリスクが低いことが他のフローと比較したときの大きな特徴です。
以下は具体的なシーケンス図となります。
シーケンスを見ていただけるとわかる通り、トークンエンドポイントでアプリ側からKeycloakへリクエストを実施しており、アプリ側が保持しているクライアントIDとクライアントシークレットがBasic認証のヘッダー情報としてリクエストに含まれた状態となります。
リクエストにクライアントIDとクライアントシークレットを含める必要があることから、実際に運用する際はクライアント情報が外部に漏出する恐れが低いコンフィデンシャルクライアントで本フローを活用することがベストプラクティスと言われております。
構築手順
以下のような構成で構築を行う。
OpenID Provider
タイトルにもある通り、OpenID ProviderとしてKeycloakを用います。
Keycloakの構築手順は公式サイトでも紹介されております。
今回はOpenJDKを手元にインストールして実行してみます。
具体的な構築手順は以下の通りです。
- OpenJDKをインストール
- keycloakをダウンロード
- keycloakを実行
- 特権ユーザを設定
- レルム作成
- クライアント作成
- テスト用ユーザ作成
OpenJDKのインストール
OpenJDKをインストールします。
今回は構築手順にのっとりOpenJDK17をインストールしましょう。
$ sudo apt install openjdk-17-jdk-headless
$ java --version
openjdk 17.0.9 2023-10-17
無事にインストールできました。
Keycloakのダウンロード
Keycloakを公式リポジトリからダウンロードします。
最新版をダウンロードしましょう。(本記事は2023年末の内容のためじゃっかんバージョンが前のものになっております)
wget https://github.com/keycloak/keycloak/releases/download/23.0.4/keycloak-23.0.4.zip
Keycloakを実行
それでは、Keycloakを動かしてみましょう。
bash bin/kc.sh start-dev
2024-01-16 19:10:11,518 WARN [io.quarkus.agroal.runtime.DataSources] (main) Datasource <default> enables XA but transaction recovery is not enabled. Please enable transaction recovery by setting quarkus.transaction-manager.enable-recovery=true, otherwise data may be lost if the application is terminated abruptly
2024-01-16 19:10:12,955 WARN [org.infinispan.PERSISTENCE] (keycloak-cache-init) ISPN000554: jboss-marshalling is deprecated and planned for removal
2024-01-16 19:10:13,192 WARN [org.infinispan.CONFIG] (keycloak-cache-init) ISPN000569: Unable to persist Infinispan internal caches as no global state enabled
特権ユーザを設定
Keycloakではデフォルトでmasterレルムが備わっております。
このレルムを編集するためには特権ユーザを用意してあげる必要があります。
今回はadminというユーザを用意してあげましょう。
レルム作成
次に、レルムを作成しましょう。
左メニューのレルム選択画面で[Create realm]というボタンがあるのでクリックしましょう。
以下の画面が出てくるので、[Realm name]でレルム名を入力します。今回は[myrealm]という名前にしておきます。
作成に成功すれば以下のような画面が出てきます。
クライアント作成
次にクライアントを作成しましょう。
左メニューで[Clients]をクリックしましょう。
次に[Create Client]ボタンをクリックします。
以下のように設定します。
設定項目 | 設定する値 |
---|---|
Client type | OpenID Connect |
Client ID | apache24 |
Name | 自由記述 |
Description | 自由記述 |
Always display in UI | Off |
次に、以下のような画面が表示されます。
こちらは以下のように設定しましょう。
設定項目 | 設定する値 |
---|---|
Client authentication | On |
Authorization | Off |
Authentication flow | 今回はStandard flow にチェックしておく |
Authentication flowのチェック項目にはいろいろありますが、OAuth2.0に照らし合わせると以下の通りです。
チェック項目 | OAuth2.0で相当するフロー |
---|---|
Standard flow | 認可コードフロー |
Implicit flow | インプリシットフロー |
Direct access grants | リソースオーナーパスワードクレデンシャル |
Service accounts roles | クライアントクレデンシャル |
後日の記事ではStandard flow以外の各フローを用いる予定なので、留意しておいてください。
最後に、ログイン時のURLに関する設定を行います。
認可コードフローとインプリシットフローではリダイレクトURIが必要になるので、Valid redirect URIs
に適切なリダイレクトURIを設定しておきます。このとき、http://localhost:81/private/callback
を指定します(こちらのURIはRPを設定するときに再登場します)。
テスト用ユーザ作成
さて、最後の手順となります。
クライアントを作成したので、あとはクライアントでログインするときのユーザ情報を追加しましょう。
ユーザ情報は左メニューの[Users]を選択し、[Add User]ボタンをクリックします。
クリックしたのち、Usernameに適当な文字列を入力して[Create]ボタンを押下することでテストユーザが作成されます(簡単な手順なのでスクショは省略)。
RelyingParty
Relying Partyを構築します。(以後はRPと略記)
RPを用意することで、外部IdPに認証機能を委託して、クライアント側がシングルサインオンを実現できるようになります。
今回RPとして利用するのは、Apacheのモジュールとして提供されている「mod_auth_openidc」です。
こちらのモジュールをwebサーバに組み込むことで、SSOのフローをクライアント側で実施できます。
以下は具体的な構築手順です。
- Apache2のインストール
- mod_auth_openidcのインストール
- 設定ファイルの編集
Apache2のインストール
Apache2はパッケージ管理ツールを使ってインストールしましょう。
$ sudo apt install apache2
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following packages were automatically installed and are no longer required:
bridge-utils dns-root-data dnsmasq-base ubuntu-fan
Use 'sudo apt autoremove' to remove them.
The following additional packages will be installed:
apache2-bin apache2-data apache2-utils bzip2 libapr1 libaprutil1 libaprutil1-dbd-sqlite3 libaprutil1-ldap liblua5.3-0
mailcap mime-support ssl-cert
Suggested packages:
apache2-doc apache2-suexec-pristine | apache2-suexec-custom www-browser bzip2-doc
The following NEW packages will be installed:
apache2 apache2-bin apache2-data apache2-utils bzip2 libapr1 libaprutil1 libaprutil1-dbd-sqlite3 libaprutil1-ldap
liblua5.3-0 mailcap mime-support ssl-cert
0 upgraded, 13 newly installed, 0 to remove and 2 not upgraded.
Need to get 2139 kB of archives.
After this operation, 8518 kB of additional disk space will be used.
Do you want to continue? [Y/n] Y
Get:1 http://archive.ubuntu.com/ubuntu jammy-updates/main amd64 libapr1 amd64 1.7.0-8ubuntu0.22.04.1 [108 kB]
...
インストール実行後、動作するか見てみましょう。
$ apache2 -v
Server version: Apache/2.4.52 (Ubuntu)
Server built: 2023-10-26T13:44:44
$ systemctl status apache2
● apache2.service - The Apache HTTP Server
Loaded: loaded (/lib/systemd/system/apache2.service; enabled; vendor preset: enabled)
Active: active (running) since Fri 2023-12-22 16:17:56 JST; 3min 54s ago
Docs: https://httpd.apache.org/docs/2.4/
Main PID: 5160 (apache2)
Tasks: 55 (limit: 9431)
Memory: 21.1M
CGroup: /system.slice/apache2.service
├─5160 /usr/sbin/apache2 -k start
├─5161 /usr/sbin/apache2 -k start
└─5162 /usr/sbin/apache2 -k start
Dec 22 16:17:56 kan-pc systemd[1]: Starting The Apache HTTP Server...
Dec 22 16:17:56 kan-pc systemd[1]: Started The Apache HTTP Server.
動いているようなので、試しにブラウザでアクセスしてみましょう。
また、今回はRPを81番ポートに対応させるため、以下のようにポートの設定を変更します。
Listen 81
<IfModule ssl_module>
Listen 443
</IfModule>
<IfModule mod_gnutls.c>
Listen 443
</IfModule>
mod_auth_openidcのインストール
さて、次はApache2でOpenID Connectを利用できるように設定を組みこみしましょう。
$ sudo apt install libapache2-mod-auth-openidc
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following packages were automatically installed and are no longer required:
bridge-utils dns-root-data dnsmasq-base ubuntu-fan
Use 'sudo apt autoremove' to remove them.
The following additional packages will be installed:
libcjose0 libhiredis0.14
The following NEW packages will be installed:
libapache2-mod-auth-openidc libcjose0 libhiredis0.14
0 upgraded, 3 newly installed, 0 to remove and 2 not upgraded.
Need to get 241 kB of archives.
After this operation, 773 kB of additional disk space will be used.
Do you want to continue? [Y/n] Y
Get:1 http://archive.ubuntu.com/ubuntu jammy/universe amd64 libhiredis0.14 amd64 0.14.1-2 [32.8 kB]
Get:2 http://archive.ubuntu.com/ubuntu jammy-updates/universe amd64 libcjose0 amd64 0.6.1+dfsg1-3ubuntu1.1 [36.9 kB]
Get:3 http://archive.ubuntu.com/ubuntu jammy/universe amd64 libapache2-mod-auth-openidc amd64 2.4.11-1 [171 kB]
Fetched 241 kB in 12s (19.8 kB/s)
Selecting previously unselected package libhiredis0.14:amd64.
(Reading database ... 27620 files and directories currently installed.)
Preparing to unpack .../libhiredis0.14_0.14.1-2_amd64.deb ...
Unpacking libhiredis0.14:amd64 (0.14.1-2) ...
Selecting previously unselected package libcjose0.
Preparing to unpack .../libcjose0_0.6.1+dfsg1-3ubuntu1.1_amd64.deb ...
Unpacking libcjose0 (0.6.1+dfsg1-3ubuntu1.1) ...
Selecting previously unselected package libapache2-mod-auth-openidc.
Preparing to unpack .../libapache2-mod-auth-openidc_2.4.11-1_amd64.deb ...
Unpacking libapache2-mod-auth-openidc (2.4.11-1) ...
Setting up libcjose0 (0.6.1+dfsg1-3ubuntu1.1) ...
Setting up libhiredis0.14:amd64 (0.14.1-2) ...
Setting up libapache2-mod-auth-openidc (2.4.11-1) ...
apache2_invoke: Enable module auth_openidc
Processing triggers for libc-bin (2.35-0ubuntu3.5) ...
インストールを実行しました。
設定ファイルの編集
次は、設定ファイルを編集しましょう。
今回は以下のように編集します。
OIDCProviderMetadataURL http://localhost:8080/realms/myrealm/.well-known/openid-configuration
OIDCClientID apache24
OIDCClientSecret xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
OIDCResponseType code
OIDCScope "openid"
OIDCSSLValidateServer Off
OIDCProviderTokenEndpointAuth client_secret_basic
OIDCRedirectURI http://localhost:81/private/callback
OIDCCryptoPassphrase passphrase
OIDCPreservePost On
<Location /private>
AuthType openid-connect
Require valid-user
</Location>
変数に関しては以下のようになっております。
変数 | 概要 |
---|---|
OIDCProviderMetadataURL | OpenID Connectに関するメタデータ |
OIDCClientID | クライアントID(Keycloakで設定した値を入力) |
OIDCClientSecret | クライアントシークレット |
OIDCResponseType | 認証フローのタイプ(今回は認可コードフローなのでcode) |
OIDCScope | スコープ |
OIDCSSLValidateServer | SSLの有効化を設定 |
OIDCProviderTokenEndpointAuth | トークンエンドポイントとの通信時の認証タイプ |
OIDCRedirectURI | リダイレクトURI |
OIDCCryptoPassphrase | cookieやセッション情報を暗号化するために用いるパスフレーズ |
OIDCPreservePost | POST実行時にリクエストパラメータを保存するか |
以上の設定を用いて接続ができるか確認してみましょう。
今回はPHPを用いて疎通確認用のechoサーバを構築しましょう。
echoサーバ
以下のようにphpをモジュールとしてインストールします。
$ sudo apt install libapache2-mod-php
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following packages were automatically installed and are no longer required:
bridge-utils dns-root-data dnsmasq-base ubuntu-fan
Use 'sudo apt autoremove' to remove them.
The following additional packages will be installed:
libapache2-mod-php8.1
Suggested packages:
php-pear
The following NEW packages will be installed:
libapache2-mod-php libapache2-mod-php8.1
0 upgraded, 2 newly installed, 0 to remove and 2 not upgraded.
Need to get 1769 kB of archives.
After this operation, 5422 kB of additional disk space will be used.
Do you want to continue? [Y/n] Y
Get:1 http://archive.ubuntu.com/ubuntu jammy-updates/main amd64 libapache2-mod-php8.1 amd64 8.1.2-1ubuntu2.14 [1766 kB]
Get:2 http://archive.ubuntu.com/ubuntu jammy/main amd64 libapache2-mod-php all 2:8.1+92ubuntu1 [2898 B]
Fetched 1769 kB in 25s (71.4 kB/s)
Selecting previously unselected package libapache2-mod-php8.1.
(Reading database ... 27910 files and directories currently installed.)
Preparing to unpack .../libapache2-mod-php8.1_8.1.2-1ubuntu2.14_amd64.deb ...
Unpacking libapache2-mod-php8.1 (8.1.2-1ubuntu2.14) ...
Selecting previously unselected package libapache2-mod-php.
Preparing to unpack .../libapache2-mod-php_2%3a8.1+92ubuntu1_all.deb ...
Unpacking libapache2-mod-php (2:8.1+92ubuntu1) ...
Setting up libapache2-mod-php8.1 (8.1.2-1ubuntu2.14) ...
Creating config file /etc/php/8.1/apache2/php.ini with new version
Module mpm_event disabled.
Enabling module mpm_prefork.
apache2_switch_mpm Switch to prefork
apache2_invoke: Enable module php8.1
Setting up libapache2-mod-php (2:8.1+92ubuntu1) ...
Processing triggers for libapache2-mod-php8.1 (8.1.2-1ubuntu2.14) ...
次に、phpをapache2で利用できるよう設定を変更します。
sudo a2enmod php8.1
sudo systemctl reload apache2.service
次に、ファイルを編集しましょう。
<?php
phpinfo();
?>
それでは、通信を実施します。
ブラウザで以下のようにURLを入力します。
http://localhost:81/private/
すると、認可コードフローに従いKeycloakのログイン画面へとリダイレクトします。
ユーザ名とパスワードに先ほどKeycloakの管理コンソール上で設定した値を入力します。
すると、以下のようにPHPによるリクエスト表示画面が出力されます。
認可コードフローが動いていることを確認できましたね。
しかし、localhostとポート番号を直打ちで通信するのは少しイケていません。
そこで、今回はロードバランサーを用いてもっとよさげなアーキテクチャにしてしまいましょう。
LoadBalancer
ロードバランサーを用いて処理を実現します。
近年、ユーザ数の増加に応じてサーバ側の負荷が増大することから、サーバ側を冗長化できるようにクライアントサーバ間にロードバランサーを挟むことが多くなってきました。
今回はそのアーキテクチャを利用してみましょう。
Keycloakの設定
今回は通信の中継地点としてロードバランサーをはさんでいるため、Keycloak側でHTTPヘッダー内のX-Forwarded-*情報を参照する必要があります。
そこで、以下のように実行オプションを追加します。
bash bin/kc.sh start-dev --proxy edge
--proxy edge
オプションを指定することで、プロキシ(今回はロードバランサー)とKeycloak間でHTTPを介した通信を可能になります。
※(2024年4月追記)最新バージョンのKeycloak24.xでは--proxy
オプションが非推奨になったので、以後は以下のように設定をお願いします。
bash bin/kc.sh start-dev --proxy-headers forwarded|xforwarded --http-enabled true
今回のアーキテクチャでは、ロードバランサーとKeycloakは同一のネットワークに存在しているので、このオプションで実行しましょう。
Nginxの設定
今回はNginxを用いてロードバランサーを実現します。
以下は構築手順となります。
- Nginxをインストール
- SSL証明書を作成
- Nginxの設定ファイルを編集
Nginxのインストール
Nginxをインストールします。
sudo apt install nginx
$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
SSL証明書を作成
OpenSSLを用いてSSL証明書を作成しましょう。
詳細な作り方に関しては、既存のサイトがあるのでここでは省略します。
最終的なアウトプットとして、以下のファイルがあることを想定する。
生成物 | 概要 |
---|---|
server.crt | 証明書 |
server.key | サーバの秘密鍵 |
Nginxの設定ファイルを編集
今回は、ドメインを設定してそれらしく通信してみようと思います。
以下のようなドメインに設定しました。
ドメイン | 機能 | 紐づいたアドレス |
---|---|---|
sso.kanta-k.com | Keycloak | localhost:8080 |
app.kanta-k.com | RelyingParty | localhost:81 |
X-Forwarded-forを処理できるよう、Keycloakとmod_auth_openidcの双方で設定を追加しましょう。
Keycloakについては、立ち上げるタイミングで「--proxy edge」オプションを追加して実行しているので、プロキシサーバから受け取ったHTTPヘッダー情報を処理する準備は整っています。
次に、mod_auth_openidcについては、OIDCXForwardedHeaders
のディレクティブを追加します。
このディレクティブはmod_auth_openidcにHTTPヘッダーに含まれているX-Forwardedの情報を解釈する命令を出すためのものです。
OIDCXForwardedHeaders X-Forwarded-Host X-Forwarded-Proto X-Forwarded-Port
また、指定URIが変更されたので、設定ファイルを書き換える必要があります。
そのため、以下のように書き換えます。
- OIDCProviderMetadataURL http://localhost:8080/realms/myrealm/.well-known/openid-configuration
+ OIDCProviderMetadataURL https://sso.kanta-k.com/realms/myrealm/.well-known/openid-configuration
OIDCClientID apache24
OIDCClientSecret G7PRQbaldo7l17AN4Ls0fDfj2jcM9oVN
OIDCResponseType code
OIDCScope "openid"
OIDCSSLValidateServer Off
OIDCProviderTokenEndpointAuth client_secret_basic
- OIDCRedirectURI http://localhost:81/private/callback
+ OIDCRedirectURI https://app.kanta-k.com/private/callback
OIDCCryptoPassphrase passphrase
OIDCPreservePost On
+ OIDCXForwardedHeaders X-Forwarded-Host X-Forwarded-Proto X-Forwarded-Port
<Location /private>
AuthType openid-connect
Require valid-user
</Location>
<Location /public>
OIDCUnAuthAction pass
AuthType openid-connect
Require valid-user
</Location>
以上の設定を稼働しているサーバに反映するために、nginxとapacheの双方を再起動しましょう。
sudo apache2ctl test
sudo systemctl restart apache2.service
sudo nginx -t
sudo systemctl restart nginx.service
実行内容を確認します。
試しに、ディベロッパーツールをオンにしてネットワークを選択した状態で以下のアドレスに接続してみましょう。
画面下部にあるリクエストの様子を見てみましょう。
まず、アプリにアクセスを実行すると302のリダイレクトが実行されます。
この時の遷移先は https://sso.kanta-k.com/
となっており、無事リダイレクトが実施されたことが確認できました。
そして、設定したユーザ名とパスワードを入力してログインを実施すると、以下のようにサインイン後の画面になります。
リクエストの様子を見てみると、sso.kanta-k.com からのレスポンスとして、リダイレクトURIが払い出されています。
次にそのURIへ認可コード付きでアクセスしており、アクセストークンを取得してから元々のアクセス先へ遷移しています。
このように、ロードバランサーを用いてドメイン付きでSSL通信を実現できることが確認できました。
リクエストの確認
それでは、次は認可コードフローでは具体的にどのような通信のやり取りをしているのか、ブラウザを用いて確認してみましょう。
ユーザがアプリにアクセス
リクエスト
GET /private/ HTTP/1.1
Host: app.kanta-k.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: ja,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: cross-site
上記のリクエストは、SSOが実行されるパスにアクセスを実施している。
レスポンス
HTTP/1.1 302 Found
Server: nginx/1.18.0 (Ubuntu)
Date: Mon, 22 Jan 2024 08:42:17 GMT
Content-Type: text/html; charset=iso-8859-1
Content-Length: 551
Connection: keep-alive
Set-Cookie: mod_auth_openidc_state_8sGyV5I83IZXfq54qIZWiUBFUNA=Cm1DiY4XMQULIc3L.KpFUzOY5m21RD3yyjsJwy6445Uwd3...; Path=/; Secure; HttpOnly; SameSite=None
Cache-Control: no-cache, no-store, max-age=0
Location: https://sso.kanta-k.com/realms/myrealm/protocol/openid-connect/auth?response_type=code&scope=openid&client_id=apache24&state=8sGyV5I83IZXfq54qIZWiUBFUNA&redirect_uri=https%3A%2F%2Fapp.kanta-k.com%2Fprivate%2Fcallback&nonce=ki2a6ORxnj3o5u7Pw3gci7zBolnqIgifFuuWAmyhoXU
レスポンスは上記の通りになった。
通信の中身を見てみると、Cookieの設定が行われており、mod_auth_openidc_state_xxxというCookieは認証リクエストと認証レスポンスを紐づけるために用いられるものです。
(具体的な説明はこちらを参照)
そして、リダイレクト先としてKeycloakの認証画面が指定されています。
フローの説明時にあったリダイレクトURIもredirect_uri
変数として含まれていることがわかると思います。
ユーザからKeycloakへリクエストを送信
リクエスト
GET /realms/myrealm/protocol/openid-connect/auth?response_type=code&scope=openid&client_id=apache24&state=8sGyV5I83IZXfq54qIZWiUBFUNA&redirect_uri=https%3A%2F%2Fapp.kanta-k.com%2Fprivate%2Fcallback&nonce=ki2a6ORxnj3o5u7Pw3gci7zBolnqIgifFuuWAmyhoXU HTTP/1.1
Host: sso.kanta-k.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: ja,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: cross-site
上記のリクエストでは、受けとった302リダイレクトを実施しています。
レスポンス
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Mon, 22 Jan 2024 08:42:17 GMT
Content-Type: text/html;charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Set-Cookie: AUTH_SESSION_ID=df064d01-41ec-41b6-a88d-c832822f2f7a; Version=1; Path=/realms/myrealm/; SameSite=None; Secure; HttpOnly
AUTH_SESSION_ID_LEGACY=df064d01-41ec-41b6-a88d-c832822f2f7a; Version=1; Path=/realms/myrealm/; HttpOnly
KC_RESTART=eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI2ZTVlM2FmNC05YWE1LTQzZTctYWI1Zi0zZTdmMGQ4MWZmZTYifQ.e...; Version=1; Path=/realms/myrealm/; HttpOnly
Cache-Control: no-store, must-revalidate, max-age=0
Content-Language: en
Content-Security-Policy: frame-src 'self'; frame-ancestors 'self'; object-src 'none';
Referrer-Policy: no-referrer
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-Robots-Tag: none
X-XSS-Protection: 1; mode=block
Content-Encoding: gzip
レスポンスとして、以下のCookieが設定された認証画面が返ってきます。
- AUTH_SESSION_ID:認証時のセッションIDで、いわゆるスティッキーセッション(今回はロードバランサーで機能を実現したため)。
- KC_RESTART:クライアントがタイムアウトした際に認証セッションを再開するために利用されるCookie情報。
認可コードを付与したリダイレクトURIを取得
リクエスト(認証&同意)
POST /realms/myrealm/login-actions/authenticate?session_code=8OcfqiYMxNIyYCyjkqf_MVnZ71Z0AMxDpopQCVGuAYI&execution=69a2dcda-6e81-4ee3-8feb-8c7a2d28ab07&client_id=apache24&tab_id=WElMWV4X040 HTTP/1.1
Host: sso.kanta-k.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: ja,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded
Content-Length: 44
Origin: null
Connection: keep-alive
Cookie: AUTH_SESSION_ID=d4e4fde9-6067-43ba-ba93-e3c450187677; AUTH_SESSION_ID_LEGACY=d4e4fde9-6067-43ba-ba93-e3c450187677; KC_RESTART=eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI2ZTVlM2FmNC05YWE1LTQzZTctYWI1Zi0zZTdmMGQ4MWZmZTYifQ.e...
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
レスポンス(リダイレクトURIを取得)
HTTP/1.1 302 Found
Server: nginx/1.18.0 (Ubuntu)
Date: Tue, 23 Jan 2024 07:04:38 GMT
Content-Length: 0
Connection: keep-alive
Cache-Control: no-store, must-revalidate, max-age=0
Set-Cookie: KEYCLOAK_LOCALE=; Version=1; Comment=Expiring cookie; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Max-Age=0; Path=/realms/myrealm/; HttpOnly
KC_RESTART=; Version=1; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Max-Age=0; Path=/realms/myrealm/; HttpOnly
KC_AUTH_STATE=; Version=1; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Max-Age=0; Path=/realms/myrealm/
KEYCLOAK_IDENTITY=eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI2ZTVlM2FmNC05YWE1LTQzZTctYWI1Zi0zZTdmMGQ4MWZmZTYifQ.e...; Version=1; Path=/realms/myrealm/; SameSite=None; Secure; HttpOnly
KEYCLOAK_IDENTITY_LEGACY=eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI2ZTVlM2FmNC05YWE1LTQzZTctYWI1Zi0zZTdmMGQ4MWZmZTYifQ.e...; Version=1; Path=/realms/myrealm/; HttpOnly
KEYCLOAK_SESSION=myrealm/5b5ae651-8456-41d0-b222-da9d817f9ad7/d4e4fde9-6067-43ba-ba93-e3c450187677; Version=1; Expires=Tue, 23-Jan-2024 17:04:38 GMT; Max-Age=36000; Path=/realms/myrealm/; SameSite=None; Secure
KEYCLOAK_SESSION_LEGACY=myrealm/5b5ae651-8456-41d0-b222-da9d817f9ad7/d4e4fde9-6067-43ba-ba93-e3c450187677; Version=1; Expires=Tue, 23-Jan-2024 17:04:38 GMT; Max-Age=36000; Path=/realms/myrealm/
KEYCLOAK_REMEMBER_ME=; Version=1; Comment=Expiring cookie; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Max-Age=0; Path=/realms/myrealm/; HttpOnly
Content-Security-Policy: frame-src 'self'; frame-ancestors 'self'; object-src 'none';
Location: https://app.kanta-k.com/private/callback?state=MrTxfdlMiYuS5Y_MAwB0NyMlBdU&session_state=d4e4fde9-6067-43ba-ba93-e3c450187677&iss=https%3A%2F%2Fsso.kanta-k.com%2Frealms%2Fmyrealm&code=8dc2c104-8eca-41cd-916c-2d36bee01b13.d4e4fde9-6067-43ba-ba93-e3c450187677.5db7d057-ea84-4b9d-a668-cfe671a18fd9
Referrer-Policy: no-referrer
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-Robots-Tag: none
X-XSS-Protection: 1; mode=block
302のリダイレクト先のURIを確認してみると、先ほど設定したリダイレクトURIになっていることが確認できます。また、認可コードがcodeとしてURI内で払出されていることも併せて確認できました。
認可コードをアプリに渡す
リクエスト
GET /private/callback?state=MrTxfdlMiYuS5Y_MAwB0NyMlBdU&session_state=d4e4fde9-6067-43ba-ba93-e3c450187677&iss=https%3A%2F%2Fsso.kanta-k.com%2Frealms%2Fmyrealm&code=8dc2c104-8eca-41cd-916c-2d36bee01b13.d4e4fde9-6067-43ba-ba93-e3c450187677.5db7d057-ea84-4b9d-a668-cfe671a18fd9 HTTP/1.1
Host: app.kanta-k.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: ja,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Cookie: mod_auth_openidc_state_MrTxfdlMiYuS5Y_MAwB0NyMlBdU=IU1kj3YUbCqTn5QQ.xK3GpM_bc0DiM8MiEXr58g7UovNjj2lWp-jeDv5LPtbYvysdxJF1h9Ta374LMgeuOPoY11XdjadzXLqswZLcDkKfCHN4QE8cKoPVgsVfhkuTNyrYFGoi88L5jTeg6Zyml-XO0Ltlis1WgjarH0Yp3KdY0k3f0xZLUq2YMDAWOB0TNOqgj5WT8Gdwk9-AWzma-D5wrqDr8KAh3PEVYPLggcOMfvRk0Tvz4YG48mCI4SOLIo2aRFVPhJOsgtWPsdW1c17YjHFxivBowfLQwAKFdQm7A3TiaXB8tMmlM4Y1gxsHRYulpKQL0LwiFvGQrImc2e48YOo7soaE_uK22u-pb2owSLjetgGAUcYiiKyvpPn-tl1BSLPZyoZDLeYMdJJsNo6_H98tZ9rBfTGAVEw91X8.U5C7HT5SH0TFI3i3hI_CRw
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
リクエスト後はフローに記載の処理がRPとOPで行われます。
- トークンエンドポイントからアクセストークンが払い出される
- RPでIDトークンを検証
- (ユーザ情報エンドポイントにアクセスしてユーザ情報を取得)
レスポンス
HTTP/1.1 302 Found
Server: nginx/1.18.0 (Ubuntu)
Date: Tue, 23 Jan 2024 07:04:39 GMT
Content-Type: text/html; charset=iso-8859-1
Content-Length: 301
Connection: keep-alive
Set-Cookie: mod_auth_openidc_state_MrTxfdlMiYuS5Y_MAwB0NyMlBdU=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Secure; HttpOnly; SameSite=None
mod_auth_openidc_session=5a7994f4-b895-4a2f-90d3-c40b971ec284; Path=/; Secure; HttpOnly; SameSite=None
Location: https://app.kanta-k.com:443/private/
Cookie上でセッション情報が払い出された状態としてレスポンスが返ってきます。
リダイレクト先は最初期にアクセスしていたURLです。
フローの初期にアクセスしていたURIへアクセス
リクエスト
GET /private/ HTTP/1.1
Host: app.kanta-k.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: ja,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Cookie: mod_auth_openidc_session=5a7994f4-b895-4a2f-90d3-c40b971ec284
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
レスポンス
HTTP/1.1 200 OK
Server: nginx/1.18.0 (Ubuntu)
Date: Tue, 23 Jan 2024 07:04:39 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 24903
Connection: keep-alive
Vary: Accept-Encoding
Content-Encoding: gzip
無事にアクセスできたことを確認できました。