fa-mcp-sdk 0.2.182 → 0.2.192
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/cli-template/.claude/agents/fa-mcp-sdk.md +158 -0
- package/cli-template/FA-MCP-SDK-DOC/00-FA-MCP-SDK-index.md +216 -0
- package/cli-template/FA-MCP-SDK-DOC/01-getting-started.md +209 -0
- package/cli-template/FA-MCP-SDK-DOC/02-tools-and-api.md +321 -0
- package/cli-template/FA-MCP-SDK-DOC/03-configuration.md +415 -0
- package/cli-template/FA-MCP-SDK-DOC/04-authentication.md +544 -0
- package/cli-template/FA-MCP-SDK-DOC/05-ad-authorization.md +476 -0
- package/cli-template/FA-MCP-SDK-DOC/06-utilities.md +394 -0
- package/cli-template/FA-MCP-SDK-DOC/07-testing-and-operations.md +171 -0
- package/dist/core/_types_/types.d.ts +0 -5
- package/dist/core/_types_/types.d.ts.map +1 -1
- package/dist/core/index.d.ts +2 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +2 -0
- package/dist/core/index.js.map +1 -1
- package/dist/core/web/home-api.js +1 -1
- package/dist/core/web/home-api.js.map +1 -1
- package/dist/core/web/openapi.d.ts +64 -0
- package/dist/core/web/openapi.d.ts.map +1 -0
- package/dist/core/web/openapi.js +235 -0
- package/dist/core/web/openapi.js.map +1 -0
- package/dist/core/web/server-http.d.ts.map +1 -1
- package/dist/core/web/server-http.js +11 -9
- package/dist/core/web/server-http.js.map +1 -1
- package/dist/core/web/static/home/index.html +4 -2
- package/dist/core/web/static/home/script.js +2 -2
- package/package.json +9 -12
- package/src/template/api/router.ts +66 -4
- package/src/template/start.ts +0 -5
- package/cli-template/FA-MCP-SDK.md +0 -2540
- package/src/template/api/swagger.ts +0 -167
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
# Authentication and Security
|
|
2
|
+
|
|
3
|
+
## Token-based Authentication
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
import {
|
|
7
|
+
ICheckTokenResult,
|
|
8
|
+
checkJwtToken,
|
|
9
|
+
generateToken
|
|
10
|
+
} from 'fa-mcp-sdk';
|
|
11
|
+
|
|
12
|
+
// Types used:
|
|
13
|
+
export interface ICheckTokenResult {
|
|
14
|
+
payload?: ITokenPayload, // Token payload with user data
|
|
15
|
+
errorReason?: string, // Error message if validation failed
|
|
16
|
+
isTokenDecrypted?: boolean, // Whether token was successfully decrypted
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ITokenPayload {
|
|
20
|
+
user: string, // Username
|
|
21
|
+
expire: number, // Expiration timestamp
|
|
22
|
+
[key: string]: any, // Additional payload data
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// checkJwtToken - validate token and return detailed result
|
|
26
|
+
// Function Signature:
|
|
27
|
+
const checkJwtToken = (arg: {
|
|
28
|
+
token: string,
|
|
29
|
+
expectedUser?: string,
|
|
30
|
+
expectedService?: string,
|
|
31
|
+
}): ICheckTokenResult {...}
|
|
32
|
+
|
|
33
|
+
// Example:
|
|
34
|
+
const tokenResult = checkJwtToken({
|
|
35
|
+
token: 'user_provided_token',
|
|
36
|
+
expectedUser: 'john_doe',
|
|
37
|
+
expectedService: 'my-mcp-server'
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
if (!tokenResult.errorReason) {
|
|
41
|
+
console.log('Valid token for user:', tokenResult.payload?.user);
|
|
42
|
+
} else {
|
|
43
|
+
console.log('Auth failed:', tokenResult.errorReason);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// generateToken - create JWT token
|
|
47
|
+
// Function Signature:
|
|
48
|
+
const generateToken = (user: string, liveTimeSec: number, payload?: any): string {...}
|
|
49
|
+
|
|
50
|
+
// Example:
|
|
51
|
+
const token = generateToken('john_doe', 3600, { role: 'admin' }); // 1 hour token
|
|
52
|
+
|
|
53
|
+
// Deprecated: authByToken was replaced by createAuthMW universal middleware
|
|
54
|
+
// Use createAuthMW instead for all authentication scenarios:
|
|
55
|
+
|
|
56
|
+
// Example - Modern approach:
|
|
57
|
+
app.post('/api/secure', createAuthMW(), (req, res) => {
|
|
58
|
+
// User is authenticated, authInfo available on req
|
|
59
|
+
const authInfo = (req as any).authInfo;
|
|
60
|
+
res.json({
|
|
61
|
+
message: 'Access granted',
|
|
62
|
+
authType: authInfo?.authType,
|
|
63
|
+
username: authInfo?.username
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Test Authentication Headers
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
import { getAuthHeadersForTests } from 'fa-mcp-sdk';
|
|
72
|
+
|
|
73
|
+
// getAuthHeadersForTests - automatically generate authentication headers for testing
|
|
74
|
+
// Function Signature:
|
|
75
|
+
function getAuthHeadersForTests(): object {...}
|
|
76
|
+
|
|
77
|
+
// Determines authentication headers based on appConfig.webServer.auth configuration.
|
|
78
|
+
// Returns Authorization header using the first valid auth method found.
|
|
79
|
+
//
|
|
80
|
+
// Priority order (CPU-optimized, fastest first):
|
|
81
|
+
// 1. permanentServerTokens - if at least one token is defined
|
|
82
|
+
// 2. basic auth - if username AND password are both set
|
|
83
|
+
// 3. JWT token - if jwtToken.encryptKey is set, generates token on the fly
|
|
84
|
+
//
|
|
85
|
+
// Returns empty object if auth is not enabled or no valid method configured.
|
|
86
|
+
|
|
87
|
+
// Examples:
|
|
88
|
+
const headers = getAuthHeadersForTests();
|
|
89
|
+
|
|
90
|
+
// Use in fetch requests
|
|
91
|
+
const response = await fetch('http://localhost:3000/mcp', {
|
|
92
|
+
method: 'POST',
|
|
93
|
+
headers: {
|
|
94
|
+
'Content-Type': 'application/json',
|
|
95
|
+
...headers // Automatically adds Authorization header if auth is enabled
|
|
96
|
+
},
|
|
97
|
+
body: JSON.stringify(requestBody)
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// Use with test clients
|
|
101
|
+
import { McpHttpClient } from 'fa-mcp-sdk';
|
|
102
|
+
|
|
103
|
+
const client = new McpHttpClient('http://localhost:3000');
|
|
104
|
+
const authHeaders = getAuthHeadersForTests();
|
|
105
|
+
const result = await client.callTool('my_tool', { query: 'test' }, authHeaders);
|
|
106
|
+
|
|
107
|
+
// Return value examples based on configuration:
|
|
108
|
+
|
|
109
|
+
// If permanentServerTokens configured:
|
|
110
|
+
// { Authorization: 'Bearer server-token-1' }
|
|
111
|
+
|
|
112
|
+
// If basic auth configured:
|
|
113
|
+
// { Authorization: 'Basic YWRtaW46cGFzc3dvcmQ=' } // base64 of 'admin:password'
|
|
114
|
+
|
|
115
|
+
// If JWT encryptKey configured:
|
|
116
|
+
// { Authorization: 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...' }
|
|
117
|
+
|
|
118
|
+
// If auth.enabled = false or no valid method:
|
|
119
|
+
// {}
|
|
120
|
+
|
|
121
|
+
// Typical test setup:
|
|
122
|
+
import { getAuthHeadersForTests, appConfig } from 'fa-mcp-sdk';
|
|
123
|
+
|
|
124
|
+
describe('MCP Server Tests', () => {
|
|
125
|
+
const baseUrl = `http://localhost:${appConfig.webServer.port}`;
|
|
126
|
+
const authHeaders = getAuthHeadersForTests();
|
|
127
|
+
|
|
128
|
+
it('should call tool with authentication', async () => {
|
|
129
|
+
const response = await fetch(`${baseUrl}/mcp`, {
|
|
130
|
+
method: 'POST',
|
|
131
|
+
headers: {
|
|
132
|
+
'Content-Type': 'application/json',
|
|
133
|
+
...authHeaders
|
|
134
|
+
},
|
|
135
|
+
body: JSON.stringify({
|
|
136
|
+
jsonrpc: '2.0',
|
|
137
|
+
method: 'tools/call',
|
|
138
|
+
params: { name: 'my_tool', arguments: { query: 'test' } },
|
|
139
|
+
id: 1
|
|
140
|
+
})
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
expect(response.ok).toBe(true);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Token Generator Authorization Handler
|
|
149
|
+
|
|
150
|
+
The Token Generator admin page (`/admin/`) can be protected with an additional
|
|
151
|
+
custom authorization layer beyond the standard authentication. This allows you
|
|
152
|
+
to implement fine-grained access control, such as restricting access to specific
|
|
153
|
+
AD groups or roles.
|
|
154
|
+
|
|
155
|
+
### Types
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
import { TokenGenAuthHandler, TokenGenAuthInput, AuthResult } from 'fa-mcp-sdk';
|
|
159
|
+
|
|
160
|
+
// Input data passed to the authorization handler
|
|
161
|
+
interface TokenGenAuthInput {
|
|
162
|
+
user: string; // Username from authentication
|
|
163
|
+
domain?: string; // Domain (only for NTLM auth)
|
|
164
|
+
payload?: Record<string, any>; // JWT payload (only for jwtToken auth)
|
|
165
|
+
authType: 'jwtToken' | 'basic' | 'ntlm' | 'permanentServerTokens';
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Authorization handler function type
|
|
169
|
+
type TokenGenAuthHandler = (input: TokenGenAuthInput) => Promise<AuthResult> | AuthResult;
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Configuration
|
|
173
|
+
|
|
174
|
+
Add `tokenGenAuthHandler` to your `McpServerData` in `src/start.ts`:
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
import { initMcpServer, McpServerData, TokenGenAuthHandler, initADGroupChecker } from 'fa-mcp-sdk';
|
|
178
|
+
|
|
179
|
+
// Example 1: Restrict to specific AD groups (NTLM authentication)
|
|
180
|
+
const { isUserInGroup } = initADGroupChecker();
|
|
181
|
+
|
|
182
|
+
const tokenGenAuthHandler: TokenGenAuthHandler = async (input) => {
|
|
183
|
+
// Only check for NTLM-authenticated users
|
|
184
|
+
if (input.authType === 'ntlm') {
|
|
185
|
+
const isAdmin = await isUserInGroup(input.user, 'TokenGeneratorAdmins');
|
|
186
|
+
if (!isAdmin) {
|
|
187
|
+
return {
|
|
188
|
+
success: false,
|
|
189
|
+
error: `User ${input.user} is not authorized to access Token Generator`,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return { success: true, username: input.user };
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
// Example 2: Check JWT payload for specific claims
|
|
197
|
+
const tokenGenAuthHandler: TokenGenAuthHandler = async (input) => {
|
|
198
|
+
if (input.authType === 'jwtToken') {
|
|
199
|
+
const roles = input.payload?.roles || [];
|
|
200
|
+
if (!roles.includes('token-admin')) {
|
|
201
|
+
return {
|
|
202
|
+
success: false,
|
|
203
|
+
error: 'Missing required role: token-admin',
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return { success: true, username: input.user };
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
// Example 3: Simple whitelist check
|
|
211
|
+
const allowedUsers = ['admin', 'john.doe', 'jane.smith'];
|
|
212
|
+
|
|
213
|
+
const tokenGenAuthHandler: TokenGenAuthHandler = (input) => {
|
|
214
|
+
if (!allowedUsers.includes(input.user.toLowerCase())) {
|
|
215
|
+
return {
|
|
216
|
+
success: false,
|
|
217
|
+
error: `User ${input.user} is not in the allowed users list`,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
return { success: true, username: input.user };
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
// Use in McpServerData
|
|
224
|
+
const serverData: McpServerData = {
|
|
225
|
+
tools,
|
|
226
|
+
toolHandler: handleToolCall,
|
|
227
|
+
agentBrief: AGENT_BRIEF,
|
|
228
|
+
agentPrompt: AGENT_PROMPT,
|
|
229
|
+
|
|
230
|
+
// Add custom authorization for Token Generator
|
|
231
|
+
tokenGenAuthHandler,
|
|
232
|
+
|
|
233
|
+
// ... other configuration
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
await initMcpServer(serverData);
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Behavior
|
|
240
|
+
|
|
241
|
+
- **If `tokenGenAuthHandler` is not provided**: All authenticated users can access Token Generator
|
|
242
|
+
- **If handler returns `{ success: true }`**: User is authorized
|
|
243
|
+
- **If handler returns `{ success: false, error: '...' }`**: User receives 403 Forbidden with error message
|
|
244
|
+
- **Handler errors**: Caught and returned as 403 with error message
|
|
245
|
+
|
|
246
|
+
### Auth Type Input Details
|
|
247
|
+
|
|
248
|
+
| Auth Type | `user` | `domain` | `payload` |
|
|
249
|
+
|-----------|--------|----------|-----------|
|
|
250
|
+
| `ntlm` | NTLM username | NTLM domain | - |
|
|
251
|
+
| `basic` | Basic auth username | - | - |
|
|
252
|
+
| `jwtToken` | JWT `user` claim | - | Full JWT payload |
|
|
253
|
+
| `permanentServerTokens` | "Unknown" | - | - |
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## Multi-Authentication System
|
|
258
|
+
|
|
259
|
+
The FA-MCP-SDK supports a comprehensive multi-authentication system that allows multiple authentication methods to work together with CPU-optimized performance ordering.
|
|
260
|
+
|
|
261
|
+
### Types and Interfaces
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
import {
|
|
265
|
+
AuthType,
|
|
266
|
+
AuthResult,
|
|
267
|
+
AuthDetectionResult,
|
|
268
|
+
CustomAuthValidator,
|
|
269
|
+
checkMultiAuth,
|
|
270
|
+
detectAuthConfiguration,
|
|
271
|
+
logAuthConfiguration,
|
|
272
|
+
createAuthMW, // Universal authentication middleware
|
|
273
|
+
getMultiAuthError, // Programmatic authentication checking
|
|
274
|
+
} from 'fa-mcp-sdk';
|
|
275
|
+
|
|
276
|
+
// Authentication types in CPU priority order (low to high cost)
|
|
277
|
+
export type AuthType = 'permanentServerTokens' | 'jwtToken' | 'basic' | 'custom';
|
|
278
|
+
|
|
279
|
+
// Custom Authentication validator function (black box - receives full request)
|
|
280
|
+
export type CustomAuthValidator = (req: any) => Promise<AuthResult> | AuthResult;
|
|
281
|
+
|
|
282
|
+
// Authentication result interface
|
|
283
|
+
export interface AuthResult {
|
|
284
|
+
success: boolean;
|
|
285
|
+
error?: string;
|
|
286
|
+
authType?: AuthType;
|
|
287
|
+
username?: string;
|
|
288
|
+
isTokenDecrypted?: boolean; // only for JWT
|
|
289
|
+
payload?: any;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Authentication detection result
|
|
293
|
+
export interface AuthDetectionResult {
|
|
294
|
+
configured: AuthType[]; // Authentication types found in configuration
|
|
295
|
+
valid: AuthType[]; // Authentication types properly configured and ready
|
|
296
|
+
errors: Record<string, string[]>; // Configuration errors by auth type
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### Core Multi-Authentication Functions
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
// checkMultiAuth - validate using all configured authentication methods
|
|
304
|
+
// Function Signature:
|
|
305
|
+
async function checkMultiAuth(req: Request): Promise<AuthResult> {...}
|
|
306
|
+
|
|
307
|
+
// Example:
|
|
308
|
+
const result = await checkMultiAuth(req);
|
|
309
|
+
|
|
310
|
+
if (result.success) {
|
|
311
|
+
console.log(`Authenticated via ${result.authType} as ${result.username}`);
|
|
312
|
+
} else {
|
|
313
|
+
console.log('Authentication failed:', result.error);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// detectAuthConfiguration - analyze auth configuration
|
|
317
|
+
// Function Signature:
|
|
318
|
+
function detectAuthConfiguration(): AuthDetectionResult {...}
|
|
319
|
+
|
|
320
|
+
// Example:
|
|
321
|
+
const detection = detectAuthConfiguration();
|
|
322
|
+
console.log('Configured auth types:', detection.configured);
|
|
323
|
+
console.log('Valid auth types:', detection.valid);
|
|
324
|
+
console.log('Configuration errors:', detection.errors);
|
|
325
|
+
|
|
326
|
+
// logAuthConfiguration - log auth system status (debugging)
|
|
327
|
+
// Function Signature:
|
|
328
|
+
function logAuthConfiguration(): void {...}
|
|
329
|
+
|
|
330
|
+
// Example:
|
|
331
|
+
logAuthConfiguration();
|
|
332
|
+
// Output:
|
|
333
|
+
// Auth system configuration:
|
|
334
|
+
// - enabled: true
|
|
335
|
+
// - configured types: permanentServerTokens, basic
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### Multi-Authentication Middleware
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
import express from 'express';
|
|
342
|
+
import {
|
|
343
|
+
createAuthMW,
|
|
344
|
+
getMultiAuthError,
|
|
345
|
+
} from 'fa-mcp-sdk';
|
|
346
|
+
|
|
347
|
+
// Universal authentication middleware with flexible options
|
|
348
|
+
const app = express();
|
|
349
|
+
|
|
350
|
+
// Basic usage - handles all authentication scenarios automatically
|
|
351
|
+
const authMW = createAuthMW();
|
|
352
|
+
app.use('/api', authMW);
|
|
353
|
+
|
|
354
|
+
app.get('/api/protected', (req, res) => {
|
|
355
|
+
const authInfo = (req as any).authInfo;
|
|
356
|
+
res.json({
|
|
357
|
+
message: 'Access granted',
|
|
358
|
+
authType: authInfo?.authType,
|
|
359
|
+
username: authInfo?.username,
|
|
360
|
+
});
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
// Advanced usage with custom options
|
|
364
|
+
const customAuthMW = createAuthMW({
|
|
365
|
+
mcpPaths: ['/mcp', '/messages', '/sse', '/custom'], // Custom MCP paths
|
|
366
|
+
logConfig: true, // Force logging
|
|
367
|
+
});
|
|
368
|
+
app.use('/custom-endpoints', customAuthMW);
|
|
369
|
+
|
|
370
|
+
// createAuthMW - Universal authentication middleware
|
|
371
|
+
// Function Signature:
|
|
372
|
+
function createAuthMW(options?: {
|
|
373
|
+
mcpPaths?: string[]; // Paths to check for public MCP requests (default: ['/mcp', '/messages', '/sse'])
|
|
374
|
+
logConfig?: boolean; // Log auth configuration on first request (default: from LOG_AUTH_CONFIG env)
|
|
375
|
+
}): (req: Request, res: Response, next: NextFunction) => Promise<void>
|
|
376
|
+
|
|
377
|
+
// Features:
|
|
378
|
+
// ✅ Combines all authentication methods (standard + custom validator)
|
|
379
|
+
// ✅ Supports public MCP resources/prompts (requireAuth: false)
|
|
380
|
+
// ✅ Configurable MCP paths
|
|
381
|
+
// ✅ CPU-optimized authentication order
|
|
382
|
+
// ✅ Automatic auth method detection
|
|
383
|
+
// ✅ Request context enrichment (req.authInfo)
|
|
384
|
+
|
|
385
|
+
// getMultiAuthError - Programmatic authentication checking
|
|
386
|
+
// Function Signature:
|
|
387
|
+
async function getMultiAuthError(req: Request): Promise<{ code: number, message: string } | undefined>
|
|
388
|
+
|
|
389
|
+
// Returns error object if authentication failed, undefined if successful
|
|
390
|
+
// Uses checkMultiAuth internally - supports all authentication methods
|
|
391
|
+
|
|
392
|
+
// Example - Custom middleware with different auth levels
|
|
393
|
+
app.use('/api/custom', async (req, res, next) => {
|
|
394
|
+
if (req.path.startsWith('/api/custom/public')) {
|
|
395
|
+
return next(); // Public endpoints
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (req.path.startsWith('/api/custom/admin')) {
|
|
399
|
+
// Admin endpoints - require server tokens only
|
|
400
|
+
const token = (req.headers.authorization || '').replace(/^Bearer */, '');
|
|
401
|
+
if (appConfig.webServer.auth.permanentServerTokens.includes(token)) {
|
|
402
|
+
return next();
|
|
403
|
+
}
|
|
404
|
+
return res.status(403).json({ error: 'Admin access required' });
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Regular endpoints - use full multi-auth
|
|
408
|
+
try {
|
|
409
|
+
const authError = await getMultiAuthError(req);
|
|
410
|
+
if (authError) {
|
|
411
|
+
res.status(authError.code).send(authError.message);
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
next();
|
|
415
|
+
} catch (error) {
|
|
416
|
+
res.status(500).send('Authentication error');
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
### Custom Authentication
|
|
422
|
+
|
|
423
|
+
You can provide custom authentication validation functions through the `McpServerData` interface. The custom validator receives the full Express request object, allowing for flexible authentication logic:
|
|
424
|
+
|
|
425
|
+
```typescript
|
|
426
|
+
import { McpServerData, CustomAuthValidator } from 'fa-mcp-sdk';
|
|
427
|
+
|
|
428
|
+
// Database-backed authentication with request context
|
|
429
|
+
const databaseAuthValidator: CustomAuthValidator = async (req): Promise<AuthResult> => {
|
|
430
|
+
try {
|
|
431
|
+
// Extract authentication data from various sources
|
|
432
|
+
const authHeader = req.headers.authorization;
|
|
433
|
+
const username = req.headers['x-username'];
|
|
434
|
+
const apiKey = req.headers['x-api-key'];
|
|
435
|
+
|
|
436
|
+
if (authHeader?.startsWith('Basic ')) {
|
|
437
|
+
const [user, pass] = Buffer.from(authHeader.slice(6), 'base64').toString().split(':');
|
|
438
|
+
const dbUser = await getUserFromDatabase(user);
|
|
439
|
+
|
|
440
|
+
if (dbUser && await comparePassword(pass, dbUser.hashedPassword)) {
|
|
441
|
+
return {
|
|
442
|
+
success: true,
|
|
443
|
+
authType: 'basic',
|
|
444
|
+
username: dbUser.username,
|
|
445
|
+
payload: { userId: dbUser.id, roles: dbUser.roles }
|
|
446
|
+
};
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
if (apiKey && username) {
|
|
451
|
+
const isValid = await validateUserApiKey(username, apiKey);
|
|
452
|
+
if (isValid) {
|
|
453
|
+
return {
|
|
454
|
+
success: true,
|
|
455
|
+
authType: 'basic',
|
|
456
|
+
username: username,
|
|
457
|
+
payload: { apiKey: apiKey.substring(0, 8) + '...' }
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
return { success: false, error: 'Invalid credentials' };
|
|
463
|
+
} catch (error) {
|
|
464
|
+
console.error('Database authentication error:', error);
|
|
465
|
+
return { success: false, error: 'Database authentication error' };
|
|
466
|
+
}
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
// Use custom validator in MCP server
|
|
470
|
+
const serverData: McpServerData = {
|
|
471
|
+
tools,
|
|
472
|
+
toolHandler,
|
|
473
|
+
agentBrief: 'My MCP Server',
|
|
474
|
+
agentPrompt: 'Server with custom authentication',
|
|
475
|
+
|
|
476
|
+
// Provide custom authentication validator (black box function)
|
|
477
|
+
customAuthValidator: databaseAuthValidator,
|
|
478
|
+
|
|
479
|
+
// ... other configuration
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
await initMcpServer(serverData);
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
### Client Usage Examples
|
|
486
|
+
|
|
487
|
+
```bash
|
|
488
|
+
# Using permanent server token
|
|
489
|
+
curl -H "Authorization: Bearer server-token-1" http://localhost:3000/mcp
|
|
490
|
+
|
|
491
|
+
# Using JWT token
|
|
492
|
+
curl -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhb..." http://localhost:3000/mcp
|
|
493
|
+
|
|
494
|
+
# Using Basic Authentication
|
|
495
|
+
curl -H "Authorization: Basic $(echo -n 'admin:password' | base64)" http://localhost:3000/mcp
|
|
496
|
+
|
|
497
|
+
# Using custom headers for custom validator
|
|
498
|
+
curl -H "X-User-ID: john.doe" \
|
|
499
|
+
-H "X-API-Key: custom-api-key-12345" \
|
|
500
|
+
-H "X-Client-IP: 192.168.1.10" \
|
|
501
|
+
http://localhost:3000/mcp
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
The multi-authentication system automatically tries authentication methods in CPU-optimized order (fastest first) and returns on the first successful match, providing both performance and flexibility.
|
|
505
|
+
|
|
506
|
+
---
|
|
507
|
+
|
|
508
|
+
## AD Group Checking
|
|
509
|
+
|
|
510
|
+
### Configuration (`config/local.yaml`)
|
|
511
|
+
|
|
512
|
+
```yaml
|
|
513
|
+
ad:
|
|
514
|
+
domains:
|
|
515
|
+
MYDOMAIN:
|
|
516
|
+
default: true
|
|
517
|
+
controllers: ['ldap://dc1.corp.com']
|
|
518
|
+
username: 'svc_account@corp.com'
|
|
519
|
+
password: '***'
|
|
520
|
+
# baseDn: 'DC=corp,DC=com' # Optional, auto-derived from controller URL
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
### Usage
|
|
524
|
+
|
|
525
|
+
```typescript
|
|
526
|
+
import { initADGroupChecker } from 'fa-mcp-sdk';
|
|
527
|
+
|
|
528
|
+
const { isUserInGroup, groupChecker } = initADGroupChecker();
|
|
529
|
+
|
|
530
|
+
const isAdmin = await isUserInGroup('john.doe', 'Admins');
|
|
531
|
+
const isDeveloper = await isUserInGroup('john.doe', 'Developers');
|
|
532
|
+
|
|
533
|
+
groupChecker.clearCache(); // Clear cache if needed
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
---
|
|
537
|
+
|
|
538
|
+
## Advanced Authorization with AD Group Membership
|
|
539
|
+
|
|
540
|
+
See the separate documentation file `05-ad-authorization.md` for detailed examples of:
|
|
541
|
+
|
|
542
|
+
1. **HTTP Server Level Access Restriction** - Using `customAuthValidator`
|
|
543
|
+
2. **Access Restriction to ALL MCP Tools** - Checking in `toolHandler`
|
|
544
|
+
3. **Access Restriction to SPECIFIC MCP Tools** - Per-tool group requirements
|