ミライトデザインアドベントカレンダー2日目です。
ミライトデザイン Advent Calendar 2021のカレンダー | Advent Calendar 2021 - Qiita
1日目は ほげさん でした。
自分も IntelliJ をずっと使っていますが背景画像が設定できるのは知りませんでした。ありがとうございました。
2日目は最近触っている Keycloak で OIDC の認可コードフローを試してみたいと思います。
この記事でやること
Keycloak で OIDC の認可コードフローを試してみます。
Laravel のコードが実際どんなことをしているのかにも触れていこうと思います。
OIDC の内容については触れていません。その代わりに自分が OIDC を勉強する際に参考にさせてもらった参考書や記事を貼っておきます。
OpenID Connect入門: 概念からセキュリティまで体系的に押さえる | 土岐 孝平 | Kindle本 | Kindleストア | Amazon
実際に動かしてみたい人は
環境構築
- github からコードを pull してくる
GitHub - kakiuchi-miraito/keycloak-oidc make up
http://localhost:8082/auth/
Keycloak の画面が表示されていれば ok です。
次に Keycloak の管理画面から認証を行うユーザーと、クライアント(今回だとLaravel)を追加します。
- Administration Console にアクセス
- id / password : admin / admin
クライアントを追加
- Clients > Create
-
Consent Required
- 同意画面を表示する設定
-
Access Type を confidential へ変更
-
Valid Redirect URIs に
http://localhost*
を入力
- Credentials の Secret をコピー(後で使う)
User を追加
- Users > Add user
- Username を入力(なんでも ok)
- Credentials から Password を設定
- Temporary は off
- ユーザーがログイン後にパスワードを変更する必要があるかどうかの設定。今回はどうでもいいので off
- Temporary は off
認可コードフローを試してみる
認可コードフローで ID token を取得
-
backend/app/Http/Controllers/TokenRequest.php
のclient_id
とclient_secret
を作成したクライアントの値に修正 -
http://localhost/getAuthorizationCode
へアクセス- Keycloak のログイン画面が表示される
- 同意画面が表示される
- ちなみに同意したら履歴が残って次回から同意画面が表示されくなる
- Users > Consents から履歴を削除できる
- ログインすると ID Token が取得できる
- JSON Web Tokens - jwt.io で ID Token をデコードすると User の情報が確認できる
- 管理画面で user の email を追加すると ID Token にも Email の情報が反映される
コード解説
認可コードを取得
backend/app/Http/Controllers/GetAuthorizationCode.php
header(
'Location: <http://localhost:8082/auth/realms/master/protocol/openid-connect/auth?'> .
'response_type=code&' .
'client_id=test-client&' .
'redirect_uri=' . urlencode('<http://localhost/tokenRequest>') . '&' .
'code_challenge=' . $codeChallenge . '&' .
'code_challenge_method=S256&' .
'scope=openid'
);
- Location
- 認可エンドポイント
- response_type
- 認可エンドポイントは必須のパラメーターとして
response_type
を要求 -
code
token
id_token
none
が指定可能 -
scope
との組み合わせでフローが決まる - OpenID Connect 全フロー解説 - Qiita
- 認可エンドポイントは必須のパラメーターとして
- client_id
- 作成したクライアントの ID を指定
- redirect_uri
- 認証完了後に認可コードをつけてリダイレクトされる uri
- scope
-
openid
が指定されている場合に、 ID Token を取得し、 OIDC になる -
code
ならアクセストークンのみ取得 - scope がとる値
-
PKCE について
Auth0を利用してOAuth 2.0のPKCEを理解する | DevelopersIO
PKCEは認可コードの横取り攻撃の対策として定義されています。
関連するのは
code_challenge_method
code_challenge
code_verifier
code_verifier
はトークンリクエストの時出てきます。
-
code_challenge_method
に記載されている方法でcode_verifier
を変換することで、code_challenge
を作成します -
code_challenge_method
code_challenge
を認可リクエストの際に認可サーバーへ送信し、認可サーバー側で保存 - アクセストークンリクエストの時に
code_verifier
を送信し、認可サーバー側でcode_verifier
からcode_challenge
生成し、保存したcode_challenge
と比較することで、第3者から認可コードが送信されていないことを確認します。-
code_challenge_method
がとりえる値はplain
orS256
- Plain は変換しないので、
code_challenge
=code_verifier
になり、セキュリティ的に脆弱 -
S256
はSHA256ハッシュ化したものをbase64URLエンコード化する手法-
S256
にを選択するべき
-
- Plain は変換しないので、
-
アクセストークンリクエスト
backend/app/Http/Controllers/TokenRequest.php
$client = new Client();
$method = 'POST';
$uri = '<http://keycloak:8080/auth/realms/master/protocol/openid-connect/token>';
$options = [
'client_id' => 'test-client',
'client_secret' => '88a69be6-eac6-4d0e-bbd5-dfd9023a8acd',
'grant_type' => 'authorization_code',
'code' => $_GET['code'],
'scope' => 'openid',
'redirect_uri' => '<http://localhost/tokenRequest>',
'code_verifier' => $codeVerifier,
];
$response = $client->request($method, $uri, ['form_params' => $options]);
リダイレクト URL に http://localhost/tokenRequest
を指定しているので、このファイルに認可コードをもった状態でリダイレクトされてきます。
認可コードをトークンエンドポイントへ送信することで、ID Token を取得しています。
-
grant_type
使用しているフローを指定します。今回は認可コードフローなのでauthorization_code
を指定 -
code_verifier
-
code_challenge
の生成に使用したcode_verifier
- OAuth2.0拡張仕様のPKCE実装紹介 〜 Yahoo! ID連携に導入しました - Yahoo! JAPAN Tech Blog
code_verifierは43文字以上、128文字以下の文字列で、構成文字は[A-Z]/[a-z]/[0-9]/“-“/“.”/“_”/“~”となっています。
-
今回は出てこなかった Resource Server について
-
【第二弾】OAuth 2.0 + OpenID Connect のフルスクラッチ実装者が知見を語る - Qiita
- クライアントアプリケーションから ID Token を受け取って ID Token の検証を行う
- 検証方法はいくつかある
終わり
次回の記事は @Nyokki さんが MySQL 関連の記事を上げてくれるそうです。
@Nyokki さんがミライト社内で MySQL の勉強会をしてくれた時はわかりやすくてかなり勉強になりました。その節はありがとうございました。🙏