@lifeaitools/clauth 1.5.23 → 1.5.25

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.
@@ -847,12 +847,12 @@ function dashboardHtml(port, whitelist, isStaged = false) {
847
847
  <div class="mcp-setup-title">claude.ai MCP Integration</div>
848
848
  <div class="oauth-fields">
849
849
  <div class="mcp-row">
850
- <span class="mcp-label">URL (all tools)</span>
850
+ <span class="mcp-label">Clauth</span>
851
851
  <span class="mcp-val" id="mcp-url">—</span>
852
852
  <button class="mcp-copy" onclick="copyMcp('mcp-url')">copy</button>
853
853
  </div>
854
854
  <div class="mcp-row">
855
- <span class="mcp-label">URL (GWS only)</span>
855
+ <span class="mcp-label">GWS</span>
856
856
  <span class="mcp-val" id="mcp-gws-url">—</span>
857
857
  <button class="mcp-copy" onclick="copyMcp('mcp-gws-url')">copy</button>
858
858
  </div>
@@ -2330,18 +2330,24 @@ async function wizRunCreateTunnel() {
2330
2330
  // Step: MCP setup in claude.ai
2331
2331
  async function wizShowMcpSetup(hostname) {
2332
2332
  wizStep = "mcp_setup";
2333
- const sseUrl = \`https://\${hostname}/sse\`;
2333
+ const clauthUrl = \`https://\${hostname}/clauth\`;
2334
+ const gwsUrl = \`https://\${hostname}/gws\`;
2334
2335
 
2335
2336
  const mcpData = await apiFetch("/mcp-setup");
2336
2337
 
2337
2338
  renderWizBody(\`
2338
- <div class="wiz-desc">Add clauth as an MCP server in claude.ai settings. Paste these values:</div>
2339
+ <div class="wiz-desc">Add two MCP connectors in claude.ai one for Clauth, one for GWS:</div>
2339
2340
  <div style="display:flex;flex-direction:column;gap:8px;margin-top:10px">
2340
2341
  <div class="mcp-row">
2341
- <span class="mcp-label">URL</span>
2342
- <span class="mcp-val" id="wiz-mcp-url">\${sseUrl}</span>
2342
+ <span class="mcp-label">Clauth URL</span>
2343
+ <span class="mcp-val" id="wiz-mcp-url">\${clauthUrl}</span>
2343
2344
  <button class="mcp-copy" onclick="wizCopy('wiz-mcp-url',this)">copy</button>
2344
2345
  </div>
2346
+ <div class="mcp-row">
2347
+ <span class="mcp-label">GWS URL</span>
2348
+ <span class="mcp-val" id="wiz-mcp-gws">\${gwsUrl}</span>
2349
+ <button class="mcp-copy" onclick="wizCopy('wiz-mcp-gws',this)">copy</button>
2350
+ </div>
2345
2351
  <div class="mcp-row">
2346
2352
  <span class="mcp-label">Client ID</span>
2347
2353
  <span class="mcp-val" id="wiz-mcp-cid">\${mcpData?.clientId || '(unlock required)'}</span>
@@ -2357,7 +2363,7 @@ async function wizShowMcpSetup(hostname) {
2357
2363
  Paste into <a class="wiz-link" href="https://claude.ai/settings/integrations" target="_blank">claude.ai → Settings → Integrations</a>
2358
2364
  </div>
2359
2365
  <div style="margin-top:8px;font-size:.78rem;color:#64748b">
2360
- GWS connector: use <code style="color:#60a5fa">https://\${hostname}/gws</code> as URL (same Client ID/Secret)
2366
+ Same Client ID/Secret for both connectors.
2361
2367
  </div>
2362
2368
  \`, [], [
2363
2369
  \`<button class="btn-wiz-primary" onclick="window.open('https://claude.ai/settings/integrations','_blank');wizShowTest()">I've Added It — Test Now</button>\`,
@@ -2937,7 +2943,7 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
2937
2943
  if (reqPath.startsWith("/.well-known/oauth-protected-resource")) {
2938
2944
  const base = oauthBase();
2939
2945
  const suffix = reqPath.replace("/.well-known/oauth-protected-resource", "").replace(/^\//, "");
2940
- const resourcePath = suffix && ["/gws", "/clauth", "/mcp", "/sse"].includes("/" + suffix) ? "/" + suffix : "/sse";
2946
+ const resourcePath = suffix && ["/gws", "/clauth", "/mcp", "/sse"].includes("/" + suffix) ? "/" + suffix : "/clauth";
2941
2947
  res.writeHead(200, { "Content-Type": "application/json", ...CORS });
2942
2948
  return res.end(JSON.stringify({
2943
2949
  resource: `${base}${resourcePath}`,
@@ -2956,7 +2962,7 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
2956
2962
  token_endpoint: `${base}/token`,
2957
2963
  registration_endpoint: `${base}/register`,
2958
2964
  response_types_supported: ["code"],
2959
- grant_types_supported: ["authorization_code"],
2965
+ grant_types_supported: ["authorization_code", "client_credentials"],
2960
2966
  code_challenge_methods_supported: ["S256"],
2961
2967
  scopes_supported: ["mcp:tools"],
2962
2968
  }));
@@ -3032,6 +3038,23 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
3032
3038
  return res.end(JSON.stringify({ error: "invalid_request" }));
3033
3039
  }
3034
3040
 
3041
+ // client_credentials grant — skip authorize/redirect, just issue token from client_id+secret
3042
+ if (body.grant_type === "client_credentials") {
3043
+ const cid = body.client_id;
3044
+ const csec = body.client_secret;
3045
+ if (cid === OAUTH_CLIENT_ID && csec === OAUTH_CLIENT_SECRET) {
3046
+ const accessToken = crypto.randomBytes(32).toString("hex");
3047
+ oauthTokens.add(accessToken);
3048
+ saveTokens(oauthTokens);
3049
+ const logMsg = `[${new Date().toISOString()}] OAuth: client_credentials token issued for ${cid.slice(0,8)}… (token=${accessToken.slice(0,8)}…)\n`;
3050
+ try { fs.appendFileSync(LOG_FILE, logMsg); } catch {}
3051
+ res.writeHead(200, { "Content-Type": "application/json", ...CORS });
3052
+ return res.end(JSON.stringify({ access_token: accessToken, token_type: "Bearer", expires_in: 86400, scope: "mcp:tools" }));
3053
+ }
3054
+ res.writeHead(401, { "Content-Type": "application/json", ...CORS });
3055
+ return res.end(JSON.stringify({ error: "invalid_client" }));
3056
+ }
3057
+
3035
3058
  if (body.grant_type !== "authorization_code") {
3036
3059
  res.writeHead(400, { "Content-Type": "application/json", ...CORS });
3037
3060
  return res.end(JSON.stringify({ error: "unsupported_grant_type" }));
@@ -3108,15 +3131,20 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
3108
3131
  return res.end(JSON.stringify({ error: "unauthorized" }));
3109
3132
  }
3110
3133
  const token = authHeader.slice(7);
3111
- if (!oauthTokens.has(token)) {
3112
- const logMsg = `[${new Date().toISOString()}] OAuth: REJECTED token ${token.slice(0,8)}… (pool=${oauthTokens.size})\n`;
3134
+ // Accept: OAuth-issued tokens OR the stable client_secret directly
3135
+ const isValidToken = oauthTokens.has(token) || token === OAUTH_CLIENT_SECRET;
3136
+ if (!isValidToken) {
3137
+ const logMsg = `[${new Date().toISOString()}] OAuth: REJECTED token ${token.slice(0,8)}… (pool=${oauthTokens.size}, secret=${token === OAUTH_CLIENT_SECRET})\n`;
3113
3138
  try { fs.appendFileSync(LOG_FILE, logMsg); } catch {}
3114
3139
  res.writeHead(401, { "Content-Type": "application/json", ...CORS });
3115
3140
  return res.end(JSON.stringify({ error: "invalid_token" }));
3116
3141
  }
3117
3142
  req._clauthRemote = true;
3118
- } else if (authHeader?.startsWith("Bearer ") && oauthTokens.has(authHeader.slice(7))) {
3119
- req._clauthRemote = true;
3143
+ } else if (authHeader?.startsWith("Bearer ")) {
3144
+ const token = authHeader.slice(7);
3145
+ if (oauthTokens.has(token) || token === OAUTH_CLIENT_SECRET) {
3146
+ req._clauthRemote = true;
3147
+ }
3120
3148
  }
3121
3149
  // fall through to MCP handling
3122
3150
  }
@@ -3194,7 +3222,8 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
3194
3222
  });
3195
3223
  return res.end(JSON.stringify({ error: "unauthorized" }));
3196
3224
  }
3197
- if (!oauthTokens.has(authHeader.slice(7))) {
3225
+ const sseToken = authHeader.slice(7);
3226
+ if (!oauthTokens.has(sseToken) && sseToken !== OAUTH_CLIENT_SECRET) {
3198
3227
  res.writeHead(401, { "Content-Type": "application/json", ...CORS });
3199
3228
  return res.end(JSON.stringify({ error: "invalid_token" }));
3200
3229
  }
@@ -3365,7 +3394,7 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
3365
3394
  if (method === "GET" && reqPath === "/mcp-setup") {
3366
3395
  const base = tunnelUrl && tunnelUrl.startsWith("http") ? tunnelUrl : null;
3367
3396
  return ok(res, {
3368
- url: base ? `${base}/sse` : null,
3397
+ url: base ? `${base}/clauth` : null,
3369
3398
  gwsUrl: base ? `${base}/gws` : null,
3370
3399
  clientId: OAUTH_CLIENT_ID,
3371
3400
  clientSecret: OAUTH_CLIENT_SECRET,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifeaitools/clauth",
3
- "version": "1.5.23",
3
+ "version": "1.5.25",
4
4
  "description": "Hardware-bound credential vault for the LIFEAI infrastructure stack",
5
5
  "type": "module",
6
6
  "bin": {