🦥

Spring Security&LINE FIDO2 Serverでパスキー認証を実装する #5 Conditional UI

2022/10/02に公開

Spring Security&LINE FIDO2 Serverでパスキー認証を実装してみるお勉強メモです。

今回はWebAuthn Level3 の新機能 Conditional UIを実装してみます。

はじめに

Conditional UIとは

WebAuthn Level3で新たに追加された機能で、より簡単にパスワードレスでログインできるUI です。

Conditional UIを使うと今までのモーダルダイアログでなく、パスワードマネージャと似たようなUIでログインできるようになります。

2022/10/1時点、W3Cのスペックで Conditional UI は Editor’s Draft のステータスですが、MacOS VenturaのSafariでは対応しているので早速使ってみたいと思います。

環境

  • macOS Ventura beta でやります
  • ブラウザは Safari(16.1) です
  • LINE FIDO2 Serverはコンテナで動かします
  • FIDO2セキュリティキーは mac のタッチIDを使います

作業開始前の状態

Username/Passwordでログインできる状態から作業を始めていきます。

画面遷移図

詳細図

Conditional UIを実装する

作業開始前の状態からConditional UIを実装しました。

画面遷移図

以下図の赤いラインのように、webauthnでログインする経路を追加します。

詳細図

WebAuthn登録の実装

ログイン後のページでWebAuthnクレデンシャルの登録処理を実装します。

ポイント1: 登録時のオプション

WebAuthn登録は navigator.credentials.create() で行いますが、ポイントは以下

  • authenticatorAttachment を "platform" にする
  • requireResidentKey を true にする

https://github.com/gebogebogebo/spring-security-fido2-login/blob/28dcc0108663632df1ebdfaeb962f220175fd52e/sample-app/src/main/resources/static/js/index.js#L13-L16

ポイント2: Attestationのtransports

WebAuthn登録が成功するとAttestationがGetできます。

macOS Ventura の Safari で登録すると transports に "internal" と "hybrid" が入ってきます。
この hybrid はLevel3で追加された項目になります。
hybridの説明を仕様で見てみる

hybrid
Indicates the respective authenticator can be contacted using a combination of (often separate) data-transport and proximity mechanisms. This supports, for example, authentication on a desktop computer using a smartphone.

ってことです。Touch IDで登録すると proximity(近接) ってことになるのでしょうか?ちょっとよくわかりませんがとにかくこういうものがついてきます。

で、問題なのが hybrid をつけたままのAttestationの場合 line-fido2-server でパースできません。(まだ対応していないようです)

なので、取っ払ってしまいます。

https://github.com/gebogebogebo/spring-security-fido2-login/blob/28dcc0108663632df1ebdfaeb962f220175fd52e/sample-app/src/main/resources/static/js/index.js#L195-L197

これで(無理やりですが)登録Successさせることができます。

WebAuthn認証の実装

WebAuthnでConditional UIを使って認証する部分を実装します。

ポイント1: フォームロード時に実行する

WebAuthnを使った認証は navigator.credentials.get() を使いますが、これをフォームロード時に実行します。

https://github.com/gebogebogebo/spring-security-fido2-login/blob/28dcc0108663632df1ebdfaeb962f220175fd52e/sample-app/src/main/resources/templates/login-fido2.html#L7

ポイント2: 認証時のオプション

navigator.credentials.get() のオプションに

  • mediation = "conditional"

を追加します。

このオプションはLevel3で追加されたものです。
PublicKeyCredential.isConditionalMediationAvailable でチェックしてから追加します。

https://github.com/gebogebogebo/spring-security-fido2-login/blob/28dcc0108663632df1ebdfaeb962f220175fd52e/sample-app/src/main/resources/static/js/index.js#L256-L260

ポイント3: textのautocomplete属性

Login-fido2.html の username text の autocomple属性に webauthn を追加します

https://github.com/gebogebogebo/spring-security-fido2-login/blob/28dcc0108663632df1ebdfaeb962f220175fd52e/sample-app/src/main/resources/templates/login-fido2.html#L16

動作確認

登録

認証

textをポイントすると「このユーザーでログインするなら指紋認証してねっ」っていうガイドが出てくるんですが、これがConditional UIだっていう理解です。

モーダルダイアログが出てくるより操作感がいいです。

よくわからなかったこと

とりあえず Conditional UI の動きは確認できましたが、今ひとつ解せない動作がアチコチにありました。

  • autocomple属性に webauthn をつけてもつけなくても何も変わりませんでした。
  • ↑と関係ありそうですが、一旦 navigator.credentials.get() が走ってしまうと別のhtmlのtextにポイントしても Conditional UI が出てきてしまいます、これは操作感的には完全に意味不明です...(回避策は見つけられませんでした)
  • フォームロード時に navigator.credentials.get() して裏で非同期でタイムアウト無しで何かやっているんだと思うんですが、このPromiseをキャンセルする方法がありません。(何もしないで画面遷移させるときはキャンセルしときたい気持ち)abortController.abort() ってしても効かないようです。別の方法があるんでしょうか、見つけられませんでした...
  • textにカーソルがある状態でEnterを押すとWebAuthnのモーダルダイアログが出てきます。「何で?」ってなります。必要ない挙動です。

おつかれさまでした

とりあえず使ってみたかんじだとログインがやりやすくなっていると感じました。

SafariだけでなくChrome, Edgeでも実装されればいい感じになるんじゃないかと思います。

追記

ChromeでのConditional UI

2023/6/18日時点、Chrome(ver 114)でもConditional UIに対応しているため、動作検証を実施しました。

Safariよりもいい感じで動く印象です。

/login の Username入力欄にConditional UIを仕込んでも違和感なく操作できるため、下図のように /login-fido2 を削除した 画面フローにしました。

ソース

ちなみにChromeで登録したパスキーは chrome://settings/passkeys から確認することができます。

各ブラウザでのConditional UI動作状況

2023/6/18時点

  • macOS 13.4
    • Chrome バージョン 114.0.1823.51 → ⭕対応済
    • Safari: バージョン16.5 → ⭕対応済
    • Edge: バージョン 114.0.1823.51 → ❌未対応

Discussion