multi-agent-protocol 0.0.3 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/docs/00-design-specification.md +36 -0
- package/docs/01-open-questions.md +0 -5
- package/docs/02-wire-protocol.md +1 -1
- package/docs/07-federation.md +81 -5
- package/docs/09-authentication.md +748 -0
- package/docs/10-environment-awareness.md +242 -0
- package/docs/10-mail-protocol.md +553 -0
- package/docs/11-anp-inspired-improvements.md +1079 -0
- package/docs/12-anp-implementation-plan.md +641 -0
- package/docs/agent-iam-integration.md +877 -0
- package/docs/agentic-mesh-integration-draft.md +459 -0
- package/docs/git-transport-draft.md +251 -0
- package/package.json +5 -4
- package/schema/meta.json +200 -2
- package/schema/schema.json +1252 -13
|
@@ -0,0 +1,748 @@
|
|
|
1
|
+
# MAP Authentication Specification
|
|
2
|
+
|
|
3
|
+
This specification defines how authentication is negotiated and performed in the Multi-Agent Protocol (MAP). The design prioritizes flexibility while providing interoperable defaults.
|
|
4
|
+
|
|
5
|
+
## Design Principles
|
|
6
|
+
|
|
7
|
+
1. **Pluggable mechanisms** - Support multiple auth methods with a standard negotiation flow
|
|
8
|
+
2. **Sensible defaults** - Built-in support for common methods (bearer tokens, API keys)
|
|
9
|
+
3. **Transport-aware** - Acknowledge that some auth happens at transport layer (mTLS)
|
|
10
|
+
4. **Optional for local** - No auth overhead for trusted local connections (stdio)
|
|
11
|
+
5. **Federation-ready** - Credentials can carry claims for cross-server authentication
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Authentication Methods
|
|
16
|
+
|
|
17
|
+
MAP defines the following standard authentication methods:
|
|
18
|
+
|
|
19
|
+
| Method | Description | Use Case |
|
|
20
|
+
|--------|-------------|----------|
|
|
21
|
+
| `none` | No authentication | Local subprocess agents, development |
|
|
22
|
+
| `bearer` | Bearer token (JWT or opaque) | OAuth2, IdP integration, M2M tokens |
|
|
23
|
+
| `api-key` | Simple API key | Simple integrations, internal services |
|
|
24
|
+
| `mtls` | Mutual TLS (transport layer) | High-security service-to-service |
|
|
25
|
+
| `did:wba` | DID-based, domain-anchored | Cross-org federation, open discovery |
|
|
26
|
+
|
|
27
|
+
### Extension Methods
|
|
28
|
+
|
|
29
|
+
Custom authentication methods MUST use the `x-` prefix:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
x-custom-auth
|
|
33
|
+
x-kerberos
|
|
34
|
+
x-saml
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Servers MUST reject unknown methods that do not use the `x-` prefix.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Wire Protocol
|
|
42
|
+
|
|
43
|
+
### Server Authentication Capabilities
|
|
44
|
+
|
|
45
|
+
Servers advertise authentication requirements in the connect response:
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
interface ServerAuthCapabilities {
|
|
49
|
+
/** Supported authentication methods (in preference order) */
|
|
50
|
+
methods: AuthMethod[];
|
|
51
|
+
|
|
52
|
+
/** Is authentication required to proceed? */
|
|
53
|
+
required: boolean;
|
|
54
|
+
|
|
55
|
+
/** OAuth2 authorization server metadata URL (RFC 8414) */
|
|
56
|
+
oauth2MetadataUrl?: string;
|
|
57
|
+
|
|
58
|
+
/** JWKS URL for local JWT verification (RFC 7517) */
|
|
59
|
+
jwksUrl?: string;
|
|
60
|
+
|
|
61
|
+
/** Realm identifier for this server */
|
|
62
|
+
realm?: string;
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Client Authentication Credentials
|
|
67
|
+
|
|
68
|
+
Clients provide credentials using:
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
interface AuthCredentials {
|
|
72
|
+
/** The authentication method being used */
|
|
73
|
+
method: AuthMethod;
|
|
74
|
+
|
|
75
|
+
/** The credential value (token, API key, etc.) */
|
|
76
|
+
credential?: string;
|
|
77
|
+
|
|
78
|
+
/** Method-specific additional data */
|
|
79
|
+
metadata?: Record<string, unknown>;
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Authentication Result
|
|
84
|
+
|
|
85
|
+
Servers respond with:
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
interface AuthResult {
|
|
89
|
+
/** Whether authentication succeeded */
|
|
90
|
+
success: boolean;
|
|
91
|
+
|
|
92
|
+
/** Authenticated principal information */
|
|
93
|
+
principal?: {
|
|
94
|
+
/** Unique identifier for this principal */
|
|
95
|
+
id: string;
|
|
96
|
+
|
|
97
|
+
/** Token issuer (for federated auth) */
|
|
98
|
+
issuer?: string;
|
|
99
|
+
|
|
100
|
+
/** Additional claims from the credential */
|
|
101
|
+
claims?: Record<string, unknown>;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
/** Error details if authentication failed */
|
|
105
|
+
error?: {
|
|
106
|
+
code: AuthErrorCode;
|
|
107
|
+
message: string;
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
type AuthErrorCode =
|
|
112
|
+
| 'invalid_credentials' // Credentials are malformed or invalid
|
|
113
|
+
| 'expired' // Credentials have expired
|
|
114
|
+
| 'insufficient_scope' // Valid credentials but insufficient permissions
|
|
115
|
+
| 'method_not_supported' // Requested method not supported by server
|
|
116
|
+
| 'auth_required'; // No credentials provided but auth is required
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Connection Flow
|
|
122
|
+
|
|
123
|
+
### Flow 1: Auth Provided Upfront (Recommended)
|
|
124
|
+
|
|
125
|
+
When the client knows the server's auth requirements:
|
|
126
|
+
|
|
127
|
+
```
|
|
128
|
+
Client Server
|
|
129
|
+
│ │
|
|
130
|
+
│──── map/connect ────────────────────────────►│
|
|
131
|
+
│ { participantType, auth: { method, credential } }
|
|
132
|
+
│ │
|
|
133
|
+
│◄─── connect response ───────────────────────│
|
|
134
|
+
│ { session, principal } │
|
|
135
|
+
│ │
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
// Request
|
|
140
|
+
{
|
|
141
|
+
"jsonrpc": "2.0",
|
|
142
|
+
"id": 1,
|
|
143
|
+
"method": "map/connect",
|
|
144
|
+
"params": {
|
|
145
|
+
"protocolVersion": 1,
|
|
146
|
+
"participantType": "client",
|
|
147
|
+
"name": "my-client",
|
|
148
|
+
"auth": {
|
|
149
|
+
"method": "bearer",
|
|
150
|
+
"credential": "eyJhbGciOiJSUzI1NiIs..."
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Response (success)
|
|
156
|
+
{
|
|
157
|
+
"jsonrpc": "2.0",
|
|
158
|
+
"id": 1,
|
|
159
|
+
"result": {
|
|
160
|
+
"sessionId": "session_01ABC",
|
|
161
|
+
"participantId": "client_01XYZ",
|
|
162
|
+
"serverCapabilities": { ... },
|
|
163
|
+
"principal": {
|
|
164
|
+
"id": "user_123",
|
|
165
|
+
"issuer": "https://auth.example.com",
|
|
166
|
+
"claims": { "scope": "read write" }
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Flow 2: Auth Negotiation
|
|
173
|
+
|
|
174
|
+
When the client doesn't know auth requirements:
|
|
175
|
+
|
|
176
|
+
```
|
|
177
|
+
Client Server
|
|
178
|
+
│ │
|
|
179
|
+
│──── map/connect ────────────────────────────►│
|
|
180
|
+
│ { participantType } │
|
|
181
|
+
│ │
|
|
182
|
+
│◄─── auth_required response ─────────────────│
|
|
183
|
+
│ { authRequired: { methods, required } } │
|
|
184
|
+
│ │
|
|
185
|
+
│──── map/authenticate ───────────────────────►│
|
|
186
|
+
│ { method, credential } │
|
|
187
|
+
│ │
|
|
188
|
+
│◄─── auth result ────────────────────────────│
|
|
189
|
+
│ { success, session, principal } │
|
|
190
|
+
│ │
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
// Initial connect (no auth)
|
|
195
|
+
{
|
|
196
|
+
"jsonrpc": "2.0",
|
|
197
|
+
"id": 1,
|
|
198
|
+
"method": "map/connect",
|
|
199
|
+
"params": {
|
|
200
|
+
"protocolVersion": 1,
|
|
201
|
+
"participantType": "client",
|
|
202
|
+
"name": "my-client"
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Server requires auth
|
|
207
|
+
{
|
|
208
|
+
"jsonrpc": "2.0",
|
|
209
|
+
"id": 1,
|
|
210
|
+
"result": {
|
|
211
|
+
"authRequired": {
|
|
212
|
+
"methods": ["bearer", "api-key"],
|
|
213
|
+
"required": true,
|
|
214
|
+
"realm": "map-server-prod",
|
|
215
|
+
"oauth2MetadataUrl": "https://auth.example.com/.well-known/oauth-authorization-server"
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Client authenticates
|
|
221
|
+
{
|
|
222
|
+
"jsonrpc": "2.0",
|
|
223
|
+
"id": 2,
|
|
224
|
+
"method": "map/authenticate",
|
|
225
|
+
"params": {
|
|
226
|
+
"method": "bearer",
|
|
227
|
+
"credential": "eyJhbGciOiJSUzI1NiIs..."
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Server confirms
|
|
232
|
+
{
|
|
233
|
+
"jsonrpc": "2.0",
|
|
234
|
+
"id": 2,
|
|
235
|
+
"result": {
|
|
236
|
+
"success": true,
|
|
237
|
+
"sessionId": "session_01ABC",
|
|
238
|
+
"participantId": "client_01XYZ",
|
|
239
|
+
"principal": {
|
|
240
|
+
"id": "user_123",
|
|
241
|
+
"claims": { "scope": "read write" }
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Flow 3: No Auth Required
|
|
248
|
+
|
|
249
|
+
For local connections or development:
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
// Request
|
|
253
|
+
{
|
|
254
|
+
"jsonrpc": "2.0",
|
|
255
|
+
"id": 1,
|
|
256
|
+
"method": "map/connect",
|
|
257
|
+
"params": {
|
|
258
|
+
"protocolVersion": 1,
|
|
259
|
+
"participantType": "agent",
|
|
260
|
+
"name": "local-worker",
|
|
261
|
+
"auth": { "method": "none" }
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Response
|
|
266
|
+
{
|
|
267
|
+
"jsonrpc": "2.0",
|
|
268
|
+
"id": 1,
|
|
269
|
+
"result": {
|
|
270
|
+
"sessionId": "session_01ABC",
|
|
271
|
+
"participantId": "agent_01XYZ",
|
|
272
|
+
"principal": {
|
|
273
|
+
"id": "anonymous"
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## Method-Specific Details
|
|
282
|
+
|
|
283
|
+
### Bearer Tokens (`bearer`)
|
|
284
|
+
|
|
285
|
+
Bearer tokens are opaque strings or JWTs. The server is responsible for validation.
|
|
286
|
+
|
|
287
|
+
**Credential format:** The raw token string
|
|
288
|
+
|
|
289
|
+
```typescript
|
|
290
|
+
{
|
|
291
|
+
"method": "bearer",
|
|
292
|
+
"credential": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
**JWT Claims (when using JWT):**
|
|
297
|
+
|
|
298
|
+
| Claim | Description | Required |
|
|
299
|
+
|-------|-------------|----------|
|
|
300
|
+
| `sub` | Subject (principal ID) | Yes |
|
|
301
|
+
| `iss` | Issuer | Recommended |
|
|
302
|
+
| `aud` | Audience (server identifier) | Recommended |
|
|
303
|
+
| `exp` | Expiration time | Recommended |
|
|
304
|
+
| `iat` | Issued at | Recommended |
|
|
305
|
+
| `scope` | Space-separated permission scopes | Optional |
|
|
306
|
+
| `map:capabilities` | MAP-specific capabilities | Optional |
|
|
307
|
+
|
|
308
|
+
**Example JWT payload:**
|
|
309
|
+
|
|
310
|
+
```json
|
|
311
|
+
{
|
|
312
|
+
"sub": "agent_worker_01",
|
|
313
|
+
"iss": "https://auth.example.com",
|
|
314
|
+
"aud": "map-server-prod",
|
|
315
|
+
"exp": 1706227200,
|
|
316
|
+
"iat": 1706223600,
|
|
317
|
+
"scope": "map:read map:write map:agent",
|
|
318
|
+
"map:capabilities": {
|
|
319
|
+
"canSpawn": true,
|
|
320
|
+
"canSend": true
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### API Keys (`api-key`)
|
|
326
|
+
|
|
327
|
+
Simple static keys for straightforward integrations.
|
|
328
|
+
|
|
329
|
+
**Credential format:** The API key string
|
|
330
|
+
|
|
331
|
+
```typescript
|
|
332
|
+
{
|
|
333
|
+
"method": "api-key",
|
|
334
|
+
"credential": "map_sk_live_abc123def456..."
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
**Security considerations:**
|
|
339
|
+
- MUST only be used over TLS
|
|
340
|
+
- Keys SHOULD be rotatable without service interruption
|
|
341
|
+
- Keys SHOULD have associated metadata (owner, scopes, expiry)
|
|
342
|
+
|
|
343
|
+
### Mutual TLS (`mtls`)
|
|
344
|
+
|
|
345
|
+
Authentication is performed at the transport layer via client certificates.
|
|
346
|
+
|
|
347
|
+
**Credential format:** No credential in the protocol; certificate is validated at transport.
|
|
348
|
+
|
|
349
|
+
```typescript
|
|
350
|
+
{
|
|
351
|
+
"method": "mtls"
|
|
352
|
+
// No credential needed - cert already validated
|
|
353
|
+
}
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
**Metadata:** The server MAY extract principal information from the certificate:
|
|
357
|
+
|
|
358
|
+
```typescript
|
|
359
|
+
{
|
|
360
|
+
"method": "mtls",
|
|
361
|
+
"metadata": {
|
|
362
|
+
"cn": "agent-worker-01.example.com",
|
|
363
|
+
"fingerprint": "sha256:abc123..."
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### No Authentication (`none`)
|
|
369
|
+
|
|
370
|
+
For trusted local connections.
|
|
371
|
+
|
|
372
|
+
```typescript
|
|
373
|
+
{
|
|
374
|
+
"method": "none"
|
|
375
|
+
}
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
**When to use:**
|
|
379
|
+
- Subprocess agents connected via stdio
|
|
380
|
+
- In-process connections
|
|
381
|
+
- Development/testing environments
|
|
382
|
+
- Behind a trusted proxy that handles auth
|
|
383
|
+
|
|
384
|
+
**Servers MAY:**
|
|
385
|
+
- Reject `none` based on transport type (e.g., require auth for WebSocket)
|
|
386
|
+
- Assign a default principal for anonymous connections
|
|
387
|
+
|
|
388
|
+
### DID:WBA (`did:wba`)
|
|
389
|
+
|
|
390
|
+
Domain-anchored decentralized identity based on the W3C `did:wba` method. Identity is derived from domain ownership: a DID like `did:wba:agents.example.com:gateway` resolves to a DID document hosted at `https://agents.example.com/gateway/did.json`.
|
|
391
|
+
|
|
392
|
+
**Credential format:** DID and cryptographic proof in metadata
|
|
393
|
+
|
|
394
|
+
```typescript
|
|
395
|
+
{
|
|
396
|
+
"method": "did:wba",
|
|
397
|
+
"metadata": {
|
|
398
|
+
"did": "did:wba:agents.example.com:gateway",
|
|
399
|
+
"proof": {
|
|
400
|
+
"type": "JsonWebSignature2020",
|
|
401
|
+
"created": "2026-02-10T12:00:00.000Z",
|
|
402
|
+
"challenge": "map_chal_01ABCDEFGHJ0123456789AB",
|
|
403
|
+
"jws": "base64url-encoded-ecdsa-signature"
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
**DID Document:** The resolved DID document MUST contain:
|
|
410
|
+
- `verificationMethod` with the public key used to verify the proof
|
|
411
|
+
- `authentication` referencing the verification method
|
|
412
|
+
- Optionally, a `service` entry of type `MAPFederationEndpoint` with the MAP WebSocket URL
|
|
413
|
+
|
|
414
|
+
**Proof verification:**
|
|
415
|
+
1. Server generates a challenge nonce (format: `map_chal_<ULID>`)
|
|
416
|
+
2. Client signs `challenge.did.created` using ECDSA P-256 with its private key
|
|
417
|
+
3. Server resolves DID → fetches DID document → extracts public key
|
|
418
|
+
4. Server verifies the JWS signature against the public key and checks proof freshness
|
|
419
|
+
|
|
420
|
+
**Security considerations:**
|
|
421
|
+
- DID documents are fetched over HTTPS — domain ownership proves identity
|
|
422
|
+
- Proof freshness is enforced via the `created` timestamp (default max age: 5 minutes)
|
|
423
|
+
- Servers MAY maintain a trusted domains list to restrict accepted DIDs
|
|
424
|
+
- DID document caching with configurable TTL prevents excessive lookups
|
|
425
|
+
|
|
426
|
+
**When to use:**
|
|
427
|
+
- Cross-organization federation where pre-shared credentials are impractical
|
|
428
|
+
- Open discovery scenarios where peers find each other via DID documents
|
|
429
|
+
- Systems that need globally unique, verifiable agent identities
|
|
430
|
+
|
|
431
|
+
**SDK support:**
|
|
432
|
+
- `DIDWBAAuthenticator` — server-side authenticator (see `ts-sdk/src/server/auth/did-wba-authenticator.ts`)
|
|
433
|
+
- `generateDIDWBAProof()` / `verifyDIDWBAProof()` — proof utilities (see `ts-sdk/src/federation/did-wba/proof.ts`)
|
|
434
|
+
- `DIDWBAResolver` — DID document resolution with caching (see `ts-sdk/src/federation/did-wba/resolver.ts`)
|
|
435
|
+
|
|
436
|
+
---
|
|
437
|
+
|
|
438
|
+
## Token Refresh
|
|
439
|
+
|
|
440
|
+
For long-lived connections with expiring tokens:
|
|
441
|
+
|
|
442
|
+
### Proactive Refresh
|
|
443
|
+
|
|
444
|
+
Client refreshes before expiration:
|
|
445
|
+
|
|
446
|
+
```typescript
|
|
447
|
+
// Request
|
|
448
|
+
{
|
|
449
|
+
"jsonrpc": "2.0",
|
|
450
|
+
"id": 10,
|
|
451
|
+
"method": "map/auth/refresh",
|
|
452
|
+
"params": {
|
|
453
|
+
"credential": "eyJhbGciOiJSUzI1NiIs..." // New token
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// Response
|
|
458
|
+
{
|
|
459
|
+
"jsonrpc": "2.0",
|
|
460
|
+
"id": 10,
|
|
461
|
+
"result": {
|
|
462
|
+
"success": true,
|
|
463
|
+
"principal": {
|
|
464
|
+
"id": "user_123",
|
|
465
|
+
"claims": { "exp": 1706230800 }
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
### Server-Initiated Expiration Warning
|
|
472
|
+
|
|
473
|
+
Server notifies client before token expires:
|
|
474
|
+
|
|
475
|
+
```typescript
|
|
476
|
+
// Notification (server → client)
|
|
477
|
+
{
|
|
478
|
+
"jsonrpc": "2.0",
|
|
479
|
+
"method": "map/auth/expiring",
|
|
480
|
+
"params": {
|
|
481
|
+
"expiresAt": 1706227200,
|
|
482
|
+
"refreshBefore": 1706226900 // Suggested refresh time
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
### Forced Re-authentication
|
|
488
|
+
|
|
489
|
+
If token expires or is revoked:
|
|
490
|
+
|
|
491
|
+
```typescript
|
|
492
|
+
// Notification (server → client)
|
|
493
|
+
{
|
|
494
|
+
"jsonrpc": "2.0",
|
|
495
|
+
"method": "map/auth/revoked",
|
|
496
|
+
"params": {
|
|
497
|
+
"reason": "token_expired",
|
|
498
|
+
"message": "Your session has expired. Please re-authenticate.",
|
|
499
|
+
"gracePeriodMs": 5000 // Time before disconnect
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
---
|
|
505
|
+
|
|
506
|
+
## Federation Authentication
|
|
507
|
+
|
|
508
|
+
For cross-server authentication in federated deployments:
|
|
509
|
+
|
|
510
|
+
### Token Requirements
|
|
511
|
+
|
|
512
|
+
Federated tokens MUST include:
|
|
513
|
+
|
|
514
|
+
| Claim | Description |
|
|
515
|
+
|-------|-------------|
|
|
516
|
+
| `iss` | Issuing server's identifier |
|
|
517
|
+
| `aud` | Target server(s) - array or string |
|
|
518
|
+
| `sub` | Original principal identifier |
|
|
519
|
+
| `map:federation` | Federation-specific claims |
|
|
520
|
+
|
|
521
|
+
**Example federated token:**
|
|
522
|
+
|
|
523
|
+
```json
|
|
524
|
+
{
|
|
525
|
+
"sub": "agent_worker_01",
|
|
526
|
+
"iss": "https://server-a.example.com",
|
|
527
|
+
"aud": ["https://server-b.example.com", "https://server-c.example.com"],
|
|
528
|
+
"exp": 1706227200,
|
|
529
|
+
"map:federation": {
|
|
530
|
+
"originServer": "server-a",
|
|
531
|
+
"delegatedCapabilities": {
|
|
532
|
+
"canSend": true,
|
|
533
|
+
"canQuery": true
|
|
534
|
+
},
|
|
535
|
+
"hopCount": 1,
|
|
536
|
+
"maxHops": 3
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
### Trust Establishment
|
|
542
|
+
|
|
543
|
+
Federated servers MUST:
|
|
544
|
+
1. Maintain an allowlist of trusted issuers
|
|
545
|
+
2. Verify token signatures against issuer's JWKS
|
|
546
|
+
3. Validate audience claims include their own identifier
|
|
547
|
+
4. Enforce hop count limits to prevent routing loops
|
|
548
|
+
|
|
549
|
+
---
|
|
550
|
+
|
|
551
|
+
## Error Handling
|
|
552
|
+
|
|
553
|
+
### Authentication Errors
|
|
554
|
+
|
|
555
|
+
| Error Code | HTTP Equivalent | Description |
|
|
556
|
+
|------------|-----------------|-------------|
|
|
557
|
+
| `AUTH_REQUIRED` | 401 | Authentication required but not provided |
|
|
558
|
+
| `INVALID_CREDENTIALS` | 401 | Credentials invalid or malformed |
|
|
559
|
+
| `EXPIRED` | 401 | Credentials have expired |
|
|
560
|
+
| `INSUFFICIENT_SCOPE` | 403 | Valid credentials but lacks required permissions |
|
|
561
|
+
| `METHOD_NOT_SUPPORTED` | 400 | Requested auth method not supported |
|
|
562
|
+
|
|
563
|
+
**Error response format:**
|
|
564
|
+
|
|
565
|
+
```typescript
|
|
566
|
+
{
|
|
567
|
+
"jsonrpc": "2.0",
|
|
568
|
+
"id": 1,
|
|
569
|
+
"error": {
|
|
570
|
+
"code": -32001, // MAP error code
|
|
571
|
+
"message": "Authentication failed",
|
|
572
|
+
"data": {
|
|
573
|
+
"authError": {
|
|
574
|
+
"code": "invalid_credentials",
|
|
575
|
+
"message": "JWT signature verification failed"
|
|
576
|
+
},
|
|
577
|
+
"authRequired": {
|
|
578
|
+
"methods": ["bearer", "api-key"],
|
|
579
|
+
"required": true
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
---
|
|
587
|
+
|
|
588
|
+
## Security Considerations
|
|
589
|
+
|
|
590
|
+
### Transport Security
|
|
591
|
+
|
|
592
|
+
1. **TLS Required** - All remote connections MUST use TLS 1.2+
|
|
593
|
+
2. **Certificate Validation** - Clients MUST validate server certificates
|
|
594
|
+
3. **No Downgrade** - Servers SHOULD reject non-TLS connections for auth methods other than `none`
|
|
595
|
+
|
|
596
|
+
### Token Security
|
|
597
|
+
|
|
598
|
+
1. **Short Lifetimes** - Bearer tokens SHOULD have lifetimes ≤ 1 hour for M2M
|
|
599
|
+
2. **Audience Validation** - Servers MUST validate `aud` claims
|
|
600
|
+
3. **Signature Algorithms** - Prefer RS256/ES256; avoid HS256 for distributed systems
|
|
601
|
+
4. **Key Rotation** - Servers SHOULD support JWKS key rotation
|
|
602
|
+
|
|
603
|
+
### API Key Security
|
|
604
|
+
|
|
605
|
+
1. **Entropy** - Keys MUST have ≥ 256 bits of entropy
|
|
606
|
+
2. **Prefix** - Keys SHOULD use identifiable prefixes (e.g., `map_sk_`)
|
|
607
|
+
3. **Hashing** - Servers MUST store only hashed keys
|
|
608
|
+
4. **Rotation** - Support key rotation without service interruption
|
|
609
|
+
|
|
610
|
+
### Logging and Audit
|
|
611
|
+
|
|
612
|
+
1. **No Credential Logging** - Credentials MUST NOT appear in logs
|
|
613
|
+
2. **Auth Events** - Log authentication attempts (success/failure) with principal ID
|
|
614
|
+
3. **Rate Limiting** - Implement rate limiting on auth endpoints
|
|
615
|
+
|
|
616
|
+
---
|
|
617
|
+
|
|
618
|
+
## Implementation Requirements
|
|
619
|
+
|
|
620
|
+
### Servers MUST
|
|
621
|
+
|
|
622
|
+
1. Support at least one of: `bearer`, `api-key`, or `none`
|
|
623
|
+
2. Advertise supported methods in auth capabilities
|
|
624
|
+
3. Return proper error codes for auth failures
|
|
625
|
+
4. Validate credentials before establishing session
|
|
626
|
+
|
|
627
|
+
### Servers SHOULD
|
|
628
|
+
|
|
629
|
+
1. Support `bearer` tokens with JWT validation
|
|
630
|
+
2. Provide JWKS endpoint or reference external JWKS
|
|
631
|
+
3. Support token refresh for long-lived connections
|
|
632
|
+
4. Implement rate limiting on authentication
|
|
633
|
+
|
|
634
|
+
### Clients MUST
|
|
635
|
+
|
|
636
|
+
1. Support providing credentials via `auth` parameter
|
|
637
|
+
2. Handle `authRequired` response and provide credentials
|
|
638
|
+
3. Handle auth errors gracefully
|
|
639
|
+
|
|
640
|
+
### Clients SHOULD
|
|
641
|
+
|
|
642
|
+
1. Implement automatic token refresh before expiration
|
|
643
|
+
2. Support credential caching with proper security
|
|
644
|
+
3. Handle `map/auth/expiring` notifications
|
|
645
|
+
|
|
646
|
+
---
|
|
647
|
+
|
|
648
|
+
## Examples
|
|
649
|
+
|
|
650
|
+
### Example 1: Production Server with JWT
|
|
651
|
+
|
|
652
|
+
```typescript
|
|
653
|
+
const server = new MAPServer({
|
|
654
|
+
auth: {
|
|
655
|
+
required: true,
|
|
656
|
+
methods: ['bearer'],
|
|
657
|
+
authenticators: [
|
|
658
|
+
new JWTAuthenticator({
|
|
659
|
+
jwksUrl: 'https://auth.example.com/.well-known/jwks.json',
|
|
660
|
+
issuer: 'https://auth.example.com',
|
|
661
|
+
audience: 'map-server-prod'
|
|
662
|
+
})
|
|
663
|
+
]
|
|
664
|
+
}
|
|
665
|
+
});
|
|
666
|
+
```
|
|
667
|
+
|
|
668
|
+
### Example 2: Internal Service with API Keys
|
|
669
|
+
|
|
670
|
+
```typescript
|
|
671
|
+
const server = new MAPServer({
|
|
672
|
+
auth: {
|
|
673
|
+
required: true,
|
|
674
|
+
methods: ['api-key'],
|
|
675
|
+
authenticators: [
|
|
676
|
+
new APIKeyAuthenticator({
|
|
677
|
+
validateKey: async (key) => {
|
|
678
|
+
const record = await db.apiKeys.findByHash(hash(key));
|
|
679
|
+
return {
|
|
680
|
+
valid: !!record && !record.revoked,
|
|
681
|
+
principalId: record?.ownerId,
|
|
682
|
+
metadata: { scopes: record?.scopes }
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
})
|
|
686
|
+
]
|
|
687
|
+
}
|
|
688
|
+
});
|
|
689
|
+
```
|
|
690
|
+
|
|
691
|
+
### Example 3: Development Server
|
|
692
|
+
|
|
693
|
+
```typescript
|
|
694
|
+
const server = new MAPServer({
|
|
695
|
+
auth: {
|
|
696
|
+
required: false,
|
|
697
|
+
methods: ['none', 'bearer'],
|
|
698
|
+
authenticators: [
|
|
699
|
+
new NoAuthAuthenticator(),
|
|
700
|
+
new JWTAuthenticator({ /* ... */ }) // Optional for testing
|
|
701
|
+
]
|
|
702
|
+
}
|
|
703
|
+
});
|
|
704
|
+
```
|
|
705
|
+
|
|
706
|
+
### Example 4: Hybrid Production
|
|
707
|
+
|
|
708
|
+
```typescript
|
|
709
|
+
const server = new MAPServer({
|
|
710
|
+
auth: {
|
|
711
|
+
required: true,
|
|
712
|
+
methods: ['bearer', 'api-key', 'mtls'],
|
|
713
|
+
authenticators: [
|
|
714
|
+
new JWTAuthenticator({ /* ... */ }),
|
|
715
|
+
new APIKeyAuthenticator({ /* ... */ }),
|
|
716
|
+
new MTLSAuthenticator({ /* ... */ })
|
|
717
|
+
]
|
|
718
|
+
}
|
|
719
|
+
});
|
|
720
|
+
```
|
|
721
|
+
|
|
722
|
+
### Example 5: Federation Server with DID:WBA
|
|
723
|
+
|
|
724
|
+
```typescript
|
|
725
|
+
import { DIDWBAAuthenticator } from '@anthropic/multi-agent-protocol/server/auth';
|
|
726
|
+
|
|
727
|
+
const server = new MAPServer({
|
|
728
|
+
auth: {
|
|
729
|
+
required: true,
|
|
730
|
+
methods: ['did:wba', 'bearer'],
|
|
731
|
+
authenticators: [
|
|
732
|
+
new DIDWBAAuthenticator({
|
|
733
|
+
trustedDomains: ['*.example.com', 'partner.org'],
|
|
734
|
+
}),
|
|
735
|
+
new JWTAuthenticator({ /* ... */ })
|
|
736
|
+
]
|
|
737
|
+
}
|
|
738
|
+
});
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
---
|
|
742
|
+
|
|
743
|
+
## Open Questions
|
|
744
|
+
|
|
745
|
+
1. **Scope standardization** - Should MAP define standard scope strings (e.g., `map:read`, `map:agent:spawn`)?
|
|
746
|
+
2. **Principal-to-permissions mapping** - Should the protocol define how claims map to capabilities?
|
|
747
|
+
3. **Multi-factor** - Is there a use case for multi-factor auth in agent connections?
|
|
748
|
+
4. **Session binding** - Should tokens be bound to specific sessions to prevent replay?
|