パスキーの概要とKeycloakでの動作確認

# はじめに

最近パスキー(Passkeys)が話題です。

[2023年は「パスキーの年」--FIDOアライアンスが報告 - ZDNET Japan](https://japan.zdnet.com/article/35212548/)

そして、[Keycloak](https://www.keycloak.org/)もサポートしています。
今日は、簡単にパスキーについてご紹介し、Keycloakの対応状況のご説明と、動作確認をしてみます。

# パスキー(Passkeys)とは

パスキーはパスワードに変わる安全な「鍵」です。そしてパスキー認証とは、従来のID/パスワードを置き換える、安全かつ便利なログイン方式です。
パスワード認証には、「手間がかかる」「攻撃を受けやすい」といった課題がありますが、パスキー認証ではこれらを解決しています。

## パスキー/WebAuthn/FIDO2など関連用語の定義

FIDOアライアンスがパスキーを発表したのは2022年5月であり、比較的新しいものですが、最近急速に盛り上がってきました。
一方これまで、パスワードレス認証として、「FIDO2」や「WebAuthn」という名前を聞いたことがある方もいるかと思います。これらとパスキーは何が違うのでしょうか。
まずは、関連用語の定義を確認したい思います。

| 名称                     | 定義                                                                                                                                                                                                                                                                                               |
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| WebAuthn                 | Webサイトがクライアント側にある認証器を用いた公開鍵認証をするための仕様と、そのための[ブラウザ側のAPI](https://developer.mozilla.org/ja/docs/Web/API/Web_Authentication_API)のこと。                                                                                                               |
| FIDO2                    | WebAuthnとCTAP2[^CTAP2]を包含する上位仕様                                                                                                                                                                                                                                                          |
| パスキー                 | 「パスワードレスFIDOクレデンシャル」のこと。つまり、FIDO2/WebAuthn[^ORUAF]のログイン対象ごとの秘密鍵・公開鍵ペアのことである。これまでのパスワードと同列であり、それを置き換えるものである。言い換えると、WebサイトごとにIDとパスワードがあったように、Webサイトごとに「パスキー」があるイメージ。 |
| 同期パスキー             | プラットフォーム(クラウド)経由で同期するパスキー。                                                                                                                                                                                                                                                 |
| デバイスバウンドパスキー | 認証器に固定されている、同期しないパスキー。                                                                                                                                                                                                                                                       |

[^CTAP2]: Client to Authenticator Protocol2。USB、NFC、またはBLEを介したFIDO2対応ブラウザーおよびオペレーティングシステムでの認証に外部認証システム（FIDOセキュリティーキー、モバイルデバイス）を使用するためのプロトコル。← [FIDOアライアンスのWebサイト](https://fidoalliance.org/?lang=ja)の「[仕様概要](https://fidoalliance.org/%E4%BB%95%E6%A7%98%E6%A6%82%E8%A6%81/?lang=ja)」(2023年12月10日 (日) 10:05 UTC閲覧) の一部を筆者が要約。
[^ORUAF]: 厳密にはFIDO2に限らず、FIDO UAFの秘密鍵・公開鍵ペアも指す模様。

なお、上記は2023年12月時点でのFIDOアラインスの定義に基づきます。これらの用語はこれまで定義が変化してきたり、FIDOアライアンスの定義が業界のコンセンサスを得ていないケースで意味の揺れがあったりすることもありました。

#### 同期するもののみを指すケース

パスキーはFIDOアライアンスの発表では、「マルチデバイスFIDOクレデンシャル」の通称という扱いでした[^R100][^R110]。つまり、当初はクラウド同期して複数デバイスで使えるもののみがパスキーでした。しかし、パスキーの勢いを見てか分かりませんが、セキュリティキーベンダーが自分たちもパスキーだと主張し始めました[^YUBIKEYPASSKEY]。その後FIDOアライアンスもSingle-device passkey(現: デバイスバウンドパスキー)を定義し、同期しない従来のFIDOクレデンシャルもパスキーとなりました[^FIRSTSINGLEDEVICEPASSKEY]。

[^R100]: [パスワードレス認証の普及を加速させる取り組み](https://fidoalliance.org/charting-an-accelerated-path-forward-for-passwordless-authentication-adoption-jp/?lang=ja)
[^R110]: [Apple、Google、MicrosoftがFIDO標準のサポート拡大にコミット、パスワードレス認証の普及を促進](https://fidoalliance.org/apple-google-and-microsoft-commit-to-expanded-support-for-fido-standard-to-accelerate-availability-of-passwordless-sign-ins-jp/?lang=ja)
[^YUBIKEYPASSKEY]: [YubiKeys, passkeys and the future of modern authentication](https://www.yubico.com/blog/passkeys-and-the-future-of-modern-authentication/)でパスキーの定義は「any WebAuthn/FIDO credentials」と発信。
[^FIRSTSINGLEDEVICEPASSKEY]: [遅くとも2022年8月時点で、Single-device passkeyという言葉が公式サイトに存在している。](https://github.com/passkeydeveloper/passkeys.dev/commit/0e4071b53adb6c1987de4737fd99a80143d812fe#diff-01328ed3607475433c1b7b5ee5286a26712bcc78f0e7c51d10f483787b654371R65)

#### 認証方式を指すケース

パスキーは「FIDOクレデンシャル」ですが、パスキー登場時の各種記事で「パスワードを使わない認証機能『パスキー』を発表」のように紹介(誤解?)されたためか、認証方式だという認識も広まりました[^AUTHMETHOD1][^AUTHMETHOD2][^AUTHMETHOD3]。

[^AUTHMETHOD1]: [iPhoneの新機能「パスキー」のセキュリティはどうなってる？ - ケータイ Watch](https://k-tai.watch.impress.co.jp/docs/news/1416627.html)
[^AUTHMETHOD2]: [Passkeys（パスキー）って結局なんなの？ パスワードの｢怖さ｣や｢面倒臭さ｣を解消する機能です](https://www.gizmodo.jp/2022/06/passkey-detail.html)
[^AUTHMETHOD3]: [Apple・Google・Microsoftがパスワードなしの認証システム「パスキー」の利用拡大に合意 - GIGAZINE](https://gigazine.net/news/20220506-apple-google-microsoft-passwordless-sign-in/)

恥ずかしながら、私もこの記事を書くまでは認証方式のことだと誤解しておりました。しかし記事を書くに当たってFIDOアライアンスの[パスキーのサイト](https://fidoalliance.org/passkeys/)や[passkeys.devの用語集](https://passkeys.dev/docs/reference/terms/)を確認した結果、認識を改めるに至りました。

## パスキーとは まとめ

ここまでをまとめると、パスキーとは、以下のように捉えていただくと良いかと思います。

:::note info
パスキーとは、パスワードに変わる安全な鍵である。「FIDO2」「WebAuthn」「パスキー認証」はいずれもパスキーでWebサイトにログインする方式であり、単に呼び方を変えたものである[^AAAAAAAA]。
:::

[^AAAAAAAA]: 先にFIDO2/WebAuthnが策定され、後からその鍵をパスキーと呼ぶことにしたという経緯から違和感があるかもしれませんが、わかりやすさを優先しています。また、FIDO2とWebAuthnは異なるものですが、あなたがWebAuthnやCTAP2を直接実装するような開発者でなければ、問題ないと思います。

# パスキーがなぜパスワードの課題を解決するのか

ここまでパスキーについてご説明してきましたが、ではなぜパスキーはパスワードの課題を解決するのでしょうか？
根本的には、共通鍵認証と公開鍵認証の違いと、認証仕様の標準化です。

パスワードはユーザーとサービスで共通の「パスワード」を共有することで、ユーザー認証を行います。パスワードを知っているのだからユーザーAさんに間違いない、という仕組みです。これは共通鍵による認証です。

また、パスワードは人間が記憶をもとに手動で入力することが出発点のため、その共通鍵の安全性はユーザーの心がけに依存しています。ここから、「簡単なパスワードを使う」「複数サイトで使い回す」「フィッシング詐欺に遭う」「覚えられない」といった課題が生じます。
この課題はパスワードマネージャーである程度解決は可能ですが、一般の方が使いこなすのは難しい面もあります。

パスキーは、ユーザー側に秘密鍵を置く公開鍵認証で、かつその仕様をWebAuthnで標準化しています。ユーザーが難しい操作を行うことなく、自動で公開鍵認証を済ませてWebサイトにログインできます。共通鍵がネットワークを流れることもなく安全であり、パスワードの課題の多くを解決しています。

# Keycloakのサポート状況

パスキーについては理解できましたが、Keycloakの対応状況はどうなのでしょうか？

Keycloakは2023年11月にリリースされたバージョン23より、パスキーをプレビュー扱いでサポートしていると明言されています。
[Keycloak 23.0.0 リリースノート](https://www.keycloak.org/2023/11/keycloak-2300-released)に、[Passkeys support](https://www.keycloak.org/2023/11/keycloak-2300-released#_passkeys_support)として記載されています。
ただしこの文章をよく見ると、

> Passkey registration and authentication are realized by the features of WebAuthn. Therefore, users of Keycloak can do passkey registration and authentication by existing WebAuthn registration and authentication.

パスキーの登録と認証はWebAuthnの機能なので、既存のWebAuthn機能でパスキーも対応していると言っているようです。またバージョン23でパスキー向けに特別に何か実装がなされたわけでもないため、このリリースノートは単に対応していることの明言に過ぎないように見えます。
なおWebAuthnは[Keycloak 8より対応](https://www.keycloak.org/docs/latest/release_notes/#w3c-web-authentication-support)しています。

# Keycloakでの動作確認

それでは実際にKeycloakでパスキー認証を設定し、動作を確認してみましょう。当記事では次のバージョンを前提とします。

Keycloak: 23.0.1

## 事前準備

Keycloakを適当な環境で起動し、管理ユーザを作成します。なお当記事ではWebAuthnを扱うため、ブラウザに入力するURLのホスト名が`localhost`でない場合は、HTTPSが必須であり、その証明書も不正な証明書を一時的に許可した状態ではWebAuthnが使えないことがあります(Windows上のChromeとEdgeで確認)。
私はこの記事のため、Amazon EC2の1インスタンスにてKeycloakを起動し、Cloudflareに独自ドメインを設定してHTTPSに対応しました。

## Keycloakの設定

Keycloak管理コンソールへアクセスします。
そして、適当に`master`レルムとは別にレルムを1個、`realm1`という名前で作成し、そこで作業します。

### WebAuthn Passwordless Policyの設定

左側のメニューの「Authentication」をクリックし、「Policies」タブをクリックし、「Webauthn Passwordless Policy」をクリックします。以下のような表示がされると思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221308/9e0e522d-ac0d-0cda-4284-41a5dd154a95.png)

以下のように設定し、「Save」をクリックします。

- Signature algorithms: `RS256`を追加(Windowsのバージョンによってはこれにしか対応していないため)
- Require resident key: `Yes`に設定(ユーザ名入力なしにWebAuthnでログインしたいため)
- User verification requirement: `Preferred`に設定(`Not specified`のままだと、ログイン時に正常に動作しないため、おそらくKeycloakのバグ)

### 認証フローの設定

Built-inと表示されている`browser`フローの一番右のメニューをクリックし、「Duplicate」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221308/8c16223f-6d15-4cea-3330-16ed06fe805d.png)
Nameにお好きな名前、例えば「Passkeys」と入力し、「Duplicate」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221308/7f02e89e-d978-35db-40e6-84f623fea9dd.png)
このような表示がされると思います。
「Cookie」以外はゴミ箱アイコンをクリックし、削除します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221308/22a6281f-4de2-8e4e-afc1-0b31debaaf78.png)

全体の「Add sub-flow」をクリックし、「Name」に「Form Auth」を設定します。Requirementを「Alternative」に設定します。
「Form Auth」の追加アイコン(+アイコン)をクリックし、「Add Step」をクリックします。「Username Password Form」を追加します。
全体の「Add step」をクリックし、「WebAuthn Passwordless Authenticator」を追加し、Requirementを「Alternative」に設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221308/86ec6524-0720-a779-1f65-9a177f817811.png)
フローが上記の表示の通りになったら、右上の「Action」メニューから「Bind flow」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221308/890e9cab-91e1-aaca-0586-56914ba5c309.png)
「Browser flow」が選択されていることを確認し、「Save」をクリックします。これで、今回作成した「Passkeys」認証フローがブラウザでのログインに適用されるようになりました。

#### 「WebAuthn Passwordless」と「WebAuthn」

WebAuthn Passwordless Authenticatorの隣には、WebAuthn Authenticatorがあります。この2つは以下のように使い分けます。

- WebAuthn Authenticator: 多要素認証における二要素目以降の認証用に使う。例えば、ワンタイムパスワードの代わりとする。
- WebAuthn Passwordless Authenticator: 最初の認証要素として使う。つまり、ID/ぱすわーど認証の代わりとして使う。そのため、パスワードレス認証と呼ばれる。

### ユーザの追加

左のメニューの「Users」から、適当に一般ユーザ1名を追加します。
「Credentials」からパスワードを設定し、「Temporary」は`Off`にします。
このユーザを用いて後でログインを行います。

## ログイン動作の確認

### 認証フローの確認(パスキー登録前)とアカウントコンソールへのログイン

適当なブラウザで `https://(ホスト名)/realms/realm1/account/`にアクセスします。 (`http`で`localhost`の場合は適宜読み替えてください)
今回は、アプリケーションは用意せず、表示されたこのKeycloakの一般ユーザ向けアカウント管理アプリを使います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221308/0b37eaa6-0fff-a69f-0dad-671bfd8a7050.png)

右上の「Sign in」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221308/ca148d4f-849e-14e0-94b1-048d92459508.png)

ここで「Try Another Way」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221308/b2ef7997-76f8-2576-403c-016c74ec0544.png)

認証フローで設定したとおり、ユーザー名+パスワードによるログインと、WebAuthnによるログインの選択式となっている事が分かります。ここで「Security Key」をクリックすると、次のような表示になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221308/f3638503-fbbc-c284-1586-7a7c9f842219.png)

「Sign in with Security Key」をクリックすると、あなたの端末環境のパスキーで認証を試みます。ただしまだパスキーをKeycloakに登録していないため、何をしても認証に失敗します。
ここはいったん「Try Another Way」をクリックし、今度は「Username and Password」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221308/ca148d4f-849e-14e0-94b1-048d92459508.png)
ユーザ名+パスワードの入力フォームに戻るので、ユーザーを作成した際のユーザ名とパスワードでログインします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221308/3d3f3f96-82cc-0ec5-3437-c88509413dd4.png)
このように、アカウントコンソールへログインが完了します。

### パスキーの登録

ここまでで、あなたは一般ユーザでアカウントコンソールへログインしました。次回のログインからパスワードではなくパスキーでログインしたいとします。その場合、Keycloakにパスキーを登録します。
左側の「Personal info」をクリックします。左側のメニューを表示します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221308/163ee0e0-cbe9-c0c4-35bf-b154a19fa3ac.png)

「Account security」の中の「Signing in」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221308/e098fb7f-7843-cecb-abc5-9d4a23833b09.png)

ここではユーザーの各種認証用クレデンシャル(パスワード、ワンタイムパスワードアプリの登録、パスキー)をユーザー自身で登録・変更することができます。パスキーを登録するには、「Passwordless」の中の「Set up Security key」をクリックします。
(Keycloakは全体的にパスキーではなく「Security key」という言葉を使っています)

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221308/9ce7deaa-33f1-6705-c085-3f3546d62467.png">

ここでは「Register」をクリックします。

<img width="350" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221308/2ccf1655-d545-02c5-2e30-55755da44a3f.png">

Windows 11 23H2 上のChrome/Edge/Firefoxの場合はこのようなダイアログが出ます。
ここの挙動は、環境(主にOSに依存)によって異なります。
このパソコンはPIN認証を設定しているため、PINを入力します。すると、

<img width="350" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221308/09dd6e8b-2658-57a3-2e2d-820204c0efab.png">

このようなダイアログになりました。
ユーザー名`user`の表示が重要です。つまりこのパスキーは、ユーザーIDを含んでいます。パスキーにはユーザーIDを含むものと含まないものがあり、含むものは「Discoverable Credentials」(旧名: Resident Keys)と呼び、ログイン時の利便性が向上します。この記事では[ポリシーの設定](#webauthn-passwordless-policyの設定)でResident Keyを強制しています。

なお、Windowsではパスキーは同期されません(さきほどのダイアログでも「このデバイスにパスキーを保存して」と説明がありました)

<img width="300" alt="Android14のパスキー保存" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221308/e97466fc-fa49-7736-ccae-12546d4ac42f.png">

参考までに、Android14のChromeの場合はこのような表示になりました。これはGoogleのアカウントが表示されている(モザイクかけました)ため、Googleアカウントに同期されるパスキーです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221308/feb8ba0a-467d-ed29-ad3e-8c7442121aa1.png)

パスキーの登録が完了するとこのように表示されます。
この状態でログインしてみましょう。
いったん、アカウントコンソール右上にある「Sign out」ボタンをクリックしてログアウトします。

<img width="200" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221308/d4da7784-098a-7b1b-37c0-c069f6fe3d8a.png">


### パスキーによるログイン

ログアウトした状態で、再度`https://(ホスト名)/realms/realm1/account/`にアクセスします。最初にログインしたときと同様、ユーザー名とパスワードの入力欄の下にある「Try Another Way」をクリックし、次の表示で「Security Key」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221308/f3638503-fbbc-c284-1586-7a7c9f842219.png)

この表示で「Sign in with Security Key」をクリックすると、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221308/65c86eef-621f-70d7-9b45-4310f38a40b5.png)

WindowsのChromeであればこのように表示され、パスキーを選択できます。
いずれか1個のパスキーを選択すると、Windows Helloのダイアログが出現します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221308/1059ba90-b40f-2dcd-1000-3ffc119c821e.png)

この場合はPINを入れると、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221308/4c6bfab7-4f82-51c4-ca42-ed12f3e71fb6.png)

無事にログインが完了しました。

## 今後の改善について

Keycloakでは、ご覧の通りパスキー認証が十分利用可能なことが分かりました。
ただ、私は以下のように、UIの改善余地もあるかなと思いました。

#### 「Security Key」

「Security Key」という表記は、YubikeyのようなUSB接続のセキュリティキーしか対応していないような印象になるため、パスキーに表記を変更するべきと思います。
というわけで、GitHubのディスカッションに提案を投稿してみました。

https://github.com/keycloak/keycloak/discussions/25500

#### 無駄な画面遷移

今回設定した認証フローでは、画面遷移に無駄が多くなっています。[認証方法の選択](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221308/b2ef7997-76f8-2576-403c-016c74ec0544.png)が必要だったり、[ボタンを押す以外ないのにわざわざ押させ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221308/f3638503-fbbc-c284-1586-7a7c9f842219.png)たり。

例えば、GitHubのように、ID/パスワードの入力フォームの下に「パスキーでログイン」ボタンを設ければ、スムーズにいけると思います。あるいは、[WebAuthn Conditional UI](https://github.com/w3c/webauthn/wiki/Explainer:-WebAuthn-Conditional-UI)に対応し、ユーザIDの入力時にパスキーが補完されるようにする、ということも考えられます。

この問題は、Keycloakは認証方式をそれぞれAuthenticatorとして実装し、それを認証フローとして管理者が自由に組み立てられることに起因しています。

KeycloakのGitHubリポジトリでも、改善が議論されています。例えば、弊社メンバーが開始した以下のディスカッションが盛り上がっています。 

https://github.com/keycloak/keycloak/discussions/15860

今後に期待したいですが、すぐにKeycloakでパスキーを使いたい場合は、UIテンプレートのカスタマイズである程度は対応可能です。

# 最後に

パスキー対応サイトは、特に2023年、急速に増えました。
FIDO2/WebAuthnは今となって思えばとっつきにくい印象があったのは確かです。認証関連に詳しいエンジニアの中で知る人ぞ知る技術であり、世の中への展開はなかなか進みませんでした。それがパスキーによって一気に空気が変わりました。

みなさまも、ご自身のアプリケーションをパスキーに対応してみてはいかがでしょうか。Keycloakを用いれば、この記事の通り簡単に対応可能です。

NRIでは、[Keycloak by OpenStandia Advent Calendar 2023](https://qiita.com/advent-calendar/2023/keycloak-by-openstandia)にて、Keycloakをインストール方法からご紹介しています。是非他の記事もご覧ください。