@credat/mcp 0.1.2-alpha.1
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/LICENSE +189 -0
- package/README.md +261 -0
- package/dist/index.cjs +425 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +130 -0
- package/dist/index.d.ts +130 -0
- package/dist/index.js +392 -0
- package/dist/index.js.map +1 -0
- package/package.json +78 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
|
|
3
|
+
import * as credat from 'credat';
|
|
4
|
+
import { DelegationConstraints, DelegationResult, ChallengeMessage } from 'credat';
|
|
5
|
+
|
|
6
|
+
interface ConstraintContext {
|
|
7
|
+
transactionValue?: number;
|
|
8
|
+
domain?: string;
|
|
9
|
+
[key: string]: unknown;
|
|
10
|
+
}
|
|
11
|
+
interface ConstraintViolation {
|
|
12
|
+
constraint: string;
|
|
13
|
+
message: string;
|
|
14
|
+
}
|
|
15
|
+
interface CredatAuthOptions {
|
|
16
|
+
/** DID of the server, used as the challenge issuer (e.g. "did:web:api.example.com") */
|
|
17
|
+
serverDid: string;
|
|
18
|
+
/** Public key of the owner who issued delegation credentials */
|
|
19
|
+
ownerPublicKey: Uint8Array;
|
|
20
|
+
/** Static agent public key (for single-agent scenarios) */
|
|
21
|
+
agentPublicKey?: Uint8Array;
|
|
22
|
+
/** Resolve an agent's public key from their DID (for multi-agent scenarios) */
|
|
23
|
+
resolveAgentKey?: (agentDid: string) => Promise<Uint8Array>;
|
|
24
|
+
/** Max age for challenges before they expire. Default: 300_000 (5 minutes) */
|
|
25
|
+
challengeMaxAgeMs?: number;
|
|
26
|
+
/** Max age for authenticated sessions. Default: 3_600_000 (1 hour) */
|
|
27
|
+
sessionMaxAgeMs?: number;
|
|
28
|
+
/** Tool name prefix. Default: "credat" → tools become "credat:challenge", "credat:authenticate" */
|
|
29
|
+
toolPrefix?: string;
|
|
30
|
+
}
|
|
31
|
+
interface ProtectOptions {
|
|
32
|
+
/** Required scopes — agent must have ALL of these */
|
|
33
|
+
scopes?: string[];
|
|
34
|
+
/** Required scopes — agent must have at least ONE of these */
|
|
35
|
+
anyScope?: string[];
|
|
36
|
+
/** Constraint context for runtime constraint validation */
|
|
37
|
+
constraintContext?: ConstraintContext | ((args: Record<string, unknown>) => ConstraintContext);
|
|
38
|
+
}
|
|
39
|
+
interface AuthContext {
|
|
40
|
+
/** Authenticated agent's DID */
|
|
41
|
+
agentDid: string;
|
|
42
|
+
/** DID of the owner who delegated permissions */
|
|
43
|
+
ownerDid: string;
|
|
44
|
+
/** Granted scopes */
|
|
45
|
+
scopes: string[];
|
|
46
|
+
/** Delegation constraints (if any) */
|
|
47
|
+
constraints?: DelegationConstraints;
|
|
48
|
+
}
|
|
49
|
+
interface SessionAuth {
|
|
50
|
+
delegationResult: DelegationResult & {
|
|
51
|
+
valid: true;
|
|
52
|
+
};
|
|
53
|
+
authenticatedAt: number;
|
|
54
|
+
}
|
|
55
|
+
interface StoredChallenge {
|
|
56
|
+
challenge: credat.ChallengeMessage;
|
|
57
|
+
sessionId: string;
|
|
58
|
+
createdAt: number;
|
|
59
|
+
}
|
|
60
|
+
interface AuthErrorPayload {
|
|
61
|
+
error: string;
|
|
62
|
+
code: string;
|
|
63
|
+
details?: string[];
|
|
64
|
+
}
|
|
65
|
+
/** Minimal subset of MCP's RequestHandlerExtra we depend on */
|
|
66
|
+
interface ToolExtra {
|
|
67
|
+
sessionId?: string;
|
|
68
|
+
signal: AbortSignal;
|
|
69
|
+
[key: string]: unknown;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
type ProtectedHandler<TArgs> = (args: TArgs, extra: ToolExtra & {
|
|
73
|
+
auth: AuthContext;
|
|
74
|
+
}) => CallToolResult | Promise<CallToolResult>;
|
|
75
|
+
declare class CredatAuth {
|
|
76
|
+
private readonly config;
|
|
77
|
+
private readonly challengeStore;
|
|
78
|
+
private readonly sessionStore;
|
|
79
|
+
private readonly protectFn;
|
|
80
|
+
constructor(options: CredatAuthOptions);
|
|
81
|
+
/** Register the credat:challenge and credat:authenticate tools on the server */
|
|
82
|
+
install(server: McpServer): void;
|
|
83
|
+
/** Wrap a tool handler to require authentication + optional scope/constraint checks */
|
|
84
|
+
protect<TArgs extends Record<string, unknown>>(options: ProtectOptions, handler: ProtectedHandler<TArgs>): (args: TArgs, extra: ToolExtra) => CallToolResult | Promise<CallToolResult>;
|
|
85
|
+
/** Check if a session is currently authenticated */
|
|
86
|
+
isAuthenticated(sessionId?: string): boolean;
|
|
87
|
+
/** Get the auth result for a session */
|
|
88
|
+
getSessionAuth(sessionId?: string): SessionAuth | undefined;
|
|
89
|
+
/** Revoke a session, forcing re-authentication */
|
|
90
|
+
revokeSession(sessionId?: string): void;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
declare const McpAuthErrorCodes: {
|
|
94
|
+
readonly NOT_AUTHENTICATED: "NOT_AUTHENTICATED";
|
|
95
|
+
readonly SESSION_EXPIRED: "SESSION_EXPIRED";
|
|
96
|
+
readonly SESSION_MISMATCH: "SESSION_MISMATCH";
|
|
97
|
+
readonly INSUFFICIENT_SCOPES: "INSUFFICIENT_SCOPES";
|
|
98
|
+
readonly CONSTRAINT_VIOLATION: "CONSTRAINT_VIOLATION";
|
|
99
|
+
readonly CONFIGURATION_ERROR: "CONFIGURATION_ERROR";
|
|
100
|
+
};
|
|
101
|
+
type McpAuthErrorCode = (typeof McpAuthErrorCodes)[keyof typeof McpAuthErrorCodes];
|
|
102
|
+
declare function authError(message: string, code: string, details?: string[]): CallToolResult;
|
|
103
|
+
declare function scopeError(required: string[], actual: string[]): CallToolResult;
|
|
104
|
+
declare function constraintError(violations: ConstraintViolation[]): CallToolResult;
|
|
105
|
+
|
|
106
|
+
declare class ChallengeStore {
|
|
107
|
+
private store;
|
|
108
|
+
private readonly maxAgeMs;
|
|
109
|
+
private readonly maxSize;
|
|
110
|
+
private insertsSinceCleanup;
|
|
111
|
+
constructor(maxAgeMs: number, maxSize?: number);
|
|
112
|
+
set(nonce: string, challenge: ChallengeMessage, sessionId: string): void;
|
|
113
|
+
/** Consume a challenge (single-use). Returns and deletes. */
|
|
114
|
+
consume(nonce: string): StoredChallenge | undefined;
|
|
115
|
+
cleanup(): void;
|
|
116
|
+
get size(): number;
|
|
117
|
+
}
|
|
118
|
+
declare class SessionStore {
|
|
119
|
+
private store;
|
|
120
|
+
private readonly maxAgeMs;
|
|
121
|
+
private insertsSinceCleanup;
|
|
122
|
+
constructor(maxAgeMs: number);
|
|
123
|
+
set(sessionId: string, auth: SessionAuth): void;
|
|
124
|
+
get(sessionId: string): SessionAuth | undefined;
|
|
125
|
+
delete(sessionId: string): boolean;
|
|
126
|
+
cleanup(): void;
|
|
127
|
+
get size(): number;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export { type AuthContext, type AuthErrorPayload, ChallengeStore, CredatAuth, type CredatAuthOptions, type McpAuthErrorCode, McpAuthErrorCodes, type ProtectOptions, type SessionAuth, SessionStore, type StoredChallenge, type ToolExtra, authError, constraintError, scopeError };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
// src/auth.ts
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
|
|
4
|
+
// src/protect.ts
|
|
5
|
+
import { hasAllScopes, hasAnyScope } from "credat";
|
|
6
|
+
|
|
7
|
+
// src/constraints.ts
|
|
8
|
+
function validateConstraints(constraints, context) {
|
|
9
|
+
if (!constraints) return [];
|
|
10
|
+
const violations = [];
|
|
11
|
+
if (constraints.maxTransactionValue != null && context.transactionValue != null) {
|
|
12
|
+
if (context.transactionValue > constraints.maxTransactionValue) {
|
|
13
|
+
violations.push({
|
|
14
|
+
constraint: "maxTransactionValue",
|
|
15
|
+
message: `${context.transactionValue} exceeds max ${constraints.maxTransactionValue}`
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
if (constraints.allowedDomains != null && context.domain != null) {
|
|
20
|
+
if (!constraints.allowedDomains.includes(context.domain)) {
|
|
21
|
+
violations.push({
|
|
22
|
+
constraint: "allowedDomains",
|
|
23
|
+
message: `Domain "${context.domain}" is not in allowed list: ${constraints.allowedDomains.join(", ")}`
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (constraints.rateLimit != null && typeof context.rateLimit === "number") {
|
|
28
|
+
if (context.rateLimit > constraints.rateLimit) {
|
|
29
|
+
violations.push({
|
|
30
|
+
constraint: "rateLimit",
|
|
31
|
+
message: `Rate ${context.rateLimit} exceeds limit ${constraints.rateLimit}`
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return violations;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// src/errors.ts
|
|
39
|
+
var McpAuthErrorCodes = {
|
|
40
|
+
NOT_AUTHENTICATED: "NOT_AUTHENTICATED",
|
|
41
|
+
SESSION_EXPIRED: "SESSION_EXPIRED",
|
|
42
|
+
SESSION_MISMATCH: "SESSION_MISMATCH",
|
|
43
|
+
INSUFFICIENT_SCOPES: "INSUFFICIENT_SCOPES",
|
|
44
|
+
CONSTRAINT_VIOLATION: "CONSTRAINT_VIOLATION",
|
|
45
|
+
CONFIGURATION_ERROR: "CONFIGURATION_ERROR"
|
|
46
|
+
};
|
|
47
|
+
function authError(message, code, details) {
|
|
48
|
+
const payload = { error: message, code };
|
|
49
|
+
if (details && details.length > 0) {
|
|
50
|
+
payload.details = details;
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
content: [{ type: "text", text: JSON.stringify(payload) }],
|
|
54
|
+
isError: true
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function scopeError(required, actual) {
|
|
58
|
+
const missing = required.filter((s) => !actual.includes(s));
|
|
59
|
+
return authError(
|
|
60
|
+
`Insufficient scopes. Missing: ${missing.join(", ")}`,
|
|
61
|
+
McpAuthErrorCodes.INSUFFICIENT_SCOPES,
|
|
62
|
+
[`required: ${required.join(", ")}`, `granted: ${actual.join(", ")}`]
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
function constraintError(violations) {
|
|
66
|
+
return authError(
|
|
67
|
+
`Constraint violation: ${violations.map((v) => v.message).join("; ")}`,
|
|
68
|
+
McpAuthErrorCodes.CONSTRAINT_VIOLATION,
|
|
69
|
+
violations.map((v) => `${v.constraint}: ${v.message}`)
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// src/protect.ts
|
|
74
|
+
var STDIO_SESSION_KEY = "__stdio__";
|
|
75
|
+
function createProtect(sessionStore) {
|
|
76
|
+
return function protect(options, handler) {
|
|
77
|
+
return (args, extra) => {
|
|
78
|
+
const sessionId = extra.sessionId ?? STDIO_SESSION_KEY;
|
|
79
|
+
const session = sessionStore.get(sessionId);
|
|
80
|
+
if (!session) {
|
|
81
|
+
return authError(
|
|
82
|
+
"Not authenticated. Call the credat:challenge tool to begin authentication.",
|
|
83
|
+
McpAuthErrorCodes.NOT_AUTHENTICATED
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
const { delegationResult } = session;
|
|
87
|
+
if (options.scopes && options.scopes.length > 0) {
|
|
88
|
+
if (!hasAllScopes(delegationResult, options.scopes)) {
|
|
89
|
+
return scopeError(options.scopes, delegationResult.scopes);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (options.anyScope && options.anyScope.length > 0) {
|
|
93
|
+
if (!hasAnyScope(delegationResult, options.anyScope)) {
|
|
94
|
+
return scopeError(options.anyScope, delegationResult.scopes);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (options.constraintContext) {
|
|
98
|
+
const context = typeof options.constraintContext === "function" ? options.constraintContext(args) : options.constraintContext;
|
|
99
|
+
const violations = validateConstraints(delegationResult.constraints, context);
|
|
100
|
+
if (violations.length > 0) {
|
|
101
|
+
return constraintError(violations);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
const authContext = {
|
|
105
|
+
agentDid: delegationResult.agent,
|
|
106
|
+
ownerDid: delegationResult.owner,
|
|
107
|
+
scopes: delegationResult.scopes,
|
|
108
|
+
constraints: delegationResult.constraints
|
|
109
|
+
};
|
|
110
|
+
const augmentedExtra = Object.assign({}, extra, {
|
|
111
|
+
auth: authContext
|
|
112
|
+
});
|
|
113
|
+
return handler(args, augmentedExtra);
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// src/session.ts
|
|
119
|
+
var DEFAULT_MAX_SIZE = 1e3;
|
|
120
|
+
var CLEANUP_THRESHOLD = 100;
|
|
121
|
+
var ChallengeStore = class {
|
|
122
|
+
store = /* @__PURE__ */ new Map();
|
|
123
|
+
maxAgeMs;
|
|
124
|
+
maxSize;
|
|
125
|
+
insertsSinceCleanup = 0;
|
|
126
|
+
constructor(maxAgeMs, maxSize = DEFAULT_MAX_SIZE) {
|
|
127
|
+
this.maxAgeMs = maxAgeMs;
|
|
128
|
+
this.maxSize = maxSize;
|
|
129
|
+
}
|
|
130
|
+
set(nonce, challenge, sessionId) {
|
|
131
|
+
this.store.set(nonce, {
|
|
132
|
+
challenge,
|
|
133
|
+
sessionId,
|
|
134
|
+
createdAt: Date.now()
|
|
135
|
+
});
|
|
136
|
+
this.insertsSinceCleanup++;
|
|
137
|
+
if (this.insertsSinceCleanup >= CLEANUP_THRESHOLD || this.store.size > this.maxSize) {
|
|
138
|
+
this.cleanup();
|
|
139
|
+
this.insertsSinceCleanup = 0;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/** Consume a challenge (single-use). Returns and deletes. */
|
|
143
|
+
consume(nonce) {
|
|
144
|
+
const entry = this.store.get(nonce);
|
|
145
|
+
if (!entry) return void 0;
|
|
146
|
+
this.store.delete(nonce);
|
|
147
|
+
if (Date.now() - entry.createdAt > this.maxAgeMs) {
|
|
148
|
+
return void 0;
|
|
149
|
+
}
|
|
150
|
+
return entry;
|
|
151
|
+
}
|
|
152
|
+
cleanup() {
|
|
153
|
+
const now = Date.now();
|
|
154
|
+
for (const [nonce, entry] of this.store) {
|
|
155
|
+
if (now - entry.createdAt > this.maxAgeMs) {
|
|
156
|
+
this.store.delete(nonce);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (this.store.size > this.maxSize) {
|
|
160
|
+
const entries = [...this.store.entries()].sort((a, b) => a[1].createdAt - b[1].createdAt);
|
|
161
|
+
const toRemove = entries.slice(0, this.store.size - this.maxSize);
|
|
162
|
+
for (const [nonce] of toRemove) {
|
|
163
|
+
this.store.delete(nonce);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
get size() {
|
|
168
|
+
return this.store.size;
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
var SessionStore = class {
|
|
172
|
+
store = /* @__PURE__ */ new Map();
|
|
173
|
+
maxAgeMs;
|
|
174
|
+
insertsSinceCleanup = 0;
|
|
175
|
+
constructor(maxAgeMs) {
|
|
176
|
+
this.maxAgeMs = maxAgeMs;
|
|
177
|
+
}
|
|
178
|
+
set(sessionId, auth) {
|
|
179
|
+
this.store.set(sessionId, auth);
|
|
180
|
+
this.insertsSinceCleanup++;
|
|
181
|
+
if (this.insertsSinceCleanup >= CLEANUP_THRESHOLD) {
|
|
182
|
+
this.cleanup();
|
|
183
|
+
this.insertsSinceCleanup = 0;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
get(sessionId) {
|
|
187
|
+
const entry = this.store.get(sessionId);
|
|
188
|
+
if (!entry) return void 0;
|
|
189
|
+
if (Date.now() - entry.authenticatedAt > this.maxAgeMs) {
|
|
190
|
+
this.store.delete(sessionId);
|
|
191
|
+
return void 0;
|
|
192
|
+
}
|
|
193
|
+
return entry;
|
|
194
|
+
}
|
|
195
|
+
delete(sessionId) {
|
|
196
|
+
return this.store.delete(sessionId);
|
|
197
|
+
}
|
|
198
|
+
cleanup() {
|
|
199
|
+
const now = Date.now();
|
|
200
|
+
for (const [sessionId, entry] of this.store) {
|
|
201
|
+
if (now - entry.authenticatedAt > this.maxAgeMs) {
|
|
202
|
+
this.store.delete(sessionId);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
get size() {
|
|
207
|
+
return this.store.size;
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
// src/tools/authenticate.ts
|
|
212
|
+
import { verifyPresentation } from "credat";
|
|
213
|
+
var STDIO_SESSION_KEY2 = "__stdio__";
|
|
214
|
+
function createAuthenticateHandler(config, challengeStore, sessionStore) {
|
|
215
|
+
return async (args, extra) => {
|
|
216
|
+
const { presentation } = args;
|
|
217
|
+
const sessionId = extra.sessionId ?? STDIO_SESSION_KEY2;
|
|
218
|
+
const stored = challengeStore.consume(presentation.nonce);
|
|
219
|
+
if (!stored) {
|
|
220
|
+
return authError(
|
|
221
|
+
"Unknown or expired challenge nonce. Request a new challenge.",
|
|
222
|
+
McpAuthErrorCodes.NOT_AUTHENTICATED
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
if (stored.sessionId !== sessionId) {
|
|
226
|
+
return authError(
|
|
227
|
+
"Challenge was issued to a different session.",
|
|
228
|
+
McpAuthErrorCodes.SESSION_MISMATCH
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
let agentPublicKey;
|
|
232
|
+
if (config.agentPublicKey) {
|
|
233
|
+
agentPublicKey = config.agentPublicKey;
|
|
234
|
+
} else if (config.resolveAgentKey) {
|
|
235
|
+
try {
|
|
236
|
+
agentPublicKey = await config.resolveAgentKey(presentation.from);
|
|
237
|
+
} catch (err) {
|
|
238
|
+
const message = err instanceof Error ? err.message : "Unknown error";
|
|
239
|
+
return authError(
|
|
240
|
+
`Failed to resolve agent public key for ${presentation.from}: ${message}`,
|
|
241
|
+
McpAuthErrorCodes.CONFIGURATION_ERROR
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
} else {
|
|
245
|
+
return authError(
|
|
246
|
+
"No agentPublicKey configured and no resolveAgentKey callback provided.",
|
|
247
|
+
McpAuthErrorCodes.CONFIGURATION_ERROR
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
const result = await verifyPresentation(presentation, {
|
|
251
|
+
challenge: stored.challenge,
|
|
252
|
+
ownerPublicKey: config.ownerPublicKey,
|
|
253
|
+
agentPublicKey,
|
|
254
|
+
challengeMaxAgeMs: config.challengeMaxAgeMs
|
|
255
|
+
});
|
|
256
|
+
if (!result.valid) {
|
|
257
|
+
const details = result.errors.map((e) => `${e.code}: ${e.message}`);
|
|
258
|
+
return authError(
|
|
259
|
+
"Authentication failed.",
|
|
260
|
+
result.errors[0]?.code ?? "HANDSHAKE_VERIFICATION_FAILED",
|
|
261
|
+
details
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
sessionStore.set(sessionId, {
|
|
265
|
+
delegationResult: result,
|
|
266
|
+
authenticatedAt: Date.now()
|
|
267
|
+
});
|
|
268
|
+
return {
|
|
269
|
+
content: [
|
|
270
|
+
{
|
|
271
|
+
type: "text",
|
|
272
|
+
text: JSON.stringify({
|
|
273
|
+
authenticated: true,
|
|
274
|
+
agent: result.agent,
|
|
275
|
+
scopes: result.scopes
|
|
276
|
+
})
|
|
277
|
+
}
|
|
278
|
+
]
|
|
279
|
+
};
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// src/tools/challenge.ts
|
|
284
|
+
import { createChallenge } from "credat";
|
|
285
|
+
var STDIO_SESSION_KEY3 = "__stdio__";
|
|
286
|
+
function createChallengeHandler(serverDid, challengeStore) {
|
|
287
|
+
return (extra) => {
|
|
288
|
+
const challenge = createChallenge({ from: serverDid });
|
|
289
|
+
const sessionId = extra.sessionId ?? STDIO_SESSION_KEY3;
|
|
290
|
+
challengeStore.set(challenge.nonce, challenge, sessionId);
|
|
291
|
+
return {
|
|
292
|
+
content: [{ type: "text", text: JSON.stringify(challenge) }]
|
|
293
|
+
};
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// src/auth.ts
|
|
298
|
+
var DEFAULT_CHALLENGE_MAX_AGE_MS = 5 * 60 * 1e3;
|
|
299
|
+
var DEFAULT_SESSION_MAX_AGE_MS = 60 * 60 * 1e3;
|
|
300
|
+
var DEFAULT_TOOL_PREFIX = "credat";
|
|
301
|
+
var STDIO_SESSION_KEY4 = "__stdio__";
|
|
302
|
+
var CredatAuth = class {
|
|
303
|
+
config;
|
|
304
|
+
challengeStore;
|
|
305
|
+
sessionStore;
|
|
306
|
+
protectFn;
|
|
307
|
+
constructor(options) {
|
|
308
|
+
if (!options.serverDid) {
|
|
309
|
+
throw new Error("CredatAuth: serverDid is required");
|
|
310
|
+
}
|
|
311
|
+
if (!options.ownerPublicKey || options.ownerPublicKey.length === 0) {
|
|
312
|
+
throw new Error("CredatAuth: ownerPublicKey is required");
|
|
313
|
+
}
|
|
314
|
+
this.config = {
|
|
315
|
+
serverDid: options.serverDid,
|
|
316
|
+
ownerPublicKey: options.ownerPublicKey,
|
|
317
|
+
agentPublicKey: options.agentPublicKey,
|
|
318
|
+
resolveAgentKey: options.resolveAgentKey,
|
|
319
|
+
challengeMaxAgeMs: options.challengeMaxAgeMs ?? DEFAULT_CHALLENGE_MAX_AGE_MS,
|
|
320
|
+
sessionMaxAgeMs: options.sessionMaxAgeMs ?? DEFAULT_SESSION_MAX_AGE_MS,
|
|
321
|
+
toolPrefix: options.toolPrefix ?? DEFAULT_TOOL_PREFIX
|
|
322
|
+
};
|
|
323
|
+
this.challengeStore = new ChallengeStore(this.config.challengeMaxAgeMs);
|
|
324
|
+
this.sessionStore = new SessionStore(this.config.sessionMaxAgeMs);
|
|
325
|
+
this.protectFn = createProtect(this.sessionStore);
|
|
326
|
+
}
|
|
327
|
+
/** Register the credat:challenge and credat:authenticate tools on the server */
|
|
328
|
+
install(server) {
|
|
329
|
+
const prefix = this.config.toolPrefix;
|
|
330
|
+
const challengeHandler = createChallengeHandler(this.config.serverDid, this.challengeStore);
|
|
331
|
+
const authenticateHandler = createAuthenticateHandler(
|
|
332
|
+
this.config,
|
|
333
|
+
this.challengeStore,
|
|
334
|
+
this.sessionStore
|
|
335
|
+
);
|
|
336
|
+
server.registerTool(
|
|
337
|
+
`${prefix}:challenge`,
|
|
338
|
+
{
|
|
339
|
+
description: "Request an authentication challenge. Returns a nonce that must be signed with your delegation credential."
|
|
340
|
+
},
|
|
341
|
+
(extra) => challengeHandler(extra)
|
|
342
|
+
);
|
|
343
|
+
server.registerTool(
|
|
344
|
+
`${prefix}:authenticate`,
|
|
345
|
+
{
|
|
346
|
+
description: "Present your signed credentials to authenticate. Requires a presentation object containing delegation proof and signed nonce.",
|
|
347
|
+
inputSchema: z.object({
|
|
348
|
+
presentation: z.object({
|
|
349
|
+
type: z.literal("credat:presentation"),
|
|
350
|
+
delegation: z.string(),
|
|
351
|
+
nonce: z.string(),
|
|
352
|
+
proof: z.string(),
|
|
353
|
+
from: z.string()
|
|
354
|
+
})
|
|
355
|
+
})
|
|
356
|
+
},
|
|
357
|
+
(args, extra) => authenticateHandler(
|
|
358
|
+
args,
|
|
359
|
+
extra
|
|
360
|
+
)
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
/** Wrap a tool handler to require authentication + optional scope/constraint checks */
|
|
364
|
+
protect(options, handler) {
|
|
365
|
+
return this.protectFn(options, handler);
|
|
366
|
+
}
|
|
367
|
+
/** Check if a session is currently authenticated */
|
|
368
|
+
isAuthenticated(sessionId) {
|
|
369
|
+
const key = sessionId ?? STDIO_SESSION_KEY4;
|
|
370
|
+
return this.sessionStore.get(key) !== void 0;
|
|
371
|
+
}
|
|
372
|
+
/** Get the auth result for a session */
|
|
373
|
+
getSessionAuth(sessionId) {
|
|
374
|
+
const key = sessionId ?? STDIO_SESSION_KEY4;
|
|
375
|
+
return this.sessionStore.get(key);
|
|
376
|
+
}
|
|
377
|
+
/** Revoke a session, forcing re-authentication */
|
|
378
|
+
revokeSession(sessionId) {
|
|
379
|
+
const key = sessionId ?? STDIO_SESSION_KEY4;
|
|
380
|
+
this.sessionStore.delete(key);
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
export {
|
|
384
|
+
ChallengeStore,
|
|
385
|
+
CredatAuth,
|
|
386
|
+
McpAuthErrorCodes,
|
|
387
|
+
SessionStore,
|
|
388
|
+
authError,
|
|
389
|
+
constraintError,
|
|
390
|
+
scopeError
|
|
391
|
+
};
|
|
392
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/auth.ts","../src/protect.ts","../src/constraints.ts","../src/errors.ts","../src/session.ts","../src/tools/authenticate.ts","../src/tools/challenge.ts"],"sourcesContent":["import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport { z } from \"zod\";\nimport { createProtect } from \"./protect.js\";\nimport { ChallengeStore, SessionStore } from \"./session.js\";\nimport { createAuthenticateHandler } from \"./tools/authenticate.js\";\nimport { createChallengeHandler } from \"./tools/challenge.js\";\nimport type {\n\tAuthContext,\n\tCredatAuthOptions,\n\tProtectOptions,\n\tSessionAuth,\n\tToolExtra,\n} from \"./types.js\";\n\nconst DEFAULT_CHALLENGE_MAX_AGE_MS = 5 * 60 * 1000; // 5 minutes\nconst DEFAULT_SESSION_MAX_AGE_MS = 60 * 60 * 1000; // 1 hour\nconst DEFAULT_TOOL_PREFIX = \"credat\";\n\nconst STDIO_SESSION_KEY = \"__stdio__\";\n\ntype ProtectedHandler<TArgs> = (\n\targs: TArgs,\n\textra: ToolExtra & { auth: AuthContext },\n) => CallToolResult | Promise<CallToolResult>;\n\nexport class CredatAuth {\n\tprivate readonly config: Required<\n\t\tPick<\n\t\t\tCredatAuthOptions,\n\t\t\t\"serverDid\" | \"ownerPublicKey\" | \"challengeMaxAgeMs\" | \"sessionMaxAgeMs\" | \"toolPrefix\"\n\t\t>\n\t> &\n\t\tPick<CredatAuthOptions, \"agentPublicKey\" | \"resolveAgentKey\">;\n\n\tprivate readonly challengeStore: ChallengeStore;\n\tprivate readonly sessionStore: SessionStore;\n\tprivate readonly protectFn: ReturnType<typeof createProtect>;\n\n\tconstructor(options: CredatAuthOptions) {\n\t\tif (!options.serverDid) {\n\t\t\tthrow new Error(\"CredatAuth: serverDid is required\");\n\t\t}\n\t\tif (!options.ownerPublicKey || options.ownerPublicKey.length === 0) {\n\t\t\tthrow new Error(\"CredatAuth: ownerPublicKey is required\");\n\t\t}\n\n\t\tthis.config = {\n\t\t\tserverDid: options.serverDid,\n\t\t\townerPublicKey: options.ownerPublicKey,\n\t\t\tagentPublicKey: options.agentPublicKey,\n\t\t\tresolveAgentKey: options.resolveAgentKey,\n\t\t\tchallengeMaxAgeMs: options.challengeMaxAgeMs ?? DEFAULT_CHALLENGE_MAX_AGE_MS,\n\t\t\tsessionMaxAgeMs: options.sessionMaxAgeMs ?? DEFAULT_SESSION_MAX_AGE_MS,\n\t\t\ttoolPrefix: options.toolPrefix ?? DEFAULT_TOOL_PREFIX,\n\t\t};\n\n\t\tthis.challengeStore = new ChallengeStore(this.config.challengeMaxAgeMs);\n\t\tthis.sessionStore = new SessionStore(this.config.sessionMaxAgeMs);\n\t\tthis.protectFn = createProtect(this.sessionStore);\n\t}\n\n\t/** Register the credat:challenge and credat:authenticate tools on the server */\n\tinstall(server: McpServer): void {\n\t\tconst prefix = this.config.toolPrefix;\n\n\t\tconst challengeHandler = createChallengeHandler(this.config.serverDid, this.challengeStore);\n\n\t\tconst authenticateHandler = createAuthenticateHandler(\n\t\t\tthis.config,\n\t\t\tthis.challengeStore,\n\t\t\tthis.sessionStore,\n\t\t);\n\n\t\t// Register challenge tool (no input — callback receives just extra)\n\t\tserver.registerTool(\n\t\t\t`${prefix}:challenge`,\n\t\t\t{\n\t\t\t\tdescription:\n\t\t\t\t\t\"Request an authentication challenge. Returns a nonce that must be signed with your delegation credential.\",\n\t\t\t},\n\t\t\t(extra) => challengeHandler(extra as ToolExtra),\n\t\t);\n\n\t\t// Register authenticate tool\n\t\tserver.registerTool(\n\t\t\t`${prefix}:authenticate`,\n\t\t\t{\n\t\t\t\tdescription:\n\t\t\t\t\t\"Present your signed credentials to authenticate. Requires a presentation object containing delegation proof and signed nonce.\",\n\t\t\t\tinputSchema: z.object({\n\t\t\t\t\tpresentation: z.object({\n\t\t\t\t\t\ttype: z.literal(\"credat:presentation\"),\n\t\t\t\t\t\tdelegation: z.string(),\n\t\t\t\t\t\tnonce: z.string(),\n\t\t\t\t\t\tproof: z.string(),\n\t\t\t\t\t\tfrom: z.string(),\n\t\t\t\t\t}),\n\t\t\t\t}),\n\t\t\t},\n\t\t\t(args, extra) =>\n\t\t\t\tauthenticateHandler(\n\t\t\t\t\targs as {\n\t\t\t\t\t\tpresentation: {\n\t\t\t\t\t\t\ttype: \"credat:presentation\";\n\t\t\t\t\t\t\tdelegation: string;\n\t\t\t\t\t\t\tnonce: string;\n\t\t\t\t\t\t\tproof: string;\n\t\t\t\t\t\t\tfrom: string;\n\t\t\t\t\t\t};\n\t\t\t\t\t},\n\t\t\t\t\textra as ToolExtra,\n\t\t\t\t),\n\t\t);\n\t}\n\n\t/** Wrap a tool handler to require authentication + optional scope/constraint checks */\n\tprotect<TArgs extends Record<string, unknown>>(\n\t\toptions: ProtectOptions,\n\t\thandler: ProtectedHandler<TArgs>,\n\t) {\n\t\treturn this.protectFn(options, handler);\n\t}\n\n\t/** Check if a session is currently authenticated */\n\tisAuthenticated(sessionId?: string): boolean {\n\t\tconst key = sessionId ?? STDIO_SESSION_KEY;\n\t\treturn this.sessionStore.get(key) !== undefined;\n\t}\n\n\t/** Get the auth result for a session */\n\tgetSessionAuth(sessionId?: string): SessionAuth | undefined {\n\t\tconst key = sessionId ?? STDIO_SESSION_KEY;\n\t\treturn this.sessionStore.get(key);\n\t}\n\n\t/** Revoke a session, forcing re-authentication */\n\trevokeSession(sessionId?: string): void {\n\t\tconst key = sessionId ?? STDIO_SESSION_KEY;\n\t\tthis.sessionStore.delete(key);\n\t}\n}\n","import type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport { hasAllScopes, hasAnyScope } from \"credat\";\nimport { validateConstraints } from \"./constraints.js\";\nimport { authError, constraintError, McpAuthErrorCodes, scopeError } from \"./errors.js\";\nimport type { SessionStore } from \"./session.js\";\nimport type { AuthContext, ProtectOptions, ToolExtra } from \"./types.js\";\n\nconst STDIO_SESSION_KEY = \"__stdio__\";\n\ntype ProtectedHandler<TArgs> = (\n\targs: TArgs,\n\textra: ToolExtra & { auth: AuthContext },\n) => CallToolResult | Promise<CallToolResult>;\n\ntype ToolHandler<TArgs> = (\n\targs: TArgs,\n\textra: ToolExtra,\n) => CallToolResult | Promise<CallToolResult>;\n\nexport function createProtect(sessionStore: SessionStore) {\n\treturn function protect<TArgs extends Record<string, unknown>>(\n\t\toptions: ProtectOptions,\n\t\thandler: ProtectedHandler<TArgs>,\n\t): ToolHandler<TArgs> {\n\t\treturn (args: TArgs, extra: ToolExtra) => {\n\t\t\tconst sessionId = extra.sessionId ?? STDIO_SESSION_KEY;\n\n\t\t\t// 1. Check authentication\n\t\t\tconst session = sessionStore.get(sessionId);\n\t\t\tif (!session) {\n\t\t\t\treturn authError(\n\t\t\t\t\t\"Not authenticated. Call the credat:challenge tool to begin authentication.\",\n\t\t\t\t\tMcpAuthErrorCodes.NOT_AUTHENTICATED,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tconst { delegationResult } = session;\n\n\t\t\t// 2. Check required scopes (ALL)\n\t\t\tif (options.scopes && options.scopes.length > 0) {\n\t\t\t\tif (!hasAllScopes(delegationResult, options.scopes)) {\n\t\t\t\t\treturn scopeError(options.scopes, delegationResult.scopes);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// 3. Check required scopes (ANY)\n\t\t\tif (options.anyScope && options.anyScope.length > 0) {\n\t\t\t\tif (!hasAnyScope(delegationResult, options.anyScope)) {\n\t\t\t\t\treturn scopeError(options.anyScope, delegationResult.scopes);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// 4. Validate constraints\n\t\t\tif (options.constraintContext) {\n\t\t\t\tconst context =\n\t\t\t\t\ttypeof options.constraintContext === \"function\"\n\t\t\t\t\t\t? options.constraintContext(args as Record<string, unknown>)\n\t\t\t\t\t\t: options.constraintContext;\n\n\t\t\t\tconst violations = validateConstraints(delegationResult.constraints, context);\n\t\t\t\tif (violations.length > 0) {\n\t\t\t\t\treturn constraintError(violations);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// 5. Build auth context and call handler\n\t\t\tconst authContext: AuthContext = {\n\t\t\t\tagentDid: delegationResult.agent,\n\t\t\t\townerDid: delegationResult.owner,\n\t\t\t\tscopes: delegationResult.scopes,\n\t\t\t\tconstraints: delegationResult.constraints,\n\t\t\t};\n\n\t\t\tconst augmentedExtra = Object.assign({}, extra, {\n\t\t\t\tauth: authContext,\n\t\t\t});\n\n\t\t\treturn handler(args, augmentedExtra);\n\t\t};\n\t};\n}\n","import type { DelegationConstraints } from \"credat\";\nimport type { ConstraintContext, ConstraintViolation } from \"./types.js\";\n\nexport function validateConstraints(\n\tconstraints: DelegationConstraints | undefined,\n\tcontext: ConstraintContext,\n): ConstraintViolation[] {\n\tif (!constraints) return [];\n\n\tconst violations: ConstraintViolation[] = [];\n\n\tif (constraints.maxTransactionValue != null && context.transactionValue != null) {\n\t\tif (context.transactionValue > constraints.maxTransactionValue) {\n\t\t\tviolations.push({\n\t\t\t\tconstraint: \"maxTransactionValue\",\n\t\t\t\tmessage: `${context.transactionValue} exceeds max ${constraints.maxTransactionValue}`,\n\t\t\t});\n\t\t}\n\t}\n\n\tif (constraints.allowedDomains != null && context.domain != null) {\n\t\tif (!constraints.allowedDomains.includes(context.domain)) {\n\t\t\tviolations.push({\n\t\t\t\tconstraint: \"allowedDomains\",\n\t\t\t\tmessage: `Domain \"${context.domain}\" is not in allowed list: ${constraints.allowedDomains.join(\", \")}`,\n\t\t\t});\n\t\t}\n\t}\n\n\tif (constraints.rateLimit != null && typeof context.rateLimit === \"number\") {\n\t\tif (context.rateLimit > constraints.rateLimit) {\n\t\t\tviolations.push({\n\t\t\t\tconstraint: \"rateLimit\",\n\t\t\t\tmessage: `Rate ${context.rateLimit} exceeds limit ${constraints.rateLimit}`,\n\t\t\t});\n\t\t}\n\t}\n\n\treturn violations;\n}\n","import type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport type { AuthErrorPayload, ConstraintViolation } from \"./types.js\";\n\n// ── Error Codes ──\n\nexport const McpAuthErrorCodes = {\n\tNOT_AUTHENTICATED: \"NOT_AUTHENTICATED\",\n\tSESSION_EXPIRED: \"SESSION_EXPIRED\",\n\tSESSION_MISMATCH: \"SESSION_MISMATCH\",\n\tINSUFFICIENT_SCOPES: \"INSUFFICIENT_SCOPES\",\n\tCONSTRAINT_VIOLATION: \"CONSTRAINT_VIOLATION\",\n\tCONFIGURATION_ERROR: \"CONFIGURATION_ERROR\",\n} as const;\n\nexport type McpAuthErrorCode = (typeof McpAuthErrorCodes)[keyof typeof McpAuthErrorCodes];\n\n// ── Error Response Builders ──\n\nexport function authError(message: string, code: string, details?: string[]): CallToolResult {\n\tconst payload: AuthErrorPayload = { error: message, code };\n\tif (details && details.length > 0) {\n\t\tpayload.details = details;\n\t}\n\treturn {\n\t\tcontent: [{ type: \"text\", text: JSON.stringify(payload) }],\n\t\tisError: true,\n\t};\n}\n\nexport function scopeError(required: string[], actual: string[]): CallToolResult {\n\tconst missing = required.filter((s) => !actual.includes(s));\n\treturn authError(\n\t\t`Insufficient scopes. Missing: ${missing.join(\", \")}`,\n\t\tMcpAuthErrorCodes.INSUFFICIENT_SCOPES,\n\t\t[`required: ${required.join(\", \")}`, `granted: ${actual.join(\", \")}`],\n\t);\n}\n\nexport function constraintError(violations: ConstraintViolation[]): CallToolResult {\n\treturn authError(\n\t\t`Constraint violation: ${violations.map((v) => v.message).join(\"; \")}`,\n\t\tMcpAuthErrorCodes.CONSTRAINT_VIOLATION,\n\t\tviolations.map((v) => `${v.constraint}: ${v.message}`),\n\t);\n}\n","import type { ChallengeMessage } from \"credat\";\nimport type { SessionAuth, StoredChallenge } from \"./types.js\";\n\nconst DEFAULT_MAX_SIZE = 1000;\nconst CLEANUP_THRESHOLD = 100;\n\n// ── Challenge Store ──\n\nexport class ChallengeStore {\n\tprivate store = new Map<string, StoredChallenge>();\n\tprivate readonly maxAgeMs: number;\n\tprivate readonly maxSize: number;\n\tprivate insertsSinceCleanup = 0;\n\n\tconstructor(maxAgeMs: number, maxSize = DEFAULT_MAX_SIZE) {\n\t\tthis.maxAgeMs = maxAgeMs;\n\t\tthis.maxSize = maxSize;\n\t}\n\n\tset(nonce: string, challenge: ChallengeMessage, sessionId: string): void {\n\t\tthis.store.set(nonce, {\n\t\t\tchallenge,\n\t\t\tsessionId,\n\t\t\tcreatedAt: Date.now(),\n\t\t});\n\n\t\tthis.insertsSinceCleanup++;\n\t\tif (this.insertsSinceCleanup >= CLEANUP_THRESHOLD || this.store.size > this.maxSize) {\n\t\t\tthis.cleanup();\n\t\t\tthis.insertsSinceCleanup = 0;\n\t\t}\n\t}\n\n\t/** Consume a challenge (single-use). Returns and deletes. */\n\tconsume(nonce: string): StoredChallenge | undefined {\n\t\tconst entry = this.store.get(nonce);\n\t\tif (!entry) return undefined;\n\n\t\tthis.store.delete(nonce);\n\n\t\tif (Date.now() - entry.createdAt > this.maxAgeMs) {\n\t\t\treturn undefined;\n\t\t}\n\n\t\treturn entry;\n\t}\n\n\tcleanup(): void {\n\t\tconst now = Date.now();\n\t\tfor (const [nonce, entry] of this.store) {\n\t\t\tif (now - entry.createdAt > this.maxAgeMs) {\n\t\t\t\tthis.store.delete(nonce);\n\t\t\t}\n\t\t}\n\n\t\t// Evict oldest if still over max size\n\t\tif (this.store.size > this.maxSize) {\n\t\t\tconst entries = [...this.store.entries()].sort((a, b) => a[1].createdAt - b[1].createdAt);\n\t\t\tconst toRemove = entries.slice(0, this.store.size - this.maxSize);\n\t\t\tfor (const [nonce] of toRemove) {\n\t\t\t\tthis.store.delete(nonce);\n\t\t\t}\n\t\t}\n\t}\n\n\tget size(): number {\n\t\treturn this.store.size;\n\t}\n}\n\n// ── Session Store ──\n\nexport class SessionStore {\n\tprivate store = new Map<string, SessionAuth>();\n\tprivate readonly maxAgeMs: number;\n\tprivate insertsSinceCleanup = 0;\n\n\tconstructor(maxAgeMs: number) {\n\t\tthis.maxAgeMs = maxAgeMs;\n\t}\n\n\tset(sessionId: string, auth: SessionAuth): void {\n\t\tthis.store.set(sessionId, auth);\n\n\t\tthis.insertsSinceCleanup++;\n\t\tif (this.insertsSinceCleanup >= CLEANUP_THRESHOLD) {\n\t\t\tthis.cleanup();\n\t\t\tthis.insertsSinceCleanup = 0;\n\t\t}\n\t}\n\n\tget(sessionId: string): SessionAuth | undefined {\n\t\tconst entry = this.store.get(sessionId);\n\t\tif (!entry) return undefined;\n\n\t\tif (Date.now() - entry.authenticatedAt > this.maxAgeMs) {\n\t\t\tthis.store.delete(sessionId);\n\t\t\treturn undefined;\n\t\t}\n\n\t\treturn entry;\n\t}\n\n\tdelete(sessionId: string): boolean {\n\t\treturn this.store.delete(sessionId);\n\t}\n\n\tcleanup(): void {\n\t\tconst now = Date.now();\n\t\tfor (const [sessionId, entry] of this.store) {\n\t\t\tif (now - entry.authenticatedAt > this.maxAgeMs) {\n\t\t\t\tthis.store.delete(sessionId);\n\t\t\t}\n\t\t}\n\t}\n\n\tget size(): number {\n\t\treturn this.store.size;\n\t}\n}\n","import type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport type { PresentationMessage } from \"credat\";\nimport { verifyPresentation } from \"credat\";\nimport { authError, McpAuthErrorCodes } from \"../errors.js\";\nimport type { ChallengeStore, SessionStore } from \"../session.js\";\nimport type { CredatAuthOptions, ToolExtra } from \"../types.js\";\n\nconst STDIO_SESSION_KEY = \"__stdio__\";\n\nexport function createAuthenticateHandler(\n\tconfig: CredatAuthOptions,\n\tchallengeStore: ChallengeStore,\n\tsessionStore: SessionStore,\n) {\n\treturn async (\n\t\targs: { presentation: PresentationMessage },\n\t\textra: ToolExtra,\n\t): Promise<CallToolResult> => {\n\t\tconst { presentation } = args;\n\t\tconst sessionId = extra.sessionId ?? STDIO_SESSION_KEY;\n\n\t\t// 1. Consume challenge (single-use nonce)\n\t\tconst stored = challengeStore.consume(presentation.nonce);\n\t\tif (!stored) {\n\t\t\treturn authError(\n\t\t\t\t\"Unknown or expired challenge nonce. Request a new challenge.\",\n\t\t\t\tMcpAuthErrorCodes.NOT_AUTHENTICATED,\n\t\t\t);\n\t\t}\n\n\t\t// 2. Verify session binding\n\t\tif (stored.sessionId !== sessionId) {\n\t\t\treturn authError(\n\t\t\t\t\"Challenge was issued to a different session.\",\n\t\t\t\tMcpAuthErrorCodes.SESSION_MISMATCH,\n\t\t\t);\n\t\t}\n\n\t\t// 3. Resolve agent public key\n\t\tlet agentPublicKey: Uint8Array;\n\t\tif (config.agentPublicKey) {\n\t\t\tagentPublicKey = config.agentPublicKey;\n\t\t} else if (config.resolveAgentKey) {\n\t\t\ttry {\n\t\t\t\tagentPublicKey = await config.resolveAgentKey(presentation.from);\n\t\t\t} catch (err) {\n\t\t\t\tconst message = err instanceof Error ? err.message : \"Unknown error\";\n\t\t\t\treturn authError(\n\t\t\t\t\t`Failed to resolve agent public key for ${presentation.from}: ${message}`,\n\t\t\t\t\tMcpAuthErrorCodes.CONFIGURATION_ERROR,\n\t\t\t\t);\n\t\t\t}\n\t\t} else {\n\t\t\treturn authError(\n\t\t\t\t\"No agentPublicKey configured and no resolveAgentKey callback provided.\",\n\t\t\t\tMcpAuthErrorCodes.CONFIGURATION_ERROR,\n\t\t\t);\n\t\t}\n\n\t\t// 4. Verify presentation\n\t\tconst result = await verifyPresentation(presentation, {\n\t\t\tchallenge: stored.challenge,\n\t\t\townerPublicKey: config.ownerPublicKey,\n\t\t\tagentPublicKey,\n\t\t\tchallengeMaxAgeMs: config.challengeMaxAgeMs,\n\t\t});\n\n\t\tif (!result.valid) {\n\t\t\tconst details = result.errors.map((e) => `${e.code}: ${e.message}`);\n\t\t\treturn authError(\n\t\t\t\t\"Authentication failed.\",\n\t\t\t\tresult.errors[0]?.code ?? \"HANDSHAKE_VERIFICATION_FAILED\",\n\t\t\t\tdetails,\n\t\t\t);\n\t\t}\n\n\t\t// 5. Store session auth\n\t\tsessionStore.set(sessionId, {\n\t\t\tdelegationResult: result,\n\t\t\tauthenticatedAt: Date.now(),\n\t\t});\n\n\t\treturn {\n\t\t\tcontent: [\n\t\t\t\t{\n\t\t\t\t\ttype: \"text\",\n\t\t\t\t\ttext: JSON.stringify({\n\t\t\t\t\t\tauthenticated: true,\n\t\t\t\t\t\tagent: result.agent,\n\t\t\t\t\t\tscopes: result.scopes,\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\t};\n}\n","import type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\nimport { createChallenge } from \"credat\";\nimport type { ChallengeStore } from \"../session.js\";\nimport type { ToolExtra } from \"../types.js\";\n\nconst STDIO_SESSION_KEY = \"__stdio__\";\n\nexport function createChallengeHandler(serverDid: string, challengeStore: ChallengeStore) {\n\treturn (extra: ToolExtra): CallToolResult => {\n\t\tconst challenge = createChallenge({ from: serverDid });\n\t\tconst sessionId = extra.sessionId ?? STDIO_SESSION_KEY;\n\n\t\tchallengeStore.set(challenge.nonce, challenge, sessionId);\n\n\t\treturn {\n\t\t\tcontent: [{ type: \"text\", text: JSON.stringify(challenge) }],\n\t\t};\n\t};\n}\n"],"mappings":"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","names":["STDIO_SESSION_KEY","STDIO_SESSION_KEY","STDIO_SESSION_KEY"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@credat/mcp",
|
|
3
|
+
"version": "0.1.2-alpha.1",
|
|
4
|
+
"description": "Trust & authentication layer for MCP servers — verify agent identity, delegated permissions, and scopes using Credat",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"require": {
|
|
16
|
+
"types": "./dist/index.d.cts",
|
|
17
|
+
"default": "./dist/index.cjs"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"README.md",
|
|
24
|
+
"LICENSE"
|
|
25
|
+
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsup",
|
|
28
|
+
"dev": "tsup --watch",
|
|
29
|
+
"test": "vitest run",
|
|
30
|
+
"test:watch": "vitest",
|
|
31
|
+
"typecheck": "tsc --noEmit",
|
|
32
|
+
"lint": "biome check src/",
|
|
33
|
+
"lint:fix": "biome check --write src/",
|
|
34
|
+
"prepublishOnly": "npm run build"
|
|
35
|
+
},
|
|
36
|
+
"keywords": [
|
|
37
|
+
"mcp",
|
|
38
|
+
"model-context-protocol",
|
|
39
|
+
"authentication",
|
|
40
|
+
"authorization",
|
|
41
|
+
"trust",
|
|
42
|
+
"agent",
|
|
43
|
+
"delegation",
|
|
44
|
+
"verifiable-credentials",
|
|
45
|
+
"did",
|
|
46
|
+
"credat",
|
|
47
|
+
"ai-agent",
|
|
48
|
+
"middleware"
|
|
49
|
+
],
|
|
50
|
+
"author": "Maxime Mansiet",
|
|
51
|
+
"license": "Apache-2.0",
|
|
52
|
+
"repository": {
|
|
53
|
+
"type": "git",
|
|
54
|
+
"url": "git+https://github.com/credat/credat-mcp.git"
|
|
55
|
+
},
|
|
56
|
+
"homepage": "https://github.com/credat/credat-mcp#readme",
|
|
57
|
+
"bugs": {
|
|
58
|
+
"url": "https://github.com/credat/credat-mcp/issues"
|
|
59
|
+
},
|
|
60
|
+
"engines": {
|
|
61
|
+
"node": ">=22.0.0"
|
|
62
|
+
},
|
|
63
|
+
"peerDependencies": {
|
|
64
|
+
"credat": "^0.2.0-alpha.1",
|
|
65
|
+
"@modelcontextprotocol/sdk": "^1.27.0",
|
|
66
|
+
"zod": "^3.25.0"
|
|
67
|
+
},
|
|
68
|
+
"devDependencies": {
|
|
69
|
+
"@biomejs/biome": "^2.4.0",
|
|
70
|
+
"@modelcontextprotocol/sdk": "^1.27.0",
|
|
71
|
+
"@types/node": "^22.0.0",
|
|
72
|
+
"credat": "^0.2.0-alpha.1",
|
|
73
|
+
"tsup": "^8.5.1",
|
|
74
|
+
"typescript": "^5.9.3",
|
|
75
|
+
"vitest": "^4.0.18",
|
|
76
|
+
"zod": "^3.25.0"
|
|
77
|
+
}
|
|
78
|
+
}
|