mcp-server-db2i 1.2.1 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +90 -308
- package/dist/auth/authMiddleware.d.ts +66 -0
- package/dist/auth/authMiddleware.d.ts.map +1 -0
- package/dist/auth/authMiddleware.js +217 -0
- package/dist/auth/authMiddleware.js.map +1 -0
- package/dist/auth/index.d.ts +9 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +10 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/tokenManager.d.ts +114 -0
- package/dist/auth/tokenManager.d.ts.map +1 -0
- package/dist/auth/tokenManager.js +255 -0
- package/dist/auth/tokenManager.js.map +1 -0
- package/dist/auth/types.d.ts +103 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +10 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/config.d.ts +128 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +185 -0
- package/dist/config.js.map +1 -1
- package/dist/db/connection.d.ts +53 -3
- package/dist/db/connection.d.ts.map +1 -1
- package/dist/db/connection.js +154 -15
- package/dist/db/connection.js.map +1 -1
- package/dist/db/queries.d.ts +29 -6
- package/dist/db/queries.d.ts.map +1 -1
- package/dist/db/queries.js +35 -12
- package/dist/db/queries.js.map +1 -1
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +97 -42
- package/dist/index.js.map +1 -1
- package/dist/openapi.d.ts +49 -0
- package/dist/openapi.d.ts.map +1 -0
- package/dist/openapi.js +634 -0
- package/dist/openapi.js.map +1 -0
- package/dist/server.d.ts +26 -2
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +68 -22
- package/dist/server.js.map +1 -1
- package/dist/tools/metadata.d.ts +10 -0
- package/dist/tools/metadata.d.ts.map +1 -1
- package/dist/tools/metadata.js +10 -6
- package/dist/tools/metadata.js.map +1 -1
- package/dist/tools/query.d.ts +4 -0
- package/dist/tools/query.d.ts.map +1 -1
- package/dist/tools/query.js +5 -3
- package/dist/tools/query.js.map +1 -1
- package/dist/transports/http.d.ts +26 -0
- package/dist/transports/http.d.ts.map +1 -0
- package/dist/transports/http.js +552 -0
- package/dist/transports/http.js.map +1 -0
- package/dist/transports/index.d.ts +8 -0
- package/dist/transports/index.d.ts.map +1 -0
- package/dist/transports/index.js +8 -0
- package/dist/transports/index.js.map +1 -0
- package/dist/transports/sessionManager.d.ts +106 -0
- package/dist/transports/sessionManager.d.ts.map +1 -0
- package/dist/transports/sessionManager.js +260 -0
- package/dist/transports/sessionManager.js.map +1 -0
- package/package.json +9 -5
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication Middleware for HTTP Transport
|
|
3
|
+
*
|
|
4
|
+
* Express middleware to validate Bearer tokens on protected routes.
|
|
5
|
+
* Supports multiple authentication modes:
|
|
6
|
+
* - 'required': Full /auth flow with per-user DB credentials (default)
|
|
7
|
+
* - 'token': Pre-shared static token, uses env DB credentials
|
|
8
|
+
* - 'none': No authentication required, uses env DB credentials
|
|
9
|
+
*/
|
|
10
|
+
import { timingSafeEqual } from 'node:crypto';
|
|
11
|
+
import { getTokenManager } from './tokenManager.js';
|
|
12
|
+
import { createChildLogger } from '../utils/logger.js';
|
|
13
|
+
import { getHttpConfig } from '../config.js';
|
|
14
|
+
const log = createChildLogger({ component: 'auth-middleware' });
|
|
15
|
+
/**
|
|
16
|
+
* Extract Bearer token from Authorization header
|
|
17
|
+
*/
|
|
18
|
+
function extractBearerToken(authHeader) {
|
|
19
|
+
if (!authHeader) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
const parts = authHeader.split(' ');
|
|
23
|
+
if (parts.length !== 2 || parts[0].toLowerCase() !== 'bearer') {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
return parts[1];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Authentication middleware for protected routes
|
|
30
|
+
*
|
|
31
|
+
* Behavior depends on MCP_AUTH_MODE:
|
|
32
|
+
* - 'required': Validates Bearer token from /auth flow, attaches token session
|
|
33
|
+
* - 'token': Validates Bearer token against static MCP_AUTH_TOKEN
|
|
34
|
+
* - 'none': Skips authentication entirely
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* app.post('/mcp', authMiddleware, (req, res) => {
|
|
38
|
+
* const session = (req as AuthenticatedRequest).tokenSession;
|
|
39
|
+
* // Use session.config for DB connection (only in 'required' mode)
|
|
40
|
+
* });
|
|
41
|
+
*/
|
|
42
|
+
export function authMiddleware(req, res, next) {
|
|
43
|
+
const httpConfig = getHttpConfig();
|
|
44
|
+
// No auth mode - skip authentication entirely
|
|
45
|
+
if (httpConfig.authMode === 'none') {
|
|
46
|
+
log.debug({ path: req.path, method: req.method, authMode: 'none' }, 'Auth disabled, allowing request');
|
|
47
|
+
return next();
|
|
48
|
+
}
|
|
49
|
+
// Token mode - validate against static token
|
|
50
|
+
if (httpConfig.authMode === 'token') {
|
|
51
|
+
const authHeader = req.headers.authorization;
|
|
52
|
+
const token = extractBearerToken(authHeader);
|
|
53
|
+
if (!token) {
|
|
54
|
+
log.debug({ path: req.path, method: req.method }, 'Missing or invalid Authorization header (token mode)');
|
|
55
|
+
res.status(401).json({
|
|
56
|
+
error: 'unauthorized',
|
|
57
|
+
error_description: 'Missing or invalid Authorization header. Use: Authorization: Bearer <token>',
|
|
58
|
+
});
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
// Use constant-time comparison to prevent timing attacks
|
|
62
|
+
const staticToken = httpConfig.staticToken ?? '';
|
|
63
|
+
const tokenBuffer = Buffer.from(token);
|
|
64
|
+
const staticTokenBuffer = Buffer.from(staticToken);
|
|
65
|
+
const tokensMatch = tokenBuffer.length === staticTokenBuffer.length &&
|
|
66
|
+
timingSafeEqual(tokenBuffer, staticTokenBuffer);
|
|
67
|
+
if (tokensMatch) {
|
|
68
|
+
log.debug({ path: req.path, method: req.method, authMode: 'token' }, 'Static token validated');
|
|
69
|
+
// Store token for session keying (will use global config for DB)
|
|
70
|
+
req.authToken = token;
|
|
71
|
+
return next();
|
|
72
|
+
}
|
|
73
|
+
log.debug({ path: req.path, method: req.method }, 'Invalid static token');
|
|
74
|
+
res.status(401).json({
|
|
75
|
+
error: 'invalid_token',
|
|
76
|
+
error_description: 'Invalid authentication token',
|
|
77
|
+
});
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
// Required mode - full token validation with per-user credentials
|
|
81
|
+
const authHeader = req.headers.authorization;
|
|
82
|
+
const token = extractBearerToken(authHeader);
|
|
83
|
+
if (!token) {
|
|
84
|
+
log.debug({ path: req.path, method: req.method }, 'Missing or invalid Authorization header');
|
|
85
|
+
res.status(401).json({
|
|
86
|
+
error: 'unauthorized',
|
|
87
|
+
error_description: 'Missing or invalid Authorization header. Use: Authorization: Bearer <token>',
|
|
88
|
+
});
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const tokenManager = getTokenManager();
|
|
92
|
+
const result = tokenManager.validateToken(token);
|
|
93
|
+
if (!result.valid || !result.session) {
|
|
94
|
+
log.debug({ path: req.path, method: req.method, error: result.error }, 'Token validation failed');
|
|
95
|
+
res.status(401).json({
|
|
96
|
+
error: 'invalid_token',
|
|
97
|
+
error_description: result.error ?? 'Token validation failed',
|
|
98
|
+
});
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
// Attach session to request
|
|
102
|
+
req.tokenSession = result.session;
|
|
103
|
+
req.authToken = token;
|
|
104
|
+
log.debug({
|
|
105
|
+
path: req.path,
|
|
106
|
+
method: req.method,
|
|
107
|
+
user: result.session.config.username,
|
|
108
|
+
host: result.session.config.hostname,
|
|
109
|
+
}, 'Request authenticated');
|
|
110
|
+
next();
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Get the current auth mode for use in route handlers
|
|
114
|
+
*/
|
|
115
|
+
export function getAuthModeFromConfig() {
|
|
116
|
+
return getHttpConfig().authMode;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Optional authentication middleware
|
|
120
|
+
*
|
|
121
|
+
* Similar to authMiddleware but doesn't require authentication.
|
|
122
|
+
* If a valid token is provided, attaches the session to the request.
|
|
123
|
+
* If no token or invalid token, continues without error.
|
|
124
|
+
*
|
|
125
|
+
* Useful for endpoints that work with or without authentication.
|
|
126
|
+
*/
|
|
127
|
+
export function optionalAuthMiddleware(req, res, next) {
|
|
128
|
+
const authHeader = req.headers.authorization;
|
|
129
|
+
const token = extractBearerToken(authHeader);
|
|
130
|
+
if (token) {
|
|
131
|
+
const tokenManager = getTokenManager();
|
|
132
|
+
const result = tokenManager.validateToken(token);
|
|
133
|
+
if (result.valid && result.session) {
|
|
134
|
+
req.tokenSession = result.session;
|
|
135
|
+
req.authToken = token;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
next();
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Rate limiting middleware for auth endpoints
|
|
142
|
+
*
|
|
143
|
+
* Simple in-memory rate limiter to prevent brute force attacks.
|
|
144
|
+
* Tracks failed attempts by IP address.
|
|
145
|
+
*/
|
|
146
|
+
const authAttempts = new Map();
|
|
147
|
+
const AUTH_RATE_LIMIT = {
|
|
148
|
+
maxAttempts: 5,
|
|
149
|
+
windowMs: 60000, // 1 minute
|
|
150
|
+
};
|
|
151
|
+
/**
|
|
152
|
+
* Get client IP from request
|
|
153
|
+
*
|
|
154
|
+
* Uses Express's req.ip which respects the 'trust proxy' setting.
|
|
155
|
+
* If proxy is trusted, req.ip will contain the client IP from X-Forwarded-For.
|
|
156
|
+
* If proxy is not trusted, req.ip will be the direct connection IP.
|
|
157
|
+
*
|
|
158
|
+
* To trust proxy headers, set app.set('trust proxy', true) or configure
|
|
159
|
+
* specific trusted proxies. Without this, X-Forwarded-For headers are ignored.
|
|
160
|
+
*/
|
|
161
|
+
function getClientIp(req) {
|
|
162
|
+
// Use Express's req.ip which respects 'trust proxy' setting
|
|
163
|
+
// This prevents IP spoofing when proxy is not trusted
|
|
164
|
+
return req.ip ?? req.socket.remoteAddress ?? 'unknown';
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Auth rate limiting middleware
|
|
168
|
+
*
|
|
169
|
+
* Limits authentication attempts per IP to prevent brute force.
|
|
170
|
+
* Should be applied to the /auth endpoint.
|
|
171
|
+
*/
|
|
172
|
+
export function authRateLimitMiddleware(req, res, next) {
|
|
173
|
+
const ip = getClientIp(req);
|
|
174
|
+
const now = Date.now();
|
|
175
|
+
// Clean up expired entries
|
|
176
|
+
const entry = authAttempts.get(ip);
|
|
177
|
+
if (entry && entry.resetAt < now) {
|
|
178
|
+
authAttempts.delete(ip);
|
|
179
|
+
}
|
|
180
|
+
const current = authAttempts.get(ip);
|
|
181
|
+
if (current && current.count >= AUTH_RATE_LIMIT.maxAttempts) {
|
|
182
|
+
const retryAfter = Math.ceil((current.resetAt - now) / 1000);
|
|
183
|
+
log.warn({ ip, attempts: current.count }, 'Auth rate limit exceeded');
|
|
184
|
+
res.status(429).json({
|
|
185
|
+
error: 'too_many_requests',
|
|
186
|
+
error_description: `Too many authentication attempts. Try again in ${retryAfter} seconds.`,
|
|
187
|
+
retry_after: retryAfter,
|
|
188
|
+
});
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
next();
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Record a failed auth attempt for rate limiting
|
|
195
|
+
*/
|
|
196
|
+
export function recordFailedAuthAttempt(req) {
|
|
197
|
+
const ip = getClientIp(req);
|
|
198
|
+
const now = Date.now();
|
|
199
|
+
const current = authAttempts.get(ip);
|
|
200
|
+
if (current && current.resetAt > now) {
|
|
201
|
+
current.count++;
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
authAttempts.set(ip, {
|
|
205
|
+
count: 1,
|
|
206
|
+
resetAt: now + AUTH_RATE_LIMIT.windowMs,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Clear rate limit for an IP (on successful auth)
|
|
212
|
+
*/
|
|
213
|
+
export function clearAuthRateLimit(req) {
|
|
214
|
+
const ip = getClientIp(req);
|
|
215
|
+
authAttempts.delete(ip);
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=authMiddleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"authMiddleware.js","sourceRoot":"","sources":["../../src/auth/authMiddleware.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication module for HTTP transport
|
|
3
|
+
*
|
|
4
|
+
* Exports all auth-related types, middleware, and utilities.
|
|
5
|
+
*/
|
|
6
|
+
export type { AuthRequest, AuthResponse, AuthErrorResponse, TokenSession, TokenValidationResult, AuthValidationResult, } from './types.js';
|
|
7
|
+
export { getTokenManager, TokenManager, type SessionCleanupCallback } from './tokenManager.js';
|
|
8
|
+
export { authMiddleware, optionalAuthMiddleware, authRateLimitMiddleware, recordFailedAuthAttempt, clearAuthRateLimit, type AuthenticatedRequest, } from './authMiddleware.js';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,YAAY,EACV,WAAW,EACX,YAAY,EACZ,iBAAiB,EACjB,YAAY,EACZ,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,KAAK,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAG/F,OAAO,EACL,cAAc,EACd,sBAAsB,EACtB,uBAAuB,EACvB,uBAAuB,EACvB,kBAAkB,EAClB,KAAK,oBAAoB,GAC1B,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication module for HTTP transport
|
|
3
|
+
*
|
|
4
|
+
* Exports all auth-related types, middleware, and utilities.
|
|
5
|
+
*/
|
|
6
|
+
// Token Manager
|
|
7
|
+
export { getTokenManager, TokenManager } from './tokenManager.js';
|
|
8
|
+
// Middleware
|
|
9
|
+
export { authMiddleware, optionalAuthMiddleware, authRateLimitMiddleware, recordFailedAuthAttempt, clearAuthRateLimit, } from './authMiddleware.js';
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAYH,gBAAgB;AAChB,OAAO,EAAE,eAAe,EAAE,YAAY,EAA+B,MAAM,mBAAmB,CAAC;AAE/F,aAAa;AACb,OAAO,EACL,cAAc,EACd,sBAAsB,EACtB,uBAAuB,EACvB,uBAAuB,EACvB,kBAAkB,GAEnB,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token Manager for HTTP Authentication
|
|
3
|
+
*
|
|
4
|
+
* Handles secure token generation, validation, session storage, and cleanup.
|
|
5
|
+
* Tokens are stored in memory with automatic cleanup of expired sessions.
|
|
6
|
+
*/
|
|
7
|
+
import type { DB2iConfig } from '../config.js';
|
|
8
|
+
import type { TokenSession, TokenValidationResult } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Callback type for session cleanup notification
|
|
11
|
+
* Used to close associated resources (e.g., connection pools) when tokens expire
|
|
12
|
+
*/
|
|
13
|
+
export type SessionCleanupCallback = (token: string) => Promise<void> | void;
|
|
14
|
+
/**
|
|
15
|
+
* Token Manager singleton for managing authentication tokens
|
|
16
|
+
*/
|
|
17
|
+
declare class TokenManager {
|
|
18
|
+
private static instance;
|
|
19
|
+
private sessions;
|
|
20
|
+
private cleanupTimer;
|
|
21
|
+
private readonly cleanupIntervalMs;
|
|
22
|
+
private cleanupCallback;
|
|
23
|
+
private constructor();
|
|
24
|
+
/**
|
|
25
|
+
* Get the singleton instance
|
|
26
|
+
*/
|
|
27
|
+
static getInstance(): TokenManager;
|
|
28
|
+
/**
|
|
29
|
+
* Generate a cryptographically secure token
|
|
30
|
+
* Uses 32 bytes (256 bits) of randomness encoded as base64url
|
|
31
|
+
*/
|
|
32
|
+
private generateTokenString;
|
|
33
|
+
/**
|
|
34
|
+
* Create a new token session
|
|
35
|
+
*
|
|
36
|
+
* @param config - DB2i configuration for this session
|
|
37
|
+
* @param durationSeconds - Optional custom token duration
|
|
38
|
+
* @returns The generated token and session info
|
|
39
|
+
*/
|
|
40
|
+
createSession(config: DB2iConfig, durationSeconds?: number): {
|
|
41
|
+
token: string;
|
|
42
|
+
expiresAt: Date;
|
|
43
|
+
expiresIn: number;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Validate a token and return the session
|
|
47
|
+
*
|
|
48
|
+
* @param token - The token to validate
|
|
49
|
+
* @returns Validation result with session if valid
|
|
50
|
+
*/
|
|
51
|
+
validateToken(token: string): TokenValidationResult;
|
|
52
|
+
/**
|
|
53
|
+
* Get a session by token without validation
|
|
54
|
+
* Used internally when token is already validated
|
|
55
|
+
*/
|
|
56
|
+
getSession(token: string): TokenSession | undefined;
|
|
57
|
+
/**
|
|
58
|
+
* Update the MCP session ID for a token
|
|
59
|
+
* Used when a stateful MCP session is established
|
|
60
|
+
*/
|
|
61
|
+
setMcpSessionId(token: string, mcpSessionId: string): void;
|
|
62
|
+
/**
|
|
63
|
+
* Revoke a token
|
|
64
|
+
*
|
|
65
|
+
* @param token - The token to revoke
|
|
66
|
+
* @returns true if token was found and revoked
|
|
67
|
+
*/
|
|
68
|
+
revokeToken(token: string): Promise<boolean>;
|
|
69
|
+
/**
|
|
70
|
+
* Get session statistics
|
|
71
|
+
*/
|
|
72
|
+
getStats(): {
|
|
73
|
+
totalSessions: number;
|
|
74
|
+
activeSessions: number;
|
|
75
|
+
expiredSessions: number;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Clean up expired sessions
|
|
79
|
+
*/
|
|
80
|
+
private cleanupExpiredSessions;
|
|
81
|
+
/**
|
|
82
|
+
* Start the cleanup timer
|
|
83
|
+
*/
|
|
84
|
+
private startCleanupTimer;
|
|
85
|
+
/**
|
|
86
|
+
* Stop the cleanup timer and clear all sessions
|
|
87
|
+
* Used for graceful shutdown
|
|
88
|
+
*/
|
|
89
|
+
shutdown(): Promise<void>;
|
|
90
|
+
/**
|
|
91
|
+
* Check if a new session can be created (advisory)
|
|
92
|
+
*
|
|
93
|
+
* Note: This is an advisory check. In Node.js single-threaded environment,
|
|
94
|
+
* there's no race condition between synchronous operations. However, if async
|
|
95
|
+
* operations occur between calling this method and createSession(), the count
|
|
96
|
+
* could change. The hard limit is enforced in createSession() which throws
|
|
97
|
+
* an error if the limit is exceeded.
|
|
98
|
+
*/
|
|
99
|
+
canCreateSession(): boolean;
|
|
100
|
+
/**
|
|
101
|
+
* Set a callback to be called when sessions are cleaned up
|
|
102
|
+
* Used to close associated resources (e.g., connection pools)
|
|
103
|
+
*
|
|
104
|
+
* @param callback - Function called with the token when a session is removed
|
|
105
|
+
*/
|
|
106
|
+
setCleanupCallback(callback: SessionCleanupCallback): void;
|
|
107
|
+
/**
|
|
108
|
+
* Internal method to notify about session cleanup
|
|
109
|
+
*/
|
|
110
|
+
private notifyCleanup;
|
|
111
|
+
}
|
|
112
|
+
export declare function getTokenManager(): TokenManager;
|
|
113
|
+
export { TokenManager };
|
|
114
|
+
//# sourceMappingURL=tokenManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenManager.d.ts","sourceRoot":"","sources":["../../src/auth/tokenManager.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG/C,OAAO,KAAK,EACV,YAAY,EACZ,qBAAqB,EACtB,MAAM,YAAY,CAAC;AAIpB;;;GAGG;AACH,MAAM,MAAM,sBAAsB,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAE7E;;GAEG;AACH,cAAM,YAAY;IAChB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAe;IACtC,OAAO,CAAC,QAAQ,CAAwC;IACxD,OAAO,CAAC,YAAY,CAA+B;IACnD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,eAAe,CAAuC;IAE9D,OAAO;IAKP;;OAEG;IACH,MAAM,CAAC,WAAW,IAAI,YAAY;IAOlC;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAI3B;;;;;;OAMG;IACH,aAAa,CACX,MAAM,EAAE,UAAU,EAClB,eAAe,CAAC,EAAE,MAAM,GACvB;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,IAAI,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE;IAsCxD;;;;;OAKG;IACH,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,qBAAqB;IA4BnD;;;OAGG;IACH,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAInD;;;OAGG;IACH,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI;IAW1D;;;;;OAKG;IACG,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAqBlD;;OAEG;IACH,QAAQ,IAAI;QACV,aAAa,EAAE,MAAM,CAAC;QACtB,cAAc,EAAE,MAAM,CAAC;QACvB,eAAe,EAAE,MAAM,CAAC;KACzB;IAoBD;;OAEG;YACW,sBAAsB;IA0BpC;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAkBzB;;;OAGG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAkB/B;;;;;;;;OAQG;IACH,gBAAgB,IAAI,OAAO;IAK3B;;;;;OAKG;IACH,kBAAkB,CAAC,QAAQ,EAAE,sBAAsB,GAAG,IAAI;IAK1D;;OAEG;YACW,aAAa;CAS5B;AAGD,wBAAgB,eAAe,IAAI,YAAY,CAE9C;AAGD,OAAO,EAAE,YAAY,EAAE,CAAC"}
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token Manager for HTTP Authentication
|
|
3
|
+
*
|
|
4
|
+
* Handles secure token generation, validation, session storage, and cleanup.
|
|
5
|
+
* Tokens are stored in memory with automatic cleanup of expired sessions.
|
|
6
|
+
*/
|
|
7
|
+
import crypto from 'node:crypto';
|
|
8
|
+
import { getHttpConfig } from '../config.js';
|
|
9
|
+
import { createChildLogger } from '../utils/logger.js';
|
|
10
|
+
const log = createChildLogger({ component: 'token-manager' });
|
|
11
|
+
/**
|
|
12
|
+
* Token Manager singleton for managing authentication tokens
|
|
13
|
+
*/
|
|
14
|
+
class TokenManager {
|
|
15
|
+
static instance;
|
|
16
|
+
sessions = new Map();
|
|
17
|
+
cleanupTimer = null;
|
|
18
|
+
cleanupIntervalMs = 60000; // 1 minute
|
|
19
|
+
cleanupCallback = null;
|
|
20
|
+
constructor() {
|
|
21
|
+
this.startCleanupTimer();
|
|
22
|
+
log.debug('Token manager initialized');
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Get the singleton instance
|
|
26
|
+
*/
|
|
27
|
+
static getInstance() {
|
|
28
|
+
if (!TokenManager.instance) {
|
|
29
|
+
TokenManager.instance = new TokenManager();
|
|
30
|
+
}
|
|
31
|
+
return TokenManager.instance;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Generate a cryptographically secure token
|
|
35
|
+
* Uses 32 bytes (256 bits) of randomness encoded as base64url
|
|
36
|
+
*/
|
|
37
|
+
generateTokenString() {
|
|
38
|
+
return crypto.randomBytes(32).toString('base64url');
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Create a new token session
|
|
42
|
+
*
|
|
43
|
+
* @param config - DB2i configuration for this session
|
|
44
|
+
* @param durationSeconds - Optional custom token duration
|
|
45
|
+
* @returns The generated token and session info
|
|
46
|
+
*/
|
|
47
|
+
createSession(config, durationSeconds) {
|
|
48
|
+
const httpConfig = getHttpConfig();
|
|
49
|
+
// Check max sessions limit
|
|
50
|
+
if (this.sessions.size >= httpConfig.maxSessions) {
|
|
51
|
+
throw new Error(`Maximum concurrent sessions (${httpConfig.maxSessions}) reached. Please try again later.`);
|
|
52
|
+
}
|
|
53
|
+
const token = this.generateTokenString();
|
|
54
|
+
const now = new Date();
|
|
55
|
+
const expiresIn = durationSeconds ?? httpConfig.tokenExpiry;
|
|
56
|
+
const expiresAt = new Date(now.getTime() + expiresIn * 1000);
|
|
57
|
+
const session = {
|
|
58
|
+
token,
|
|
59
|
+
config,
|
|
60
|
+
createdAt: now,
|
|
61
|
+
expiresAt,
|
|
62
|
+
lastUsedAt: now,
|
|
63
|
+
};
|
|
64
|
+
this.sessions.set(token, session);
|
|
65
|
+
log.info({
|
|
66
|
+
sessionCount: this.sessions.size,
|
|
67
|
+
expiresIn,
|
|
68
|
+
host: config.hostname,
|
|
69
|
+
// Note: username intentionally omitted for PII compliance
|
|
70
|
+
}, 'Token session created');
|
|
71
|
+
return { token, expiresAt, expiresIn };
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Validate a token and return the session
|
|
75
|
+
*
|
|
76
|
+
* @param token - The token to validate
|
|
77
|
+
* @returns Validation result with session if valid
|
|
78
|
+
*/
|
|
79
|
+
validateToken(token) {
|
|
80
|
+
if (!token || typeof token !== 'string') {
|
|
81
|
+
return { valid: false, error: 'Invalid token format' };
|
|
82
|
+
}
|
|
83
|
+
const session = this.sessions.get(token);
|
|
84
|
+
if (!session) {
|
|
85
|
+
log.debug({ tokenPrefix: token.substring(0, 8) }, 'Token not found');
|
|
86
|
+
return { valid: false, error: 'Token not found or expired' };
|
|
87
|
+
}
|
|
88
|
+
// Check expiration
|
|
89
|
+
if (new Date() > session.expiresAt) {
|
|
90
|
+
log.debug({ tokenPrefix: token.substring(0, 8), expiredAt: session.expiresAt }, 'Token expired');
|
|
91
|
+
this.sessions.delete(token);
|
|
92
|
+
return { valid: false, error: 'Token expired' };
|
|
93
|
+
}
|
|
94
|
+
// Update last used timestamp
|
|
95
|
+
session.lastUsedAt = new Date();
|
|
96
|
+
return { valid: true, session };
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Get a session by token without validation
|
|
100
|
+
* Used internally when token is already validated
|
|
101
|
+
*/
|
|
102
|
+
getSession(token) {
|
|
103
|
+
return this.sessions.get(token);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Update the MCP session ID for a token
|
|
107
|
+
* Used when a stateful MCP session is established
|
|
108
|
+
*/
|
|
109
|
+
setMcpSessionId(token, mcpSessionId) {
|
|
110
|
+
const session = this.sessions.get(token);
|
|
111
|
+
if (session) {
|
|
112
|
+
session.mcpSessionId = mcpSessionId;
|
|
113
|
+
log.debug({ tokenPrefix: token.substring(0, 8), mcpSessionId }, 'MCP session ID associated with token');
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Revoke a token
|
|
118
|
+
*
|
|
119
|
+
* @param token - The token to revoke
|
|
120
|
+
* @returns true if token was found and revoked
|
|
121
|
+
*/
|
|
122
|
+
async revokeToken(token) {
|
|
123
|
+
const session = this.sessions.get(token);
|
|
124
|
+
if (!session) {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
this.sessions.delete(token);
|
|
128
|
+
// Notify cleanup callback to close associated resources
|
|
129
|
+
await this.notifyCleanup(token);
|
|
130
|
+
log.info({
|
|
131
|
+
tokenPrefix: token.substring(0, 8),
|
|
132
|
+
sessionCount: this.sessions.size,
|
|
133
|
+
}, 'Token revoked');
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Get session statistics
|
|
138
|
+
*/
|
|
139
|
+
getStats() {
|
|
140
|
+
const now = new Date();
|
|
141
|
+
let activeSessions = 0;
|
|
142
|
+
let expiredSessions = 0;
|
|
143
|
+
for (const session of this.sessions.values()) {
|
|
144
|
+
if (now <= session.expiresAt) {
|
|
145
|
+
activeSessions++;
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
expiredSessions++;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return {
|
|
152
|
+
totalSessions: this.sessions.size,
|
|
153
|
+
activeSessions,
|
|
154
|
+
expiredSessions,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Clean up expired sessions
|
|
159
|
+
*/
|
|
160
|
+
async cleanupExpiredSessions() {
|
|
161
|
+
const now = new Date();
|
|
162
|
+
const expiredTokens = [];
|
|
163
|
+
for (const [token, session] of this.sessions.entries()) {
|
|
164
|
+
if (now > session.expiresAt) {
|
|
165
|
+
expiredTokens.push(token);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (expiredTokens.length > 0) {
|
|
169
|
+
for (const token of expiredTokens) {
|
|
170
|
+
this.sessions.delete(token);
|
|
171
|
+
// Notify cleanup callback to close associated resources
|
|
172
|
+
await this.notifyCleanup(token);
|
|
173
|
+
}
|
|
174
|
+
log.info({
|
|
175
|
+
expiredCount: expiredTokens.length,
|
|
176
|
+
remainingCount: this.sessions.size,
|
|
177
|
+
}, 'Cleaned up expired sessions');
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Start the cleanup timer
|
|
182
|
+
*/
|
|
183
|
+
startCleanupTimer() {
|
|
184
|
+
if (this.cleanupTimer) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
this.cleanupTimer = setInterval(() => {
|
|
188
|
+
this.cleanupExpiredSessions();
|
|
189
|
+
}, this.cleanupIntervalMs);
|
|
190
|
+
// Don't block process exit
|
|
191
|
+
this.cleanupTimer.unref();
|
|
192
|
+
log.debug({ intervalMs: this.cleanupIntervalMs }, 'Session cleanup timer started');
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Stop the cleanup timer and clear all sessions
|
|
196
|
+
* Used for graceful shutdown
|
|
197
|
+
*/
|
|
198
|
+
async shutdown() {
|
|
199
|
+
if (this.cleanupTimer) {
|
|
200
|
+
clearInterval(this.cleanupTimer);
|
|
201
|
+
this.cleanupTimer = null;
|
|
202
|
+
}
|
|
203
|
+
// Notify cleanup callback for all remaining sessions
|
|
204
|
+
const tokens = Array.from(this.sessions.keys());
|
|
205
|
+
for (const token of tokens) {
|
|
206
|
+
await this.notifyCleanup(token);
|
|
207
|
+
}
|
|
208
|
+
const sessionCount = this.sessions.size;
|
|
209
|
+
this.sessions.clear();
|
|
210
|
+
log.info({ clearedSessions: sessionCount }, 'Token manager shutdown');
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Check if a new session can be created (advisory)
|
|
214
|
+
*
|
|
215
|
+
* Note: This is an advisory check. In Node.js single-threaded environment,
|
|
216
|
+
* there's no race condition between synchronous operations. However, if async
|
|
217
|
+
* operations occur between calling this method and createSession(), the count
|
|
218
|
+
* could change. The hard limit is enforced in createSession() which throws
|
|
219
|
+
* an error if the limit is exceeded.
|
|
220
|
+
*/
|
|
221
|
+
canCreateSession() {
|
|
222
|
+
const httpConfig = getHttpConfig();
|
|
223
|
+
return this.sessions.size < httpConfig.maxSessions;
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Set a callback to be called when sessions are cleaned up
|
|
227
|
+
* Used to close associated resources (e.g., connection pools)
|
|
228
|
+
*
|
|
229
|
+
* @param callback - Function called with the token when a session is removed
|
|
230
|
+
*/
|
|
231
|
+
setCleanupCallback(callback) {
|
|
232
|
+
this.cleanupCallback = callback;
|
|
233
|
+
log.debug('Session cleanup callback registered');
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Internal method to notify about session cleanup
|
|
237
|
+
*/
|
|
238
|
+
async notifyCleanup(token) {
|
|
239
|
+
if (this.cleanupCallback) {
|
|
240
|
+
try {
|
|
241
|
+
await this.cleanupCallback(token);
|
|
242
|
+
}
|
|
243
|
+
catch (err) {
|
|
244
|
+
log.error({ err, tokenPrefix: token.substring(0, 8) }, 'Error in cleanup callback');
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
// Export singleton getter
|
|
250
|
+
export function getTokenManager() {
|
|
251
|
+
return TokenManager.getInstance();
|
|
252
|
+
}
|
|
253
|
+
// Export the class type for testing
|
|
254
|
+
export { TokenManager };
|
|
255
|
+
//# sourceMappingURL=tokenManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenManager.js","sourceRoot":"","sources":["../../src/auth/tokenManager.ts"],"names":[],"mappings":"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"}
|