forked from doctly/switchboard
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmcp-auth.js
More file actions
49 lines (44 loc) · 1.83 KB
/
Copy pathmcp-auth.js
File metadata and controls
49 lines (44 loc) · 1.83 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// Pure auth helpers for the per-session MCP WebSocket server.
// Extracted from mcp-bridge.js so we can unit-test them without
// spinning up a real ws server.
const crypto = require('crypto');
// MCP clients (the Claude CLI) never send an Origin header. Browsers always
// do. Rejecting any request that carries one mitigates DNS-rebind / browser
// pivot attacks against 127.0.0.1.
function originAllowed(headers) {
if (!headers) return true;
// Case-insensitive lookup — the ws library lowercases headers, but be
// defensive in case this is called from somewhere that doesn't.
for (const k of Object.keys(headers)) {
if (k.toLowerCase() === 'origin' && headers[k]) return false;
}
return true;
}
// Constant-time token compare. Returns false on any length mismatch,
// missing value, or non-string input — never throws.
function tokenMatches(provided, expected) {
if (typeof expected !== 'string' || expected.length === 0) return false;
if (typeof provided !== 'string' || provided.length === 0) return false;
try {
const a = Buffer.from(provided);
const b = Buffer.from(expected);
if (a.length !== b.length) return false;
return crypto.timingSafeEqual(a, b);
} catch {
return false;
}
}
// Full handshake check used on every incoming ws connection. Returns
// { ok: true } or { ok: false, code, reason } suitable for ws.close().
function validateHandshake(req, expectedToken) {
const headers = req && req.headers ? req.headers : {};
if (!originAllowed(headers)) {
return { ok: false, code: 4003, reason: 'Origin not allowed' };
}
const provided = headers['x-claude-code-ide-authorization'] || '';
if (!tokenMatches(provided, expectedToken)) {
return { ok: false, code: 4001, reason: 'Unauthorized' };
}
return { ok: true };
}
module.exports = { originAllowed, tokenMatches, validateHandshake };