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

196 lines
6.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 ツール統合ヘルパー
```
## 設定
```yaml
# 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. 自己署名証明書の生成
```bash
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
```bash
# 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. テスト
```bash
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 呼び出し
```typescript
// 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 初期化
```typescript
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);
}
```