Files
MCPletA2A/PASSKEY.md
2026-03-31 15:59:59 +09:00

6.4 KiB
Raw Permalink Blame History

MCPletA2A Passkey 認証

真正の WebAuthn/FIDO2 Passkey 認証。外部 amiPro FIDO2 サーバーと連携。

アーキテクチャ

Host (Node.js)
  │
  ├── PasskeyServer ──→ ブラウザページ起動(自己完結型フロー)
  │                        │
  │                        ├── userId 入力
  │                        ├── FIDO2 サーバーにユーザー確認
  │                        ├── 未登録 → 自動で Passkey 登録Touch ID 等)
  │                        ├── 認証Touch ID 等)
  │                        └── 結果を Host に HTTP callback
  │
  ├── PasskeyPlatformService ──→ AmiProFIDO2Client ──→ FIDO2 Server
  │     (統一管理: 登録/認証/セッション)                 (https://fido2.epk.amipro.me)
  │
  └── PasskeyAPIServer (REST proxy, port 8443)
        /api/passkey/attestation/options
        /api/passkey/attestation/result
        /api/passkey/assertion/options
        /api/passkey/assertion/result

ファイル構成

platform_impl/src/passkey/
├── amipro-fido2-client.ts   # amiPro FIDO2 サーバー REST クライアント
├── platform-service.ts      # 統一 Passkey 管理サービス
├── passkey-server.ts        # ブラウザ WebAuthn 式典サーバー(自己完結型ページ)
├── api-server.ts            # REST API プロキシ
├── index.ts                 # エクスポート
├── storage.ts               # IPasskeyStorage インターフェース (ローカル用、予備)
├── challenge-manager.ts     # チャレンジ管理 (ローカル用、予備)
├── fido2-backend.ts         # FIDO2 検証 (ローカル用、予備)
├── client.ts                # ブラウザ側 WebAuthn クライアント
└── mcplet-helper.ts         # MCPlet ツール統合ヘルパー

設定

# reference_impl/config/reference.yaml
passkey:
  mode: https                                    # localhost | https
  rpId: a2a-demo.mcplet.ai                      # WebAuthn Relying Party ID
  fido2ServerUrl: https://fido2.epk.amipro.me    # 外部 FIDO2 サーバー
  apiPort: 8443                                  # REST API ポート

ユーザーフロー

新規ユーザー(未登録)

ブラウザページ起動
  ↓
1. userId 入力(または自動入力)
  ↓
2. FIDO2 サーバーに問い合わせ → "ユーザーが存在しません"
  ↓
3. 自動で登録モードに切替 → Touch ID 等で Passkey 登録
  ↓
4. 登録成功 → 自動で認証に進む
  ↓
5. Touch ID 等で認証
  ↓
6. session + username を Host に返却

既存ユーザー(登録済み)

ブラウザページ起動
  ↓
1. userId 入力(または自動入力)
  ↓
2. FIDO2 サーバーに問い合わせ → OKallowCredentials 返却)
  ↓
3. Touch ID 等で認証
  ↓
4. session + username を Host に返却

amiPro FIDO2 Server API

エンドポイント 説明
POST /attestation/options 登録オプション取得challenge + publicKey params
POST /attestation/result 登録結果送信attestationObject 検証)
POST /assertion/options 認証オプション取得challenge + allowCredentials
POST /assertion/result 認証結果送信signature 検証)
POST /usr/validsession セッション検証
POST /usr/delsession ログアウト

エンコーディング: Base64URLdfido2-lib.js 互換)

開発環境セットアップ

1. /etc/hosts

127.0.0.1 a2a-demo.mcplet.ai

2. 自己署名証明書の生成

cd platform_impl
mkdir -p certs
openssl req -x509 -newkey rsa:2048 \
  -keyout certs/key.pem -out certs/cert.pem \
  -days 365 -nodes -subj "/CN=a2a-demo.mcplet.ai" \
  -addext "subjectAltName=DNS:a2a-demo.mcplet.ai,IP:127.0.0.1"

3. 証明書の信頼macOS

# Admin 権限あり
sudo security add-trusted-cert -d -r trustRoot \
  -k /Library/Keychains/System.keychain certs/cert.pem

# Admin 権限なしEdge で有効、Chrome は完全再起動が必要)
security add-trusted-cert -r trustRoot \
  -k ~/Library/Keychains/login.keychain-db certs/cert.pem

4. テスト

cd platform_impl

# API 接続テストのみ
npx tsx test-passkey.ts

# 統一フロー(自動登録+認証)
npx tsx test-passkey.ts test a2a-test-user

# 新規ユーザーuserId 手入力)
npx tsx test-passkey.ts new

重要な注意事項

WebAuthn rpId とドメインの一致

WebAuthn は rpId がブラウザページのドメインと一致することを要求する。 http://127.0.0.1 のページでは rpId a2a-demo.mcplet.ai は使用不可。 必ず /etc/hosts + HTTPSまたは Chrome flagで一致させること。

Passkey のブラウザ間共有

macOS の Touch ID / iCloud キーチェーンで作成された Passkey はブラウザ間で共有される。 ただし Chrome の --user-data-dir=tmp で起動すると隔離プロファイルとなり、 プラットフォーム Passkey にアクセスできない。常にデフォルトブラウザを使用すること。

本番環境 vs 開発環境

項目 開発環境 本番環境
証明書 自己署名 + キーチェーン信頼 正規 SSL 証明書
rpId a2a-demo.mcplet.ai (hosts) 実ドメイン (DNS)
FIDO2 server fido2.epk.amipro.me 同 or 専用サーバー
PASSKEY_DEV_HTTP 設定可Chrome flag 必要) 不使用

Platform 統合

base-agent.ts での Passkey 呼び出し

// action 型ツールで auth.required === 'passkey' の場合、自動で仪式を開始
if (tool.meta.mcpletType === 'action' && tool.meta.auth?.required === 'passkey') {
  const assertion = await this.performPasskeyCeremony(tool);
  if (!assertion) {
    return this.errorResult(toolName, 'Passkey authentication failed', 'AUTH_FAILED');
  }
  callParams = { ...args, _mcplet_auth: assertion };
}

MCPletHost 初期化

if (config.passkey) {
  this.passkeyPlatformService = new PasskeyPlatformService(rpId, fido2ServerUrl);
  this.passkeyAPIServer = new PasskeyAPIServer(platformService, rpId, origin);
  this.passkeyAPIServer.start(config.passkey.apiPort || 8443);
  this.passkeyServer = new PasskeyServer(rpId, fido2ServerUrl);
}