@lifeaitools/clauth 1.5.13 → 1.5.15

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.
@@ -2880,17 +2880,19 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
2880
2880
 
2881
2881
  // ── OAuth Discovery (RFC 9728 + RFC 8414) ──────────────
2882
2882
  if (reqPath.startsWith("/.well-known/oauth-protected-resource")) {
2883
- const base = oauthBase();
2884
- // Derive resource URL from the well-known path suffix
2885
- // /.well-known/oauth-protected-resource → /mcp
2886
- // /.well-known/oauth-protected-resource/mcp → /mcp
2887
- // /.well-known/oauth-protected-resource/gws → /gws
2888
- // /.well-known/oauth-protected-resource/clauth → /clauth
2883
+ // Only advertise OAuth for /mcp — /gws and /clauth are open (no OAuth).
2884
+ // Advertising OAuth on open paths causes claude.ai to do an OAuth dance,
2885
+ // get a token, then have no retry context (since the original 200 wasn't a 401).
2889
2886
  const suffix = reqPath.replace("/.well-known/oauth-protected-resource", "").replace(/^\//, "") || "mcp";
2890
- const resourcePath = suffix === "sse" ? "mcp" : suffix;
2887
+ if (suffix !== "mcp" && suffix !== "") {
2888
+ // Path-specific OAuth metadata requested for a non-mcp path — 404 it
2889
+ res.writeHead(404, { "Content-Type": "application/json", ...CORS });
2890
+ return res.end(JSON.stringify({ error: "not_found" }));
2891
+ }
2892
+ const base = oauthBase();
2891
2893
  res.writeHead(200, { "Content-Type": "application/json", ...CORS });
2892
2894
  return res.end(JSON.stringify({
2893
- resource: `${base}/${resourcePath}`,
2895
+ resource: `${base}/mcp`,
2894
2896
  authorization_servers: [base],
2895
2897
  scopes_supported: ["mcp:tools"],
2896
2898
  bearer_methods_supported: ["header"],
@@ -3029,41 +3031,23 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
3029
3031
  return "clauth";
3030
3032
  }
3031
3033
 
3032
- // ── MCP OAuth-protected endpoint (for claude.ai web) ──
3033
- // POST /mcp|/gws|/clauth requires Bearer token; returns 401 to trigger OAuth flow
3034
+ // ── MCP endpoint auth log all incoming POSTs, accept with or without token ──
3035
+ // /gws and /clauth are open (no OAuth gate) tunnel URL is the shared secret.
3036
+ // OAuth flow is still supported (tokens accepted when present) but not required.
3034
3037
  if (method === "POST" && isMcpPath) {
3035
3038
  const authHeader = req.headers.authorization;
3036
- // Detailed auth logging — helps diagnose claude.ai post-token failures
3037
3039
  const authLogMsg = [
3038
3040
  `[${new Date().toISOString()}] MCP POST ${reqPath}`,
3039
3041
  ` Authorization: ${authHeader ? (authHeader.startsWith("Bearer ") ? `Bearer ${authHeader.slice(7, 15)}… (known=${oauthTokens.has(authHeader.slice(7))})` : authHeader.slice(0, 30) + "…") : "(none)"}`,
3040
3042
  ` mcp-protocol-version: ${req.headers["mcp-protocol-version"] || "(not set)"}`,
3041
3043
  ` accept: ${req.headers["accept"] || "(not set)"}`,
3042
- ` x-forwarded-for: ${req.headers["x-forwarded-for"] || "(not set)"}`,
3043
3044
  ].join("\n") + "\n";
3044
3045
  try { fs.appendFileSync(LOG_FILE, authLogMsg); } catch {}
3045
-
3046
- if (!authHeader || !authHeader.startsWith("Bearer ")) {
3047
- const base = oauthBase();
3048
- // Path-specific resource metadata URL so claude.ai gets the right resource URI
3049
- const pathName = reqPath === "/mcp" ? "mcp" : reqPath.slice(1);
3050
- res.writeHead(401, {
3051
- "Content-Type": "application/json",
3052
- "WWW-Authenticate": `Bearer resource_metadata="${base}/.well-known/oauth-protected-resource/${pathName}"`,
3053
- ...CORS,
3054
- });
3055
- return res.end(JSON.stringify({ error: "unauthorized" }));
3056
- }
3057
- const token = authHeader.slice(7);
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 {}
3061
- res.writeHead(401, { "Content-Type": "application/json", ...CORS });
3062
- return res.end(JSON.stringify({ error: "invalid_token" }));
3046
+ // Mark as remote if a valid token is present (for vault access scoping)
3047
+ if (authHeader?.startsWith("Bearer ") && oauthTokens.has(authHeader.slice(7))) {
3048
+ req._clauthRemote = true;
3063
3049
  }
3064
- // Token validmark as remote caller (claude.ai via tunnel)
3065
- req._clauthRemote = true;
3066
- // fall through to MCP handling below
3050
+ // fall through to MCP handling no 401 gate on these paths
3067
3051
  }
3068
3052
 
3069
3053
  // ── MCP Streamable HTTP transport (2025-03-26 spec) ──
@@ -3125,9 +3109,9 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
3125
3109
  return res.end(JSON.stringify({ jsonrpc: "2.0", id, error: { code: -32601, message: `Unknown method: ${rpcMethod}` } }));
3126
3110
  }
3127
3111
 
3128
- // ── MCP SSE transport (legacy) ───────────────────────
3129
- // GET /sse — open SSE stream, receive endpoint event
3130
- if (method === "GET" && reqPath === "/sse") {
3112
+ // ── MCP SSE transport /sse and namespaced paths ────
3113
+ // GET /sse|/gws|/clauth — open SSE stream, receive endpoint event
3114
+ if (method === "GET" && (reqPath === "/sse" || isMcpPath)) {
3131
3115
  const sessionId = `ses_${++sseCounter}_${Date.now()}`;
3132
3116
 
3133
3117
  res.writeHead(200, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifeaitools/clauth",
3
- "version": "1.5.13",
3
+ "version": "1.5.15",
4
4
  "description": "Hardware-bound credential vault for the LIFEAI infrastructure stack",
5
5
  "type": "module",
6
6
  "bin": {