はじめに
パスワード認証の限界が明確になった現在、分散型識別子(DID: Decentralized Identifier)とウォレットベース認証は、次世代の認証基盤として注目されています。本記事では、Model Context Protocol (MCP)環境にDID/ウォレット認証を統合し、真のゼロトラストアーキテクチャを実現する方法を解説します。
なぜDIDとウォレット認証なのか
従来の認証システムが抱える課題:
- 中央集権的リスク: ID管理を単一のプロバイダーに依存
- パスワード漏洩: サーバー側の侵害によるクレデンシャル流出
- プライバシー侵害: 必要以上の個人情報の提供
- フィッシング攻撃: パスワード入力を狙った攻撃
DID/ウォレット認証はこれらの課題を根本から解決します。
🏗️ アーキテクチャ概要
主要コンポーネント
1. DIDウォレット(ユーザーエージェント)
役割: ユーザーのデジタルアイデンティティと鍵を安全に管理
主要機能:
interface DIDWallet {
// DID管理
did: string; // did:example:123456789abcdefghi
privateKey: CryptoKey; // 秘密鍵(安全に保管)
publicKey: CryptoKey; // 公開鍵
// VC(検証可能な資格情報)管理
credentials: VerifiableCredential[];
// 認証メソッド
signChallenge(challenge: string): Promise<Signature>;
presentCredential(types: string[]): Promise<VerifiablePresentation>;
// 生体認証統合
authenticateUser(): Promise<boolean>;
}
実装例(Web Crypto API使用):
class MCPDIDWallet {
constructor() {
this.did = null;
this.keyPair = null;
this.credentials = [];
}
/**
* 新しいDIDと鍵ペアを生成
*/
async initialize() {
// 1. 鍵ペアの生成(ECDSA P-256)
this.keyPair = await window.crypto.subtle.generateKey(
{
name: "ECDSA",
namedCurve: "P-256"
},
true, // 抽出可能
["sign", "verify"]
);
// 2. 公開鍵からDIDを生成
const publicKeyJwk = await window.crypto.subtle.exportKey(
"jwk",
this.keyPair.publicKey
);
// DID生成(簡略化した例)
const publicKeyHash = await this._hashPublicKey(publicKeyJwk);
this.did = `did:web:example.com:users:${publicKeyHash}`;
// 3. IndexedDBに安全に保存
await this._storeKeys();
return this.did;
}
/**
* チャレンジに署名(認証時に使用)
*/
async signChallenge(challenge) {
if (!this.keyPair) {
throw new Error("Wallet not initialized");
}
// 生体認証の確認
const authenticated = await this._authenticateUser();
if (!authenticated) {
throw new Error("Biometric authentication failed");
}
// チャレンジに署名
const encoder = new TextEncoder();
const data = encoder.encode(challenge);
const signature = await window.crypto.subtle.sign(
{
name: "ECDSA",
hash: { name: "SHA-256" }
},
this.keyPair.privateKey,
data
);
return {
did: this.did,
challenge: challenge,
signature: this._arrayBufferToBase64(signature),
algorithm: "ES256"
};
}
/**
* VCを選択的に提示
*/
async presentCredential(requestedTypes) {
// 要求されたタイプのVCをフィルタ
const matchingCredentials = this.credentials.filter(vc =>
requestedTypes.includes(vc.type)
);
if (matchingCredentials.length === 0) {
throw new Error("No matching credentials found");
}
// ユーザーに確認を求める
const approved = await this._requestUserConsent(matchingCredentials);
if (!approved) {
throw new Error("User denied credential presentation");
}
// Verifiable Presentationを作成
const presentation = {
"@context": ["https://www.w3.org/2018/credentials/v1"],
"type": ["VerifiablePresentation"],
"verifiableCredential": matchingCredentials,
"holder": this.did,
"proof": await this._createPresentationProof(matchingCredentials)
};
return presentation;
}
/**
* 生体認証(WebAuthn使用)
*/
async _authenticateUser() {
try {
// WebAuthn APIを使用した生体認証
const assertion = await navigator.credentials.get({
publicKey: {
challenge: window.crypto.getRandomValues(new Uint8Array(32)),
timeout: 60000,
userVerification: "required"
}
});
return assertion !== null;
} catch (error) {
console.error("Biometric authentication failed:", error);
return false;
}
}
// ヘルパーメソッド
async _hashPublicKey(jwk) {
const str = JSON.stringify(jwk);
const buffer = new TextEncoder().encode(str);
const hash = await window.crypto.subtle.digest("SHA-256", buffer);
return this._arrayBufferToBase64(hash).substring(0, 16);
}
_arrayBufferToBase64(buffer) {
return btoa(String.fromCharCode(...new Uint8Array(buffer)));
}
async _storeKeys() {
// IndexedDBへの保存(実装は省略)
}
async _requestUserConsent(credentials) {
// ユーザーUIでの確認(実装は省略)
return true;
}
async _createPresentationProof(credentials) {
// プレゼンテーション証明の作成(実装は省略)
return {};
}
}
2. 分散型台帳(DIDレジストリ)
役割: DIDドキュメントの不変的な記録と検証
技術選択肢:
| プラットフォーム | 特徴 | ユースケース |
|---|---|---|
| Ethereum | 最も成熟したスマートコントラクト基盤 | パブリックな企業間認証 |
| Hyperledger Indy | プライバシー重視の許可型台帳 | エンタープライズ内部 |
| ION (on Bitcoin) | Layer 2ソリューション、低コスト | 大規模展開 |
| did:web | DNS/HTTPSベース、中央集権的だが簡単 | プロトタイプ・小規模 |
DIDドキュメント例:
{
"@context": [
"https://www.w3.org/ns/did/v1",
"https://w3id.org/security/suites/jws-2020/v1"
],
"id": "did:web:example.com:users:abc123",
"verificationMethod": [{
"id": "did:web:example.com:users:abc123#key-1",
"type": "JsonWebKey2020",
"controller": "did:web:example.com:users:abc123",
"publicKeyJwk": {
"kty": "EC",
"crv": "P-256",
"x": "WKn-ZIGevcwGIyyrzFoZNBdaq9_TsqzGl96oc0CWuis",
"y": "y77t-RvAHRKTsSGdIYUfweuOvwrvDD-Q3Hv5J0fSKbE"
}
}],
"authentication": [
"did:web:example.com:users:abc123#key-1"
],
"assertionMethod": [
"did:web:example.com:users:abc123#key-1"
]
}
3. MCPサーバー(検証者)
役割: DID認証の検証とアクセス制御
実装例(Node.js + Express):
const express = require('express');
const { Resolver } = require('did-resolver');
const { getResolver } = require('web-did-resolver');
const jose = require('jose');
class MCPAuthServer {
constructor() {
this.app = express();
this.didResolver = new Resolver(getResolver());
this.activeChallenges = new Map();
this._setupRoutes();
}
_setupRoutes() {
// 1. 認証開始エンドポイント
this.app.post('/auth/challenge', async (req, res) => {
const { did } = req.body;
// チャレンジ生成
const challenge = this._generateChallenge();
const expiresAt = Date.now() + 5 * 60 * 1000; // 5分間有効
this.activeChallenges.set(challenge, {
did,
expiresAt,
used: false
});
res.json({ challenge, expiresIn: 300 });
});
// 2. 署名検証エンドポイント
this.app.post('/auth/verify', async (req, res) => {
const { did, challenge, signature, algorithm } = req.body;
try {
// チャレンジの有効性確認
const challengeData = this.activeChallenges.get(challenge);
if (!challengeData || challengeData.used) {
return res.status(401).json({ error: 'Invalid challenge' });
}
if (Date.now() > challengeData.expiresAt) {
return res.status(401).json({ error: 'Challenge expired' });
}
if (challengeData.did !== did) {
return res.status(401).json({ error: 'DID mismatch' });
}
// DIDドキュメント取得
const didDocument = await this.didResolver.resolve(did);
if (!didDocument || !didDocument.didDocument) {
return res.status(404).json({ error: 'DID not found' });
}
// 公開鍵取得
const verificationMethod = didDocument.didDocument.verificationMethod[0];
const publicKeyJwk = verificationMethod.publicKeyJwk;
// 署名検証
const publicKey = await jose.importJWK(publicKeyJwk, algorithm);
const isValid = await this._verifySignature(
challenge,
signature,
publicKey
);
if (!isValid) {
return res.status(401).json({ error: 'Invalid signature' });
}
// チャレンジを使用済みにマーク
challengeData.used = true;
// セッショントークン発行
const sessionToken = await this._issueSessionToken(did);
res.json({
authenticated: true,
sessionToken,
expiresIn: 3600
});
} catch (error) {
console.error('Verification error:', error);
res.status(500).json({ error: 'Verification failed' });
}
});
// 3. VC検証エンドポイント
this.app.post('/auth/verify-credential', async (req, res) => {
const { presentation, requiredCredentialTypes } = req.body;
try {
// プレゼンテーションの検証
const isValid = await this._verifyPresentation(presentation);
if (!isValid) {
return res.status(401).json({ error: 'Invalid presentation' });
}
// 必要なVCの確認
const hasRequiredCredentials = this._checkCredentialTypes(
presentation.verifiableCredential,
requiredCredentialTypes
);
if (!hasRequiredCredentials) {
return res.status(403).json({
error: 'Missing required credentials'
});
}
// VCから権限を抽出
const permissions = this._extractPermissions(
presentation.verifiableCredential
);
res.json({
verified: true,
permissions,
holder: presentation.holder
});
} catch (error) {
console.error('Credential verification error:', error);
res.status(500).json({ error: 'Verification failed' });
}
});
}
_generateChallenge() {
const array = new Uint8Array(32);
require('crypto').randomFillSync(array);
return Buffer.from(array).toString('base64');
}
async _verifySignature(challenge, signatureB64, publicKey) {
try {
const signature = Buffer.from(signatureB64, 'base64');
const data = Buffer.from(challenge, 'utf-8');
// Web Crypto APIでの検証(Node.js 15+)
const crypto = require('crypto').webcrypto;
const isValid = await crypto.subtle.verify(
{ name: 'ECDSA', hash: 'SHA-256' },
publicKey,
signature,
data
);
return isValid;
} catch (error) {
console.error('Signature verification error:', error);
return false;
}
}
async _issueSessionToken(did) {
// JWTセッショントークンの発行
const secret = new TextEncoder().encode(process.env.JWT_SECRET);
const token = await new jose.SignJWT({ did })
.setProtectedHeader({ alg: 'HS256' })
.setIssuedAt()
.setExpirationTime('1h')
.sign(secret);
return token;
}
async _verifyPresentation(presentation) {
// プレゼンテーション証明の検証(実装簡略化)
// 実際には発行者の署名も検証する必要がある
return presentation.holder && presentation.verifiableCredential;
}
_checkCredentialTypes(credentials, requiredTypes) {
const presentTypes = credentials.flatMap(vc => vc.type);
return requiredTypes.every(type => presentTypes.includes(type));
}
_extractPermissions(credentials) {
// VCから権限情報を抽出
const permissions = [];
for (const credential of credentials) {
if (credential.credentialSubject.role) {
permissions.push({
resource: credential.credentialSubject.resource || '*',
actions: credential.credentialSubject.permissions || ['read'],
constraints: credential.credentialSubject.constraints || {}
});
}
}
return permissions;
}
start(port = 3000) {
this.app.listen(port, () => {
console.log(`MCP Auth Server listening on port ${port}`);
});
}
}
// 起動
const server = new MCPAuthServer();
server.start();
4. 資格情報発行者(Credential Issuer)
役割: ユーザーの属性を検証しVCを発行
VC(検証可能な資格情報)の例:
{
"@context": [
"https://www.w3.org/2018/credentials/v1",
"https://example.com/contexts/employee/v1"
],
"id": "https://example.com/credentials/employee/12345",
"type": ["VerifiableCredential", "EmployeeCredential"],
"issuer": "did:web:example.com:hr",
"issuanceDate": "2025-01-01T00:00:00Z",
"expirationDate": "2025-12-31T23:59:59Z",
"credentialSubject": {
"id": "did:web:example.com:users:abc123",
"employeeId": "EMP-12345",
"department": "Engineering",
"role": "Senior Developer",
"permissions": ["read", "write", "execute"],
"resource": "mcp://tools/code-execution",
"constraints": {
"maxExecutionTime": 300,
"allowedLanguages": ["python", "javascript"]
}
},
"proof": {
"type": "JsonWebSignature2020",
"created": "2025-01-01T00:00:00Z",
"verificationMethod": "did:web:example.com:hr#key-1",
"proofPurpose": "assertionMethod",
"jws": "eyJhbGciOiJFUzI1NiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19..signature"
}
}
🔄 認証フロー詳細
シーケンス図
ユーザー ウォレット MCPサーバー DIDレジストリ 発行者
| | | | |
|--[1]接続開始-------------->| | |
| |<--[2]チャレンジ要求---------| | |
|<--[3]生体認証要求----------| | |
|--[4]認証成功-------------->| | |
| |--[5]署名作成->| | |
| |--[6]署名送信---------------->| |
| | |--[7]DID解決->| |
| | |<-[8]公開鍵---| |
| | |--[9]署名検証-> |
| |<--[10]VC要求-----------------| |
|<--[11]VC選択確認-----------| | |
|--[12]承認----------------->| | |
| |--[13]VP送信---------------->| |
| | |--[14]VC検証-------------->|
| | |<--[15]発行者検証----------|
| |<--[16]アクセス許可----------| |
| | | | |
ステップ詳細
1-2. 接続開始とチャレンジ生成
// クライアント側
async function initiateAuth(did) {
const response = await fetch('https://mcp-server.example.com/auth/challenge', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ did })
});
const { challenge, expiresIn } = await response.json();
return challenge;
}
3-5. 生体認証と署名
// ウォレット側
async function authenticateAndSign(challenge) {
const wallet = new MCPDIDWallet();
await wallet.initialize();
// 生体認証 + 署名
const signedResponse = await wallet.signChallenge(challenge);
return signedResponse;
}
6-9. 署名検証
// サーバー側(前述のコード参照)
10-13. VC要求とプレゼンテーション
// クライアント側
async function presentCredentials(sessionToken, requiredTypes) {
const wallet = new MCPDIDWallet();
// VCを選択的に提示
const presentation = await wallet.presentCredential(requiredTypes);
const response = await fetch('https://mcp-server.example.com/auth/verify-credential', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${sessionToken}`
},
body: JSON.stringify({
presentation,
requiredCredentialTypes: requiredTypes
})
});
const { verified, permissions } = await response.json();
return permissions;
}
🎯 メリットとセキュリティ効果
| 観点 | 従来のパスワード認証 | DID/ウォレット認証 |
|---|---|---|
| セキュリティ | サーバー侵害でパスワード流出 | 秘密鍵はクライアント側のみ、流出リスクゼロ |
| プライバシー | 全属性をサーバーに送信 | 必要な属性のみ選択的開示 |
| フィッシング耐性 | 脆弱(パスワード入力を狙える) | 耐性高(秘密鍵は入力されない) |
| ユーザー体験 | パスワード記憶・入力が必要 | 生体認証のみ(パスワード不要) |
| 監査証跡 | ログの改ざん可能性 | 暗号署名による改ざん防止 |
| マルチテナント | 各サービスで個別管理 | 1つのDIDで複数サービス利用 |
⚠️ 実装上の考慮事項
1. 鍵の紛失対策
課題: 秘密鍵を紛失するとアクセス不可
対策:
- ソーシャルリカバリー: 信頼する複数の連絡先に鍵の断片を預ける
- バックアップコード: 安全な場所に保管する復旧コード
- マルチデバイス同期: 暗号化された鍵の安全な同期
class KeyRecoverySystem {
/**
* Shamirの秘密分散法で鍵を分割
*/
async splitKey(privateKey, threshold, totalShares) {
// secrets.js-grempe等のライブラリを使用
const shares = secrets.share(privateKey, totalShares, threshold);
return shares;
}
/**
* 分散された鍵を復元
*/
async recoverKey(shares) {
const recovered = secrets.combine(shares);
return recovered;
}
}
2. パフォーマンスへの影響
懸念: ブロックチェーンクエリのレイテンシ
対策:
- キャッシング: DIDドキュメントを一定期間キャッシュ
- Layer 2ソリューション: ION等の高速なDIDメソッド使用
- did:webの活用: プロトタイプ段階ではHTTPSベースで開始
class DIDCache {
constructor(ttl = 3600000) { // 1時間
this.cache = new Map();
this.ttl = ttl;
}
async resolve(did) {
const cached = this.cache.get(did);
if (cached && Date.now() - cached.timestamp < this.ttl) {
return cached.document;
}
// キャッシュミス時はレジストリから取得
const document = await this.resolver.resolve(did);
this.cache.set(did, {
document,
timestamp: Date.now()
});
return document;
}
}
3. 既存システムとの統合
段階的な移行戦略:
Phase 1: ハイブリッド認証(1-2ヶ月)
- 既存のパスワード認証と並行運用
- 一部ユーザーでDID認証をパイロット実施
class HybridAuthSystem {
async authenticate(credentials) {
if (credentials.type === 'password') {
return await this.legacyAuth(credentials);
} else if (credentials.type === 'did') {
return await this.didAuth(credentials);
}
}
}
Phase 2: 段階的移行(3-6ヶ月)
- 全ユーザーにDIDウォレット発行を推奨
- パスワード認証は段階的に非推奨化
Phase 3: 完全移行(6ヶ月以降)
- DID認証のみに統一
- レガシーシステムの廃止
4. コストとスケーラビリティ
| 項目 | 推定コスト | スケーラビリティ |
|---|---|---|
| DIDドキュメント登録 | $0.01-$1/DID(ブロックチェーン依存) | 高(一度だけ) |
| 認証トランザクション | $0(オフチェーン検証) | 非常に高 |
| VC発行 | $0-$0.1/VC | 高 |
| インフラ | 通常のAPIサーバーと同等 | 水平スケール可能 |
コスト最適化:
- did:webを使用すればブロックチェーンコストゼロ
- VCの有効期限を長く設定して発行頻度を削減
5. セキュリティ監査ポイント
必須の監査項目:
- 鍵管理: 秘密鍵が適切に保護されているか
- チャレンジの一意性: リプレイ攻撃への耐性
- VC検証: 発行者の署名と失効状態の確認
- タイムスタンプ検証: 有効期限の適切な処理
- エラーハンドリング: 機密情報の漏洩防止
class SecurityAuditor {
async auditAuthFlow(authLog) {
const issues = [];
// チャレンジの再利用チェック
if (this.challengeUsedBefore(authLog.challenge)) {
issues.push({
severity: 'HIGH',
message: 'Challenge reuse detected (replay attack)'
});
}
// 署名アルゴリズムの安全性チェック
if (!this.isSecureAlgorithm(authLog.algorithm)) {
issues.push({
severity: 'MEDIUM',
message: `Weak algorithm detected: ${authLog.algorithm}`
});
}
// VCの有効期限チェック
if (authLog.credential && this.isExpired(authLog.credential)) {
issues.push({
severity: 'HIGH',
message: 'Expired credential accepted'
});
}
return issues;
}
challengeUsedBefore(challenge) {
// 実装: Redis等でチャレンジ履歴を確認
return false;
}
isSecureAlgorithm(algorithm) {
const secureAlgorithms = ['ES256', 'ES384', 'ES512', 'EdDSA'];
return secureAlgorithms.includes(algorithm);
}
isExpired(credential) {
const expirationDate = new Date(credential.expirationDate);
return expirationDate < new Date();
}
}
📊 実装ロードマップ
Phase 1: プロトタイプ構築(1-2ヶ月)
目標: 基本的なDID認証フローを実装
タスク:
- did:web方式でDIDウォレットのプロトタイプ作成
- MCPサーバーに署名検証機能を追加
- 簡単なVCの発行・検証機能を実装
- 開発環境でのエンドツーエンドテスト
成果物:
// 最小限の動作するプロトタイプ
const wallet = new MCPDIDWallet();
await wallet.initialize();
const challenge = await initiateAuth(wallet.did);
const signature = await wallet.signChallenge(challenge);
const session = await verifyAuth(signature);
console.log('Authentication successful:', session);
Phase 2: エンタープライズ機能追加(2-3ヶ月)
目標: 本番運用に必要な機能を実装
タスク:
- 鍵の紛失対策(ソーシャルリカバリー)
- VC失効メカニズム(Revocation Registry)
- 監査ログとコンプライアンス機能
- 既存認証システムとの統合
- パフォーマンス最適化(キャッシング等)
成果物:
// エンタープライズグレードの実装
class EnterpriseAuthSystem {
async authenticate(did, challenge) {
// 認証
const session = await this.verifySignature(did, challenge);
// 監査ログ記録
await this.auditLog.record({
event: 'authentication',
did: did,
timestamp: new Date(),
result: 'success'
});
// コンプライアンスチェック
await this.complianceEngine.verify(session);
return session;
}
}
Phase 3: スケールと最適化(3-6ヶ月)
目標: 大規模運用への対応
タスク:
- 水平スケーリング対応
- マルチリージョン展開
- DIDキャッシュの最適化
- セキュリティ監査と脆弱性診断
- ユーザートレーニングとドキュメント整備
🔧 実践的なトラブルシューティング
よくある問題と解決策
問題1: 署名検証が失敗する
// デバッグコード
async function debugSignatureVerification(challenge, signature, publicKey) {
console.log('Challenge:', challenge);
console.log('Signature (base64):', signature);
console.log('Public Key:', await crypto.subtle.exportKey('jwk', publicKey));
// ステップバイステップで検証
try {
const signatureBuffer = Buffer.from(signature, 'base64');
const dataBuffer = Buffer.from(challenge, 'utf-8');
const isValid = await crypto.subtle.verify(
{ name: 'ECDSA', hash: 'SHA-256' },
publicKey,
signatureBuffer,
dataBuffer
);
console.log('Verification result:', isValid);
return isValid;
} catch (error) {
console.error('Verification error details:', error);
throw error;
}
}
問題2: DIDドキュメントが見つからない
// フォールバックメカニズム
class ResilientDIDResolver {
constructor() {
this.resolvers = [
new PrimaryResolver(),
new BackupResolver(),
new CachedResolver()
];
}
async resolve(did) {
for (const resolver of this.resolvers) {
try {
const result = await resolver.resolve(did);
if (result) return result;
} catch (error) {
console.warn(`Resolver ${resolver.constructor.name} failed:`, error);
// 次のresolverを試行
}
}
throw new Error(`Could not resolve DID: ${did}`);
}
}
問題3: VC検証のパフォーマンス問題
// バッチ検証で最適化
class OptimizedVCVerifier {
async verifyBatch(credentials) {
// 並列で検証
const verificationPromises = credentials.map(vc =>
this.verifyCredential(vc)
);
const results = await Promise.allSettled(verificationPromises);
return results.map((result, index) => ({
credential: credentials[index],
valid: result.status === 'fulfilled' && result.value,
error: result.status === 'rejected' ? result.reason : null
}));
}
async verifyCredential(credential) {
// キャッシュチェック
const cacheKey = this.getCacheKey(credential);
const cached = await this.cache.get(cacheKey);
if (cached !== null) {
return cached;
}
// 実際の検証
const isValid = await this._performVerification(credential);
// 結果をキャッシュ(有効期限まで)
const ttl = this.calculateTTL(credential.expirationDate);
await this.cache.set(cacheKey, isValid, ttl);
return isValid;
}
getCacheKey(credential) {
return `vc:${credential.id}:${credential.proof.jws}`;
}
calculateTTL(expirationDate) {
const now = Date.now();
const expiration = new Date(expirationDate).getTime();
return Math.max(0, expiration - now);
}
}
🌐 実世界のユースケース
ケース1: 金融機関のMCP統合
要件:
- 厳格なKYC/AML要求
- 監査証跡の完全性
- 高いセキュリティ基準
実装アプローチ:
class FinancialInstitutionMCP {
async authenticateCustomer(did) {
// 1. DID認証
const session = await this.didAuth.authenticate(did);
// 2. KYC VCの要求
const kycCredential = await this.requestCredential(did, [
'KYCCredential',
'AMLCheckCredential'
]);
// 3. リスクスコアリング
const riskScore = await this.calculateRisk(kycCredential);
if (riskScore > 0.7) {
// 追加認証要求
await this.requestMFA(did);
}
// 4. トランザクション権限の付与
return {
session,
permissions: this.determinePermissions(kycCredential, riskScore),
auditTrail: await this.createAuditTrail(did, kycCredential)
};
}
}
ケース2: ヘルスケアデータアクセス
要件:
- HIPAA準拠
- 患者のデータ主権
- 細かい権限制御
実装アプローチ:
class HealthcareMCP {
async authorizeDataAccess(patientDID, doctorDID, dataType) {
// 1. 医師のVCを検証
const doctorCredential = await this.verifyMedicalLicense(doctorDID);
// 2. 患者の同意確認
const consent = await this.checkPatientConsent(
patientDID,
doctorDID,
dataType
);
if (!consent) {
// 患者に同意を求める
await this.requestConsent(patientDID, doctorDID, dataType);
throw new Error('Patient consent required');
}
// 3. アクセス権限を時間制限付きで発行
return {
accessToken: await this.issueTemporaryAccessToken(
doctorDID,
dataType,
{ expiresIn: 3600 } // 1時間
),
auditLog: await this.logAccess(patientDID, doctorDID, dataType)
};
}
}
ケース3: サプライチェーン管理
要件:
- 複数組織間の信頼
- 商品のトレーサビリティ
- 改ざん防止
実装アプローチ:
class SupplyChainMCP {
async recordShipment(organizationDID, shipmentData) {
// 1. 組織の認証情報を検証
const orgCredential = await this.verifyOrganization(organizationDID);
// 2. 出荷VCを作成
const shipmentVC = await this.issueShipmentCredential({
issuer: organizationDID,
subject: shipmentData.productId,
attributes: {
origin: shipmentData.origin,
destination: shipmentData.destination,
timestamp: new Date(),
custody: organizationDID
}
});
// 3. ブロックチェーンにアンカー
await this.anchorToBlockchain(shipmentVC);
return shipmentVC;
}
async verifyProductAuthenticity(productId) {
// 出荷履歴のVCチェーンを検証
const chain = await this.getCredentialChain(productId);
for (const vc of chain) {
const isValid = await this.verifyCredential(vc);
if (!isValid) {
return { authentic: false, failedAt: vc };
}
}
return { authentic: true, chain };
}
}
📚 まとめと次のステップ
まとめ
DIDとウォレットベース認証は、MCP環境において以下を実現します:
✅ 真のゼロトラスト: 秘密鍵がサーバーに送信されない
✅ データ主権: ユーザーが自身のアイデンティティと属性を管理
✅ パスワードレス: 生体認証による快適なUX
✅ 監査証跡: 暗号署名による改ざん防止
✅ 相互運用性: 標準化されたプロトコル(W3C DID/VC)
次のステップ
-
学習リソース:
-
実験環境の構築:
# サンプルプロジェクトのクローン git clone https://github.com/example/mcp-did-auth cd mcp-did-auth # 依存関係のインストール npm install # 開発サーバーの起動 npm run dev -
コミュニティへの参加:
-
パイロットプロジェクトの立ち上げ:
- 小規模なユースケースで概念実証
- 既存システムとの統合テスト
- フィードバック収集と改善
参考実装
- DID Resolver: https://github.com/decentralized-identity/did-resolver
- Veramo Framework: https://veramo.io/
- Web DID: https://github.com/decentralized-identity/web-did-resolver
- Universal Resolver: https://dev.uniresolver.io/
注意: MCPはAnthropicが開発した比較的新しいプロトコルです。最新の情報については、公式ドキュメントを参照してください。