KeycloakのWebAuthn Passwordlessについて

## きっかけ
- [Keycloak 8.0 にWebAuthnがサポートされるようになりました。](https://www.keycloak.org/2019/11/keycloak-800-released.html)
- [さらにKeycloak 9.0 ではWebAuthnのパスワードレス認証ができるようになりました。](https://www.keycloak.org/docs/latest/release_notes/#keycloak-9-0-0) 😄 
- KeycloakもWebAuthn対応が充実してきたし、Authenticatorの登録及びAuthenticatorを使った認証を試してみようと思いました。

## やってみたこと
- ほぼドキュメント記載のことをつらつら実施
  - WebAuthnポリシーの設定
  - WebAuthnフローの作成
  - Authenticatorの登録と認証
  - Credintialsのデータ保存確認

### 前提条件
- KeycloakにlocalhostもしくはHTTPSでアクセスできるようにしておく必要があります。
  - [Web Authentication API](https://developer.mozilla.org/ja/docs/Web/API/Web_Authentication_API)にも書かれてます。
- FIDO2(CTAP2)対応のAuthenticatorを準備しておく
  - Authenticatorがない場合はChromeの拡張機能の[Virtual Authenticator Tabs](https://chrome.google.com/webstore/detail/virtual-authenticators-ta/gafbpmlmeiikmhkhiapjlfjgdioafmja)を利用すれば、仮想のAuthenticatorとして利用することができます。
    - ちなみにAttestation Conveyance Preference を direct にするとバリデーションのチェックにかかってエラーになります。

### [WebAuthnポリシーの設定について](https://www.keycloak.org/docs/latest/server_admin/index.html#_webauthn-policy)
- ver9.0では[WebAuthn Policy]と[WebAuthn Passwordless Policy]の設定ができるようになっています。
<img width="1288" alt="WebAuthnPasswordless" src="https://user-images.githubusercontent.com/30227802/75085074-b2073d00-5568-11ea-83e3-2c46ce8b610e.png">

今回はパスワードレスを検証してみたいので、[WebAuthn Passwordless Policy]の設定を行います。

- Relying Party Entity Name
  - KeycloakがRelying Partyとなりますので、Relying Partyの名称を入力します。
- Relying Party ID
  - [Relying Party IDはホストドメイン名(keycloak.example.com)の形式でないといけないようです。](https://www.w3.org/TR/webauthn/#relying-party-identifier)

- Attestation Conveyance Preference 
  - Acceptables AAGUIDsの設定をする場合はAuthenticatorの情報の検証をする必要があるので direct にします。
- Authenticator Attachment 
  - 許可するAuthenticator が外部デバイスか、内部デバイスかに選択をします。
    - platform 内部デバイスをAuthenticatorとして利用する場合
    - cross-platform USBやNFCやBluetoothで外部デバイスをAuthenticatorとして利用する場合
- User Verification Requirement
  - Authenticator独自の認証を求めるかどうかの設定になります。(デフォルト値 preferred)
    - required 必ず認証を求める
    - preferred Authenticatorが対応してたら認証を求める
    - discouraged 認証を求めない
- Acceptable AAGUIDs 
  - AAGUID(Authenticator Attestation Global Unique Identifier)とはAuthenticatorのプロダクトごとの識別子のことです。
  - [プロダクトごとにAAGUIDがあり、](https://support.yubico.com/support/solutions/articles/15000028710-yubikey-hardware-fido2-aaguids)特定のプロダクトのAutenticatorのみしか認めない場合に利用します。

### WebAuthnを使った際の認証フローの作成について
- [ドキュメントに記載のあるWebAuthn Passwordless もしくは OTP&Password の認証フロー](https://www.keycloak.org/docs/latest/server_admin/index.html#passwordless-webauthn-together-with-two-factor)を試します
- ドキュメントに記載されている通りに認証フローを作成します。
  - KerberosとIdentity ProviderのExecutionは不要だと思ったので、設定しませんでした。以下のような画面の設定になります。
<img width="1182" alt="authentication_flow" src="https://user-images.githubusercontent.com/30227802/75109526-8c149200-5667-11ea-8733-2b76afc483d1.png">

- ブラウザフローを作成した認証フローに変更します。

<img width="425" alt="Binding" src="https://user-images.githubusercontent.com/30227802/75109552-bcf4c700-5667-11ea-84fc-242b1cc01cd4.png">

### Authenticatorの登録について
- Authenticator の登録はユーザ登録時やユーザ情報を編集する画面にある[必要なアクション]から[WebAuthn Register Passwordless]を設定します。
  - ユーザを大量に登録する場合は[REST APIを使って、RequiredActionsの値を設定](https://www.keycloak.org/docs-api/9.0/rest-api/index.html#_userrepresentation)すると楽かなと思いました。
<img width="799" alt=" RequiredActio" src="https://user-images.githubusercontent.com/30227802/75095047-45298c80-55d4-11ea-8be6-84a655805975.png">

- ` https://{Keycloakサーバホスト}/auth/realms/{レルム名}/account `にアクセスするとブラウザフローが開始されます。
- 今回の認証フローはWebAuthn PasswordlessもしくはOTP&Passwordなので、はじめにOTP&Passwordによる認証を行った後、Authenticatorの登録が求められるようになります。
  - 下の画像ではOTPの部分はカットしてます。

![webauthn_regist](https://user-images.githubusercontent.com/30227802/75135251-4ec20a00-5724-11ea-9c08-78b2ede69d62.gif)

### Authenticatorを使った認証について

- OTP&PasswordとAuthenticator(Keycloakの画面ではSecurity Key)を登録すると、[Try Another Way]が表示されるので、どちらで認証するか選択することができるようになっています。

![webauthn](https://user-images.githubusercontent.com/30227802/75134833-d60e7e00-5722-11ea-91d5-3cce86474ea1.gif)

### Credentialデータの保存について
- DB上はCREDENTIALSというテーブルにデータが保存されていました。
- USER_IDはUSER_ENTITYテーブルのUSER_IDと紐づいています。
<img width="877" alt="none" src="https://user-images.githubusercontent.com/30227802/75088055-4da9a500-558b-11ea-8216-a8217d54a225.png">

- AAGUIDが ` "aaguid": "00000000-0000-0000-0000-000000000000", ` となっていたので何で？って思いました。
- よくよく考えるとAttestation Conveyance Preference を direct にしていなかったため、AAGUIDの情報が登録されていなかったようです。
- 試しにAttestation Conveyance Preference を directにするとAAGUIDの情報が登録されていました。
<img width="921" alt="direct" src="https://user-images.githubusercontent.com/30227802/75088058-5ef2b180-558b-11ea-8aed-8de3fcf2d3cd.png">

## 感想
- Acceptable AAGUIDs について
  - UnAccesptable AAGUIDsの設定(ブラックリストの設定)ができるようにしたいという場合は設定が大変なのかなと思いました。
  - [FIDO Alliance Metadata Service](https://fidoalliance.org/metadata/)にAuthenticatorの一覧が管理されているようです。
  - このMetadata Service(MDS)に登録されているAAGUIDを[Accesptable AAGUIDs]に設定するのが良さそうに思いました。
- AAGUIDについて
  - AAGUIDはどうやって採番されてるんだろうっていう疑問があります。
  - [FIDOアライアンスの資料](https://fidoalliance.org/specs/fido-v2.0-rd-20180702/fido-metadata-statement-v2.0-rd-20180702.html#authenticator-attestation-guid-aaguid-typedef)には、AAGUIDはAuthenticatorの製造元が重複しないように割り当てるとありました。
  - MDSに登録されている限りは重複したりしなさそう。