@kya-os/mcp-i
Version:
The TypeScript MCP framework with identity features built-in
83 lines (82 loc) • 3.01 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.apiKeyAuthMiddleware = apiKeyAuthMiddleware;
const zod_1 = require("zod");
const apiKeyAuthMiddlewareConfigSchema = zod_1.z
.object({
apiKey: zod_1.z.string().optional(),
headerName: zod_1.z.string().optional(),
validateApiKey: zod_1.z
.function()
.args(zod_1.z.string())
.returns(zod_1.z.promise(zod_1.z.boolean()))
.optional(),
})
.strict()
.refine((config) => config.apiKey !== undefined || config.validateApiKey !== undefined, {
message: "Either 'apiKey' or 'validateApiKey' must be provided",
})
.refine((config) => !(config.apiKey !== undefined && config.validateApiKey !== undefined), {
message: "'apiKey' and 'validateApiKey' are mutually exclusive - provide only one",
});
const errorMessage = "Unauthorized: Missing or invalid API key";
/**
* Middleware to authenticate requests using an API key.
* @param config - Configuration object containing either a static API key or a function to validate the API key.
* @returns Express middleware function.
*
* @example
* ```ts
* const middleware = apiKeyAuthMiddleware({
* apiKey: process.env.API_KEY!,
* });
* ```
*
* @example
* ```ts
* const middleware = apiKeyAuthMiddleware({
* validateApiKey: async (key) => {
* return key === process.env.API_KEY!;
* },
* });
* ```
*/
function apiKeyAuthMiddleware(config) {
// To do, we can maybe work on better error handling here, like typing the error messages etc
const response = apiKeyAuthMiddlewareConfigSchema.safeParse(config);
if (!response.success) {
const hasApiKey = "apiKey" in config;
const hasValidateApiKey = "validateApiKey" in config;
if (hasApiKey && hasValidateApiKey) {
throw new Error("'apiKey' and 'validateApiKey' are mutually exclusive - provide only one");
}
else if (!hasApiKey && !hasValidateApiKey) {
throw new Error("Either 'apiKey' or 'validateApiKey' must be provided");
}
else {
throw new Error(`Invalid configuration: ${response.error.message}`);
}
}
const headerName = config.headerName ?? "x-api-key";
const apiKey = "apiKey" in config ? config.apiKey : undefined;
const validateApiKey = "validateApiKey" in config ? config.validateApiKey : undefined;
return async (req, res, next) => {
const apiKeyHeader = req.header(headerName);
if (!apiKeyHeader) {
res.status(401).json({ error: errorMessage });
return;
}
if ("apiKey" in config && apiKeyHeader !== apiKey) {
res.status(401).json({ error: errorMessage });
return;
}
if (validateApiKey) {
const isValid = await validateApiKey(apiKeyHeader);
if (!isValid) {
res.status(401).json({ error: errorMessage });
return;
}
}
next();
};
}