mcp-framework
Version:
Framework for building Model Context Protocol (MCP) servers in Typescript
74 lines (73 loc) • 2.36 kB
JavaScript
import { logger } from "../../core/Logger.js";
import { DEFAULT_AUTH_ERROR } from "../types.js";
export const DEFAULT_API_KEY_HEADER_NAME = "X-API-Key";
/**
* API key-based authentication provider
*/
export class APIKeyAuthProvider {
config;
constructor(config) {
this.config = {
headerName: DEFAULT_API_KEY_HEADER_NAME,
...config
};
if (!this.config.keys?.length) {
throw new Error("At least one API key is required");
}
}
/**
* Get the number of configured API keys
*/
getKeyCount() {
return this.config.keys.length;
}
/**
* Get the configured header name
*/
getHeaderName() {
return this.config.headerName;
}
async authenticate(req) {
logger.debug(`API Key auth attempt from ${req.socket.remoteAddress}`);
logger.debug(`All request headers: ${JSON.stringify(req.headers, null, 2)}`);
const headerVariations = [
this.config.headerName,
this.config.headerName.toLowerCase(),
this.config.headerName.toUpperCase(),
'x-api-key',
'X-API-KEY',
'X-Api-Key'
];
logger.debug(`Looking for header variations: ${headerVariations.join(', ')}`);
let apiKey;
let matchedHeader;
for (const [key, value] of Object.entries(req.headers)) {
const lowerKey = key.toLowerCase();
if (headerVariations.some(h => h.toLowerCase() === lowerKey)) {
apiKey = Array.isArray(value) ? value[0] : value;
matchedHeader = key;
break;
}
}
if (!apiKey) {
logger.debug(`API Key header missing}`);
logger.debug(`Available headers: ${Object.keys(req.headers).join(', ')}`);
return false;
}
logger.debug(`Found API key in header: ${matchedHeader}`);
for (const validKey of this.config.keys) {
if (apiKey === validKey) {
logger.debug(`API Key authentication successful`);
return true;
}
}
logger.debug(`Invalid API Key provided`);
return false;
}
getAuthError() {
return {
...DEFAULT_AUTH_ERROR,
message: "Invalid API key"
};
}
}