TL;DR(3行まとめ)
- ✅A2A ProtocolにDID署名を追加し、中央レジストリへの事前登録なしでAgent認証を実現
- ✅ did:web(HTTPS信頼)とdid:ethr(ブロックチェーン信頼)の2パターンをサポート
- ✅ did:webとdid:ethr間でクロスドメイン認証が可能
リポジトリ:
- GitHub: https://github.com/humuhimi/a2a-did
- npm: https://www.npmjs.com/package/a2a-did
npm install a2a-did
はじめに
AI Agent間の通信プロトコルとしてA2A Protocolが注目を集めています。しかし、現場で実装しようとすると、いくつかの課題に直面します。
Note: 本記事は
@a2a-js/sdk(A2A Protocol Specification v0.3.0互換)を前提としています。
1. OAuthの事前登録コスト
個々のAgentに独立したidentityを持たせようとすると、Agent生成のたびにOAuth client_idの事前登録が必要になります。同じclient_idを共有すると、どのAgentからのリクエストか区別できません。
2. 中央レジストリへの依存
誰がレジストリを運用するのか?レジストリがダウンしたら?すべてのAgentを中央に登録したくない場合は?
3. クロスドメイン認証の困難さ
異なる組織のAgent同士が通信するとき、どうやって相手のidentityを検証しますか?
本記事では、DID(Decentralized Identifier)を使ってこれらの問題に対処する一つのアプローチを紹介します。実際に動くコードと一緒に、すぐ試せる形でお届けします。
クイックスタート(3分で動作確認)
前提条件
- Node.js 18以上
- npm / pnpm / yarn
まずは動かしてみましょう。詳しい説明は後回しです。
# 新規プロジェクトを作成 mkdir my-a2a-project cd my-a2a-project npm init -y # a2a-didをインストール npm install a2a-did tsx
デモ1実行(did:web identity作成)
demo1.tsを作成:
import { createAgentDIDService } from 'a2a-did'; async function main() { console.log('=== Creating DID:web Identity ===\n'); const service = await createAgentDIDService(['web']); const identity = await service.createIdentity({ method: 'web', agentId: 'my-agent', config: { type: 'web', domain: 'example.com' } }); console.log('✓ Identity created successfully!\n'); console.log('DID:', identity.did); console.log('Key ID:', identity.keyId); console.log('Private key length:', identity.privateKey.length, 'bytes'); } main().catch(console.error);
実行:
npx tsx demo1.ts
期待される出力:
=== Creating DID:web Identity === ✓ Identity created successfully! DID: did:web:example.com:agents:my-agent Key ID: did:web:example.com:agents:my-agent#key-1 Private key length: 32 bytes
できること/できないこと
✅ できること
- 中央IdPへの事前登録不要でAgent認証(DIDベースの自己主権型認証)
- 暗号的に検証可能な認証(JWS署名)
- クロスドメインでのAgent認証(組織をまたいだ通信)
- 複数のDID method対応(did:web、did:ethr)
❌ できないこと(範囲外)
1. 委任(Delegation)
A2Aコミュニティでは委任された認可(delegated authorization)の議論が進行中ですが、これは「誰が誰を代理しているか」の証明レイヤーであり、本実装の認証レイヤーとは分離しています。Verifiable Credentialsなどを使った別のレイヤーで解決する必要があります。
2. Fine-grained Access Control
ツール単位、リソース単位の動的権限制御は、Policy Engineの領域です。DIDは「誰か」を証明するだけで、「何ができるか」は扱いません。
3. 本番環境での追加実装
このライブラリは認証レイヤーのみを提供します。本番環境では、プラットフォーム側で以下の実装が必要です:
- リプレイ攻撃対策(
iat/exp/jti検証) - DID Documentキャッシュ
- Rate limiting、監視
詳細はSECURITY.mdを参照してください。
なぜDID署名が必要か
OAuth 2.0の課題(動的Agentのユースケース)
- 事前登録の手間: Agent追加ごとにクライアント登録が必要
- スケールしない: 100個のAgentで100回の登録
- 動的な追加が困難: 実行時のAgent生成に対応できない
中央レジストリの課題
- 管理コスト: 誰がレジストリを運用するのか
- Single Point of Failure: レジストリがダウンすると全滅
- プライバシー: すべてのAgentを中央に登録したくない
A2Aコミュニティでもこれらの問題が指摘されており、中央集権的なAgent Registryは単一障害点(SPOF)や検閲リスクの懸念が議論されています。
DID署名による解決策
A2Aメッセージに署名を追加 ↓ DID Documentで公開鍵を配布 ↓ 署名検証 → Agentの真正性を確認
この仕組みにより、事前登録や中央レジストリへの依存を減らし、Agent間の相互認証を実現できます(基本的な認証レイヤーとして)。
Note: A2A仕様では Agent Cardの署名手順は明記されていますが、メッセージ署名の標準フィールドは未整備です。本記事では、メッセージ署名をプロトコル拡張として実装する方法にフォーカスしています。
デモ1: did:webで署名→検証
did:webとは
- HTTPS信頼モデル: HTTPSで公開鍵を配布
- シンプル: Webサーバーがあれば使える
- 用途: 企業の公式Agent、固定Endpoint
重要: did:webは本番環境ではHTTPS必須です。
ローカル検証ではmkcert等でHTTPSを用意するか、開発用にlocalhost例外を許可したresolverを使用してください。
Step 1: DID Identity作成
import { createAgentDIDService } from 'a2a-did';
// did:web serviceを初期化
const service = await createAgentDIDService(['web']);
// Agent用のDID作成
const identity = await service.createIdentity({
method: 'web',
agentId: 'my-agent',
config: {
type: 'web',
domain: 'example.com'
// port: 443 はデフォルトなので省略可能
}
});
console.log('DID:', identity.did);
// → did:web:example.com:agents:my-agent
ポイント:
- did:webはHTTPSとDNSを信頼モデルとして使う(既存Webインフラ活用)
- DID Document配置場所:
did:web:example.comならhttps://example.com/.well-known/did.jsondid:web:example.com:agents:my-agentならhttps://example.com/agents/my-agent/did.json本番環境では
privateKeyを安全に保存(型: Uint8Array)
Step 2: A2Aメッセージに署名
重要: signatureフィールドはA2A仕様の標準フィールドではなく、プロトコル拡張として追加しています。運用環境では、HTTPヘッダー(例:Authorization: DIDAuth <jws>)での運搬も選択肢です。
Step 3: 署名を検証(受信側)
import { verifySignedA2ARequest } from 'a2a-did';
// Express/Fastifyなどのエンドポイント
app.post('/a2a', async (req, res) => {
if (req.body.signature) {
const result = await verifySignedA2ARequest(req.body);
if (!result.valid) {
return res.json({
jsonrpc: '2.0',
error: { code: -32600, message: 'Invalid signature' },
id: req.body.id
});
}
console.log(`✅ Authenticated message from: ${result.senderDid}`);
}
// 通常のA2A処理へ...
});
何が起きているか:
1. 署名から送信者のDIDを抽出
2. DID Resolutionで公開鍵を取得
3. JWS署名を検証
4. 成功すればresult.senderDidに送信者のidentityが入る
デモ2: did:ethrで動的なEndpoint解決
did:webはDNSへの依存が気になる方向けに、Ethereumブロックチェーンを使うdid:ethrも試せます。
did:ethrとは
- ブロックチェーン信頼モデル: Ethereumで管理
- 更新可能: Endpointを後から変更できる
- 用途: 動的Agent、クロスドメイン認証
セットアップ
import { createAgentDIDService } from 'a2a-did';
const service = await createAgentDIDService(['ethr']);
const identity = await service.createIdentity({
method: 'ethr',
agentId: 'blockchain-agent',
config: {
type: 'ethr',
network: 'sepolia', // テストネット
rpcUrl: 'https://sepolia.infura.io/v3/YOUR_INFURA_KEY'
}
});
console.log('DID:', identity.did);
// → did:ethr:sepolia:0x1234567890abcdef...
Agent Card解決のフロー
┌─────────────────┐
│ did:ethr:... │
└────────┬────────┘
│ 1. DID Resolution
↓
┌─────────────────────────┐
│ DID Document │
│ { │
│ "service": [{ │
│ "type": "A2AAgentCard",│
│ "serviceEndpoint": │
│ "ipfs://Qm..." │
│ }] │
│ } │
└────────┬────────────────┘
│ 2. IPFS Fetch
↓
┌─────────────────────────┐
│ Agent Card │
│ { │
│ "a2a_endpoint": │
│ "https://agent.../a2a"│
│ } │
└─────────────────────────┘
Agent Cardへのアクセス
import { resolveA2AEndpoint } from 'a2a-did';
// DID → A2A endpointをワンステップで解決
const endpoint = await resolveA2AEndpoint('did:ethr:sepolia:0x123...');
console.log('A2A Endpoint:', endpoint);
// → https://agent.example.com/a2a
// これで通信可能
await fetch(endpoint, {
method: 'POST',
body: JSON.stringify(signedMessage)
});
アーキテクチャの意図:
did:ethr + IPFS + HTTPSの組み合わせは、IDとメタデータ(Agent Card)は改ざん耐性の高い経路に置き、通信自体は現実的なHTTPSで行うという設計です。これにより「誰と話しているか」の検証と運用現実性を両立できます。
設計上のトレードオフ: 完全な分散化を目指すなら、通信プロトコルもlibp2pなどのP2Pプロトコルを使うべきです。HTTPSエンドポイントを使う本実装は、運用上の現実解として既存インフラとの互換性を優先しています。将来的には、Agent CardのエンドポイントをP2Pアドレス(例:/ip4/.../tcp/.../p2p/...)に拡張することも可能です。
did:web vs did:ethrの使い分け
| 特徴 | did:web | did:ethr |
|---|---|---|
| 信頼モデル | HTTPS/TLS (Web PKI) | ブロックチェーン |
| セットアップ | 簡単(HTTPSサーバーのみ) | 中程度(RPC設定必要) |
| 解決速度 | 速い(HTTPS) | やや遅い(RPC + IPFS) |
| 改ざん耐性 | 中(HTTPS/DNS依存) | 高(ブロックチェーン) |
| 運用コスト | 低(既存インフラ) | 低(解決は無料、作成・更新にガス代) |
| 推奨ケース | 企業Agent | 動的Agent、クロスドメイン |
まとめ
本記事では、A2A ProtocolにDID署名を追加する実装を紹介しました:
- 中央レジストリへの事前登録不要でAgent認証(DIDベースの自己主権型認証)
- 暗号的に検証可能な認証(JWS署名)
- すぐに試せる実装例(did:web、did:ethr)
- 既存A2A実装との共存可能(@a2a-js/sdkと併用)
フルスタックデモ(次回記事などで紹介)
a2a-didはコアライブラリですが、「実際のアプリでどう使うか」を示すため、フルスタックデモを準備中です:
詳細は次回の記事で紹介します!
最後に
何かありましたら投げてもらえると助かります🙏



















