fa-mcp-sdk 0.4.76 → 0.4.77
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 +319 -314
- package/bin/fa-mcp.js +85 -68
- package/cli-template/.claude/agents/javascript-pro.md +276 -276
- package/cli-template/.claude/settings.json +50 -50
- package/cli-template/.claude/skills/upgrade-guide/SKILL.md +2 -1
- package/cli-template/.oxfmtrc.json +41 -0
- package/cli-template/.oxlintrc.json +120 -0
- package/cli-template/CLAUDE.md +358 -355
- package/cli-template/FA-MCP-SDK-DOC/00-FA-MCP-SDK-index.md +132 -132
- package/cli-template/FA-MCP-SDK-DOC/01-getting-started.md +146 -146
- package/cli-template/FA-MCP-SDK-DOC/02-1-tools-and-api.md +431 -431
- package/cli-template/FA-MCP-SDK-DOC/02-2-prompts-and-resources.md +201 -201
- package/cli-template/FA-MCP-SDK-DOC/03-configuration.md +384 -384
- package/cli-template/FA-MCP-SDK-DOC/04-authentication.md +412 -412
- package/cli-template/FA-MCP-SDK-DOC/05-ad-authorization.md +196 -196
- package/cli-template/FA-MCP-SDK-DOC/06-utilities.md +163 -163
- package/cli-template/FA-MCP-SDK-DOC/07-testing-and-operations.md +127 -127
- package/cli-template/jest.config.js +27 -30
- package/cli-template/package.json +10 -5
- package/cli-template/prompt-example-new-MCP.md +101 -101
- package/cli-template/readme-docs/SKILLS.md +1 -1
- package/cli-template/tsconfig.json +58 -58
- package/cli-template/update.cjs +41 -38
- package/config/custom-environment-variables.yaml +63 -63
- package/config/development.yaml +4 -4
- package/config/production.yaml +4 -4
- package/config/test.yaml +26 -26
- package/dist/core/_types_/TNtlm.d.ts.map +1 -1
- package/dist/core/_types_/active-directory-config.d.ts.map +1 -1
- package/dist/core/_types_/config.d.ts.map +1 -1
- package/dist/core/_types_/types.d.ts.map +1 -1
- package/dist/core/ad/group-checker.d.ts.map +1 -1
- package/dist/core/ad/group-checker.js.map +1 -1
- package/dist/core/agent-tester/agent-tester-router.d.ts.map +1 -1
- package/dist/core/agent-tester/agent-tester-router.js +6 -6
- package/dist/core/agent-tester/agent-tester-router.js.map +1 -1
- package/dist/core/agent-tester/check-llm.d.ts.map +1 -1
- package/dist/core/agent-tester/check-llm.js.map +1 -1
- package/dist/core/agent-tester/services/SummaryMemory.d.ts.map +1 -1
- package/dist/core/agent-tester/services/SummaryMemory.js +3 -9
- package/dist/core/agent-tester/services/SummaryMemory.js.map +1 -1
- package/dist/core/agent-tester/services/TesterAgentService.d.ts.map +1 -1
- package/dist/core/agent-tester/services/TesterAgentService.js +25 -27
- package/dist/core/agent-tester/services/TesterAgentService.js.map +1 -1
- package/dist/core/agent-tester/services/TesterMcpClientService.d.ts.map +1 -1
- package/dist/core/agent-tester/services/TesterMcpClientService.js +26 -25
- package/dist/core/agent-tester/services/TesterMcpClientService.js.map +1 -1
- package/dist/core/auth/admin-auth.d.ts.map +1 -1
- package/dist/core/auth/admin-auth.js +5 -5
- package/dist/core/auth/admin-auth.js.map +1 -1
- package/dist/core/auth/agent-tester-auth.d.ts.map +1 -1
- package/dist/core/auth/agent-tester-auth.js +1 -6
- package/dist/core/auth/agent-tester-auth.js.map +1 -1
- package/dist/core/auth/basic.d.ts.map +1 -1
- package/dist/core/auth/basic.js.map +1 -1
- package/dist/core/auth/ip-check.d.ts.map +1 -1
- package/dist/core/auth/ip-check.js +1 -1
- package/dist/core/auth/ip-check.js.map +1 -1
- package/dist/core/auth/jwt.d.ts.map +1 -1
- package/dist/core/auth/jwt.js +1 -1
- package/dist/core/auth/jwt.js.map +1 -1
- package/dist/core/auth/middleware.d.ts.map +1 -1
- package/dist/core/auth/middleware.js +9 -6
- package/dist/core/auth/middleware.js.map +1 -1
- package/dist/core/auth/multi-auth.d.ts.map +1 -1
- package/dist/core/auth/multi-auth.js +6 -6
- package/dist/core/auth/multi-auth.js.map +1 -1
- package/dist/core/auth/revocation.d.ts.map +1 -1
- package/dist/core/auth/revocation.js +2 -6
- package/dist/core/auth/revocation.js.map +1 -1
- package/dist/core/auth/token-generator/ntlm/ntlm-auth-options.d.ts.map +1 -1
- package/dist/core/auth/token-generator/ntlm/ntlm-auth-options.js +2 -2
- package/dist/core/auth/token-generator/ntlm/ntlm-auth-options.js.map +1 -1
- package/dist/core/auth/token-generator/ntlm/ntlm-domain-config.js +1 -1
- package/dist/core/auth/token-generator/ntlm/ntlm-domain-config.js.map +1 -1
- package/dist/core/auth/token-generator/ntlm/ntlm-integration.d.ts.map +1 -1
- package/dist/core/auth/token-generator/ntlm/ntlm-integration.js +4 -2
- package/dist/core/auth/token-generator/ntlm/ntlm-integration.js.map +1 -1
- package/dist/core/auth/token-generator/server.d.ts.map +1 -1
- package/dist/core/auth/token-generator/server.js.map +1 -1
- package/dist/core/bootstrap/init-config.d.ts.map +1 -1
- package/dist/core/bootstrap/init-config.js +2 -2
- package/dist/core/bootstrap/init-config.js.map +1 -1
- package/dist/core/bootstrap/startup-info.d.ts.map +1 -1
- package/dist/core/bootstrap/startup-info.js +3 -7
- package/dist/core/bootstrap/startup-info.js.map +1 -1
- package/dist/core/cache/cache.d.ts.map +1 -1
- package/dist/core/cache/cache.js +2 -2
- package/dist/core/cache/cache.js.map +1 -1
- package/dist/core/consul/deregister.d.ts.map +1 -1
- package/dist/core/consul/deregister.js.map +1 -1
- package/dist/core/consul/get-consul-api.d.ts.map +1 -1
- package/dist/core/consul/get-consul-api.js +1 -2
- package/dist/core/consul/get-consul-api.js.map +1 -1
- package/dist/core/db/pg-db.d.ts.map +1 -1
- package/dist/core/db/pg-db.js +3 -3
- package/dist/core/db/pg-db.js.map +1 -1
- package/dist/core/debug.d.ts.map +1 -1
- package/dist/core/debug.js.map +1 -1
- package/dist/core/errors/BaseMcpError.d.ts.map +1 -1
- package/dist/core/errors/BaseMcpError.js.map +1 -1
- package/dist/core/errors/ValidationError.d.ts.map +1 -1
- package/dist/core/errors/ValidationError.js.map +1 -1
- package/dist/core/errors/errors.d.ts.map +1 -1
- package/dist/core/errors/errors.js +1 -1
- package/dist/core/errors/errors.js.map +1 -1
- package/dist/core/index.d.ts +6 -6
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +5 -5
- package/dist/core/index.js.map +1 -1
- package/dist/core/init-mcp-server.d.ts.map +1 -1
- package/dist/core/init-mcp-server.js.map +1 -1
- package/dist/core/logger.d.ts.map +1 -1
- package/dist/core/logger.js +1 -1
- package/dist/core/logger.js.map +1 -1
- package/dist/core/mcp/create-mcp-server.d.ts.map +1 -1
- package/dist/core/mcp/create-mcp-server.js +1 -1
- package/dist/core/mcp/create-mcp-server.js.map +1 -1
- package/dist/core/mcp/prompts.d.ts.map +1 -1
- package/dist/core/mcp/prompts.js.map +1 -1
- package/dist/core/mcp/readme-assembler.d.ts.map +1 -1
- package/dist/core/mcp/readme-assembler.js +3 -1
- package/dist/core/mcp/readme-assembler.js.map +1 -1
- package/dist/core/mcp/resources.d.ts.map +1 -1
- package/dist/core/mcp/resources.js.map +1 -1
- package/dist/core/mcp/server-stdio.d.ts.map +1 -1
- package/dist/core/utils/formatToolResult.d.ts.map +1 -1
- package/dist/core/utils/formatToolResult.js.map +1 -1
- package/dist/core/utils/port-checker.d.ts.map +1 -1
- package/dist/core/utils/port-checker.js.map +1 -1
- package/dist/core/utils/rate-limit.d.ts.map +1 -1
- package/dist/core/utils/rate-limit.js +2 -8
- package/dist/core/utils/rate-limit.js.map +1 -1
- package/dist/core/utils/testing/BaseMcpClient.d.ts.map +1 -1
- package/dist/core/utils/testing/BaseMcpClient.js.map +1 -1
- package/dist/core/utils/testing/McpHttpClient.d.ts.map +1 -1
- package/dist/core/utils/testing/McpHttpClient.js +2 -2
- package/dist/core/utils/testing/McpHttpClient.js.map +1 -1
- package/dist/core/utils/testing/McpSseClient.d.ts.map +1 -1
- package/dist/core/utils/testing/McpSseClient.js +3 -8
- package/dist/core/utils/testing/McpSseClient.js.map +1 -1
- package/dist/core/utils/testing/McpStdioClient.d.ts.map +1 -1
- package/dist/core/utils/testing/McpStdioClient.js.map +1 -1
- package/dist/core/utils/testing/McpStreamableHttpClient.d.ts.map +1 -1
- package/dist/core/utils/testing/McpStreamableHttpClient.js +7 -8
- package/dist/core/utils/testing/McpStreamableHttpClient.js.map +1 -1
- package/dist/core/utils/utils.d.ts.map +1 -1
- package/dist/core/utils/utils.js +3 -5
- package/dist/core/utils/utils.js.map +1 -1
- package/dist/core/web/admin-router.d.ts.map +1 -1
- package/dist/core/web/admin-router.js +3 -3
- package/dist/core/web/admin-router.js.map +1 -1
- package/dist/core/web/cors.d.ts.map +1 -1
- package/dist/core/web/cors.js.map +1 -1
- package/dist/core/web/favicon-svg.d.ts.map +1 -1
- package/dist/core/web/favicon-svg.js +1 -5
- package/dist/core/web/favicon-svg.js.map +1 -1
- package/dist/core/web/home-api.d.ts.map +1 -1
- package/dist/core/web/home-api.js +7 -8
- package/dist/core/web/home-api.js.map +1 -1
- package/dist/core/web/openapi.d.ts.map +1 -1
- package/dist/core/web/openapi.js +1 -3
- package/dist/core/web/openapi.js.map +1 -1
- package/dist/core/web/server-http.d.ts.map +1 -1
- package/dist/core/web/server-http.js +4 -4
- package/dist/core/web/server-http.js.map +1 -1
- package/dist/core/web/static/agent-tester/index.html +323 -323
- package/dist/core/web/static/agent-tester/script.js +311 -200
- package/dist/core/web/static/agent-tester/styles.css +1840 -1840
- package/dist/core/web/static/home/index.html +220 -220
- package/dist/core/web/static/home/script.js +72 -43
- package/dist/core/web/static/styles.css +927 -927
- package/dist/core/web/static/token-gen/index.html +136 -136
- package/dist/core/web/static/token-gen/script.js +58 -56
- package/dist/core/web/svg-icons.d.ts.map +1 -1
- package/dist/core/web/svg-icons.js +1 -5
- package/dist/core/web/svg-icons.js.map +1 -1
- package/package.json +10 -5
- package/{cli-template/.claude/hooks/eslint-fix.cjs → scripts/cc-hook-oxlint-oxfmt-fix.cjs} +109 -100
- package/scripts/generate-jwt.js +5 -9
- package/scripts/kill-port.js +5 -2
- package/scripts/npm/run.js +1 -2
- package/scripts/remove-nul.js +1 -1
- package/scripts/update-sdk.js +36 -14
- package/src/template/api/router.ts +3 -3
- package/src/template/prompts/agent-brief.ts +0 -1
- package/src/template/start.ts +3 -8
- package/src/template/tools/handle-tool-call.ts +3 -3
- package/src/template/tools/tools.ts +3 -7
- package/src/tests/jest-simple-reporter.js +1 -1
- package/src/tests/mcp/sse/mcp-sse-client-handling.md +111 -111
- package/src/tests/mcp/sse/test-sse-npm-package.js +2 -3
- package/src/tests/mcp/test-cases.js +6 -7
- package/src/tests/mcp/test-http.js +2 -2
- package/src/tests/mcp/test-sse.js +9 -7
- package/src/tests/mcp/test-stdio.js +12 -8
- package/src/tests/utils.ts +4 -3
- package/cli-template/eslint.config.js +0 -27
|
@@ -1,412 +1,412 @@
|
|
|
1
|
-
# Authentication
|
|
2
|
-
|
|
3
|
-
## Auth Types
|
|
4
|
-
|
|
5
|
-
```typescript
|
|
6
|
-
type TTokenType = 'permanent' | 'JWT';
|
|
7
|
-
|
|
8
|
-
interface AuthResult {
|
|
9
|
-
success: boolean;
|
|
10
|
-
error?: string;
|
|
11
|
-
authType?: 'permanentServerTokens' | 'jwtToken' | 'basic' | 'custom';
|
|
12
|
-
username?: string;
|
|
13
|
-
isTokenDecrypted?: boolean;
|
|
14
|
-
payload?: any;
|
|
15
|
-
}
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
## Token Operations
|
|
19
|
-
|
|
20
|
-
```typescript
|
|
21
|
-
import { generateToken } from 'fa-mcp-sdk';
|
|
22
|
-
|
|
23
|
-
// Generate JWT (liveTimeSec = seconds until expiry)
|
|
24
|
-
const token = generateToken('john_doe', 3600, { role: 'admin' }); // 1 hour
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
## Test Authentication
|
|
28
|
-
|
|
29
|
-
```typescript
|
|
30
|
-
import { getAuthHeadersForTests, McpHttpClient, appConfig } from 'fa-mcp-sdk';
|
|
31
|
-
|
|
32
|
-
// Auto-generates auth headers based on config (permanent → basic → JWT priority)
|
|
33
|
-
const headers = getAuthHeadersForTests();
|
|
34
|
-
|
|
35
|
-
// Usage
|
|
36
|
-
const response = await fetch(`http://localhost:${appConfig.webServer.port}/mcp`, {
|
|
37
|
-
method: 'POST',
|
|
38
|
-
headers: { 'Content-Type': 'application/json', ...headers },
|
|
39
|
-
body: JSON.stringify({ jsonrpc: '2.0', method: 'tools/call', params: {...}, id: 1 })
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
// With test client
|
|
43
|
-
const client = new McpHttpClient('http://localhost:3000');
|
|
44
|
-
const result = await client.callTool('tool', args, getAuthHeadersForTests());
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
## Admin Panel Authentication
|
|
48
|
-
|
|
49
|
-
The admin panel (`/admin`) supports 4 authentication types and can be configured with a single type or multiple types:
|
|
50
|
-
|
|
51
|
-
```yaml
|
|
52
|
-
# config/default.yaml
|
|
53
|
-
adminPanel:
|
|
54
|
-
enabled: true
|
|
55
|
-
# Single type (string)
|
|
56
|
-
authType: 'basic'
|
|
57
|
-
# Or multiple types (array) — login page shows tabs to choose
|
|
58
|
-
authType: ['jwtToken', 'basic']
|
|
59
|
-
# 'none' / null / empty array / not set — panel opens WITHOUT authentication
|
|
60
|
-
# (convenience for local development; do NOT use in production).
|
|
61
|
-
authType: 'none'
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
**Supported types:** `permanentServerTokens`, `basic`, `jwtToken`, `ntlm`, `none` (= open access)
|
|
65
|
-
|
|
66
|
-
When multiple types are configured (e.g. `['jwtToken', 'basic']`), the login page shows tabs:
|
|
67
|
-
- **Token** tab — for `permanentServerTokens` and `jwtToken` authentication
|
|
68
|
-
- **Login** tab — for `basic` (username/password) authentication
|
|
69
|
-
|
|
70
|
-
For `permanentServerTokens`, `basic`, `jwtToken` — credentials are taken from `webServer.auth` section.
|
|
71
|
-
For `ntlm` — uses AD configuration from `ad.domains` section.
|
|
72
|
-
|
|
73
|
-
### JWT Admin Requirement: `payload.allow === 'gen-token'`
|
|
74
|
-
|
|
75
|
-
When `jwtToken` is used to authenticate into the admin panel (`/admin`), the decoded
|
|
76
|
-
payload **must** contain `allow: 'gen-token'`. Any JWT without this claim is rejected
|
|
77
|
-
with `401` even if it decrypts and is not expired. This prevents short-lived JWTs
|
|
78
|
-
issued for other purposes (e.g. the Agent Tester page auto-fills a JWT into its
|
|
79
|
-
`Authorization` header — TTL is configurable via `agentTester.tokenTTLSec`, default
|
|
80
|
-
30 min) from being replayed against `/admin` to mint arbitrary long-lived tokens.
|
|
81
|
-
|
|
82
|
-
Generate an admin-capable JWT by including `allow=gen-token` in the payload:
|
|
83
|
-
|
|
84
|
-
```bash
|
|
85
|
-
node scripts/generate-jwt.js -u admin -ttl 30d -p "allow=gen-token"
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
`permanentServerTokens` and `basic` admin auth are unaffected — this check applies
|
|
89
|
-
only to the `jwtToken` admin path.
|
|
90
|
-
|
|
91
|
-
## Token Generator Authorization
|
|
92
|
-
|
|
93
|
-
Protect `/admin/` page with custom authorization:
|
|
94
|
-
|
|
95
|
-
```typescript
|
|
96
|
-
import { TokenGenAuthHandler, initADGroupChecker } from 'fa-mcp-sdk';
|
|
97
|
-
|
|
98
|
-
const { isUserInGroup } = initADGroupChecker();
|
|
99
|
-
|
|
100
|
-
const tokenGenAuthHandler: TokenGenAuthHandler = async (input) => {
|
|
101
|
-
// input: { user, domain?, payload?, authType }
|
|
102
|
-
if (input.authType === 'ntlm') {
|
|
103
|
-
const isAdmin = await isUserInGroup(input.user, 'TokenGeneratorAdmins');
|
|
104
|
-
if (!isAdmin) return { success: false, error: `User not authorized` };
|
|
105
|
-
}
|
|
106
|
-
return { success: true, username: input.user };
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
const serverData: McpServerData = { ..., tokenGenAuthHandler };
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
## Multi-Authentication System
|
|
113
|
-
|
|
114
|
-
### createAuthMW()
|
|
115
|
-
|
|
116
|
-
Universal middleware supporting all auth methods:
|
|
117
|
-
|
|
118
|
-
```typescript
|
|
119
|
-
import { createAuthMW } from 'fa-mcp-sdk';
|
|
120
|
-
|
|
121
|
-
const authMW = createAuthMW();
|
|
122
|
-
app.use('/api', authMW);
|
|
123
|
-
|
|
124
|
-
app.get('/api/protected', (req, res) => {
|
|
125
|
-
const authInfo = (req as any).authInfo;
|
|
126
|
-
res.json({ authType: authInfo?.authType, username: authInfo?.username });
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
// Advanced options
|
|
130
|
-
const authMW = createAuthMW({
|
|
131
|
-
mcpPaths: ['/mcp', '/messages', '/sse'], // Paths with public resource access
|
|
132
|
-
logConfig: true, // Log config on first request
|
|
133
|
-
});
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
### getMultiAuthError()
|
|
137
|
-
|
|
138
|
-
Programmatic auth checking:
|
|
139
|
-
|
|
140
|
-
```typescript
|
|
141
|
-
import { getMultiAuthError } from 'fa-mcp-sdk';
|
|
142
|
-
|
|
143
|
-
const authError = await getMultiAuthError(req);
|
|
144
|
-
if (authError) {
|
|
145
|
-
return res.status(authError.code).send(authError.message);
|
|
146
|
-
}
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
### Custom Authentication
|
|
150
|
-
|
|
151
|
-
`customAuthValidator` runs **before** standard auth (`Authorization` header check).
|
|
152
|
-
|
|
153
|
-
**Execution order:**
|
|
154
|
-
1. `customAuthValidator` is called first
|
|
155
|
-
2. If `success: true` → request is allowed, standard auth is **skipped**
|
|
156
|
-
3. If `success: false` → falls through to standard auth (`permanentServerTokens` / `basic` / `jwtToken`)
|
|
157
|
-
4. If standard auth also fails → 401
|
|
158
|
-
|
|
159
|
-
This allows using service-specific credentials (e.g. `x-api-key`, `x-service-token`) as an alternative
|
|
160
|
-
to the MCP `Authorization` header, without disabling standard auth entirely.
|
|
161
|
-
|
|
162
|
-
```typescript
|
|
163
|
-
import { CustomAuthValidator, AuthResult } from 'fa-mcp-sdk';
|
|
164
|
-
|
|
165
|
-
// Example: bypass MCP auth if service-specific header is present
|
|
166
|
-
const customValidator: CustomAuthValidator = async (req): Promise<AuthResult> => {
|
|
167
|
-
const apiKey = req.headers['x-api-key'];
|
|
168
|
-
if (apiKey && await validateApiKey(apiKey)) {
|
|
169
|
-
return { success: true, authType: 'custom', username: 'api-user' };
|
|
170
|
-
}
|
|
171
|
-
// Return false → falls through to standard Authorization header check
|
|
172
|
-
return { success: false, error: 'No valid API key' };
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
const serverData: McpServerData = { ..., customAuthValidator: customValidator };
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
**Example: allow requests with upstream service headers to bypass MCP auth**
|
|
179
|
-
|
|
180
|
-
```typescript
|
|
181
|
-
// Clients that pass x-service-token OR x-username+x-password are allowed in
|
|
182
|
-
// without an MCP Authorization token. Clients without these headers still
|
|
183
|
-
// need a valid Authorization header (permanentToken / basic / JWT).
|
|
184
|
-
const serviceHeadersValidator: CustomAuthValidator = (req) => {
|
|
185
|
-
const h = req.headers as Record<string, string>;
|
|
186
|
-
if (h['x-service-token'] || (h['x-username'] && h['x-password'])) {
|
|
187
|
-
return { success: true, authType: 'custom' };
|
|
188
|
-
}
|
|
189
|
-
return { success: false, error: 'No service credentials and no MCP Authorization token' };
|
|
190
|
-
};
|
|
191
|
-
```
|
|
192
|
-
|
|
193
|
-
> **Note:** `customAuthValidator` receives a request with **normalized** (lowercased) header names.
|
|
194
|
-
> `authInfo` is **not** set on `req` when the validator runs — it is set by the middleware only after
|
|
195
|
-
> successful authentication completes.
|
|
196
|
-
|
|
197
|
-
## Agent Tester Authentication
|
|
198
|
-
|
|
199
|
-
Protect the Agent Tester (`/agent-tester/*`) with `agentTester.useAuth`:
|
|
200
|
-
|
|
201
|
-
```yaml
|
|
202
|
-
agentTester:
|
|
203
|
-
useAuth: true # Require authentication for Agent Tester
|
|
204
|
-
webServer:
|
|
205
|
-
auth:
|
|
206
|
-
enabled: true
|
|
207
|
-
permanentServerTokens: ['my-secret-token']
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
Or via ENV: `AGENT_TESTER_USE_AUTH=true`
|
|
211
|
-
|
|
212
|
-
When `useAuth` is `true`, the full multi-auth middleware is applied to Agent Tester routes — the same authentication used for MCP endpoints (`permanentServerTokens` / `basic` / `jwtToken` / `custom`). Browser users see a login dialog; headless clients pass `Authorization` header directly.
|
|
213
|
-
|
|
214
|
-
See [Agent Tester docs](08-agent-tester-and-headless-api.md#authentication-agenttesteruseauth) for details on the login flow, session management, and API endpoints.
|
|
215
|
-
|
|
216
|
-
## AD Group Checking
|
|
217
|
-
|
|
218
|
-
### Configuration
|
|
219
|
-
|
|
220
|
-
```yaml
|
|
221
|
-
# config/local.yaml
|
|
222
|
-
ad:
|
|
223
|
-
domains:
|
|
224
|
-
MYDOMAIN:
|
|
225
|
-
default: true
|
|
226
|
-
controllers: ['ldap://dc1.corp.com']
|
|
227
|
-
username: 'svc_account@corp.com'
|
|
228
|
-
password: '***'
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
### Usage
|
|
232
|
-
|
|
233
|
-
```typescript
|
|
234
|
-
import { initADGroupChecker } from 'fa-mcp-sdk';
|
|
235
|
-
|
|
236
|
-
const { isUserInGroup, groupChecker } = initADGroupChecker();
|
|
237
|
-
|
|
238
|
-
const isAdmin = await isUserInGroup('john.doe', 'Admins');
|
|
239
|
-
groupChecker.clearCache(); // Clear if needed
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
## JWT IP Restriction
|
|
243
|
-
|
|
244
|
-
When `webServer.auth.jwtToken.isCheckIP` is `true`, JWT tokens can include an `ip` field in their payload to restrict which client IPs may use the token.
|
|
245
|
-
|
|
246
|
-
### Configuration
|
|
247
|
-
|
|
248
|
-
```yaml
|
|
249
|
-
# config/default.yaml
|
|
250
|
-
webServer:
|
|
251
|
-
auth:
|
|
252
|
-
jwtToken:
|
|
253
|
-
isCheckIP: true # Enable IP checking
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
### Token Generation
|
|
257
|
-
|
|
258
|
-
When generating a token (via admin UI or `generateToken()`), include the `ip` field in the payload:
|
|
259
|
-
|
|
260
|
-
```typescript
|
|
261
|
-
const token = generateToken('john_doe', 3600, {
|
|
262
|
-
service: 'my-mcp-server',
|
|
263
|
-
ip: '192.168.1.100, 10.0.0.0/24',
|
|
264
|
-
});
|
|
265
|
-
```
|
|
266
|
-
|
|
267
|
-
The `ip` field is a string of IP addresses and/or CIDR masks, separated by commas, semicolons, or spaces.
|
|
268
|
-
|
|
269
|
-
In the admin UI (`/admin`), there is a dedicated "Allowed IP addresses" field for entering these values.
|
|
270
|
-
|
|
271
|
-
### Behavior
|
|
272
|
-
|
|
273
|
-
| `isCheckIP` | `payload.ip` | Client IP | Result |
|
|
274
|
-
|-------------|-------------|-----------|--------|
|
|
275
|
-
| `false` | any | any | IP not checked |
|
|
276
|
-
| `true` | empty/missing | any | IP not checked (pass-through) |
|
|
277
|
-
| `true` | `10.0.0.0/24` | `10.0.0.5` | Allowed |
|
|
278
|
-
| `true` | `10.0.0.0/24` | `192.168.1.1` | Denied |
|
|
279
|
-
| `true` | `192.168.1.1, 10.0.0.0/8` | `10.5.5.5` | Allowed (covered by /8) |
|
|
280
|
-
|
|
281
|
-
Supported formats: IPv4, IPv6, CIDR notation (e.g., `10.0.0.0/24`, `fe80::/10`), IPv4-mapped IPv6 (`::ffff:192.168.1.1`).
|
|
282
|
-
|
|
283
|
-
## Client Examples
|
|
284
|
-
|
|
285
|
-
```bash
|
|
286
|
-
# Permanent token
|
|
287
|
-
curl -H "Authorization: Bearer server-token-1" http://localhost:3000/mcp
|
|
288
|
-
|
|
289
|
-
# JWT
|
|
290
|
-
curl -H "Authorization: Bearer eyJ..." http://localhost:3000/mcp
|
|
291
|
-
|
|
292
|
-
# Basic Auth
|
|
293
|
-
curl -H "Authorization: Basic $(echo -n 'admin:password' | base64)" http://localhost:3000/mcp
|
|
294
|
-
|
|
295
|
-
# Custom headers
|
|
296
|
-
curl -H "X-API-Key: custom-key" http://localhost:3000/mcp
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
## CLI Token Generator
|
|
300
|
-
|
|
301
|
-
Generate JWT tokens from the command line without starting the server:
|
|
302
|
-
|
|
303
|
-
```bash
|
|
304
|
-
node scripts/generate-jwt.js -u <username> -ttl <duration> [-s <service>] [-p <params>]
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
| Option | ENV | Description |
|
|
308
|
-
|--------|-----|-------------|
|
|
309
|
-
| `-u`, `--username` | `JWT_PAYLOAD_USERNAME` | Username (required) |
|
|
310
|
-
| `-ttl` | `JWT_TTL` | Token lifetime: `<N>s` \| `<N>m` \| `<N>d` \| `<N>y` (required) |
|
|
311
|
-
| `-s`, `--service-name` | `JWT_PAYLOAD_SERVICE_NAME` | Service name (optional) |
|
|
312
|
-
| `-p`, `--params` | `JWT_PAYLOAD_PARAMS` | Extra payload `key=value;key=value` (optional) |
|
|
313
|
-
|
|
314
|
-
The `encryptKey` is read from config `webServer.auth.jwtToken.encryptKey` (via `config/local.yaml` or ENV `WS_TOKEN_ENCRYPT_KEY`).
|
|
315
|
-
|
|
316
|
-
**Examples:**
|
|
317
|
-
|
|
318
|
-
```bash
|
|
319
|
-
# 30-day token with service name
|
|
320
|
-
node scripts/generate-jwt.js -u admin -ttl 30d -s my-mcp-server
|
|
321
|
-
|
|
322
|
-
# 1-year token with extra payload fields
|
|
323
|
-
node scripts/generate-jwt.js -u svc-account -ttl 1y -p "role=admin;team=backend"
|
|
324
|
-
|
|
325
|
-
# Via environment variables
|
|
326
|
-
JWT_PAYLOAD_USERNAME=admin JWT_TTL=8d node scripts/generate-jwt.js
|
|
327
|
-
```
|
|
328
|
-
|
|
329
|
-
## Claude Code Skill: `/gen-jwt`
|
|
330
|
-
|
|
331
|
-
Interactive JWT token generation via Claude Code. Invoke with `/gen-jwt` or natural language (e.g. "сгенерируй токен для vpupkin на 1 год").
|
|
332
|
-
|
|
333
|
-
The skill parses your request for `username`, `ttl`, `service`, `request` (ticket ID), `ip`, and extra key=value params. If required params (`username`, `ttl`) are missing, it asks interactively. Optional params (`request`, `ip`) are prompted once with an option to skip.
|
|
334
|
-
|
|
335
|
-
Runs `node scripts/generate-jwt.js` under the hood.
|
|
336
|
-
|
|
337
|
-
**Example:**
|
|
338
|
-
```
|
|
339
|
-
/gen-jwt для vpupkin, по заявке REQ-12345, на 1 год, role=admin, IP 10.0.0.0/24
|
|
340
|
-
```
|
|
341
|
-
|
|
342
|
-
Skill location: `.claude/skills/gen-jwt/SKILL.md`
|
|
343
|
-
|
|
344
|
-
## JWT Generation API
|
|
345
|
-
|
|
346
|
-
HTTP endpoint for programmatic JWT token generation. Disabled by default.
|
|
347
|
-
|
|
348
|
-
### Configuration
|
|
349
|
-
|
|
350
|
-
```yaml
|
|
351
|
-
# config/default.yaml
|
|
352
|
-
webServer:
|
|
353
|
-
genJwtApiEnable: true # Enable POST /gen-jwt endpoint
|
|
354
|
-
auth:
|
|
355
|
-
enabled: true # Auth must be enabled — endpoint requires valid credentials
|
|
356
|
-
jwtToken:
|
|
357
|
-
encryptKey: 'your-secret-key-here'
|
|
358
|
-
```
|
|
359
|
-
|
|
360
|
-
Or via ENV: `WS_GEN_JWT_API_ENABLE=true`
|
|
361
|
-
|
|
362
|
-
### Usage
|
|
363
|
-
|
|
364
|
-
```bash
|
|
365
|
-
# POST /gen-jwt with any configured auth method
|
|
366
|
-
curl -X POST http://localhost:3000/gen-jwt \
|
|
367
|
-
-H "Content-Type: application/json" \
|
|
368
|
-
-u "admin:password" \
|
|
369
|
-
-d '{
|
|
370
|
-
"username": "testuser",
|
|
371
|
-
"ttl": "30d",
|
|
372
|
-
"service": "my-mcp-server",
|
|
373
|
-
"params": "role=admin;team=backend"
|
|
374
|
-
}'
|
|
375
|
-
```
|
|
376
|
-
|
|
377
|
-
### Request Body
|
|
378
|
-
|
|
379
|
-
| Field | Type | Required | Description |
|
|
380
|
-
|-------|------|----------|-------------|
|
|
381
|
-
| `username` | string | yes | Username for the token |
|
|
382
|
-
| `ttl` | string | yes | Token lifetime: `<N>s` \| `<N>m` \| `<N>d` \| `<N>y` |
|
|
383
|
-
| `service` | string | no | Service name |
|
|
384
|
-
| `params` | string \| object | no | Extra payload. String: `"key=value;key=value"`. Object: `{"key": "value"}` |
|
|
385
|
-
|
|
386
|
-
### Response
|
|
387
|
-
|
|
388
|
-
```json
|
|
389
|
-
{
|
|
390
|
-
"success": true,
|
|
391
|
-
"token": "1718000000000.a1b2c3...",
|
|
392
|
-
"user": "testuser",
|
|
393
|
-
"expire": "2025-07-10T12:00:00.000Z",
|
|
394
|
-
"ttlSeconds": 2592000
|
|
395
|
-
}
|
|
396
|
-
```
|
|
397
|
-
|
|
398
|
-
## Token Generator App
|
|
399
|
-
|
|
400
|
-
```typescript
|
|
401
|
-
import { generateTokenApp } from 'fa-mcp-sdk';
|
|
402
|
-
|
|
403
|
-
generateTokenApp(); // Port 3030
|
|
404
|
-
generateTokenApp(1234); // Custom port
|
|
405
|
-
```
|
|
406
|
-
|
|
407
|
-
**Endpoints:**
|
|
408
|
-
- `/` - Web UI
|
|
409
|
-
- `/admin/api/generate-token` - POST: Generate token
|
|
410
|
-
- `/admin/api/validate-token` - POST: Validate token
|
|
411
|
-
- `/admin/api/service-info` - GET: Service info
|
|
412
|
-
- `/admin/api/auth-status` - GET: Auth status
|
|
1
|
+
# Authentication
|
|
2
|
+
|
|
3
|
+
## Auth Types
|
|
4
|
+
|
|
5
|
+
```typescript
|
|
6
|
+
type TTokenType = 'permanent' | 'JWT';
|
|
7
|
+
|
|
8
|
+
interface AuthResult {
|
|
9
|
+
success: boolean;
|
|
10
|
+
error?: string;
|
|
11
|
+
authType?: 'permanentServerTokens' | 'jwtToken' | 'basic' | 'custom';
|
|
12
|
+
username?: string;
|
|
13
|
+
isTokenDecrypted?: boolean;
|
|
14
|
+
payload?: any;
|
|
15
|
+
}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Token Operations
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
import { generateToken } from 'fa-mcp-sdk';
|
|
22
|
+
|
|
23
|
+
// Generate JWT (liveTimeSec = seconds until expiry)
|
|
24
|
+
const token = generateToken('john_doe', 3600, { role: 'admin' }); // 1 hour
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Test Authentication
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { getAuthHeadersForTests, McpHttpClient, appConfig } from 'fa-mcp-sdk';
|
|
31
|
+
|
|
32
|
+
// Auto-generates auth headers based on config (permanent → basic → JWT priority)
|
|
33
|
+
const headers = getAuthHeadersForTests();
|
|
34
|
+
|
|
35
|
+
// Usage
|
|
36
|
+
const response = await fetch(`http://localhost:${appConfig.webServer.port}/mcp`, {
|
|
37
|
+
method: 'POST',
|
|
38
|
+
headers: { 'Content-Type': 'application/json', ...headers },
|
|
39
|
+
body: JSON.stringify({ jsonrpc: '2.0', method: 'tools/call', params: {...}, id: 1 })
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// With test client
|
|
43
|
+
const client = new McpHttpClient('http://localhost:3000');
|
|
44
|
+
const result = await client.callTool('tool', args, getAuthHeadersForTests());
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Admin Panel Authentication
|
|
48
|
+
|
|
49
|
+
The admin panel (`/admin`) supports 4 authentication types and can be configured with a single type or multiple types:
|
|
50
|
+
|
|
51
|
+
```yaml
|
|
52
|
+
# config/default.yaml
|
|
53
|
+
adminPanel:
|
|
54
|
+
enabled: true
|
|
55
|
+
# Single type (string)
|
|
56
|
+
authType: 'basic'
|
|
57
|
+
# Or multiple types (array) — login page shows tabs to choose
|
|
58
|
+
authType: ['jwtToken', 'basic']
|
|
59
|
+
# 'none' / null / empty array / not set — panel opens WITHOUT authentication
|
|
60
|
+
# (convenience for local development; do NOT use in production).
|
|
61
|
+
authType: 'none'
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Supported types:** `permanentServerTokens`, `basic`, `jwtToken`, `ntlm`, `none` (= open access)
|
|
65
|
+
|
|
66
|
+
When multiple types are configured (e.g. `['jwtToken', 'basic']`), the login page shows tabs:
|
|
67
|
+
- **Token** tab — for `permanentServerTokens` and `jwtToken` authentication
|
|
68
|
+
- **Login** tab — for `basic` (username/password) authentication
|
|
69
|
+
|
|
70
|
+
For `permanentServerTokens`, `basic`, `jwtToken` — credentials are taken from `webServer.auth` section.
|
|
71
|
+
For `ntlm` — uses AD configuration from `ad.domains` section.
|
|
72
|
+
|
|
73
|
+
### JWT Admin Requirement: `payload.allow === 'gen-token'`
|
|
74
|
+
|
|
75
|
+
When `jwtToken` is used to authenticate into the admin panel (`/admin`), the decoded
|
|
76
|
+
payload **must** contain `allow: 'gen-token'`. Any JWT without this claim is rejected
|
|
77
|
+
with `401` even if it decrypts and is not expired. This prevents short-lived JWTs
|
|
78
|
+
issued for other purposes (e.g. the Agent Tester page auto-fills a JWT into its
|
|
79
|
+
`Authorization` header — TTL is configurable via `agentTester.tokenTTLSec`, default
|
|
80
|
+
30 min) from being replayed against `/admin` to mint arbitrary long-lived tokens.
|
|
81
|
+
|
|
82
|
+
Generate an admin-capable JWT by including `allow=gen-token` in the payload:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
node scripts/generate-jwt.js -u admin -ttl 30d -p "allow=gen-token"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`permanentServerTokens` and `basic` admin auth are unaffected — this check applies
|
|
89
|
+
only to the `jwtToken` admin path.
|
|
90
|
+
|
|
91
|
+
## Token Generator Authorization
|
|
92
|
+
|
|
93
|
+
Protect `/admin/` page with custom authorization:
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
import { TokenGenAuthHandler, initADGroupChecker } from 'fa-mcp-sdk';
|
|
97
|
+
|
|
98
|
+
const { isUserInGroup } = initADGroupChecker();
|
|
99
|
+
|
|
100
|
+
const tokenGenAuthHandler: TokenGenAuthHandler = async (input) => {
|
|
101
|
+
// input: { user, domain?, payload?, authType }
|
|
102
|
+
if (input.authType === 'ntlm') {
|
|
103
|
+
const isAdmin = await isUserInGroup(input.user, 'TokenGeneratorAdmins');
|
|
104
|
+
if (!isAdmin) return { success: false, error: `User not authorized` };
|
|
105
|
+
}
|
|
106
|
+
return { success: true, username: input.user };
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const serverData: McpServerData = { ..., tokenGenAuthHandler };
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Multi-Authentication System
|
|
113
|
+
|
|
114
|
+
### createAuthMW()
|
|
115
|
+
|
|
116
|
+
Universal middleware supporting all auth methods:
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
import { createAuthMW } from 'fa-mcp-sdk';
|
|
120
|
+
|
|
121
|
+
const authMW = createAuthMW();
|
|
122
|
+
app.use('/api', authMW);
|
|
123
|
+
|
|
124
|
+
app.get('/api/protected', (req, res) => {
|
|
125
|
+
const authInfo = (req as any).authInfo;
|
|
126
|
+
res.json({ authType: authInfo?.authType, username: authInfo?.username });
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Advanced options
|
|
130
|
+
const authMW = createAuthMW({
|
|
131
|
+
mcpPaths: ['/mcp', '/messages', '/sse'], // Paths with public resource access
|
|
132
|
+
logConfig: true, // Log config on first request
|
|
133
|
+
});
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### getMultiAuthError()
|
|
137
|
+
|
|
138
|
+
Programmatic auth checking:
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
import { getMultiAuthError } from 'fa-mcp-sdk';
|
|
142
|
+
|
|
143
|
+
const authError = await getMultiAuthError(req);
|
|
144
|
+
if (authError) {
|
|
145
|
+
return res.status(authError.code).send(authError.message);
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Custom Authentication
|
|
150
|
+
|
|
151
|
+
`customAuthValidator` runs **before** standard auth (`Authorization` header check).
|
|
152
|
+
|
|
153
|
+
**Execution order:**
|
|
154
|
+
1. `customAuthValidator` is called first
|
|
155
|
+
2. If `success: true` → request is allowed, standard auth is **skipped**
|
|
156
|
+
3. If `success: false` → falls through to standard auth (`permanentServerTokens` / `basic` / `jwtToken`)
|
|
157
|
+
4. If standard auth also fails → 401
|
|
158
|
+
|
|
159
|
+
This allows using service-specific credentials (e.g. `x-api-key`, `x-service-token`) as an alternative
|
|
160
|
+
to the MCP `Authorization` header, without disabling standard auth entirely.
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
import { CustomAuthValidator, AuthResult } from 'fa-mcp-sdk';
|
|
164
|
+
|
|
165
|
+
// Example: bypass MCP auth if service-specific header is present
|
|
166
|
+
const customValidator: CustomAuthValidator = async (req): Promise<AuthResult> => {
|
|
167
|
+
const apiKey = req.headers['x-api-key'];
|
|
168
|
+
if (apiKey && await validateApiKey(apiKey)) {
|
|
169
|
+
return { success: true, authType: 'custom', username: 'api-user' };
|
|
170
|
+
}
|
|
171
|
+
// Return false → falls through to standard Authorization header check
|
|
172
|
+
return { success: false, error: 'No valid API key' };
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const serverData: McpServerData = { ..., customAuthValidator: customValidator };
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**Example: allow requests with upstream service headers to bypass MCP auth**
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
// Clients that pass x-service-token OR x-username+x-password are allowed in
|
|
182
|
+
// without an MCP Authorization token. Clients without these headers still
|
|
183
|
+
// need a valid Authorization header (permanentToken / basic / JWT).
|
|
184
|
+
const serviceHeadersValidator: CustomAuthValidator = (req) => {
|
|
185
|
+
const h = req.headers as Record<string, string>;
|
|
186
|
+
if (h['x-service-token'] || (h['x-username'] && h['x-password'])) {
|
|
187
|
+
return { success: true, authType: 'custom' };
|
|
188
|
+
}
|
|
189
|
+
return { success: false, error: 'No service credentials and no MCP Authorization token' };
|
|
190
|
+
};
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
> **Note:** `customAuthValidator` receives a request with **normalized** (lowercased) header names.
|
|
194
|
+
> `authInfo` is **not** set on `req` when the validator runs — it is set by the middleware only after
|
|
195
|
+
> successful authentication completes.
|
|
196
|
+
|
|
197
|
+
## Agent Tester Authentication
|
|
198
|
+
|
|
199
|
+
Protect the Agent Tester (`/agent-tester/*`) with `agentTester.useAuth`:
|
|
200
|
+
|
|
201
|
+
```yaml
|
|
202
|
+
agentTester:
|
|
203
|
+
useAuth: true # Require authentication for Agent Tester
|
|
204
|
+
webServer:
|
|
205
|
+
auth:
|
|
206
|
+
enabled: true
|
|
207
|
+
permanentServerTokens: ['my-secret-token']
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Or via ENV: `AGENT_TESTER_USE_AUTH=true`
|
|
211
|
+
|
|
212
|
+
When `useAuth` is `true`, the full multi-auth middleware is applied to Agent Tester routes — the same authentication used for MCP endpoints (`permanentServerTokens` / `basic` / `jwtToken` / `custom`). Browser users see a login dialog; headless clients pass `Authorization` header directly.
|
|
213
|
+
|
|
214
|
+
See [Agent Tester docs](08-agent-tester-and-headless-api.md#authentication-agenttesteruseauth) for details on the login flow, session management, and API endpoints.
|
|
215
|
+
|
|
216
|
+
## AD Group Checking
|
|
217
|
+
|
|
218
|
+
### Configuration
|
|
219
|
+
|
|
220
|
+
```yaml
|
|
221
|
+
# config/local.yaml
|
|
222
|
+
ad:
|
|
223
|
+
domains:
|
|
224
|
+
MYDOMAIN:
|
|
225
|
+
default: true
|
|
226
|
+
controllers: ['ldap://dc1.corp.com']
|
|
227
|
+
username: 'svc_account@corp.com'
|
|
228
|
+
password: '***'
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Usage
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
import { initADGroupChecker } from 'fa-mcp-sdk';
|
|
235
|
+
|
|
236
|
+
const { isUserInGroup, groupChecker } = initADGroupChecker();
|
|
237
|
+
|
|
238
|
+
const isAdmin = await isUserInGroup('john.doe', 'Admins');
|
|
239
|
+
groupChecker.clearCache(); // Clear if needed
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
## JWT IP Restriction
|
|
243
|
+
|
|
244
|
+
When `webServer.auth.jwtToken.isCheckIP` is `true`, JWT tokens can include an `ip` field in their payload to restrict which client IPs may use the token.
|
|
245
|
+
|
|
246
|
+
### Configuration
|
|
247
|
+
|
|
248
|
+
```yaml
|
|
249
|
+
# config/default.yaml
|
|
250
|
+
webServer:
|
|
251
|
+
auth:
|
|
252
|
+
jwtToken:
|
|
253
|
+
isCheckIP: true # Enable IP checking
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Token Generation
|
|
257
|
+
|
|
258
|
+
When generating a token (via admin UI or `generateToken()`), include the `ip` field in the payload:
|
|
259
|
+
|
|
260
|
+
```typescript
|
|
261
|
+
const token = generateToken('john_doe', 3600, {
|
|
262
|
+
service: 'my-mcp-server',
|
|
263
|
+
ip: '192.168.1.100, 10.0.0.0/24',
|
|
264
|
+
});
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
The `ip` field is a string of IP addresses and/or CIDR masks, separated by commas, semicolons, or spaces.
|
|
268
|
+
|
|
269
|
+
In the admin UI (`/admin`), there is a dedicated "Allowed IP addresses" field for entering these values.
|
|
270
|
+
|
|
271
|
+
### Behavior
|
|
272
|
+
|
|
273
|
+
| `isCheckIP` | `payload.ip` | Client IP | Result |
|
|
274
|
+
|-------------|-------------|-----------|--------|
|
|
275
|
+
| `false` | any | any | IP not checked |
|
|
276
|
+
| `true` | empty/missing | any | IP not checked (pass-through) |
|
|
277
|
+
| `true` | `10.0.0.0/24` | `10.0.0.5` | Allowed |
|
|
278
|
+
| `true` | `10.0.0.0/24` | `192.168.1.1` | Denied |
|
|
279
|
+
| `true` | `192.168.1.1, 10.0.0.0/8` | `10.5.5.5` | Allowed (covered by /8) |
|
|
280
|
+
|
|
281
|
+
Supported formats: IPv4, IPv6, CIDR notation (e.g., `10.0.0.0/24`, `fe80::/10`), IPv4-mapped IPv6 (`::ffff:192.168.1.1`).
|
|
282
|
+
|
|
283
|
+
## Client Examples
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
# Permanent token
|
|
287
|
+
curl -H "Authorization: Bearer server-token-1" http://localhost:3000/mcp
|
|
288
|
+
|
|
289
|
+
# JWT
|
|
290
|
+
curl -H "Authorization: Bearer eyJ..." http://localhost:3000/mcp
|
|
291
|
+
|
|
292
|
+
# Basic Auth
|
|
293
|
+
curl -H "Authorization: Basic $(echo -n 'admin:password' | base64)" http://localhost:3000/mcp
|
|
294
|
+
|
|
295
|
+
# Custom headers
|
|
296
|
+
curl -H "X-API-Key: custom-key" http://localhost:3000/mcp
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
## CLI Token Generator
|
|
300
|
+
|
|
301
|
+
Generate JWT tokens from the command line without starting the server:
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
node scripts/generate-jwt.js -u <username> -ttl <duration> [-s <service>] [-p <params>]
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
| Option | ENV | Description |
|
|
308
|
+
|--------|-----|-------------|
|
|
309
|
+
| `-u`, `--username` | `JWT_PAYLOAD_USERNAME` | Username (required) |
|
|
310
|
+
| `-ttl` | `JWT_TTL` | Token lifetime: `<N>s` \| `<N>m` \| `<N>d` \| `<N>y` (required) |
|
|
311
|
+
| `-s`, `--service-name` | `JWT_PAYLOAD_SERVICE_NAME` | Service name (optional) |
|
|
312
|
+
| `-p`, `--params` | `JWT_PAYLOAD_PARAMS` | Extra payload `key=value;key=value` (optional) |
|
|
313
|
+
|
|
314
|
+
The `encryptKey` is read from config `webServer.auth.jwtToken.encryptKey` (via `config/local.yaml` or ENV `WS_TOKEN_ENCRYPT_KEY`).
|
|
315
|
+
|
|
316
|
+
**Examples:**
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
# 30-day token with service name
|
|
320
|
+
node scripts/generate-jwt.js -u admin -ttl 30d -s my-mcp-server
|
|
321
|
+
|
|
322
|
+
# 1-year token with extra payload fields
|
|
323
|
+
node scripts/generate-jwt.js -u svc-account -ttl 1y -p "role=admin;team=backend"
|
|
324
|
+
|
|
325
|
+
# Via environment variables
|
|
326
|
+
JWT_PAYLOAD_USERNAME=admin JWT_TTL=8d node scripts/generate-jwt.js
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
## Claude Code Skill: `/gen-jwt`
|
|
330
|
+
|
|
331
|
+
Interactive JWT token generation via Claude Code. Invoke with `/gen-jwt` or natural language (e.g. "сгенерируй токен для vpupkin на 1 год").
|
|
332
|
+
|
|
333
|
+
The skill parses your request for `username`, `ttl`, `service`, `request` (ticket ID), `ip`, and extra key=value params. If required params (`username`, `ttl`) are missing, it asks interactively. Optional params (`request`, `ip`) are prompted once with an option to skip.
|
|
334
|
+
|
|
335
|
+
Runs `node scripts/generate-jwt.js` under the hood.
|
|
336
|
+
|
|
337
|
+
**Example:**
|
|
338
|
+
```
|
|
339
|
+
/gen-jwt для vpupkin, по заявке REQ-12345, на 1 год, role=admin, IP 10.0.0.0/24
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
Skill location: `.claude/skills/gen-jwt/SKILL.md`
|
|
343
|
+
|
|
344
|
+
## JWT Generation API
|
|
345
|
+
|
|
346
|
+
HTTP endpoint for programmatic JWT token generation. Disabled by default.
|
|
347
|
+
|
|
348
|
+
### Configuration
|
|
349
|
+
|
|
350
|
+
```yaml
|
|
351
|
+
# config/default.yaml
|
|
352
|
+
webServer:
|
|
353
|
+
genJwtApiEnable: true # Enable POST /gen-jwt endpoint
|
|
354
|
+
auth:
|
|
355
|
+
enabled: true # Auth must be enabled — endpoint requires valid credentials
|
|
356
|
+
jwtToken:
|
|
357
|
+
encryptKey: 'your-secret-key-here'
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
Or via ENV: `WS_GEN_JWT_API_ENABLE=true`
|
|
361
|
+
|
|
362
|
+
### Usage
|
|
363
|
+
|
|
364
|
+
```bash
|
|
365
|
+
# POST /gen-jwt with any configured auth method
|
|
366
|
+
curl -X POST http://localhost:3000/gen-jwt \
|
|
367
|
+
-H "Content-Type: application/json" \
|
|
368
|
+
-u "admin:password" \
|
|
369
|
+
-d '{
|
|
370
|
+
"username": "testuser",
|
|
371
|
+
"ttl": "30d",
|
|
372
|
+
"service": "my-mcp-server",
|
|
373
|
+
"params": "role=admin;team=backend"
|
|
374
|
+
}'
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### Request Body
|
|
378
|
+
|
|
379
|
+
| Field | Type | Required | Description |
|
|
380
|
+
|-------|------|----------|-------------|
|
|
381
|
+
| `username` | string | yes | Username for the token |
|
|
382
|
+
| `ttl` | string | yes | Token lifetime: `<N>s` \| `<N>m` \| `<N>d` \| `<N>y` |
|
|
383
|
+
| `service` | string | no | Service name |
|
|
384
|
+
| `params` | string \| object | no | Extra payload. String: `"key=value;key=value"`. Object: `{"key": "value"}` |
|
|
385
|
+
|
|
386
|
+
### Response
|
|
387
|
+
|
|
388
|
+
```json
|
|
389
|
+
{
|
|
390
|
+
"success": true,
|
|
391
|
+
"token": "1718000000000.a1b2c3...",
|
|
392
|
+
"user": "testuser",
|
|
393
|
+
"expire": "2025-07-10T12:00:00.000Z",
|
|
394
|
+
"ttlSeconds": 2592000
|
|
395
|
+
}
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
## Token Generator App
|
|
399
|
+
|
|
400
|
+
```typescript
|
|
401
|
+
import { generateTokenApp } from 'fa-mcp-sdk';
|
|
402
|
+
|
|
403
|
+
generateTokenApp(); // Port 3030
|
|
404
|
+
generateTokenApp(1234); // Custom port
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
**Endpoints:**
|
|
408
|
+
- `/` - Web UI
|
|
409
|
+
- `/admin/api/generate-token` - POST: Generate token
|
|
410
|
+
- `/admin/api/validate-token` - POST: Validate token
|
|
411
|
+
- `/admin/api/service-info` - GET: Service info
|
|
412
|
+
- `/admin/api/auth-status` - GET: Auth status
|