@lifeaitools/clauth 1.5.10 → 1.5.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/cli/commands/serve.js
CHANGED
|
@@ -2472,7 +2472,7 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
2472
2472
|
const CORS = {
|
|
2473
2473
|
"Access-Control-Allow-Origin": "*",
|
|
2474
2474
|
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
|
2475
|
-
"Access-Control-Allow-Headers": "Content-Type, Authorization, Mcp-Session-Id",
|
|
2475
|
+
"Access-Control-Allow-Headers": "Content-Type, Authorization, Mcp-Session-Id, mcp-protocol-version, mcp-session-id",
|
|
2476
2476
|
};
|
|
2477
2477
|
|
|
2478
2478
|
// ── MCP SSE session tracking ──────────────────────────────
|
|
@@ -2505,7 +2505,16 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
2505
2505
|
// ── OAuth provider (self-contained for claude.ai MCP) ──────
|
|
2506
2506
|
const oauthClients = new Map(); // client_id → { client_secret, redirect_uris, client_name }
|
|
2507
2507
|
const oauthCodes = new Map(); // code → { client_id, redirect_uri, code_challenge, expires }
|
|
2508
|
-
|
|
2508
|
+
|
|
2509
|
+
// Persist tokens to disk so daemon restarts don't invalidate claude.ai sessions
|
|
2510
|
+
const TOKENS_FILE = path.join(os.tmpdir(), "clauth-oauth-tokens.json");
|
|
2511
|
+
function loadTokens() {
|
|
2512
|
+
try { return new Set(JSON.parse(fs.readFileSync(TOKENS_FILE, "utf8"))); } catch { return new Set(); }
|
|
2513
|
+
}
|
|
2514
|
+
function saveTokens(set) {
|
|
2515
|
+
try { fs.writeFileSync(TOKENS_FILE, JSON.stringify([...set])); } catch {}
|
|
2516
|
+
}
|
|
2517
|
+
const oauthTokens = loadTokens(); // active access tokens — persisted across restarts
|
|
2509
2518
|
|
|
2510
2519
|
// Pre-generate a stable client for claude.ai (shown at startup)
|
|
2511
2520
|
const OAUTH_CLIENT_ID = crypto.randomBytes(16).toString("hex");
|
|
@@ -2998,6 +3007,7 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
2998
3007
|
oauthCodes.delete(body.code);
|
|
2999
3008
|
const accessToken = crypto.randomBytes(32).toString("hex");
|
|
3000
3009
|
oauthTokens.add(accessToken);
|
|
3010
|
+
saveTokens(oauthTokens);
|
|
3001
3011
|
|
|
3002
3012
|
const logMsg = `[${new Date().toISOString()}] OAuth: token issued for client ${stored.client_id}\n`;
|
|
3003
3013
|
try { fs.appendFileSync(LOG_FILE, logMsg); } catch {}
|
|
@@ -3023,6 +3033,16 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
3023
3033
|
// POST /mcp|/gws|/clauth — requires Bearer token; returns 401 to trigger OAuth flow
|
|
3024
3034
|
if (method === "POST" && isMcpPath) {
|
|
3025
3035
|
const authHeader = req.headers.authorization;
|
|
3036
|
+
// Detailed auth logging — helps diagnose claude.ai post-token failures
|
|
3037
|
+
const authLogMsg = [
|
|
3038
|
+
`[${new Date().toISOString()}] MCP POST ${reqPath}`,
|
|
3039
|
+
` Authorization: ${authHeader ? (authHeader.startsWith("Bearer ") ? `Bearer ${authHeader.slice(7, 15)}… (known=${oauthTokens.has(authHeader.slice(7))})` : authHeader.slice(0, 30) + "…") : "(none)"}`,
|
|
3040
|
+
` mcp-protocol-version: ${req.headers["mcp-protocol-version"] || "(not set)"}`,
|
|
3041
|
+
` accept: ${req.headers["accept"] || "(not set)"}`,
|
|
3042
|
+
` x-forwarded-for: ${req.headers["x-forwarded-for"] || "(not set)"}`,
|
|
3043
|
+
].join("\n") + "\n";
|
|
3044
|
+
try { fs.appendFileSync(LOG_FILE, authLogMsg); } catch {}
|
|
3045
|
+
|
|
3026
3046
|
if (!authHeader || !authHeader.startsWith("Bearer ")) {
|
|
3027
3047
|
const base = oauthBase();
|
|
3028
3048
|
// Path-specific resource metadata URL so claude.ai gets the right resource URI
|
|
@@ -3036,6 +3056,8 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
3036
3056
|
}
|
|
3037
3057
|
const token = authHeader.slice(7);
|
|
3038
3058
|
if (!oauthTokens.has(token)) {
|
|
3059
|
+
const badTokenLog = `[${new Date().toISOString()}] OAuth: REJECTED token ${token.slice(0,8)}… (pool size=${oauthTokens.size})\n`;
|
|
3060
|
+
try { fs.appendFileSync(LOG_FILE, badTokenLog); } catch {}
|
|
3039
3061
|
res.writeHead(401, { "Content-Type": "application/json", ...CORS });
|
|
3040
3062
|
return res.end(JSON.stringify({ error: "invalid_token" }));
|
|
3041
3063
|
}
|
package/package.json
CHANGED
|
@@ -169,7 +169,7 @@ async function handleRevoke(sb: any, body: any, mh: string) {
|
|
|
169
169
|
|
|
170
170
|
async function handleStatus(sb: any, body: any, mh: string) {
|
|
171
171
|
let q = sb.from("clauth_services")
|
|
172
|
-
.select("name, label, key_type, enabled, vault_key, last_retrieved, last_rotated, created_at, project")
|
|
172
|
+
.select("name, label, key_type, enabled, vault_key, last_retrieved, last_rotated, created_at, project, description")
|
|
173
173
|
.order("project", { ascending: true, nullsFirst: true })
|
|
174
174
|
.order("name");
|
|
175
175
|
if (body.project) q = q.eq("project", body.project);
|