📱

スマホアプリでパスキーをサポートする3つの方法

に公開
2025/12/08

これは「Digital Identity技術勉強会 #iddance Advent Calendar 2025」7日目の記事です。
https://qiita.com/advent-calendar/2025/iddance

こんにちは。最近アプリに入門しようと思いつづけていたのでアプリのパスキーの話をします。

まとめ

  • スマートフォンアプリにおいてパスキーの実装をするには以下の3つの選択肢
    • 「ネイティブ実装」
    • 「System WebView(CustomTabs/ASWebAuthenticationSession)」
    • 「Embedded WebView(WebView/WKWebView)」
  • 「ネイティブ実装」はシームレスなUX実現、App専用の機能を利用できる。
  • 「System WebView」はWebブラウザ用のコンテンツを流用できる。ブラウザと同じUXを提供可。
  • 「Embedded WebView」でもパスキーを利用できる。
  • 既存のWebコンテンツを利用したいかどうかで「ネイティブ実装」「System WebView」のどちらかを選択するのが良いと思う。

ネイティブ実装

「ネイティブ実装」ではアプリ内でUIを持ち、その中でAndroidならば「Credentials Manager API」、iOSなら「ASAuthorizationPlatformPublicKeyCredentialProvider」を利用してパスキーの操作を行います。アプリ内で全てが完結するためUX上は最もよくなる可能性が高いです。ただしWebブラウザもサポートしている場合は実装管理コストもかかってしまいます。

パスキーが利用できるときだけパスキーで認証する

ネイティブ実装ではpreferImmediatelyAvailableCredentialsオプションが利用できます。このオプションを指定することで「デバイス内に利用できるパスキーが存在するときだけパスキーでログインする」UXを実現できます。

例として、Androidアプリにおいてログイン画面表示時にパスキーの認証APIを呼び出したとします。
このオプションで指定する値によって以下のような違いがあります。

デバイスにパスキーがある場合 デバイスにパスキーがない場合
(preferImmediately: true)
デバイスにパスキーがない場合
(preferImmediately: false)
パスキープロンプト表示される 従来のログイン画面を表示する デバイスにパスキーがない場合は従来のログイン画面 エラートーストが表示される デバイスにパスキーがない場合は従来のログイン画面(immediate=false)

ちなみに、Webブラウザにおいては「Passkey Autofill(Conditional Get)」を通じて似たようなUXが実現可能性です。ただし、ページ内にautocomplete=webauthnを指定したinput属性が必要等の制限があります。また、アプリのような即時発動をするためにはautofocus属性の指定が必要ですが、Chromeではまだサポートされていないようです。

System WebView

「System WebView」ではアプリ内でブラウザアプリを開き、そこでパスキーの操作を行います。
AndroidアプリではCustomTabs、iOSアプリにおいてはASWebAuthenticationSession(SafariとCookie共有あり)/SFSafariViewController(Cookie共有なし)を利用します。

この方法ではWebブラウザを利用しているため、ブラウザ上で利用できるパスキーの機能をすべて利用できます(おそらく)。

ブラウザとアプリでログイン済みアカウントが異なることの考慮

CustomTabsやASWebAuthenticationSessionはブラウザとCookieを共有します。そのため
アプリとブラウザでログイン済みのアカウントが異なるケースを考慮する必要があります。

  • (認証)アプリでアカウントAでログイン中、System WebViewで再認証をしようとしたらアカウントBで再認証が発生した
  • (パスキー管理)アプリでアカウントAでログイン中、System WebViewでパスキー管理ページを開いたらアカウントBのコンテンツが表示された

これらのケースにおいては、System WebView起動時に操作するアカウントの確認画面を一度挟み、アプリと異なる場合は変えてもらう等の仕組みが必要になると考えています。もしくは、System WebViewをプライベートモードで起動しつつ、アプリ側でログインしているアカウントをブラウザでもログイン状態にできるようにする必要がありそうです。

なお、System WebViewのプライベートモードについては以下のパラメータが各OSで提供されています。

デフォルトブラウザの設定

AndroidのCustomTabsにおいてオプションの指定をしない場合、利用されるブラウザはデバイスのデフォルトブラウザに依存します。
Androidの一部ブラウザではサポートしていない機能等もあるため、Chromeを強制したい場合は以下のような指定をすることができるようです。

customTabsIntent.intent.setPackage("com.android.chrome")


左:指定なしのCustomTabsでデフォルトブラウザのFirefoxが起動、右:CustomTabsでChromeを指定した場合

Embedded WebView

「Embedded WebView」では「System Browser」と同じくWebコンテンツを利用しつつ、表示につかうブラウザをアプリに組み込んだWebView(Android)やWKWebView(iOS)を利用します。
「System Browser」と異なりアプリ用のCookieJarを保持しているため、アプリはアプリのアカウント、ブラウザはブラウザのアカウントといった使い分けができます。
注意点としてiOSのWKWebViewはおおよそSafariと同じ挙動なのですが、AndroidのWebViewではいくつかの注意点があるため次節で説明をします。

AndroidのWebViewにおけるパスキーサポート

AndroidのWebViewはandroid.webkit.WebViewが1.12.0以上であればオプションの設定のみで利用できます。 その他Appとドメインの紐付けは必要ですが、ネイティブ実装よりははるかに楽に実現できるようになりました。(昔はもっと大変だった)

WebView でユーザーを認証する | Android Developers

注意点として2025/12の現時点では、AndroidのWebViewはPasskey Autofillをサポートしていません。
そのため、今の段階でパスキーのサポートが必要な場合は、System WebViewを利用するか、Autofillを前提としないログインUXを実装する必要があります。
なお、AndroidのWebViewでAutofillなどWebAuthn APIの機能をサポートする予定らしいです。また、System WebViewに移行までの猶予対応とかではなくちゃんとした対応とのこと。(GoogleのAndorid担当の方から聞いた)

結局どれで実現すべきなのか

筆者はアプリのプロダクトを担当したことはないので素人判断になってしまいますが、以下の感じかなと思いました。

  1. すでにアプリ用のログインUIがあったりWebブラウザのサポートが不要 → 「ネイティブ実装」
  2. アプリとブラウザでログインしているアカウントのズレが影響なし/軽微 → 「System WebView」
  3. 最終手段として「Embedded WebView」

1について、アプリとWebで異なる仕様の場合、ユーザに混乱が発生する可能性があるので、可能なら統合できていると良いと思いました。
2,3について、可能であれば「System WebView」を利用すべきだと考えています。理由として、「現時点でサポートされている機能が限定的」かつ「利用RPが少ない実装が故のバグ発見の少なそう」が挙げられます。

おわりに

アプリとWeb、実装の違いから仕様のズレが生まれがちなのですが、ユーザ目線では「どっちも同じサービスを使っているのになんかうまくいかない」という体験につながってしまうと考えています。これの大きな原因は仕様の管理漏れです。
アプリでパスキーをサポートする、という明確な機能追加ではありますが、追加する仕組みはなるべくシンプルにすることで管理しきれない仕様が生まれることを防げるとよいなと思います。

参考にした

Discussion