Keycloakで多要素認証を試してみる（ワンタイムパスワード認証編）

# 今日やること
Keycloakアドベンドカレンダー12日目は、Keycloakの「多要素認証」に触れてみようと思います。

「多要素認証」とは、**アクセス権を得るのに必要な本人確認のための『複数』の要素（証拠）をユーザーに要求する認証方式**です。例えば、複数の要素を組み合わせることで、よりセキュアな認証を可能にしたり、利便性を下げずに、セキュリティを向上させたりできます。

ここでは、**Keycloakが提供する「多要素認証」の設定を確認しながら、「ワンタイムパスワード認証(OTP)」を試してみよう**と思います。

# 事前準備
 * Keycloak環境（スタンドアロン構成でOK）
 * Keycloak管理ユーザと一般ユーザ
 * 「FreeOTP」がインストールされた「iPhone」端末 （「Google Authenticator」がインストールされた「Android」端末でも可）

※セットアップやユーザ登録等は、Keycloakアドベンドカレンダー2日目の「[Keycloakのセットアップ](https://qiita.com/tamura__246/private/13fc301e9409fef77bf3)」、および、3日目の「[KeycloakでOpenID Connectを使ってみる（Spring Bootアプリケーション編）](https://qiita.com/tamura__246/private/4290a1035e1adcab733b)」をご参考にして下さい。
※今回はデフォルトでセットアップされている「masterレルム」をそのまま使います。(実際はレルムを分けて作るのが望ましいです)

# Keycloak管理設定を確認してみる
Keycloak管理コンソール `http://(KEYCLOAK_FQDN)/auth/admin/master/console/` にアクセス、および、管理ユーザでログインし、`[設定] > [認証]`の内容を確認します。

## 「フロー」タブ

「認証フロー」は、各種処理パターン毎（ブラウザ認証フロー/登録フロー/ダイレクトグラントフロー/クレデンシャルリセットフロー/クライアント認証フロー）に分けられてビルトイン設定されており、その中の`Browser`が、ブラウザで認証する基本パターンの「認証フロー」となっています。

![adcal#1.png](https://qiita-image-store.s3.amazonaws.com/0/209798/412753e4-ef00-2b0c-6f16-93ceeec4fe97.png)

上記キャプチャーの「認証フロー」の設定は、Keycloakデフォルトのものですが、ここに設定されている「認証タイプ」の概要は以下の通りとなり、上から順番に評価されてゆきます。なお、このフローをコピー編集して、順序を入れ替えたり、他の認証タイプを設定したり、フローをグループ化して何段にもネスト設定したりすることが可能です。

|認証タイプ|(ネスト)|認証内容|概要|
|:--|:--|:--|:--|
|Cookie|-|認証クッキー認証|認証クッキー内容が有効であることをチェックします。|
|Kerberos|-|Kerberos認証|Kerberos Distribution Center（KDC）で認証されたユーザかどうかチェックします。※[SPNEGO](https://tools.ietf.org/html/rfc4178)プロコトルにより認証を行います。|
|Identity Provider Redirector|-|外部IDP認証|外部のIDPやSNSなど第３者認証情報を利用して、SAMLやOpenID Connectを使って認証を行います。|
|Forms|-|フォーム認証|フォーム入力による認証が使用されます。内容はネストされた認証タイプに依存します。|
|〃|Username Password Form|IDパスワードで行うフォーム認証|フォーム入力されたIDとパスワードをチェックします。|
|〃|OTP Form|ワンタイムパスワードで行うフォーム認証|フォーム入力されたワンタイムパスワードをチェックします。|


また、フロー内の各「認証タイプ」には、実行後の動作を決定する「必要条件」が設定可能です。その条件には以下の4種類があります。

|必要条件|意味|概要|実行後の結果|
|:--|:--|:--|:--|
|ALTERNATIVE|代替|選択された「認証タイプ」が次以降の「認証タイプ」の代替をします。|成功すると、次の「認証タイプ」に進むことなく、認証成功となります。失敗すると、次の「認証タイプ」に進みます。|
|REQUIRED|必須|選択された「認証タイプ」の成功が必要になります。|成功すると、次の「認証タイプ」に進みます。失敗すると、次の「認証タイプ」に進むことなく、認証失敗となります。|
|OPTIONAL|任意|選択された「認証タイプ」はユーザ設定（任意）により実行されます。（※２）|発動された場合、成功すると、次の「認証タイプ」に進みます。失敗すると、次の「認証タイプ」に進むことなく、認証失敗となります。|
|DISABLED|無効|選択された「認証タイプ」が無効になります。|実行されません。|

※２　ユーザの任意で、ユーザ自身が設定を行うことで発動する「認証タイプ」です。一律に強制させたい場合は、`REQUIRED`を設定して下さい。この設定をした後、初めてログインした際は、強制的にその「認証タイプ」のオプション設定を促されます。

上述のキャプチャの「認証フロー」設定を図解すると以下にようになります。
![adcal-flow.png](https://qiita-image-store.s3.amazonaws.com/0/207022/6c606e0c-ca23-9786-f44e-2c735b338b4f.png)

ここでは、「認証フロー」設定の意味を理解しながら、`OPT Form`の「認証タイプ」の「必要条件」が、デフォルトでセットアップされている`OPTIONAL`となっていることを確認して、次に進みます。

## 「OTPポリシー」タブ

「OPT」の方式やハッシュアルゴリズムなどが変更できます。ここでは、一旦デフォルトのままとして、次に進みます。

![adcal#2.png](https://qiita-image-store.s3.amazonaws.com/0/209798/713d517a-7541-3937-7bdd-92164710d020.png)

# ユーザ設定をしてみる
アカウント設定画面 `http://(KEYCLOAK_FQDN)/auth/realms/master/account/` にアクセス、および、一般ユーザでログインし、`[アカウント設定画面] > [Authenticator]`でワンタイムパスワード設定を行います。
![adcal#4.png](https://qiita-image-store.s3.amazonaws.com/0/209798/31d0c356-fa59-441b-36d9-d3df16c8b710.png)

「iPhone」端末を取り出し、「FreeOTP」を起動します。
<img width="375" alt="adcal#3.png" src="https://qiita-image-store.s3.amazonaws.com/0/206135/381fc018-861c-ab89-fccb-a8f623ce23fa.png">

ユーザ設定画面上に表示されている、２次元バーコードを「FreeOTP」で読み取り、トークン登録します。
<img width="375" alt="adcal#5.png" src="https://qiita-image-store.s3.amazonaws.com/0/206135/0dea5c9e-74b3-9168-986e-838a52b02f91.png">

そのまま、登録されたトークンをタップすると「ワンタイムパスワード」が表示されます。
<img width="375" alt="adcal#6.png" src="https://qiita-image-store.s3.amazonaws.com/0/206135/d717050c-f043-e085-5e7e-c7e3cc3f82a8.png">

その「ワンタイムパスワード」をアカウント設定画面の`ワンタイムコード`に入力します。
![adcal#7.png](https://qiita-image-store.s3.amazonaws.com/0/209798/e1486729-fc5c-74bf-0882-231f834aaf05.png)

これで「OTP」の設定は完了です。
![adcal#8.png](https://qiita-image-store.s3.amazonaws.com/0/209798/907f4b59-9aa1-9993-af94-bead8e8ad039.png)


# 認証の確認をしてみる
一度ログアウトをしてから、ユーザ設定画面 `http://(KEYCLOAK_FQDN)/auth/realms/master/account/` にアクセスします。
「ログイン画面」が表示されました。「ID」と「パスワード」を入力し、ログインします。
![adcal#9.png](https://qiita-image-store.s3.amazonaws.com/0/209798/8ab4abbd-2dac-35a8-c8d2-9f97dba1b230.png)

続けて、「ワンタイムコード入力画面」が表示されるようになりました。
![adcal#10.png](https://qiita-image-store.s3.amazonaws.com/0/209798/b37459a3-1e73-e2e3-11dc-c636ccbba4ff.png)

「iPhone」端末を取り出し、「FreeOTP」を起動し、ワンタイムパスワードを発行します。
<img width="375" alt="adcal#11.png" src="https://qiita-image-store.s3.amazonaws.com/0/206135/bd1b16e7-2534-7d5e-b8b9-93db11bb33fb.png">

発行された「ワンタイムパスワード」を、「ワンタイムコード入力画面」の`ワンタイムコード`に入力します。
![adcal#12.png](https://qiita-image-store.s3.amazonaws.com/0/209798/c50a3585-8850-006c-40bd-65bd423deb74.png)

無事ログインできました！
![adcal#13.png](https://qiita-image-store.s3.amazonaws.com/0/209798/33355f06-cb97-1b2e-9a9f-0d606bfc20b0.png)

# まとめ
今回、「多要素認証」の例として、Keycloak標準搭載である「ワンタイムパスワード認証(OTP)」を試してみましたが、とても簡単に、よりセキュアな認証設定が出来ました。**カスタマイズ開発をしなくてもある程度柔軟に、「認証フロー」の定義変更だけで、要件に合わせて複数の認証を組み合わせた「多要素認証」を実現できる**ようになっています。万が一、標準搭載にない認証要件があれば、「[Keycloak Documentation - Server Development (Authentication SPI)](http://keycloak-documentation.openstandia.jp/master/ja_JP/server_development/index.html#_auth_spi)」を参照しながら、「認証タイプ」のカスタマイズ開発も可能です。

# 参考資料

 - [Keycloak Documentation - Server Administration (Authentication Flows)](http://www.keycloak.org/docs/latest/server_admin/topics/authentication/flows.html)
 - [NRI OpenStandia Keycloak日本語ドキュメント - Server Admin ( 認証フロー )](http://keycloak-documentation.openstandia.jp/master/ja_JP/server_admin/index.html#_authentication-flows)