keycloakのWebAuthnサポート実現までのいきさつ

# keycloakのWebAuthnサポート実現までのいきさつ

## はじめに

日立製作所の乗松隆志と申します。keycloakのWebAuthnサポート実現までのいきさつについて記します。

**Notes: 本文内の各リンク先について、今後変更される可能性がありますのでご承知おきください。**

## まえおき : keycloakへのcontribution

keycloakへのcontributionの方法ですが、keycloakの[GOVERNANCE.md](https://github.com/keycloak/keycloak/blob/master/GOVERNANCE.md)に記載があります。

keycloakは、[JIRAのチケット](https://issues.jboss.org/projects/KEYCLOAK/issues)で作業管理をしています。

バグ修正や、ちょっとした機能拡張の場合は、JIRAのチケットを作成して、変更を実装し、Pull-Requestを出します。

大規模な機能拡張の場合は、keycloakの[developers mailing list](https://lists.jboss.org/mailman/listinfo/keycloak-dev)に提案を投稿し、メンテナーと議論します。
必要に応じて、設計ドキュメントを作成して、[keycloak-community](https://github.com/keycloak/keycloak-community)というgithubのプロジェクトの方に投稿し、メンテナーと議論します。作業を進めてよければ、JIRAのチケットを作成して、変更を実装し、Pull-Requestを出します。

Pull-Requestを出す際、第一行目のコメントは、

```"JIRAのチケット番号" "JIRAのチケットのタイトル"```

とするのがお作法のようです。JIRAのチケットとPull-Requestを対応させるためのようです。


## WebAuthnとは

W3Cが策定している、[Web Authentication](https://www.w3.org/TR/webauthn/)というWebブラウザを利用した認証方式の標準仕様です。もともとは、FIDO Allianceで策定されていました。

## サポートまでの過程

keycloak-communityの方に[W3C Web Authentication - Two-Factor](https://github.com/keycloak/keycloak-community/blob/master/design/web-authn-two-factor.md)というドキュメントが作成され、keycloakでWebAuthnのRelying Partyとしての機能をサポートしようという方針になりました。そこで、実際にやってみようと思い立ち、2019/3末あたりから作業を始めました。

### ライブラリの選定

keycloakのWebAuthnサポートについてのドキュメント
[W3C Web Authentication - Two-Factor](https://github.com/keycloak/keycloak-community/blob/master/design/web-authn-two-factor.md)では、いくつか課題が挙げられ、JIRAのチケットが作成されています。その中に、[WebAuthnの処理を行うためのライブラリの選定](https://issues.jboss.org/browse/KEYCLOAK-9358)があります。

ここでは、その候補が2つ挙げられていますが、[ynojima](https://github.com/ynojima)さんの[webauthn4j](https://github.com/webauthn4j/webauthn4j)を使わせていただくことにしました。以後、ynojimaさんにはいろいろとお助けいただきました。

### テスト方法の調査

keycloakのWebAuthnサポートについてのドキュメント
[W3C Web Authentication - Two-Factor](https://github.com/keycloak/keycloak-community/blob/master/design/web-authn-two-factor.md)で挙げられている課題として、[WebAuthnのテスト方法の調査
](https://issues.jboss.org/browse/KEYCLOAK-9359)があります。ynojimaさんに助言いただいたとおり、Chromeの[Web Authentication Testing API](https://docs.google.com/document/d/1bp2cMgjm2HSpvL9-WsJoIQMsBi1oKGQY6CvWD-9WmIQ/edit#)を利用することとしました。

ただ、これでできることは限定的であり、筆者が出したpull-requestのインテグレーションテスト用のコードでは基本的なテストしか行えませんでした。本原稿を執筆段階でも本作業は継続して行われております。

### 設計ドキュメントの作成

WebAuthnのサポートは大規模な機能拡張であるため、設計ドキュメントを作成し、keycloak-communityへ[W3C Web Authentication - Implementation stages - Authenticator only](https://github.com/keycloak/keycloak-community/blob/master/design/web-authn-authenticator.md)として投稿しました。

作業のスコープを、以下の3つと定めています。

* 2FAとしてのWebAuthnのサポート
* WebAuthnの処理を行うためのライブラリの選定
* WebAuthnのテスト方法の調査

2FAとしてのWebAuthnのサポートですが、JIRAの[チケット](https://issues.jboss.org/browse/KEYCLOAK-9360)があります。他2つは前述のとおりです。

設計ドキュメントには、本作業を行う上で他の作業の進捗に影響を受ける旨を記しています。

* [Application Initiated Actions](https://github.com/keycloak/keycloak-community/blob/master/design/application-initiated-actions.md)
* [Managing multi-factor authentication and Step-up authentication in Keycloak](https://github.com/keycloak/keycloak-community/blob/master/design/multi-factor-admin-and-step-up.md)

前者は、ユーザーが自身の登録したWebAuthn対応の認証器の情報を閲覧・変更・削除する機能に影響を与えます。設計ドキュメントでは、このApplication Initiated Actionsが実現していない前提で、既存のUser Account Serviceで行うとしました。

後者は、keycloakの認可認証フローの構造をリファクタリングするものです。スイスの[Cloud Trust](https://github.com/cloudtrust)というチームがこれを担当しました。これにより、ユーザーの登録したWebAuthn対応認証器の情報管理と、WebAuthnでのユーザー認証機能の使われ方に影響が出ます。設計ドキュメントでは、このManaging multi-factor authentication and Step-up authentication in Keycloakが実現していない前提で、一時的な方法で管理をするとしました。

ちなみに、WebAuthn対応認証器自体を登録するわけではなく、認証器内で生成された公開鍵とそれに関する情報を登録します。が、便宜上以下では「WebAuthn対応認証器を登録」という表現を使用します。

### プロトタイプの作成

設計ドキュメントの作成と並行して、プロトタイプをkeycloakでいうところのプロバイダの形式で実装し、機能がどのように実現できるかどうか、実験用に作成しました。まず弊社の[t-mogi](https://qiita.com/t-mogi)が実装し、それを筆者が改造していきました。このプロトタイプですが、githubのwebauthn4jのプロジェクトに間借りさせていただき、[Keycloak WebAuthn Authenticator
](https://github.com/webauthn4j/keycloak-webauthn-authenticator)という名前で作成しました。

プロバイダの形式で実装したため、keycloak動作中に動的にデプロイしたりアンデプロイしたりできます。できれば本番のPull-Requestでもプロバイダの形式で実装できないかと考えておりましたが、WebAuthn対応の認証器の登録をするRegistrationという機能が、Required Actionという形式で実現することを[W3C Web Authentication - Two-Factor](https://github.com/keycloak/keycloak-community/blob/master/design/web-authn-two-factor.md)に書かれており、かつRequired Actionの設定がプロバイダの形式では実現できなかったため、あきらめました。

このプロトタイプですが、ありがたいことにいろいろな方からご意見をIssueの形でいただいております。ですが、現在本番のPull-Requestがmergeされたことで本プロトタイプは本来の役割を終えたとし、実質活動をしておりません。

### keycloak本体への実装

プロトタイプ作成で得られた知見をもとに、本番のPull-Requestを作成しました。

#### 1st step

設計ドキュメントでは、WebAuthnサポートは段階を踏んで実現するとしました。まずは1段階目として、WebAuthnの仕様にあるWebAuthn対応の認証器の登録(Registration)とそれによるユーザーの認証(Authentication)の機能を実装しました。JIRAのチケットは[KEYCLOAK-9360](https://issues.jboss.org/browse/KEYCLOAK-9360)で、Pull-Requestは[#6248](https://github.com/keycloak/keycloak/pull/6248)となります。8/19に投稿し、いろいろやり取りがあり、10/2にmergeされました。

少々時間がかかりましたが、その要因の一つとして、新しく依存プロジェクトを追加したことが挙げられます。本WebAuthnサポートでは、WebAuthnのRegistrationおよびAuthentication時のRelying Partyにおける検証処理に[webauthn4j](https://github.com/webauthn4j/webauthn4j)を使用させていただきました。これが新しくkeycloakの依存プロジェクトとして追加されるため、keycloakのメンテナー側でもチェックを実施したようで、その為に時間がかかったようです。

また、このWebAuthnサポートは新機能であるため、ドキュメントの記載が必要となります。keycloakにはドキュメント用のレポジトリである[keycloak-documentation](https://github.com/keycloak/keycloak-documentation)があります。ここにドキュメントのPull-Requestを出しました。[#737](https://github.com/keycloak/keycloak-documentation/pull/737)となります。ドキュメント担当(?)の方からレビューを受け、相当指摘を受けましたが、mergeされました。

#### 2nd step

次いで、2段階目としてWebAuthn対応の認証器の登録時のAttestation Statementの検証機能を実装しました。

webauthn4jの機能を利用して検証するにあたり、keycloakとしてwebauthn4jにこんな機能があったらいいな、というものを実装してwebauthn4jに[Pull-Request](https://github.com/webauthn4j/webauthn4j/pull/164)を出したところ、mergeしていただきました。そこで、まずはwebauthn4jを同機能が実装されたバージョンにバージョンアップする[Pull-Request](https://github.com/keycloak/keycloak/pull/6401)を出しmergeされたのち、mergeされた機能を利用して2段階目のPull-Requestをkeycloakに出しました。JIRAのチケットは[KEYCLOAK-11372](https://issues.jboss.org/browse/KEYCLOAK-11372)で、Pull-Request[#6449](https://github.com/keycloak/keycloak/pull/6449)はとなります。

1段階目と同じく、ドキュメントの記載が必要となるため、ドキュメントのPull-Requestを出しました。[#779](https://github.com/keycloak/keycloak-documentation/pull/779)となります。

### 認証フローのリファクタリング

本WebAuthnサポートと並行して、前述のスイスのCloud Trustチームが認証フローのリファクタリング[Managing multi-factor authentication and Step-up authentication in Keycloak](https://github.com/keycloak/keycloak-community/blob/master/design/multi-factor-admin-and-step-up.md)対応のPull-Requestを実装していました。こちらの作業からみると、本WebAuthnサポートは2FAの認証処理用の機能ブロック(keycloakでいうところのAuthenticatorのプロバイダ)として見えます。本WebAuthnサポート用のPull-Requestがmergeされたのち、これを取り込んでPull-Requestを投稿し、mergeされました。JIRAのチケットは[KEYCLOAK-11745](https://issues.jboss.org/browse/KEYCLOAK-11745)で、Pull-Requestは[#6459](https://github.com/keycloak/keycloak/pull/6459)となります。なかなか大掛かりな作業であったようです。

ドキュメントの記載について、筆者が1段階目で追加したWebAuthnサポートの記述をCloud Trustチームが書き換えることになったため、筆者の方でもそれをレビューし、コメントして反映していただきました。

これにより、OTPに加えてWebAuthn対応の認証器での2FAが可能となりました。また、ユーザーが複数のOTPおよびWebAuthn対応の認証器を登録し、管理できるようになりました。

### ひとまず完成

上記を含んだ[keycloak-8.0.0](https://www.keycloak.org/2019/11/keycloak-800-released.html)が2019/11/15にリリースされました。

## その後

keycloakのメンテナーの方で、Follow Upタスクとしてユーザービリティの向上を主としてさらに改造がなされるようです。これについて、keycloakのdevelopers mailing listの[WebAuthn : Next Plan](https://lists.jboss.org/pipermail/keycloak-dev/2019-November/012924.html)というスレッドで議論がされています。

今回、弊社のチームメンバやwebauthn4jのynojimaさんなど様々な方からご助力いただき、基本的なWebAuthnサポート機能をkeycloakにcontributionできました。今後様々な方の手でより使い勝手のよいものに改造されていくことを期待しています。また、筆者自身もその一助を担えればと考えております。