fa-mcp-sdk 0.2.132 → 0.2.133
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/package.json +3 -1
- package/src/template/_examples/multi-auth-examples.ts +541 -0
- package/src/template/_types_/common.d.ts +0 -0
- package/src/template/api/router.ts +38 -0
- package/src/template/api/swagger.ts +167 -0
- package/src/template/asset/favicon.svg +4 -0
- package/src/template/custom-resources.ts +11 -0
- package/src/template/prompts/agent-brief.ts +8 -0
- package/src/template/prompts/agent-prompt.ts +10 -0
- package/src/template/prompts/custom-prompts.ts +12 -0
- package/src/template/start.ts +71 -0
- package/src/template/tools/handle-tool-call.ts +55 -0
- package/src/template/tools/tools.ts +88 -0
- package/src/tests/jest-simple-reporter.js +10 -0
- package/src/tests/mcp/sse/mcp-sse-client-handling.md +111 -0
- package/src/tests/mcp/sse/test-sse-npm-package.js +96 -0
- package/src/tests/mcp/test-cases.js +143 -0
- package/src/tests/mcp/test-http.js +63 -0
- package/src/tests/mcp/test-sse.js +67 -0
- package/src/tests/mcp/test-stdio.js +78 -0
- package/src/tests/utils.ts +156 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fa-mcp-sdk",
|
|
3
3
|
"productName": "FA MCP SDK",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.133",
|
|
5
5
|
"description": "Core infrastructure and templates for building Model Context Protocol (MCP) servers with TypeScript",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "dist/core/index.js",
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
"dist/core/**/*",
|
|
14
14
|
"cli-template/**/*",
|
|
15
15
|
"bin/**/*",
|
|
16
|
+
"src/template/**/*",
|
|
17
|
+
"src/tests/**/*",
|
|
16
18
|
"README.md"
|
|
17
19
|
],
|
|
18
20
|
"bin": {
|
|
@@ -0,0 +1,541 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Примеры использования системы мультиаутентификации fa-mcp-sdk
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import express from 'express';
|
|
6
|
+
import {
|
|
7
|
+
appConfig,
|
|
8
|
+
createAuthMW,
|
|
9
|
+
getMultiAuthError,
|
|
10
|
+
checkMultiAuth,
|
|
11
|
+
checkCombinedAuth,
|
|
12
|
+
detectAuthConfiguration,
|
|
13
|
+
logAuthConfiguration,
|
|
14
|
+
McpServerData,
|
|
15
|
+
CustomAuthValidator,
|
|
16
|
+
AuthResult,
|
|
17
|
+
} from '../../core/index.js';
|
|
18
|
+
|
|
19
|
+
// ========================================================================
|
|
20
|
+
// ПРИМЕР:
|
|
21
|
+
// ========================================================================
|
|
22
|
+
|
|
23
|
+
const app = express();
|
|
24
|
+
|
|
25
|
+
// Middleware с логированием конфигурации при запуске
|
|
26
|
+
process.env.LOG_AUTH_CONFIG = 'true';
|
|
27
|
+
const authWithLogging = createAuthMW();
|
|
28
|
+
|
|
29
|
+
app.use('/api/v2', authWithLogging);
|
|
30
|
+
|
|
31
|
+
// ========================================================================
|
|
32
|
+
// ПРИМЕР 3: КАСТОМНАЯ ЛОГИКА АУТЕНТИФИКАЦИИ
|
|
33
|
+
// ========================================================================
|
|
34
|
+
|
|
35
|
+
app.use('/api/custom', async (req, res, next) => {
|
|
36
|
+
// Публичные эндпоинты
|
|
37
|
+
if (req.path.startsWith('/api/custom/public')) {
|
|
38
|
+
return next();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Для админских эндпоинтов требуем только permanent tokens
|
|
42
|
+
if (req.path.startsWith('/api/custom/admin')) {
|
|
43
|
+
const token = (req.headers.authorization || '').replace(/^Bearer */, '');
|
|
44
|
+
const auth = appConfig.webServer.auth;
|
|
45
|
+
|
|
46
|
+
if (auth.permanentServerTokens.includes(token)) {
|
|
47
|
+
return next();
|
|
48
|
+
} else {
|
|
49
|
+
return res.status(403).json({ error: 'Admin access required' });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
// Для остальных используем полную мультиаутентификацию
|
|
55
|
+
const authError = await getMultiAuthError(req);
|
|
56
|
+
if (authError) {
|
|
57
|
+
res.status(authError.code).send(authError.message);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
next();
|
|
61
|
+
} catch {
|
|
62
|
+
res.status(500).send('Authentication error');
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// ========================================================================
|
|
68
|
+
// ПРИМЕР 4: РОУТЕР С РАЗНЫМИ УРОВНЯМИ ДОСТУПА
|
|
69
|
+
// ========================================================================
|
|
70
|
+
|
|
71
|
+
const apiRouter = express.Router();
|
|
72
|
+
|
|
73
|
+
// Публичные роуты - без аутентификации
|
|
74
|
+
apiRouter.get('/health', (req, res) => {
|
|
75
|
+
res.json({ status: 'ok' });
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Защищенные роуты - с мультиаутентификацией
|
|
79
|
+
apiRouter.use('/protected', authWithLogging);
|
|
80
|
+
|
|
81
|
+
apiRouter.get('/protected/profile', (req, res) => {
|
|
82
|
+
const authInfo = (req as any).authInfo;
|
|
83
|
+
res.json({
|
|
84
|
+
profile: {
|
|
85
|
+
authType: authInfo.authType,
|
|
86
|
+
username: authInfo.username || 'anonymous',
|
|
87
|
+
permissions: getPermissionsForAuthType(authInfo.authType),
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
apiRouter.get('/protected/data', (req, res) => {
|
|
93
|
+
const authInfo = (req as any).authInfo;
|
|
94
|
+
|
|
95
|
+
// Разные данные в зависимости от типа аутентификации
|
|
96
|
+
let data;
|
|
97
|
+
switch (authInfo.authType) {
|
|
98
|
+
case 'permanentServerTokens':
|
|
99
|
+
data = { level: 'server', access: 'full' };
|
|
100
|
+
break;
|
|
101
|
+
case 'basic':
|
|
102
|
+
data = { level: 'basic', access: 'limited', username: authInfo.username };
|
|
103
|
+
break;
|
|
104
|
+
case 'pat':
|
|
105
|
+
data = { level: 'api', access: 'token-based' };
|
|
106
|
+
break;
|
|
107
|
+
case 'jwtToken':
|
|
108
|
+
data = { level: 'jwt', access: 'custom', payload: authInfo.payload };
|
|
109
|
+
break;
|
|
110
|
+
default:
|
|
111
|
+
data = { level: 'unknown', access: 'none' };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
res.json({ data, authInfo: authInfo.authType });
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
app.use('/api/v3', apiRouter);
|
|
118
|
+
|
|
119
|
+
// ========================================================================
|
|
120
|
+
// ПРИМЕР 5: ПРОГРАММНОЕ ТЕСТИРОВАНИЕ ТОКЕНОВ
|
|
121
|
+
// ========================================================================
|
|
122
|
+
|
|
123
|
+
app.post('/api/test-token', async (req, res) => {
|
|
124
|
+
const { token } = req.body;
|
|
125
|
+
|
|
126
|
+
if (!token) {
|
|
127
|
+
return res.status(400).json({ error: 'Token required' });
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
try {
|
|
131
|
+
const result = await checkMultiAuth(req);
|
|
132
|
+
|
|
133
|
+
return res.json({
|
|
134
|
+
valid: result.success,
|
|
135
|
+
authType: result.authType,
|
|
136
|
+
tokenType: result.tokenType,
|
|
137
|
+
error: result.error,
|
|
138
|
+
username: result.username,
|
|
139
|
+
hasPayload: !!result.payload,
|
|
140
|
+
});
|
|
141
|
+
} catch {
|
|
142
|
+
return res.status(500).json({ error: 'Authentication test failed' });
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// ========================================================================
|
|
147
|
+
// ПРИМЕР 6: MIDDLEWARE ДЛЯ РАЗНЫХ ТИПОВ API
|
|
148
|
+
// ========================================================================
|
|
149
|
+
|
|
150
|
+
// REST API - требует любую валидную аутентификацию
|
|
151
|
+
app.use('/rest', authWithLogging);
|
|
152
|
+
|
|
153
|
+
// GraphQL API - требует user-level аутентификацию (не server tokens)
|
|
154
|
+
app.use('/graphql', async (req, res, next) => {
|
|
155
|
+
try {
|
|
156
|
+
const authError = await getMultiAuthError(req);
|
|
157
|
+
if (authError) {
|
|
158
|
+
return res.status(authError.code).send(authError.message);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const authInfo = (req as any).authInfo;
|
|
162
|
+
if (authInfo.authType === 'permanentServerTokens') {
|
|
163
|
+
return res.status(403).json({
|
|
164
|
+
error: 'GraphQL API requires user authentication, server tokens not allowed',
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return next();
|
|
169
|
+
} catch {
|
|
170
|
+
return res.status(500).send('Authentication error');
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// WebSocket API - только JWT токены (для real-time connections)
|
|
175
|
+
app.use('/ws', async (req, res, next) => {
|
|
176
|
+
try {
|
|
177
|
+
const authError = await getMultiAuthError(req);
|
|
178
|
+
if (authError) {
|
|
179
|
+
return res.status(authError.code).send(authError.message);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const authInfo = (req as any).authInfo;
|
|
183
|
+
if (authInfo.authType !== 'jwtToken') {
|
|
184
|
+
return res.status(403).json({
|
|
185
|
+
error: 'WebSocket API requires JWT tokens for session management',
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return next();
|
|
190
|
+
} catch {
|
|
191
|
+
return res.status(500).send('Authentication error');
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
// ========================================================================
|
|
196
|
+
// ПРИМЕР 7: ИСПОЛЬЗОВАНИЕ CHECKCOMBIПEDAUTH С КАСТОМНОЙ ВАЛИДАЦИЕЙ
|
|
197
|
+
// ========================================================================
|
|
198
|
+
|
|
199
|
+
// Пример кастомной функции аутентификации
|
|
200
|
+
const customAuthValidator: CustomAuthValidator = async (req): Promise<AuthResult> => {
|
|
201
|
+
// Черный ящик для кастомной логики аутентификации
|
|
202
|
+
const userHeader = req.headers['x-user-id'];
|
|
203
|
+
const apiKey = req.headers['x-api-key'];
|
|
204
|
+
const clientIP = req.headers['x-real-ip'] || req.connection?.remoteAddress;
|
|
205
|
+
|
|
206
|
+
try {
|
|
207
|
+
// Пример: проверка IP-адреса из whitelist
|
|
208
|
+
const allowedIPs = ['127.0.0.1', '192.168.1.0/24'];
|
|
209
|
+
if (!(await isIPAllowed(clientIP, allowedIPs))) {
|
|
210
|
+
return { success: false, error: `IP address ${clientIP} not in whitelist` };
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Пример: проверка специального API ключа
|
|
214
|
+
if (apiKey && userHeader) {
|
|
215
|
+
const isValidKey = await validateApiKeyForUser(apiKey, userHeader);
|
|
216
|
+
if (!isValidKey) {
|
|
217
|
+
return { success: false, error: 'Invalid API key for user' };
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
success: true,
|
|
222
|
+
authType: 'basic',
|
|
223
|
+
tokenType: 'apiKey',
|
|
224
|
+
username: userHeader,
|
|
225
|
+
payload: {
|
|
226
|
+
clientIP,
|
|
227
|
+
apiKeyPrefix: apiKey.substring(0, 8) + '...',
|
|
228
|
+
validatedAt: new Date().toISOString()
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Пример: проверка времени работы (только рабочие часы)
|
|
234
|
+
const now = new Date();
|
|
235
|
+
const hour = now.getHours();
|
|
236
|
+
const isWorkingHours = hour >= 9 && hour <= 17;
|
|
237
|
+
|
|
238
|
+
if (!isWorkingHours) {
|
|
239
|
+
return { success: false, error: 'Access only allowed during business hours (9-17)' };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Пример: проверка заголовка User-Agent
|
|
243
|
+
const userAgent = req.headers['user-agent'];
|
|
244
|
+
if (userAgent?.includes('bot') || userAgent?.includes('crawler')) {
|
|
245
|
+
return { success: false, error: 'Bots and crawlers are not allowed' };
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Разрешаем доступ с базовой информацией
|
|
249
|
+
return {
|
|
250
|
+
success: true,
|
|
251
|
+
authType: 'basic',
|
|
252
|
+
tokenType: 'custom',
|
|
253
|
+
username: `guest-${clientIP}`,
|
|
254
|
+
payload: {
|
|
255
|
+
clientIP,
|
|
256
|
+
userAgent,
|
|
257
|
+
accessTime: new Date().toISOString(),
|
|
258
|
+
businessHoursAccess: isWorkingHours
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
} catch (error) {
|
|
262
|
+
return {
|
|
263
|
+
success: false,
|
|
264
|
+
error: `Custom authentication error: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
// Демонстрация использования checkCombinedAuth напрямую
|
|
270
|
+
app.post('/api/combined-auth-test', async (req, res) => {
|
|
271
|
+
try {
|
|
272
|
+
// checkCombinedAuth проверяет и стандартную auth + кастомный валидатор
|
|
273
|
+
const result = await checkCombinedAuth(req);
|
|
274
|
+
|
|
275
|
+
if (result.success) {
|
|
276
|
+
res.json({
|
|
277
|
+
message: 'Combined authentication successful',
|
|
278
|
+
authType: result.authType,
|
|
279
|
+
tokenType: result.tokenType,
|
|
280
|
+
username: result.username,
|
|
281
|
+
});
|
|
282
|
+
} else {
|
|
283
|
+
res.status(401).json({
|
|
284
|
+
error: 'Combined authentication failed',
|
|
285
|
+
reason: result.error,
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
} catch {
|
|
289
|
+
res.status(500).json({ error: 'Authentication system error' });
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
// Пример middleware, который использует combined auth
|
|
294
|
+
const combinedAuthMiddleware = async (req: any, res: any, next: any) => {
|
|
295
|
+
try {
|
|
296
|
+
const result = await checkCombinedAuth(req);
|
|
297
|
+
|
|
298
|
+
if (!result.success) {
|
|
299
|
+
return res.status(401).json({ error: result.error });
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Добавляем информацию об аутентификации в request
|
|
303
|
+
req.authInfo = {
|
|
304
|
+
authType: result.authType,
|
|
305
|
+
tokenType: result.tokenType,
|
|
306
|
+
username: result.username,
|
|
307
|
+
payload: result.payload,
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
next();
|
|
311
|
+
} catch {
|
|
312
|
+
res.status(500).json({ error: 'Authentication error' });
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
app.use('/api/protected-combined', combinedAuthMiddleware);
|
|
317
|
+
|
|
318
|
+
app.get('/api/protected-combined/data', (req, res) => {
|
|
319
|
+
const authInfo = (req as any).authInfo;
|
|
320
|
+
res.json({
|
|
321
|
+
message: 'Access granted with combined auth',
|
|
322
|
+
auth: authInfo,
|
|
323
|
+
timestamp: new Date().toISOString(),
|
|
324
|
+
});
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
// ========================================================================
|
|
328
|
+
// ПРИМЕР 8: КОНФИГУРАЦИЯ MCP СЕРВЕРА С КАСТОМНЫМ ВАЛИДАТОРОМ
|
|
329
|
+
// ========================================================================
|
|
330
|
+
|
|
331
|
+
// Пример того, как настроить MCP сервер с кастомным валидатором
|
|
332
|
+
const mcpServerDataExample: McpServerData = {
|
|
333
|
+
tools: [],
|
|
334
|
+
toolHandler: async () => ({}),
|
|
335
|
+
agentBrief: 'Example MCP Server with Custom Auth',
|
|
336
|
+
agentPrompt: 'An example server demonstrating custom authentication',
|
|
337
|
+
|
|
338
|
+
// Кастомный валидатор аутентификации
|
|
339
|
+
customAuthValidator: async (req): Promise<AuthResult> => {
|
|
340
|
+
console.log('🔐 Custom auth validator called');
|
|
341
|
+
|
|
342
|
+
try {
|
|
343
|
+
// Логика валидации может быть любой:
|
|
344
|
+
const authHeader = req.headers.authorization;
|
|
345
|
+
const specialToken = req.headers['x-special-token'];
|
|
346
|
+
const clientCert = req.headers['x-client-cert'];
|
|
347
|
+
|
|
348
|
+
// Пример 1: Проверка специального токена
|
|
349
|
+
if (specialToken === 'secret-company-token-2024') {
|
|
350
|
+
console.log('✅ Authentication via special token');
|
|
351
|
+
return {
|
|
352
|
+
success: true,
|
|
353
|
+
authType: 'basic',
|
|
354
|
+
tokenType: 'specialToken',
|
|
355
|
+
username: 'company-user',
|
|
356
|
+
payload: {
|
|
357
|
+
tokenType: 'company',
|
|
358
|
+
issuedAt: new Date().toISOString(),
|
|
359
|
+
level: 'company-wide'
|
|
360
|
+
}
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Пример 2: Проверка клиентского сертификата
|
|
365
|
+
if (clientCert && (await validateClientCertificate(clientCert))) {
|
|
366
|
+
console.log('✅ Authentication via client certificate');
|
|
367
|
+
return {
|
|
368
|
+
success: true,
|
|
369
|
+
authType: 'basic',
|
|
370
|
+
tokenType: 'clientCert',
|
|
371
|
+
username: 'cert-user',
|
|
372
|
+
payload: {
|
|
373
|
+
certificateFingerprint: clientCert.substring(0, 32) + '...',
|
|
374
|
+
validatedAt: new Date().toISOString(),
|
|
375
|
+
level: 'certificate-based'
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// Пример 3: Интеграция с внешней системой аутентификации
|
|
381
|
+
if (authHeader?.startsWith('Bearer ')) {
|
|
382
|
+
const token = authHeader.slice(7);
|
|
383
|
+
const isValid = await validateExternalToken(token);
|
|
384
|
+
if (isValid) {
|
|
385
|
+
console.log('✅ Authentication via external system');
|
|
386
|
+
return {
|
|
387
|
+
success: true,
|
|
388
|
+
authType: 'basic',
|
|
389
|
+
tokenType: 'externalToken',
|
|
390
|
+
username: 'external-user',
|
|
391
|
+
payload: {
|
|
392
|
+
tokenPrefix: token.substring(0, 8) + '...',
|
|
393
|
+
validatedAt: new Date().toISOString(),
|
|
394
|
+
level: 'external-system'
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
console.log('❌ Custom authentication failed');
|
|
401
|
+
return { success: false, error: 'No valid authentication method found' };
|
|
402
|
+
} catch (error) {
|
|
403
|
+
console.log('❌ Custom authentication error:', error);
|
|
404
|
+
return {
|
|
405
|
+
success: false,
|
|
406
|
+
error: `Custom authentication error: ${error instanceof Error ? error.message : 'Unknown error'}`
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
},
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
// Утилитные функции для примеров
|
|
413
|
+
async function isIPAllowed (ip: string, allowedIPs: string[]): Promise<boolean> {
|
|
414
|
+
// Заглушка для проверки IP
|
|
415
|
+
return allowedIPs.some(allowed => ip.includes(allowed.split('/')[0]!));
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
async function validateApiKeyForUser (apiKey: string, userId: string): Promise<boolean> {
|
|
419
|
+
// Заглушка для проверки API ключа пользователя
|
|
420
|
+
return apiKey.length > 20 && userId.length > 0;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
async function validateClientCertificate (cert: string): Promise<boolean> {
|
|
424
|
+
// Заглушка для проверки клиентского сертификата
|
|
425
|
+
return cert.includes('-----BEGIN CERTIFICATE-----');
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
async function validateExternalToken (token: string): Promise<boolean> {
|
|
429
|
+
// Заглушка для проверки токена во внешней системе
|
|
430
|
+
try {
|
|
431
|
+
// Здесь может быть HTTP запрос к внешней системе
|
|
432
|
+
return token.length > 10;
|
|
433
|
+
} catch {
|
|
434
|
+
return false;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// ========================================================================
|
|
439
|
+
// УТИЛИТНЫЕ ФУНКЦИИ
|
|
440
|
+
// ========================================================================
|
|
441
|
+
|
|
442
|
+
function getPermissionsForAuthType (authType: string): string[] {
|
|
443
|
+
const permissions: Record<string, string[]> = {
|
|
444
|
+
'permanentServerTokens': ['read', 'write', 'admin', 'server'],
|
|
445
|
+
'jwtToken': ['read', 'write', 'session'],
|
|
446
|
+
'pat': ['read', 'write', 'api'],
|
|
447
|
+
'basic': ['read', 'basic'],
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
return permissions[authType] || ['read'];
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// ========================================================================
|
|
454
|
+
// ПРИМЕР 9: ИНИЦИАЛИЗАЦИЯ С ДИАГНОСТИКОЙ
|
|
455
|
+
// ========================================================================
|
|
456
|
+
|
|
457
|
+
function initializeAuthSystem () {
|
|
458
|
+
console.log('🔐 Initializing Multi-Authentication System...');
|
|
459
|
+
|
|
460
|
+
// Диагностика конфигурации
|
|
461
|
+
const { configured, errors } = detectAuthConfiguration();
|
|
462
|
+
|
|
463
|
+
console.log('📊 Auth Configuration:');
|
|
464
|
+
console.log(` Enabled: ${!!appConfig.webServer?.auth?.enabled}`);
|
|
465
|
+
console.log(` Configured: ${configured.join(', ')}`);
|
|
466
|
+
|
|
467
|
+
if (Object.keys(errors).length > 0) {
|
|
468
|
+
console.warn('⚠️ Configuration Issues:');
|
|
469
|
+
Object.entries(errors).forEach(([type, errors]) => {
|
|
470
|
+
console.warn(` ${type}: ${(errors as string[]).join(', ')}`);
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// Логирование для отладки
|
|
475
|
+
logAuthConfiguration();
|
|
476
|
+
|
|
477
|
+
console.log('✅ Multi-Authentication System initialized successfully');
|
|
478
|
+
|
|
479
|
+
return {
|
|
480
|
+
configured: configured,
|
|
481
|
+
errors: errors,
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
// ========================================================================
|
|
486
|
+
// ПРИМЕР 11: ТЕСТИРОВАНИЕ COMBINED AUTH
|
|
487
|
+
// ========================================================================
|
|
488
|
+
|
|
489
|
+
async function testCombinedAuth () {
|
|
490
|
+
console.log('🧪 Testing Combined Authentication...');
|
|
491
|
+
|
|
492
|
+
// Создаем тестовый запрос
|
|
493
|
+
const mockRequest = {
|
|
494
|
+
headers: {
|
|
495
|
+
authorization: 'Bearer test-token',
|
|
496
|
+
'x-user-id': 'test-user',
|
|
497
|
+
'x-api-key': 'test-api-key-12345',
|
|
498
|
+
'user-agent': 'PostmanRuntime/7.28.0',
|
|
499
|
+
},
|
|
500
|
+
connection: { remoteAddress: '127.0.0.1' },
|
|
501
|
+
};
|
|
502
|
+
|
|
503
|
+
try {
|
|
504
|
+
// @ts-ignore
|
|
505
|
+
const result = await checkCombinedAuth(mockRequest);
|
|
506
|
+
|
|
507
|
+
if (result.success) {
|
|
508
|
+
console.log('✅ Combined authentication test: PASSED');
|
|
509
|
+
console.log(` Auth Type: ${result.authType}`);
|
|
510
|
+
console.log(` Token Type: ${result.tokenType}`);
|
|
511
|
+
console.log(` Username: ${result.username || 'N/A'}`);
|
|
512
|
+
} else {
|
|
513
|
+
console.log('❌ Combined authentication test: FAILED');
|
|
514
|
+
console.log(` Error: ${result.error}`);
|
|
515
|
+
}
|
|
516
|
+
} catch (error) {
|
|
517
|
+
console.log('❌ Combined authentication test: ERROR');
|
|
518
|
+
console.log(` Exception: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
console.log('🧪 Combined authentication testing completed');
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// ========================================================================
|
|
525
|
+
// ЭКСПОРТ ДЛЯ ИСПОЛЬЗОВАНИЯ
|
|
526
|
+
// ========================================================================
|
|
527
|
+
|
|
528
|
+
// Экспортируем все функции и примеры для использования
|
|
529
|
+
export {
|
|
530
|
+
// Примеры конфигурации
|
|
531
|
+
mcpServerDataExample,
|
|
532
|
+
customAuthValidator,
|
|
533
|
+
combinedAuthMiddleware,
|
|
534
|
+
|
|
535
|
+
// Функции тестирования
|
|
536
|
+
initializeAuthSystem,
|
|
537
|
+
testCombinedAuth,
|
|
538
|
+
|
|
539
|
+
// Утилиты
|
|
540
|
+
getPermissionsForAuthType,
|
|
541
|
+
};
|
|
File without changes
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Router, Request, Response } from 'express';
|
|
2
|
+
import { logger, createAuthMW, IEndpointsOn404 } from '../../core/index.js';
|
|
3
|
+
|
|
4
|
+
export const apiRouter: Router | null = Router();
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Template for API routes
|
|
8
|
+
* Modify this file to implement your specific API endpoints
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
// Create universal auth middleware
|
|
12
|
+
const authMW = createAuthMW();
|
|
13
|
+
|
|
14
|
+
// Example protected endpoint using auth middleware
|
|
15
|
+
apiRouter.get('/example', authMW, async (req: Request, res: Response) => {
|
|
16
|
+
try {
|
|
17
|
+
logger.info('Example endpoint called');
|
|
18
|
+
|
|
19
|
+
res.json({
|
|
20
|
+
success: true,
|
|
21
|
+
message: 'This is a template endpoint',
|
|
22
|
+
data: {
|
|
23
|
+
timestamp: new Date().toISOString(),
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
} catch (error) {
|
|
27
|
+
logger.error('Error in example endpoint:', error);
|
|
28
|
+
res.status(500).json({
|
|
29
|
+
success: false,
|
|
30
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
export const endpointsOn404: IEndpointsOn404 = {
|
|
36
|
+
myEndpoints1: ['/my-endpoint-1', '/my-endpoint-2'],
|
|
37
|
+
myEndpoint3: '/my-endpoint-3',
|
|
38
|
+
};
|