@ai-capabilities-suite/mcp-debugger-core
Version:
Core debugging engine for Node.js and TypeScript applications. Provides Inspector Protocol integration, breakpoint management, variable inspection, execution control, profiling, hang detection, and source map support.
251 lines • 7.85 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.AuthManager = void 0;
const crypto_1 = require("crypto");
/**
* Manages authentication for MCP connections
* Provides token-based authentication and API key validation
*/
class AuthManager {
constructor(config = { enabled: false }) {
this.tokens = new Map();
this.apiKeys = new Map();
this.validTokens = new Set(); // Pre-configured valid tokens
this.config = {
tokenExpirationMs: 60 * 60 * 1000, // 1 hour default
...config,
};
// Add pre-configured tokens if provided
if (config.tokens) {
config.tokens.forEach((token) => this.validTokens.add(token));
}
}
/**
* Generate a new authentication token
* @param metadata Optional metadata to associate with the token
* @returns The generated token
*/
generateToken(metadata) {
const token = (0, crypto_1.randomBytes)(32).toString('hex');
const now = new Date();
const expiresAt = new Date(now.getTime() + (this.config.tokenExpirationMs || 60 * 60 * 1000));
const authToken = {
token,
createdAt: now,
expiresAt,
metadata,
};
this.tokens.set(token, authToken);
return authToken;
}
/**
* Authenticate a token and return detailed result
* @param token The token to authenticate
* @returns Authentication result with status and error message
*/
authenticate(token) {
if (!this.config.enabled) {
return { authenticated: true }; // Authentication disabled
}
// Check for null/undefined/empty tokens
if (!token || token.trim() === '') {
return {
authenticated: false,
error: 'Authentication token is required',
};
}
// Use constant-time comparison to prevent timing attacks
const isValid = this.constantTimeCompare(token);
if (!isValid) {
return {
authenticated: false,
error: 'Invalid authentication token',
};
}
return { authenticated: true };
}
/**
* Constant-time comparison to prevent timing attacks
* @param token The token to check
* @returns True if the token is valid
*/
constantTimeCompare(token) {
// Check pre-configured tokens
if (this.validTokens.size > 0) {
let isValid = false;
for (const validToken of this.validTokens) {
// Use crypto.timingSafeEqual for constant-time comparison
const tokenBuffer = Buffer.from(token);
const validBuffer = Buffer.from(validToken);
// Pad buffers to same length to avoid timing leaks
const maxLen = Math.max(tokenBuffer.length, validBuffer.length);
const paddedToken = Buffer.alloc(maxLen);
const paddedValid = Buffer.alloc(maxLen);
tokenBuffer.copy(paddedToken);
validBuffer.copy(paddedValid);
try {
if (paddedToken.equals(paddedValid)) {
isValid = true;
}
}
catch {
// Continue checking other tokens
}
}
return isValid;
}
// Check generated tokens
return this.validateToken(token);
}
/**
* Validate an authentication token
* @param token The token to validate
* @returns True if the token is valid and not expired
*/
validateToken(token) {
if (!this.config.enabled) {
return true; // Authentication disabled
}
const authToken = this.tokens.get(token);
if (!authToken) {
return false;
}
// Check if token is expired
if (new Date() > authToken.expiresAt) {
this.tokens.delete(token);
return false;
}
return true;
}
/**
* Revoke an authentication token
* @param token The token to revoke
* @returns True if the token was found and revoked
*/
revokeToken(token) {
return this.tokens.delete(token);
}
/**
* Clean up expired tokens
*/
cleanupExpiredTokens() {
const now = new Date();
for (const [token, authToken] of this.tokens.entries()) {
if (now > authToken.expiresAt) {
this.tokens.delete(token);
}
}
}
/**
* Create an API key
* @param name Name for the API key
* @param metadata Optional metadata to associate with the key
* @returns The created API key (with the unhashed key)
*/
createApiKey(name, metadata) {
const key = (0, crypto_1.randomBytes)(32).toString('hex');
const hashedKey = this.hashApiKey(key);
const apiKey = {
key, // Return the unhashed key only once
hashedKey,
name,
createdAt: new Date(),
enabled: true,
metadata,
};
this.apiKeys.set(hashedKey, apiKey);
return apiKey;
}
/**
* Validate an API key
* @param key The API key to validate
* @returns True if the key is valid and enabled
*/
validateApiKey(key) {
if (!this.config.enabled || !this.config.requireApiKey) {
return true; // API key validation disabled
}
const hashedKey = this.hashApiKey(key);
const apiKey = this.apiKeys.get(hashedKey);
if (!apiKey) {
return false;
}
return apiKey.enabled;
}
/**
* Revoke an API key
* @param key The API key to revoke
* @returns True if the key was found and revoked
*/
revokeApiKey(key) {
const hashedKey = this.hashApiKey(key);
const apiKey = this.apiKeys.get(hashedKey);
if (!apiKey) {
return false;
}
apiKey.enabled = false;
return true;
}
/**
* Delete an API key
* @param key The API key to delete
* @returns True if the key was found and deleted
*/
deleteApiKey(key) {
const hashedKey = this.hashApiKey(key);
return this.apiKeys.delete(hashedKey);
}
/**
* Get all API keys (without the actual key values)
* @returns Array of API keys with hashed keys
*/
getAllApiKeys() {
return Array.from(this.apiKeys.values()).map(({ key, ...rest }) => rest);
}
/**
* Hash an API key for secure storage
* @param key The API key to hash
* @returns The hashed key
*/
hashApiKey(key) {
return (0, crypto_1.createHash)('sha256').update(key).digest('hex');
}
/**
* Check if authentication is enabled
* @returns True if authentication is enabled
*/
isEnabled() {
return this.config.enabled;
}
/**
* Check if API key validation is required
* @returns True if API key validation is required
*/
requiresApiKey() {
return this.config.enabled && (this.config.requireApiKey || false);
}
/**
* Get the number of active tokens
* @returns The number of active tokens
*/
getActiveTokenCount() {
this.cleanupExpiredTokens();
return this.tokens.size;
}
/**
* Get the number of API keys
* @returns The number of API keys
*/
getApiKeyCount() {
return this.apiKeys.size;
}
/**
* Clear all tokens and API keys
*/
clear() {
this.tokens.clear();
this.apiKeys.clear();
}
}
exports.AuthManager = AuthManager;
//# sourceMappingURL=auth-manager.js.map