Bedrud ドキュメント

FIDO2/WebAuthnパスキーエンドポイントを使用すると、生体認証またはセキュリティキーを使って登録・認証を行うことができます。

概要

パスキーフローはチャレンジ・レスポンスプロトコルを使用し、それぞれ2つのフェーズで構成されます:

フローフェーズ1(開始)フェーズ2(完了)
登録POST /api/auth/passkey/register/beginPOST /api/auth/passkey/register/finish
ログインPOST /api/auth/passkey/login/beginPOST /api/auth/passkey/login/finish

登録フロー

パスキーを登録するにはユーザーがログインしている必要があります(既存のアカウントにリンクされます)。

ステップ1:登録の開始

POST /api/auth/passkey/register/begin

ヘッダー: Authorization: Bearer <accessToken>

レスポンス (200):

{
  "publicKey": {
    "challenge": "base64url-encoded-challenge",
    "rp": {
      "name": "Bedrud",
      "id": "meet.example.com"
    },
    "user": {
      "id": "base64url-encoded-user-id",
      "name": "user@example.com",
      "displayName": "John Doe"
    },
    "pubKeyCredParams": [
      { "type": "public-key", "alg": -7 },
      { "type": "public-key", "alg": -257 }
    ],
    "authenticatorSelection": {
      "userVerification": "preferred"
    }
  }
}

これをブラウザのWebAuthn APIに渡します:

const credential = await navigator.credentials.create({
  publicKey: response.publicKey
});

ステップ2:登録の完了

POST /api/auth/passkey/register/finish

ヘッダー: Authorization: Bearer <accessToken>

リクエストボディ: ブラウザからのクレデンシャルレスポンス(base64urlエンコード):

{
  "id": "credential-id",
  "rawId": "base64url-raw-id",
  "response": {
    "attestationObject": "base64url-attestation",
    "clientDataJSON": "base64url-client-data"
  },
  "type": "public-key"
}

レスポンス (200):

{
  "message": "passkey registered"
}

ログインフロー

ステップ1:ログインの開始

POST /api/auth/passkey/login/begin

認証は不要です。

レスポンス (200):

{
  "publicKey": {
    "challenge": "base64url-encoded-challenge",
    "rpId": "meet.example.com",
    "userVerification": "preferred"
  }
}

これをブラウザのWebAuthn APIに渡します:

const assertion = await navigator.credentials.get({
  publicKey: response.publicKey
});

ステップ2:ログインの完了

POST /api/auth/passkey/login/finish

リクエストボディ: ブラウザからのアサーションレスポンス:

{
  "id": "credential-id",
  "rawId": "base64url-raw-id",
  "response": {
    "authenticatorData": "base64url-auth-data",
    "clientDataJSON": "base64url-client-data",
    "signature": "base64url-signature"
  },
  "type": "public-key"
}

レスポンス (200):

{
  "accessToken": "eyJ...",
  "refreshToken": "eyJ...",
  "user": {
    "id": "uuid",
    "email": "user@example.com",
    "name": "John Doe"
  }
}

セキュリティ

対策説明
チャレンジ/レスポンスサーバー生成のランダムチャレンジをセッションに保存
オリジン検証厳格なオリジンとRelying Party IDの検証
カウンター検証クローンされたオーセンティケーターからの保護
セキュアな転送HTTPS用に設計され、URLセーフなbase64エンコードを使用

実装の詳細

  • バックエンド: go-passkeys/go-passkeys を使用したpure GoでのWebAuthn検証
  • セッションストレージ: チャレンジはGorillaセッションに保存(gothic.Store経由)
  • データベース: パスキークレデンシャルは Passkey モデルに保存(クレデンシャルID、公開鍵、カウンター)
  • フロントエンド(Web): navigator.credentials APIと src/lib/auth.ts のbase64urlヘルパー
  • Android: ネイティブパスキーサポート用のCredentials API
  • iOS: パスキーサポート用のASAuthorizationController

テスト方法

  1. 既存のアカウントでログインします
  2. パスキー登録ボタンをクリックします(ヘッダー/ダッシュボードの指紋アイコン)
  3. 生体認証プロンプトを完了します
  4. ログアウトします
  5. ログインページで「Passkeyでサインイン」をクリックします
  6. 生体認証プロンプトを完了して認証します

関連ドキュメント

  • 認証フロー - パスキーの登録/ログインセレモニーの解説
  • 認証API - すべての認証エンドポイント