2024-08-01

Android 14 (Credential Manager) におけるパスキー対応が面倒すぎる

DroidKaigiにココらへんの話をしようと思って、CfP書いたけど落ちたので、自分用の覚書。

Firefox (GeckoView) AndroidでCredentail Managerの対応を入れたのが、GeckoViewとしてはバイナリサイズを大きくしたくないため、JetPackを一切使わずにCredential Manager経由でWebAuthn対応を行うコードをJavaでスクラッチで書いた。おそらくJavaでスクラッチで書いたのはChromeとGeckoViewだけだし、おそらくこの2つの製品以外でスクラッチ実装がされることは今後もないと思う。

しかもGeckoViewはWebブラウザエンジンなわけだから、いろんなWebサイトで実行可能な必要がある (= オリジンを正確に設定する必要がある)。この値を設定するのもおそらくBlinkとGeckoの2つの製品以外でほぼ存在しないであろう。なので、実装にあたって、実装者、すなわち自分しか被害者がいない事例がいろいろあることになるわけだ。自分はGoogleの人じゃないので、社内情報アクセスできないからね。

従来の実装方法

Google Mobile Service (GMS) にFIDO API (https://developers.google.com/android/reference/com/google/android/gms/fido/fido2/package-summary)が提供されていた。これを使うことでWebAuthnの実装が簡単にできるようになっている。またとあるバージョン以降であればパスキーをGoogle Password Managerに登録することが可能。これは古いAndroidでも動作する。

Credential Managerとは

Andorid 14から Credential Managerという仕組みが追加された。このCredential Managerはサービスとして実装されるもので、サードパーティに対して認証要求を移譲できる。ただこのサービスには3つのメソッドしか存在しなくて、登録・認証・削除があるだけだ。幅広い認証情報を扱えるようにAndroidチームの人は考えためであろうけど、この3つのメソッドには引数がBundleしか実質存在しない。すなわちアプリケーションはBundleに各々好き勝手に情報を入れると、OSにインストールされたCredential Managerサービスたちが、登録成功とか返すようになっている。なおBundleに何入れるべきか?なんてものも存在しない

Credential Managerを使ったパスキー実装

パスキーを実装するには、このBundleになにかを入れる必要があるのだが、そんな情報はdeveloper.android.comには一切存在しない。GeckoViewでは一通りのBundle定義をしているが、これらはJetPackのコードとChromeのコードから持ってきている。オープンソースだったからいいようなものだが、これらの謎定義はGoogle社内でしか共有されていないような感じなので、まぁなんというかEU頑張れって感じ。ここには入れていないが、Google Password Managerだけ無視するっぽく見える定義もあったりする

実際問題、JetPackを使った場合はここらのBundle問題はJetPack側で吸収されるので、そこらは問題になりえないのだが、問題はリクエスト用のJSONは自分たちで組み立てないといけないってことだ。 なので、WebAuthnの仕様をちゃんと理解しないといけない。それをアプリケーション開発者に求めるのはどうかと思う。なおレスポンスもJSONむき出しで渡される。検証とかしたい場合は、むき出しで渡されたJSONデータを展開して検証しないといけない。

以前のGMSのFIDO APIではJSONむき出しな仕組みにはなっていない。引数はBuilderが用意されているので、必要なパラメータを設定するだけで行える。ちなみにGeckoViewでいうと、これがCredential Manager版ここがFIDO API版なのだが、Credential Manager版は自分でJSONを組み立てるのに対して、FIDO API版はBuilderで引数を組み立てる。

あと、検証時 (Assertion) には落とし穴が実は存在してて、クライアントサイドで検証する際には、レスポンスのJSONの ClientDataJsonハッシュを使って検証を行うわけだけど、レスポンス内のこの値は正しくない可能性があって、リクエスト時にBundleに入れたClientDataJsonのハッシュが正しい値だったりする (これは2日悩んだ)

現在のCredential Managerがどの認証方法を対応しているか?

そんな方法はない。

とりあえず試してみて、サービスがTYPE_NO_CREATE_OPTIONSを返してくれれば、たぶん対応していないということがわかる。なお、1Passwordは最初のリクエストでサービス自体がクラッシュしたりするので正しく動かなかったりする。GeckoViewだと登録時はResident KeyがRequiredのときだけCrednetial Managerを使うようにしているが、Preferredの場合にどうするかは決めかねている。

また、認証を行おうとしてるクレデンシャルがGoogle Password Managerで認証可能かどうか?みたいなのはFIDO API経由で確認は可能なので、認証可能であればGMSのFIDO2 APIを使って、認証できない場合は Credential Managerを使ういうこともできる。GeckoViewもChromeもそのようなコードを入れている。

結論

GMSのFIDO APIがCredential Manager対応すれば、みんなハッピーだったのでは?やっとサードパーティ製品で対応が増えてきたところだしさ

0 件のコメント: