@lifeaitools/clauth 1.5.26 → 1.5.27

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.
Files changed (2) hide show
  1. package/cli/commands/serve.js +13 -150
  2. package/package.json +1 -1
@@ -2934,158 +2934,21 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
2934
2934
  return res.end();
2935
2935
  }
2936
2936
 
2937
- // ── OAuth Discovery (RFC 9728 + RFC 8414) ──────────────
2938
- // claude.ai probes these for ALL remote MCP connections.
2939
- // resource MUST match the connector URL configured in claude.ai.
2940
- // /.well-known/oauth-protected-resource/gws resource=/gws
2941
- // /.well-known/oauth-protected-resource/sse → resource=/sse
2942
- // /.well-known/oauth-protected-resource → resource=/sse (default)
2943
- if (reqPath.startsWith("/.well-known/oauth-protected-resource")) {
2944
- const base = oauthBase();
2945
- const suffix = reqPath.replace("/.well-known/oauth-protected-resource", "").replace(/^\//, "");
2946
- const resourcePath = suffix && ["/gws", "/clauth", "/mcp", "/sse"].includes("/" + suffix) ? "/" + suffix : "/clauth";
2947
- res.writeHead(200, { "Content-Type": "application/json", ...CORS });
2948
- return res.end(JSON.stringify({
2949
- resource: `${base}${resourcePath}`,
2950
- authorization_servers: [base],
2951
- scopes_supported: ["mcp:tools"],
2952
- bearer_methods_supported: ["header"],
2953
- }));
2954
- }
2955
-
2956
- if (reqPath === "/.well-known/oauth-authorization-server") {
2957
- const base = oauthBase();
2958
- res.writeHead(200, { "Content-Type": "application/json", ...CORS });
2959
- return res.end(JSON.stringify({
2960
- issuer: base,
2961
- authorization_endpoint: `${base}/authorize`,
2962
- token_endpoint: `${base}/token`,
2963
- registration_endpoint: `${base}/register`,
2964
- response_types_supported: ["code"],
2965
- grant_types_supported: ["authorization_code", "client_credentials"],
2966
- code_challenge_methods_supported: ["S256"],
2967
- scopes_supported: ["mcp:tools"],
2968
- }));
2969
- }
2970
-
2971
- // ── Dynamic Client Registration (RFC 7591) ──────────────
2972
- if (method === "POST" && reqPath === "/register") {
2973
- let body;
2974
- try { body = await readBody(req); } catch {
2975
- res.writeHead(400, { "Content-Type": "application/json", ...CORS });
2976
- return res.end(JSON.stringify({ error: "invalid_request" }));
2977
- }
2978
- const clientId = crypto.randomBytes(16).toString("hex");
2979
- const clientSecret = crypto.randomBytes(32).toString("hex");
2980
- const client = {
2981
- client_id: clientId, client_secret: clientSecret,
2982
- client_name: body.client_name || "unknown",
2983
- redirect_uris: body.redirect_uris || [],
2984
- grant_types: body.grant_types || ["authorization_code"],
2985
- response_types: body.response_types || ["code"],
2986
- token_endpoint_auth_method: body.token_endpoint_auth_method || "client_secret_post",
2987
- };
2988
- oauthClients.set(clientId, client);
2989
- const logMsg = `[${new Date().toISOString()}] OAuth: registered client ${clientId} (${client.client_name})\n`;
2990
- try { fs.appendFileSync(LOG_FILE, logMsg); } catch {}
2991
- res.writeHead(201, { "Content-Type": "application/json", ...CORS });
2992
- return res.end(JSON.stringify(client));
2993
- }
2994
-
2995
- // ── Authorization endpoint — auto-approve ──────────────
2996
- if (method === "GET" && reqPath === "/authorize") {
2997
- const clientId = url.searchParams.get("client_id");
2998
- const redirectUri = url.searchParams.get("redirect_uri");
2999
- const state = url.searchParams.get("state");
3000
- const codeChallenge = url.searchParams.get("code_challenge");
3001
- const codeChallengeMethod = url.searchParams.get("code_challenge_method");
3002
-
3003
- if (!clientId || !redirectUri) {
3004
- res.writeHead(400, { "Content-Type": "text/plain", ...CORS });
3005
- return res.end("Missing client_id or redirect_uri");
3006
- }
3007
-
3008
- const code = crypto.randomBytes(32).toString("hex");
3009
- oauthCodes.set(code, {
3010
- client_id: clientId, redirect_uri: redirectUri,
3011
- code_challenge: codeChallenge, code_challenge_method: codeChallengeMethod,
3012
- expires: Date.now() + 300_000,
3013
- });
3014
-
3015
- const redirect = new URL(redirectUri);
3016
- redirect.searchParams.set("code", code);
3017
- if (state) redirect.searchParams.set("state", state);
3018
-
3019
- const logMsg = `[${new Date().toISOString()}] OAuth: authorize → code issued for ${clientId}, redirecting to ${redirect.origin}\n`;
3020
- try { fs.appendFileSync(LOG_FILE, logMsg); } catch {}
3021
- res.writeHead(302, { Location: redirect.toString(), ...CORS });
3022
- return res.end();
2937
+ // ── OAuth Discovery 404 all endpoints ──────────────
2938
+ // claude.ai probes well-known for remote MCP. If it gets 200, it insists on
2939
+ // completing OAuth (which succeeds server-side but claude.ai never uses the token).
2940
+ // Returning 404 makes claude.ai skip OAuth and connect directly.
2941
+ if (reqPath.startsWith("/.well-known/oauth-protected-resource") ||
2942
+ reqPath === "/.well-known/oauth-authorization-server") {
2943
+ res.writeHead(404, { "Content-Type": "application/json", ...CORS });
2944
+ return res.end(JSON.stringify({ error: "not_found" }));
3023
2945
  }
3024
2946
 
3025
- // ── Token endpoint ──────────────────────────────────────
3026
- if (method === "POST" && reqPath === "/token") {
3027
- let body;
3028
- const ct = req.headers["content-type"] || "";
3029
- try {
3030
- if (ct.includes("application/json")) {
3031
- body = await readBody(req);
3032
- } else {
3033
- const raw = await readRawBody(req);
3034
- body = Object.fromEntries(new URLSearchParams(raw));
3035
- }
3036
- } catch {
3037
- res.writeHead(400, { "Content-Type": "application/json", ...CORS });
3038
- return res.end(JSON.stringify({ error: "invalid_request" }));
3039
- }
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
-
3058
- if (body.grant_type !== "authorization_code") {
3059
- res.writeHead(400, { "Content-Type": "application/json", ...CORS });
3060
- return res.end(JSON.stringify({ error: "unsupported_grant_type" }));
3061
- }
3062
-
3063
- const stored = oauthCodes.get(body.code);
3064
- if (!stored || stored.expires < Date.now()) {
3065
- oauthCodes.delete(body.code);
3066
- res.writeHead(400, { "Content-Type": "application/json", ...CORS });
3067
- return res.end(JSON.stringify({ error: "invalid_grant" }));
3068
- }
3069
-
3070
- // PKCE verification
3071
- if (stored.code_challenge && body.code_verifier) {
3072
- const computed = sha256base64url(body.code_verifier);
3073
- if (computed !== stored.code_challenge) {
3074
- oauthCodes.delete(body.code);
3075
- res.writeHead(400, { "Content-Type": "application/json", ...CORS });
3076
- return res.end(JSON.stringify({ error: "invalid_grant", error_description: "PKCE verification failed" }));
3077
- }
3078
- }
3079
-
3080
- oauthCodes.delete(body.code);
3081
- const accessToken = crypto.randomBytes(32).toString("hex");
3082
- oauthTokens.add(accessToken);
3083
- saveTokens(oauthTokens);
3084
-
3085
- const logMsg = `[${new Date().toISOString()}] OAuth: token issued for client ${stored.client_id} (token=${accessToken.slice(0,8)}…)\n`;
3086
- try { fs.appendFileSync(LOG_FILE, logMsg); } catch {}
3087
- res.writeHead(200, { "Content-Type": "application/json", ...CORS });
3088
- return res.end(JSON.stringify({ access_token: accessToken, token_type: "Bearer", expires_in: 86400 }));
2947
+ if ((method === "POST" && reqPath === "/register") ||
2948
+ (method === "GET" && reqPath === "/authorize") ||
2949
+ (method === "POST" && reqPath === "/token")) {
2950
+ res.writeHead(404, { "Content-Type": "application/json", ...CORS });
2951
+ return res.end(JSON.stringify({ error: "not_found" }));
3089
2952
  }
3090
2953
 
3091
2954
  // ── MCP path helpers ──
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifeaitools/clauth",
3
- "version": "1.5.26",
3
+ "version": "1.5.27",
4
4
  "description": "Hardware-bound credential vault for the LIFEAI infrastructure stack",
5
5
  "type": "module",
6
6
  "bin": {