まだパスワードで認証してるの？Passkeysを使ってパスワードを駆逐してやる！

こんにちは。[virapture株式会社](https://virapture.com)でCEOしながら[ラグナロク株式会社](https://ragna-rock.com)でもCKOとして働いている[もぐめっと](https://mogmet.com)です。

![mogmet](https://user-images.githubusercontent.com/1166342/175216261-91dea414-07e5-4937-9a68-e25fe946e146.jpg)
最近牛宮城へいったのですが、宮迫さんに遭遇することができました。もっとお話してみたかったです。


本日は先日のWWDCで発表があったパスワードを駆逐できるPasskeysについて仕組みや実装法を紹介します。

https://developer.apple.com/videos/play/wwdc2022/10092/

Passkeysの説明、仕組み、実装といった流れで紹介していきます！

## Passkeysとは?

Passkeysはパスワード無しで認証ができてしまうすごい仕組みです。
どんなものか画像をもとに説明します。

サービスに作成されたアカウントにpasskeyを紐付けてみます。
<img width="300" alt="image" src="https://user-images.githubusercontent.com/1166342/175182993-4eccee1d-8e38-4c51-9f25-97bdab8b51a5.png">

アカウントに対して暗号化されたキーペアが生成され、iCloud keychainに保存されるのでどのapple deviceでも使えるようになります。
<img width="1488" alt="image" src="https://user-images.githubusercontent.com/1166342/175183058-422eaa20-e3b4-4c8b-b8d9-61cafdf20f82.png">

一回サインアウトします。
<img width="300" alt="image" src="https://user-images.githubusercontent.com/1166342/175183120-c7b9be03-9d7f-491a-afb8-fb9edeb3ceef.png">

Passkeyを使ったサインインができるようになっております。
<img width="1200" alt="image" src="https://user-images.githubusercontent.com/1166342/175183249-381c41f7-fe2a-4fb8-beb4-c58a4fa8686c.png">

Webでもサインインが可能です。

<img width="1855" alt="image" src="https://user-images.githubusercontent.com/1166342/175183183-740ae7f3-50fe-48d7-83f5-1f1be3705889.png">

友達のPCからログインすることもできます。

<img width="600" alt="image" src="https://user-images.githubusercontent.com/1166342/175183346-a91c50f7-0e1f-4079-9014-f9ca1dcce9a7.png">

ログインするとQRが表示されるのでデバイスで読み取ります。

<img width="1642" alt="image" src="https://user-images.githubusercontent.com/1166342/175183364-86380c8c-9fa1-4d59-87b8-82e8f1462b4a.png">

passkeyでののサイン員を求められるので続けるとサインインができます。

<img width="1870" alt="image" src="https://user-images.githubusercontent.com/1166342/175183531-cbfbd29a-836d-473e-b508-52ee0cc48f55.png">

Airdropでpasskeyの共有も可能です。
パスワード一覧からpasskeyを選択します。
<img width="1249" alt="image" src="https://user-images.githubusercontent.com/1166342/175183605-43dfe131-5637-4d36-8057-83f6ed95fd76.png">

共有する相手を選択すると共有できます。

<img width="1346" alt="image" src="https://user-images.githubusercontent.com/1166342/175183613-574518f4-67b6-419f-a9b0-b202155813b6.png">

## Passkeysの仕組み

### Appleデバイスでの認証の仕組み

基本的にはFIDO（FIDO Allianceが策定、標準化する認証に関する仕様）の認証方式に則っています。

Passkeyで登録を始めると、デバイスでアカウントごとに秘密鍵と公開鍵が生成されます。

<img width="1920" alt="image" src="https://user-images.githubusercontent.com/1166342/175209566-41d3943e-ecdc-4dce-a99f-1c0530e078fa.png">

生成された公開鍵をサーバに保管します。秘密鍵はデバイスに保管されてサインインに使用されます。
サーバは秘密鍵を知ることはないのでデバイスは安全です！

<img width="1692" alt="image" src="https://user-images.githubusercontent.com/1166342/175209623-728390f9-855a-45c8-a828-280880b2a200.png">

クライアントからサインインを求められたらサーバはchallengeコードをクライアントに求めます
（ちなみにpasskeysはWebAuthnのES256を使ってます）
クライアントは秘密鍵を使ってchallengeコードを署名してサーバに送ります。
サーバは署名されたchallengeコードが正しいものかを公開鍵を使って検証します。
問題なければサインインさせます。

<img width="1920" alt="image" src="https://user-images.githubusercontent.com/1166342/175209684-efb85b3d-e638-4c6a-a676-c5d822328f17.png">

### QRを使ったWeb認証の仕組み

認証してほしいクライアントでQRを表示します。
QRの中身は1回しか使えない暗号鍵のペアを暗号化したURLが含まれています。
デバイスはQRをスキャンしたらbluetoothを使ってサーバ情報やルーティング情報をクライアントに送ります。
（この辺のやり取りはローカルで完結するのでサーバは読むことができない）

<img width="1920" alt="image" src="https://user-images.githubusercontent.com/1166342/175210513-92a0ae19-3da2-47fd-9429-75da5e4aabac.png">

proximity proofを確認することで、ちゃんと近くにデバイスがあって、メールとかで送られたQRを読み取ってないかチェックしている。
こうすることでフェイクのwebsiteじゃないかというのを確認している。
（リモートにあるフェイクサイトだとbluetooth advertisementで交換することはできない）

<img width="1912" alt="image" src="https://user-images.githubusercontent.com/1166342/175212594-a0398edc-3565-4ecf-83f0-e1397fe33334.png">

ローカルでの検証が終わったら、２つのデバイスはrelayサーバに繋げて、FIDO CTAP規格を使って認証します。

<img width="1632" alt="image" src="https://user-images.githubusercontent.com/1166342/175213044-532fa4bc-e9ea-41e9-b383-da89dc003877.png">

cf: CTAPはFIDO2対応デバイスがBluetooth、USB、または近距離無線通信（NFC）を介して外部/ローミングオーセンティケーターと通信するためのインターフェイスを提供する仕様

https://www.okta.com/jp/blog/2019/04/the-ultimate-guide-to-fido2-and-webauthn-terminology/

## Passkeysのデザイン

どんな感じでPasskeysをデザインするかを紹介します。

新しくSF. symbolが使えるようになっているのでこれを使いましょう。

<img width="1266" alt="image" src="https://user-images.githubusercontent.com/1166342/175183693-f4dc00a8-4f64-4340-b559-9837662cd644.png">

AutoFillを使ってpasskeysが使えるように見せましょう。

<img width="1315" alt="image" src="https://user-images.githubusercontent.com/1166342/175184857-562a42df-f256-4b3d-8ce3-afa9192080ec.png">

複数のアカウントや端末に対応しましょう。

<img width="1604" alt="image" src="https://user-images.githubusercontent.com/1166342/175184947-6fed1ab9-a50b-4c78-a37b-88551c414c63.png">


## Passkeysの実装方法

仕組みやUIがわかったところで実装方法を紹介します。

PasskeysはSign In With AppleなどのAPIが入っているASAthorization APIに追加されています。
ASAthorization APIを使って実装してく様子を紹介します。

### サーバ側の実装

apple-app-site-associationのjsonをサーバに配置します。

<img width="1563" alt="image" src="https://user-images.githubusercontent.com/1166342/175185126-f415a753-9d66-46ee-880e-687e31883c33.png">

チャレンジの発行や検証などのサーバ側の実装はAndroid版ですが、下記を参考にしたらできる・・・かもしれません。（時間がなくて未検証）

https://logmi.jp/tech/articles/322823

### iOSの実装

textContentType = .usernameのテキストフィールドを準備します。

<img width="1920" alt="image" src="https://user-images.githubusercontent.com/1166342/175205067-24989e3f-403d-46d5-bae3-92a024b068fe.png">

最初にサーバからチャレンジを取得します。
その後、Passkeyを実施するためにチャレンジからrequestとproviderを作り、ASAuthorizationControllerに渡してrequestします。

<img width="1896" alt="image" src="https://user-images.githubusercontent.com/1166342/175205147-975b1328-6b01-4752-95a2-16545fa85ce9.png">

signinが成功するとdidCompleteWithAuthorization callbackが呼び出されるので、サーバに値を渡してあげてサーバで検証を行った上でサインインを完了させます。

<img width="1783" alt="image" src="https://user-images.githubusercontent.com/1166342/175205243-f957232f-19c7-4898-b767-d6b38a08e8b6.png">

その他の実装として、performRequestsにすると後述するnearby deviceなどのpasskeysの全ての機能が有効になります。

<img width="1255" alt="image" src="https://user-images.githubusercontent.com/1166342/175205485-b283314b-a2ef-42f3-9d40-78221761605d.png">


### Webの実装

inputタグのautocompleteにusernameとwebauthnを指定します。

<img width="1920" alt="image" src="https://user-images.githubusercontent.com/1166342/175205629-86f28e29-45b5-4328-980d-779d0ad4f633.png">

ブラウザがサポートしてるかチェックしした後に、iOSの時と同じ用にサーバから取得したチャレンジデータを指定してnavigator.credentioals.getを実行します。
その後帰ってきたオブジェクトをサーバに返して検証をした後にサインインさせます。

<img width="1450" alt="image" src="https://user-images.githubusercontent.com/1166342/175205678-4a40819e-6d9a-4b28-a75a-bcfd5b0b4814.png">

webでPasskeysを使う場合の補足としては、３点あります。

1. Autofill-assistedリクエストをするときは、ページを作る早い段階でリクエストを作りましょう。
2. モーダルのWebAuthnリクエストは、ボタンのクリックなど、ユーザの行動で行うようにしましょう。
3. Safariの古い認証をリプレースします。既存の認証は引き続き機能し、作成されたデバイスにバインドされますが、新しいプラットフォームの認証はパスキーとして作成されます。パスキーは認証器の妥当性の検証([attestation-statement](https://www.w3.org/TR/webauthn/#attestation-statement))を提供しないため、登録時に従来の認証と区別することができます。

### 様々なログイン方法の実装

ここからは他に3つのログイン方法の実装方法を紹介します。

#### 1. passkeyで複数アカウント

一つのサービスでマルチアカウントを使うことができます。

<img width="300" alt="image" src="https://user-images.githubusercontent.com/1166342/175208006-45fd7719-23d8-4de6-8d71-1ed746aacbc6.png">

実装としては、複数の認証情報をrequest.allowedCredentialsに指定してあげます。

<img width="1848" alt="image" src="https://user-images.githubusercontent.com/1166342/175208097-f83b649c-f28a-4739-9bab-def6e9d65904.png">

#### 2. passkeyを使わない認証に戻す

Passkeysを持っていない端末の場合、デフォルトはQRが表示されますが、そうさせず他の認証方法（今回はパスワードでの認証）を掲示するときに使うケースです。

<img width="1360" alt="image" src="https://user-images.githubusercontent.com/1166342/175208424-31bd19a3-da04-4d27-8a6c-221fd315552c.png">

passkeysでの認証をする際にオプションを指定します。
オプションを指定するとerrorがcallbackでかえってくるようになる

<img width="1920" alt="image" src="https://user-images.githubusercontent.com/1166342/175208541-34ae5494-4315-48de-9406-b91c35bbb778.png">

エラーの内容を見て、他の認証方法を表示してあげるかの制御をします。

<img width="1920" alt="image" src="https://user-images.githubusercontent.com/1166342/175208604-010299d3-c504-4f0f-bb0c-920a059ecd47.png">

#### 3. 複合認証リクエスト

パスワードやSignInWithAppleなど複数のサインイン方法を掲示するケースの実装方法を紹介します。

<img width="440" alt="image" src="https://user-images.githubusercontent.com/1166342/175208689-3beef64f-d0c9-4268-bb8d-d064045b5618.png">

ASAuthorizationControllerを生成するときに複数のauthorizationのrequestを指定するだけです。

<img width="1920" alt="image" src="https://user-images.githubusercontent.com/1166342/175208764-fee615e5-b2b2-4fc1-a39e-bd07837ffe6a.png">

callbackも複数のパターンになるのでいい感じに処理してあげましょう。

<img width="1908" alt="image" src="https://user-images.githubusercontent.com/1166342/175208773-ebc15689-f465-46f6-baee-8a60127475aa.png">

## 多要素認証のリスク比較

つまり、Passkeysはどのケースにも強いっす！

<img width="1920" alt="image" src="https://user-images.githubusercontent.com/1166342/175214169-a2ed3f03-446d-4d24-8fa9-93ba217e12e2.png">

## もぐめっとの所管

Androidは一足先にFIDOのAPIは持っているようです。

https://developers.google.com/identity/fido/android/native-apps

なので頑張ればandroidでもpasskeysのログインはできそうな雰囲気があります。


## まとめ

Passkeysをまとめるとこんな感じになります。

- Passkeysはパスワードを駆逐する
- Passkeysはwebでも使える
- 公開鍵と秘密鍵の仕組みを使ってます



最後に、[ワンナイト人狼オンライン](https://online.1nite-jinro.com/link/blog)というゲームを作ってます！よかったら遊んでね！

https://online.1nite-jinro.com/link/blog

他にも[Camecon](https://camecon.me/)や[Offcha](https://offcha.com/)、問い合わせ対応が簡単にできる[CSmart](https://csmart.jp/lp)といったサービスも作ってるのでよかったら使ってね！

https://camecon.me/

https://offcha.com/

https://csmart.jp/lp

また、チームビルディングや技術顧問、Firebaseの設計やアドバイスといったお話も受け付けてますので御用の方は弊社までお問い合わせください。

https://virapture.com/

ラグナロクでもエンジニアやデザイナーのメンバーを募集しています！！楽しくぶち上げたい人はぜひお話ししましょう！！

https://ragna-rock.com