Slide 1

Slide 1 text

DenoとHonoでWebAuthnを 使ったログインを実装する ayame113 (2024/2/14)

Slide 2

Slide 2 text

自己紹介 ● ayame113 ○ https://github.com/ayame113 ○ https://twitter.com/_ayame113_ ○ ● 学生です(大学院生) ● ふだんは研究の一環として、奈良の大学院のカーシェアサービスを作ってい る ○ 乗り捨て OK、気軽にカーシェア 奈良先端大の実証実験(朝日新聞) ○ Denoで作ってます https://naismon.deno.dev ○ ここでWeb Authnを使いたかったので、DenoとWeb Authnを組み合わせられるか試してみた

Slide 3

Slide 3 text

つくったもの:2文字チャット ● 2文字しか入力できないチャット ● https://pass-key-memo.deno.dev/

Slide 4

Slide 4 text

使った技術 ● https://pass-key-memo.deno.dev/ ● Deno ● デプロイ先 - deno deploy ● フロントエンドフレームワーク - 🍋Fresh ● バックエンドフレームワーク - 🔥Hono (RPCモード) ● WebAuthn用のJSライブラリ - SimpleWebAuthn ● 認証ライブラリ - Firebase Authentication ● データベース - Deno KV ● tailwind

Slide 5

Slide 5 text

パスキーによるログイン ● パスキーを使ってログインする仕組みを採用 ● デバイスの生体認証を使える (ユーザーがパスワードを覚えておく必要が無い) ● ログインの際にユーザー名とパスワードを入力する必 要なし ● iOS, Android, Windows等が対応 ● 秘密鍵による署名を使ったログイン方式

Slide 6

Slide 6 text

パスキーによるログイン 認証器 (ユーザーのデバイス) ブラウザ サーバー ①ランダム文字列(challenge) ※サーバー側で生成して認証器に渡す ②ランダム文字列(challenge)に秘密鍵で署名したもの ※認証器で署名してサーバーに渡す 署名が公開鍵で検証出来たら ログイン成功!

Slide 7

Slide 7 text

パスキーによるログイン ● サーバー側で保存する必要がある情報 ○ ユーザーID ○ 認証器ID ○ 認証器の公開鍵 ↓こういうDBが必要 認証器ID 認証器に紐づいた公開鍵 xxxxx Uint8Array [0, 12, 71, …] yyyyy Uint8Array [36, 41, 53, …] zzzzz Uint8Array [97, 11, 25, …] …

Slide 8

Slide 8 text

開発でよかったところ ● FreshとHonoの相性がいい ○ どちらもWeb標準APIベース ○ Freshのハンドラー:Requestオブジェクトを受け取ってResponseオブジェクトを返す関数 ○ Honoのハンドラー:Requestオブジェクトを受け取ってResponseオブジェクトを返す関数 ○ →そのまま渡せる!! // routes/api/[...path].ts const app = new Hono() .basePath("/api") .get("/hello", (c) => c.text("hello")); export const handler: Handler = app.fetch;

Slide 9

Slide 9 text

よかったところ ● HonoのRPCモードが便利 ○ HonoのRPCモード=バックエンド側のAPIエンドポイントを、フロント側からメソッド呼び出 しのように呼び出せる機能 ○ SimpleWebAuthn(Web Authn用のライブラリ)の型定義が複雑だった ○ フロントエンドとバックエンドの間で、複雑な形式のオブジェクトを受け渡しする必要があ る ○ HonoのRPCモードを使うと、型定義がフロントエンドとバックエンドの間で共用できるので、 安全にフロントエンドとバックエンドの間でオブジェクトの受け渡しができる ● Deno KV ○ Web Authnで扱う必要があるUint8Arrayなどのデータをそのまま保存できて便利だった

Slide 10

Slide 10 text

開発でつまづいたところ ● WebAuthnのログイン方法は(だいたい)3種類ある ○ 2段階認証の2段階目としてWeb Authnを使うパターン ■ GitHubとか ○ ユーザー名 + Web Authn でログインするパターン(Web Authnはパスワード代わり) ○ Web Authnだけでログインするパターン(Discoverable Credential) ■ ログイン時にユーザー名を入力する必要が無いので手軽 ■ 今回はこの方式を採用 qiitaなどのWebAuthnの紹介記事でも、使われている方式は様々 →どの記事を参照して実装すればいいのか迷った

Slide 11

Slide 11 text

開発でつまずいたところ ● WebAuthnが担当するのはログイン処理まで ○ →ログインした後の認証のしくみは、cookieやJWTを使って自分で実装する必要がある ○ 今回は、ログインが成功したらFirebase Authenticationのカスタムトークンを発行し、 ログイン後のトークンの管理はFirebaseのライブラリに任せた ● DenoでFirebase AuthenticationのJWT検証部分が動かなかった ○ Node.jsのポリフィルが不完全なのが原因ぽい ○ 別のjwt検証ライブラリ(jose)を使って回避した

Slide 12

Slide 12 text

まとめ ● HonoとFresh便利 ● WebAuthn単体のログインの実装はライブラリを使えばそれほど難しくない ● 本番運用する際は、WebAuthn以外のログイン方法を組み合わせる必要がある かもしれない ○ Windowsでログインした後、同じアカウントを使ってAndroidでログインする方法は? ○ ユーザーがデバイスを紛失した場合は? →感想:他のログイン方法と組み合わせると実装難易度が上がりそう