Automatic passkey upgradeに対応していないライブラリで無理矢理対応させてみた

Safari18、iOS18から、パスワードマネージャーを使ってパスワードを自動入力してログインした後、そのままサイレントでパスキーを登録してしまうという機能が使えるようになりました。

https://developer.apple.com/videos/play/wwdc2024/10125/

普通に実装したい方は、geboさんのこちらのブログ記事を参照頂くのがいいと思います。

https://zenn.dev/gebo/articles/webauthn-safari18-passkey-upgrades



ところで、Automatic passkey upgradeは、冒頭のとおり、ユーザの操作なく完全サイレントで登録処理が走るため、本来WebAuthnであれば必須である、User Presence（ユーザ存在）の確認もなく、UPフラグがゼロで結果が返却される訳ですが、UVだけでなくUPもゼロというのは今までのFIDO/WebAuthnの世界では前例が無く、この機能に対応していないライブラリでは問答無用でエラーになってしまいます。

なお、UVフラグは、User Verificationを行ったことを示すフラグです。こちらのブログ記事で少し説明しています。

https://sizu.me/kkoiwai/posts/vrbdbodcbsub

ちなみに、皆さん大好きTypeScriptのWebAuthnライブラリである、[SimpleWebAuthn](https://simplewebauthn.dev/)も、10月リリースの[バージョン11](https://github.com/MasterKale/SimpleWebAuthn/releases/tag/v11.0.0)から、User Presenceの確認を省略できるようになりました。

![SimpleWebAuthnのリリースノート](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135431/05f46d77-2056-6dbc-0b55-5d0d2ca940b0.png)


私もGlitchという無料ホスティングサービスで、NodejsとSimpleWebAuthnを使って、この機能を実装してみようと試してみました。ですが、Glitchで対応している[Nodejsは非常に古く](https://help.glitch.com/hc/en-us/articles/16287495688845-What-version-of-Node-can-I-use-and-how-do-I-change-update-upgrade-it)、バージョン16であり、最新のSimpleWebAuthnは対応していないようです。


仕方が無いので、パスキー作成レスポンスをライブラリに渡す前に、無理矢理UPフラグを１にしてみました。
そのソースがコレです。クライアント（ブラウザ）側、サーバ側、どちらで実行しても大丈夫だと思います。

ね、簡単でしょ？

```js

function forceUPflag(cred){
  if(cred.response.attestationObject.startsWith("o2NmbXRkbm9uZWdhdHRTdG10oGhhdXRoRGF0")){
    // when the first part of CBOR matches ("fmt":"none","attStmt":{},"authData")
    const attObj = base64url.toBuffer(cred.response.attestationObject);
    let view = new Uint8Array(attObj); 
    let flag = view[62];
    console.log(flag.toString(2).padStart(8, '0'));
    flag = flag | 1 ;// UP flag
    console.log(flag.toString(2).padStart(8, '0'));
    view[62] = flag;
    cred.response.attestationObject = base64url.encode(view);
  }
  return cred;
}

```

と、急にソースを見せられても、何をやっているのかちんぷんかんぷんだと思うので、もう少しだけ説明させてください。

まず、WebAuthn APIを実行して得られるPublicKeyCredentialオブジェクトの中には、Attestaton Objectというデータが含まれます。Attestation Objectは[CBOR形式](https://cbor.io/)のデータになっており、構造は下図の様になっています。

![Attestaton Objectの構造（W3C WebAuthn仕様より)](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135431/a3baf199-413f-9e14-c50e-b2b578f01582.png)
https://www.w3.org/TR/webauthn-3/#attestation-object


我々が操作したいUPフラグが含まれるのは、Attestation Objectの中に入れ子で含まれるAuthenticator Dataの、33バイト目のFLAGSと書いてある部分になります。

ここで、もう一度Attestation Objectの図を見て頂くと、`fmt`(Attestation Format)と`authData`(Authenticatior Data)の間には、`attStmt`(Attestation Statement)というデータが挟まっていることが分かります。

しかしながら、通常同期パスキーにはAttestation Statementが付与されないため、`fmt` は `none` だし、`attStmt`の中身は空になります.

つまり、Authenticator Dataに行き着くまでのデータ構造は、固定されているということが分かります。
それを簡易的に確認するため、functionの一番最初のif文で冒頭のデータ構造をbase64url文字列のまま比較しています。

Authenticator Dataに行き着いた後も、フラグの前は32byteのRP ID hashで固定されているため、結果的に、フラグは必ずAttestaton Objectの63バイト目となります。

そして、UPフラグは、そのバイトの "the least significant bit"となりますので、ビット演算で`1`と`OR`して、UPフラグを強制的に立てています。

FIDO認証やWebAuthnを調べたことがある方であれば、もしかしたら、「**あれ、UPフラグを改変したら、署名検証に失敗するのでは**」と心配される方もいるかも知れません。非常に鋭いです！
ですが、パスキーの登録時、Attestationを利用しない場合は、公開鍵の情報だけが送信され、署名検証は行われないのです。

と、言うことで、Glitchで立てたサーバ上で、無事、Automatic passkey upgradeによるパスキーの登録ができるようになりました。
当たり前ですが、よい子は絶対にマネしないでください。ちゃんとライブラリ側の対応を待ちましょう。


![実際に動作している様子](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135431/e5831b86-7268-6167-ddf7-44f8ad08e23e.gif)



# 最後に宣伝です！

「パスキーのすべて」という本を、３人で執筆しました。
1月27日発売予定です。

https://amzn.asia/d/gsK7tyC

(まだリンク先は仮の書名になっているかもです)

パスキーという言葉が生まれる前のFIDO認証の歴史から、Automatic upgradeをはじめとする、最新のWebAuthn Level3で導入される予定の新機能まで、幅広く取り上げています。

今回、それなりにマニアックな記事を投稿させてもらいましたが、逆に言えば、**こんなマニアックなこと以外はほとんどこの本でカバーしている**ということです。

この記事では、Automatic passkey upgradeを利用する場合のWebAuthn APIの呼び方、UP、UVフラグの詳細や、そもそもAttestationとは何なのかといったことすら説明しませんでしたが、この本で詳しく説明しています。
パスキーの導入を社内で説得したい人も、これから開発しますという人も、幅広く満足頂ける本になっていると自負しています。
すでにAmazonだけでなく、[丸善ジュンク堂](https://www.maruzenjunkudo.co.jp/products/9784297146535)や、[紀伊國屋書店](https://www.kinokuniya.co.jp/f/dsg-01-9784297146535)、さらには[楽天ブックス](https://books.rakuten.co.jp/rb/18080194/)や[ヨドバシ\.com](https://www.yodobashi.com/product/100000009003999594/)等で予約できるようになっていますので、是非ぜひ自分用、保存用、布教用に一人３冊お買い求め頂けると幸いです！

# おまけ

JavaScriptでバイナリを無理矢理いじくってみた系の記事が好きな人はこちらもご覧ください。

https://qiita.com/kkoiwai/items/d410b054797ff89f6398


何かを強引に実装してみた系の記事が好きな人はこちらもご覧ください。

https://qiita.com/kkoiwai/items/ffe33b1d37a208cf365e

個人的に一番書いていて楽しかったのは、やっぱり物理的にモノが動いたときなのですが、あまり閲覧数が伸びなかったので見て頂きたいです。

https://qiita.com/kkoiwai/items/1289c12a01c14c28baaa