WebAuthn の利用/実装パターンを考える

ritou です。

WebAuthn が普及し始めるタイミングになると、サービスがその恩恵を受けるために開発者はどのように使うべきか、実装をすべきかを考えるでしょう。

今までの経験から、きっとこれぐらいの実装パターンに落ち着くんだろうなってのを書いておきます。

## 前提

現状の WebAuthn については、多くの開発者は仕様の解説や Client (ブラウザ)実装の足並み、 Authenticator の普及具合を様子見している状態かもしれません。

この記事では、少し先の未来、つまりそれらが満足いく状態になり、実装するタイミングを想定しています。

* 仕様
  * WebAuthn の安全性/利便性が様々な方面から評価され、普通にプロダクションで使うにも**不安のない**状態
* 実装 : 鶏も卵もある。あとは食べるだけ状態。
  * Client = ブラウザの実装がある程度整い、ほとんどの環境で動作する
  * (FIDO2)に対応した Authenticator も揃っている
* ユーザー
  * 先行実装したサービスに触れる機会が増えている
  * (TVで紹介されたりと)一般的に認識されている

まぁ、希望的観測的なところですが、今のソーシャルログインのように「これ使おうか」って簡単に言えるような状況を想定しています。

## 誰が WebAuthn を実装すべきか問題

どれだけ安全/便利な認証方式が出てきたとしても、ユーザーのアクションを必要とする以上はユーザーの負担となりえます。

現在、あなたはどれだけの数のパスワードを管理しているでしょうか？
WebAuthn(FIDO認証)がパスワード認証を全て置き換えられた時、今管理しているパスワードを扱うサービスに対して一つ一つ WebAuthn の Authenticator の登録をしていくことを想像できますか？

この問いに対して、既存の仕組みでいうと「ソーシャルログイン」つまりID連携は、ユーザー１人あたりの認証負荷を軽減する選択肢の一つでしょう。

OpenID Connect(以下、OIDC) などのID連携における登場人物と WebAuthn の登場人物の組み合わせはいくつか考えられます。
前にちょっと整理したこともあります。
[OpenID Connect のあれが WebAuthn のこれになったらどうなるかって話](https://ritou.hatenablog.com/entry/2018/09/28/032327)

この話も含めて少し煮詰めていくと、最終的に利用パターンというか実装パターンは3種類ぐらいに落ち着く気がしています。

## 利用/実装パターン

### 1. ID連携を提供している Identity Provider が WebAuthn を実装

今回の前提では、ID連携機能を提供している IdP が WebAuthn を導入するケースも増えると思います。
[WebAuthn の課題と認識されつつあるアカウントリカバリー](https://qiita.com/ritou/items/20679287600b61b124fc) なども含め、色々めんどうなところは IdP にお任せし、サービスは**ID連携を用いてその恩恵を受ける**のもありでしょう。

![無題のプレゼンテーション (6).png](https://qiita-image-store.s3.amazonaws.com/0/10192/f0c276de-ca48-6534-96e7-37be90159bcc.png)

* :+1:
  * ユーザーは IdP 上で**のみ** WebAuthn の登録/認証を行う
  * サービスは WebAuthn を実装しなくて良い
* :-1:
  * ID連携機能は実装が必要
  * IdP が WebAuthn をどのような形でサポートするかわからない
  * サービスは IdP に WebAuthn の認証を要求できない(かもしれない)

良くも悪くも IdP に依存するパターンですが、サービス側は WebAuthn に関する実装は不要でID連携のところだけ実装する必要があります。OIDCの実装つったら私の出番だ。

Google に実装された場合、当然ですが意識の高い Google ユーザーは色々なサービスをより安全に利用できるようになります。
しかし、そんなユーザーばかりではないですし、以前ネットで流れていた２段階認証の設定割合などを見ても自発的に使わないユーザーもいるでしょう。

サービス側が決済を扱うなど、IdP依存するにしても細かいところをしっかりやる必要がある場合は、以前から言われているような「認証レベル」「認証強度」の表現の仕組みを用いてサービス側から IdP に要求ができたり、ID連携結果に含まれるなど、細かいハンドリングができるようになる必要があるでしょうし、そこを含めてアピールできれば IdP の価値も上がりそうですね。

### 2. サービスのドメイン上で WebAuthn 認証はするが、公開鍵の管理等はBaaSが担当

認証機能やデータストアなど"当たり前に必要な機能"を BaaS / IDaaS　を利用して開発リソースをサービス本体の機能に集中させているところもあるでしょう。
パスワード認証 + ソーシャルログインという組み合わせやソーシャルログインだけでも利用可能であるなど、割と柔軟に使うことができる BaaS / IDaaS が WebAuthn を実装することも十分に考えられます。

BaaS / IDaaS で IdP と連携する場合は上記1のパターンになります。
ここではサービスが自ドメイン上でユーザーとやり取りしつつも Baas / IDaaS が提供する　WebAuthn 機能を利用するイメージです。

![無題のプレゼンテーション (5).png](https://qiita-image-store.s3.amazonaws.com/0/10192/214bb61b-f771-c3e2-aa3d-21b2b90cdb02.png)

* :+1:
  * サービスはユーザーに WebAuthn の処理を強制できる
  * サービスは WebAuthn のバックエンド処理を実装しなくて良い
  * WebAuthn のフロントエンド処理も BaaS / IDaaS の SDK などのサポートにより楽に実装できる(かもしれない)
* :-1:
  * 仕様のどこまでサポートされているかは BaaS / IDaaS に依存する。
  * ユーザーはサービス毎に登録/認証処理を行う必要がある
  * 認証処理を行うドメインをサービス内で統一しておくなど、WebAuthn の特徴に合わせたアプリケーション設計が必要(かも)

これでも自前で全部実装するよりははるかにスピード感と安全性を保てると思います。

#### (妄想)Firebase が WebAuthn に対応したら

先ほどの図にも入れちゃいましたが、Firebase が WebAuthn に対応したらみんな喜びそうですね。私の仕事なくなっちゃう。けど、そんな時代なのかもしれない。
そこで、もし対応されたらこんな感じで使われるのかな？っていう妄想みたいなのをちょっと書いておきます。

振り返りになりますが、WebAuthn をサービスで使うにはざっくり Authenticator (実際は公開鍵)の登録と認証の2つの機能が必要です。

既存の Firebase の機能の感じだと、メアド/パスワードでユーザー作成、もしくはID連携や他の方法でもとりあえず新規ユーザー作っちゃう感じですが、WebAuthn 対応となると、こんな感じでしょうか。

* WebAuthn で登録
  * 1. ユーザーにメアドや電話番号など、識別子を入力させる。`navigator.credentials.create()` 呼ぶときに細かい指定したかったらオプションっぽくここで渡す。
  * 2. JavaScriptとかでそれらの識別子を送り、Firebase はすでにユーザーが存在していたらエラー、その場合は [アカウントに複数の認証プロバイダをリンクする](https://firebase.google.com/docs/auth/web/account-linking?hl=ja) あたりでなんとかする。 ※スクリーニング対策？んなもん知らん
  * 3. Firebase はユーザーが存在しなかったら仮でIDみたいなの振りつつ `navigator.credentials.create()` に必要な情報をサービスに返す
  * 4. サービスのドメイン上で Authenticator の登録処理が走る
  * 5. `Attestation` などを Firebase に送る
  * 6. Firebase は仮IDだったのをちゃんとしたIDとして作成して ID Token を返す
* WebAuthn でログイン(2 step) : ResidentKey 使わない
  * 1. ユーザーにメアドや電話番号など、識別子を入力させる。`navigator.credentials.get()` 呼ぶときに細かい指定したかったらオプションっぽくここで渡す。
  * 2. JavaScriptとかでそれらの識別子を送り、Firebase はすでにユーザーが存在してなかったらエラー、その場合は登録フローに戻す。 ※スクリーニング対策？んなもん知らん
  * 3. Firebase はユーザーが存在したら公開鍵を含んだ `navigator.credentials.get()` に必要な情報をサービスに返す
  * 4. サービスのドメイン上で Authenticator によるログイン処理が走る
  * 5. `Assertion` などを Firebase に送る
  * 6. Firebase は検証して正しかったら ID Token を返す
* WebAuthn でログイン(1 step) : ResidentKey 使う
  * 1. ResidentKey 使ってログインさせることを Firebase に伝える。`navigator.credentials.get()` 呼ぶときに細かい指定したかったらオプションっぽくここで渡す。
  * 2. JavaScriptとかでそれらの識別子を送り、Firebase はすでにユーザーが存在してなかったらエラー、その場合は登録フローに戻す。 ※スクリーニング対策？んなもん知らん
  * 3. Firebase は公開鍵を含まない `navigator.credentials.get()` に必要な情報をサービスに返す
  * 4. サービスのドメイン上で Authenticator によるログイン処理が走る
  * 5. `Assertion` などを Firebase に送る
  * 6. Firebase は検証して正しかったら ID Token を返す

```JavaScript
// 登録
var identifier = getIdentifierFromUserInput();
firebase.auth().signUpWithWebAuthn(identifier, options)
    .then(function (confirmationResult) {
      // 登録完了まで一気にやってくれたら嬉しい
    }).catch(function (error) {
      // エラー
    });

// ログイン(2step)
var identifier = getIdentifierFromUserInput();
firebase.auth().signInWithWebAuthn(identifier, options)
    .then(function (confirmationResult) {
      // ログイン完了まで一気にやってくれたら嬉しい
    }).catch(function (error) {
      // エラー
    });
```

**途中で「なんで私はよそのプロダクトの設計してるの...」って思いましたがまぁ気にしない**

WebAuthn の仕様では、外部IdPとのID連携の場合と違って「登録済みだったらログイン、お初だったら登録」みたいな感じにはならなそうですが、この辺がどう実装されるかは楽しみなところです。

**あくまで妄想なので試しても何もおきませんよ**

### 3. サービス側が全て実装

最後は自前でがっつり実装するパターンです。図や :+1: :-1: はもはや省略します。
これ、非推奨では？と思うかもしれませんが、状況次第ではそうでもないかもしれません。

例えば、FIDOアライアンスにはFIDO認証を提供するサーバーの認証の仕組みが存在します。
例えば LINE の場合。

[FIDO at LINE: パスワードのいらない世界に向けて](https://engineering.linecorp.com/ja/blog/fido-at-line/)

> FIDO AllianceはFIDOのエコシステムを構成するベンダー間の相互運用性を検証することを主な目的とする認証プログラムを運営しています。また、FIDO Allianceは、FIDO・オペレーション(登録、認証、登録取り消し)の相互運用性を確立することが立証された製品に対して認証書を発行します。FIDO Allianceは90日ごとに相互運用性テストイベントを開催しており、現在、FIDO仕様を遵守しようとするすべてのベンダーは、自社製品の相互運用性を当該イベントで立証します。私は2018年11月、ソウルで開かれた相互運用性テストイベントに参加しており、私が開発したサーバーがFIDO UAF、U2FやFIDO2仕様を遵守して他のベンダーのFIDO認証装置(16種類) に対してイベントを通じて立証することができました。

こういうのがOSSで提供され、サービスが使うことができたら、比較的安全に実装することも可能でしょう。
例えば LINE の場合。

<blockquote class="twitter-tweet" lang="ja"><a href="https://twitter.com/FIDOAlliance/status/1070963967898599425">https://twitter.com/FIDOAlliance/status/1070963967898599425</a></blockquote>

と、ここまでおおごとじゃなくても、ライブラリ、サーバー開発も盛んですし、適合性テストを通して品質もわかると思うので、気をつけて実装すれば大丈夫...かなと思ったりします。知らんけど。

## まとめ

* 利用/実装パターンは3種類になりそう
* みんな大好き Firebase の実装どうなるかなーと妄想した
* 自前実装も安全にできそうなのは良いことですね

ここ最近、個人的には「WebAuthnはIdPに任せるべき！個々のサービスは手を出すな!」みたいな流れを推して行こうかと思ってたのですが、 パスワード認証やID連携に比べても WebAuthn 自体は安全性が高い仕組みなので、実際はこの3パターンに落ち着くのではないかという感じになったので今回の記事を書いてみました。

これが役に立つ時がくるといいですね。
ではまた。