@lifeaitools/clauth 1.5.21 → 1.5.23

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,10 +847,15 @@ 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</span>
850
+ <span class="mcp-label">URL (all tools)</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
+ <div class="mcp-row">
855
+ <span class="mcp-label">URL (GWS only)</span>
856
+ <span class="mcp-val" id="mcp-gws-url">—</span>
857
+ <button class="mcp-copy" onclick="copyMcp('mcp-gws-url')">copy</button>
858
+ </div>
854
859
  <div class="mcp-row">
855
860
  <span class="mcp-label">Client ID</span>
856
861
  <span class="mcp-val" id="mcp-client-id">—</span>
@@ -862,7 +867,10 @@ function dashboardHtml(port, whitelist, isStaged = false) {
862
867
  <button class="mcp-copy" onclick="copyMcp('mcp-client-secret')">copy</button>
863
868
  </div>
864
869
  </div>
865
- <div style="font-size:.72rem;color:#64748b;margin-top:4px">Paste these into <a href="https://claude.ai/settings/integrations" target="_blank" style="color:#60a5fa">claude.ai Settings → Integrations</a></div>
870
+ <div style="display:flex;align-items:center;gap:8px;margin-top:6px">
871
+ <div style="font-size:.72rem;color:#64748b">Paste into <a href="https://claude.ai/settings/integrations" target="_blank" style="color:#60a5fa">claude.ai → Integrations</a></div>
872
+ <button class="mcp-copy" onclick="rollMcpCreds()" style="font-size:.7rem">Roll Credentials</button>
873
+ </div>
866
874
  </div>
867
875
 
868
876
  <div class="wizard-panel" id="wizard-panel">
@@ -1939,10 +1947,12 @@ async function toggleMcpSetup() {
1939
1947
  try {
1940
1948
  const m = await fetch(BASE + "/mcp-setup").then(r => r.json());
1941
1949
  document.getElementById("mcp-url").textContent = m.url || "(tunnel not running)";
1950
+ document.getElementById("mcp-gws-url").textContent = m.gwsUrl || "(tunnel not running)";
1942
1951
  document.getElementById("mcp-client-id").textContent = m.clientId || "—";
1943
1952
  document.getElementById("mcp-client-secret").textContent = m.clientSecret || "—";
1944
1953
  } catch {
1945
1954
  document.getElementById("mcp-url").textContent = "(error fetching)";
1955
+ document.getElementById("mcp-gws-url").textContent = "(error fetching)";
1946
1956
  }
1947
1957
  }
1948
1958
  }
@@ -1958,6 +1968,17 @@ function copyMcp(elId) {
1958
1968
  }).catch(() => {});
1959
1969
  }
1960
1970
 
1971
+ async function rollMcpCreds() {
1972
+ if (!confirm("Roll MCP credentials? You will need to re-add connectors in claude.ai with the new credentials.")) return;
1973
+ try {
1974
+ const resp = await fetch(BASE + "/roll-mcp-creds", { method: "POST" }).then(r => r.json());
1975
+ if (resp.client_id) {
1976
+ document.getElementById("mcp-client-id").textContent = resp.client_id;
1977
+ document.getElementById("mcp-client-secret").textContent = resp.client_secret;
1978
+ }
1979
+ } catch(e) { alert("Failed: " + e.message); }
1980
+ }
1981
+
1961
1982
  // ── Tunnel Setup Wizard ─────────────────────
1962
1983
  let wizStep = null;
1963
1984
  let wizData = {};
@@ -2335,13 +2356,28 @@ async function wizShowMcpSetup(hostname) {
2335
2356
  <div style="margin-top:10px;font-size:.78rem;color:#64748b">
2336
2357
  Paste into <a class="wiz-link" href="https://claude.ai/settings/integrations" target="_blank">claude.ai → Settings → Integrations</a>
2337
2358
  </div>
2359
+ <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)
2361
+ </div>
2338
2362
  \`, [], [
2339
2363
  \`<button class="btn-wiz-primary" onclick="window.open('https://claude.ai/settings/integrations','_blank');wizShowTest()">I've Added It — Test Now</button>\`,
2364
+ \`<button class="btn-wiz-secondary" onclick="wizRollCreds()">Roll Credentials</button>\`,
2340
2365
  \`<button class="btn-wiz-secondary" onclick="wizShowTest()">Skip Test</button>\`,
2341
2366
  \`<button class="btn-wiz-secondary" onclick="closeSetupWizard()">Done</button>\`
2342
2367
  ]);
2343
2368
  }
2344
2369
 
2370
+ async function wizRollCreds() {
2371
+ if (!confirm("Roll MCP credentials? Existing claude.ai connectors will need to be re-added with the new credentials.")) return;
2372
+ try {
2373
+ const resp = await apiFetch("/roll-mcp-creds", { method: "POST" });
2374
+ if (resp?.client_id) {
2375
+ document.getElementById("wiz-mcp-cid").textContent = resp.client_id;
2376
+ document.getElementById("wiz-mcp-sec").textContent = resp.client_secret;
2377
+ }
2378
+ } catch(e) { alert("Failed to roll credentials: " + e.message); }
2379
+ }
2380
+
2345
2381
  function wizCopy(elId, btn) {
2346
2382
  const val = document.getElementById(elId)?.textContent?.trim();
2347
2383
  if (!val) return;
@@ -2531,8 +2567,8 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
2531
2567
  return creds;
2532
2568
  }
2533
2569
  const stableCreds = loadOrCreateCreds();
2534
- const OAUTH_CLIENT_ID = stableCreds.client_id;
2535
- const OAUTH_CLIENT_SECRET = stableCreds.client_secret;
2570
+ let OAUTH_CLIENT_ID = stableCreds.client_id;
2571
+ let OAUTH_CLIENT_SECRET = stableCreds.client_secret;
2536
2572
  oauthClients.set(OAUTH_CLIENT_ID, {
2537
2573
  client_id: OAUTH_CLIENT_ID, client_secret: OAUTH_CLIENT_SECRET,
2538
2574
  client_name: "claude.ai", redirect_uris: ["https://claude.ai/api/mcp/auth_callback"],
@@ -2894,12 +2930,17 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
2894
2930
 
2895
2931
  // ── OAuth Discovery (RFC 9728 + RFC 8414) ──────────────
2896
2932
  // claude.ai probes these for ALL remote MCP connections.
2897
- // resource MUST match the connector URL configured in claude.ai (/sse).
2933
+ // resource MUST match the connector URL configured in claude.ai.
2934
+ // /.well-known/oauth-protected-resource/gws → resource=/gws
2935
+ // /.well-known/oauth-protected-resource/sse → resource=/sse
2936
+ // /.well-known/oauth-protected-resource → resource=/sse (default)
2898
2937
  if (reqPath.startsWith("/.well-known/oauth-protected-resource")) {
2899
2938
  const base = oauthBase();
2939
+ const suffix = reqPath.replace("/.well-known/oauth-protected-resource", "").replace(/^\//, "");
2940
+ const resourcePath = suffix && ["/gws", "/clauth", "/mcp", "/sse"].includes("/" + suffix) ? "/" + suffix : "/sse";
2900
2941
  res.writeHead(200, { "Content-Type": "application/json", ...CORS });
2901
2942
  return res.end(JSON.stringify({
2902
- resource: `${base}/sse`,
2943
+ resource: `${base}${resourcePath}`,
2903
2944
  authorization_servers: [base],
2904
2945
  scopes_supported: ["mcp:tools"],
2905
2946
  bearer_methods_supported: ["header"],
@@ -3322,13 +3363,42 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
3322
3363
 
3323
3364
  // GET /mcp-setup — OAuth credentials for claude.ai MCP setup (localhost only)
3324
3365
  if (method === "GET" && reqPath === "/mcp-setup") {
3366
+ const base = tunnelUrl && tunnelUrl.startsWith("http") ? tunnelUrl : null;
3325
3367
  return ok(res, {
3326
- url: tunnelUrl && tunnelUrl.startsWith("http") ? `${tunnelUrl}/mcp` : null,
3368
+ url: base ? `${base}/sse` : null,
3369
+ gwsUrl: base ? `${base}/gws` : null,
3327
3370
  clientId: OAUTH_CLIENT_ID,
3328
3371
  clientSecret: OAUTH_CLIENT_SECRET,
3329
3372
  });
3330
3373
  }
3331
3374
 
3375
+ // POST /roll-mcp-creds — generate new client ID/secret, invalidate all tokens
3376
+ if (method === "POST" && reqPath === "/roll-mcp-creds") {
3377
+ if (lockedGuard(res)) return;
3378
+ const newId = crypto.randomBytes(16).toString("hex");
3379
+ const newSecret = crypto.randomBytes(32).toString("hex");
3380
+ // Update in-memory
3381
+ oauthClients.delete(OAUTH_CLIENT_ID);
3382
+ OAUTH_CLIENT_ID = newId;
3383
+ OAUTH_CLIENT_SECRET = newSecret;
3384
+ stableCreds.client_id = newId;
3385
+ stableCreds.client_secret = newSecret;
3386
+ try { fs.writeFileSync(CREDS_FILE, JSON.stringify(stableCreds)); } catch {}
3387
+ // Register new client
3388
+ oauthClients.set(newId, {
3389
+ client_id: newId, client_secret: newSecret,
3390
+ client_name: "claude.ai", redirect_uris: ["https://claude.ai/api/mcp/auth_callback"],
3391
+ grant_types: ["authorization_code"], response_types: ["code"],
3392
+ token_endpoint_auth_method: "client_secret_post",
3393
+ });
3394
+ // Invalidate all existing tokens
3395
+ oauthTokens.clear();
3396
+ saveTokens(oauthTokens);
3397
+ const logMsg = `[${new Date().toISOString()}] OAuth: rolled credentials — new client ${newId.slice(0,8)}…, all tokens invalidated\n`;
3398
+ try { fs.appendFileSync(LOG_FILE, logMsg); } catch {}
3399
+ return ok(res, { client_id: newId, client_secret: newSecret, tokens_invalidated: true });
3400
+ }
3401
+
3332
3402
  // POST /tunnel — start or stop tunnel manually (action in body)
3333
3403
  if (method === "POST" && reqPath === "/tunnel") {
3334
3404
  if (lockedGuard(res)) return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifeaitools/clauth",
3
- "version": "1.5.21",
3
+ "version": "1.5.23",
4
4
  "description": "Hardware-bound credential vault for the LIFEAI infrastructure stack",
5
5
  "type": "module",
6
6
  "bin": {