66 lines
2.6 KiB
JavaScript
66 lines
2.6 KiB
JavaScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
|
|
import { createMcpAuthMiddleware } from "../auth/mcpAuthMiddleware.js";
|
|
|
|
async function invoke(middleware, headers = {}, path = "/mcp") {
|
|
const req = { headers: { ...headers }, path };
|
|
const response = {};
|
|
const res = {
|
|
status(code) { response.status = code; return this; },
|
|
setHeader() { return this; },
|
|
end(body) { response.body = JSON.parse(body); return this; },
|
|
};
|
|
let nextCalled = false;
|
|
await middleware(req, res, () => { nextCalled = true; });
|
|
return { req, response, nextCalled };
|
|
}
|
|
|
|
test("rejects missing and invalid MCP secrets", async () => {
|
|
const middleware = createMcpAuthMiddleware({ validateToken: async () => null });
|
|
assert.equal((await invoke(middleware)).response.status, 401);
|
|
assert.equal((await invoke(middleware, { "x-mcp-secret": "bad" })).response.status, 401);
|
|
});
|
|
|
|
test("replaces spoofed identity and enforces project scope", async () => {
|
|
const middleware = createMcpAuthMiddleware({
|
|
validateToken: async () => ({ user: "owner", project: "allowed" }),
|
|
});
|
|
const result = await invoke(middleware, {
|
|
"x-mcp-secret": "valid",
|
|
"x-acai-user": "attacker",
|
|
"x-project-name": "other",
|
|
});
|
|
assert.equal(result.nextCalled, true);
|
|
assert.equal(result.req.headers["x-acai-user"], "owner");
|
|
assert.equal(result.req.headers["x-project-name"], "allowed");
|
|
});
|
|
|
|
test("requires project context for user-wide tokens", async () => {
|
|
const middleware = createMcpAuthMiddleware({
|
|
validateToken: async () => ({ user: "owner", project: "" }),
|
|
});
|
|
assert.equal((await invoke(middleware, { "x-mcp-secret": "valid" })).response.status, 400);
|
|
const accepted = await invoke(middleware, {
|
|
"x-mcp-secret": "valid",
|
|
"x-project-name": "demo",
|
|
});
|
|
assert.equal(accepted.nextCalled, true);
|
|
});
|
|
|
|
test("revocation is effective on the next request in an existing session", async () => {
|
|
let revoked = false;
|
|
const middleware = createMcpAuthMiddleware({
|
|
validateToken: async () => revoked ? null : ({ user: "owner", project: "demo" }),
|
|
});
|
|
const headers = { "x-mcp-secret": "valid", "x-project-name": "demo" };
|
|
assert.equal((await invoke(middleware, headers)).nextCalled, true);
|
|
revoked = true;
|
|
assert.equal((await invoke(middleware, headers)).response.status, 401);
|
|
});
|
|
|
|
test("does not apply MCP auth to legacy SSE paths", async () => {
|
|
const middleware = createMcpAuthMiddleware({ validateToken: async () => null });
|
|
assert.equal((await invoke(middleware, { "x-acai-user": "legacy" }, "/sse")).nextCalled, true);
|
|
});
|