@lifeaitools/clauth 1.5.30 → 1.5.31
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 +4 -109
- package/package.json +1 -1
package/cli/commands/serve.js
CHANGED
|
@@ -2944,115 +2944,10 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
2944
2944
|
return res.end(JSON.stringify({ error: "not_found" }));
|
|
2945
2945
|
}
|
|
2946
2946
|
|
|
2947
|
-
// ──
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
res.writeHead(400, { "Content-Type": "application/json", ...CORS });
|
|
2952
|
-
return res.end(JSON.stringify({ error: "invalid_request" }));
|
|
2953
|
-
}
|
|
2954
|
-
const clientId = crypto.randomBytes(16).toString("hex");
|
|
2955
|
-
const clientSecret = crypto.randomBytes(32).toString("hex");
|
|
2956
|
-
const client = {
|
|
2957
|
-
client_id: clientId, client_secret: clientSecret,
|
|
2958
|
-
client_name: body.client_name || "unknown",
|
|
2959
|
-
redirect_uris: body.redirect_uris || [],
|
|
2960
|
-
grant_types: body.grant_types || ["authorization_code"],
|
|
2961
|
-
response_types: body.response_types || ["code"],
|
|
2962
|
-
token_endpoint_auth_method: body.token_endpoint_auth_method || "client_secret_post",
|
|
2963
|
-
};
|
|
2964
|
-
oauthClients.set(clientId, client);
|
|
2965
|
-
const logMsg = `[${new Date().toISOString()}] OAuth: registered client ${clientId} (${client.client_name})\n`;
|
|
2966
|
-
try { fs.appendFileSync(LOG_FILE, logMsg); } catch {}
|
|
2967
|
-
res.writeHead(201, { "Content-Type": "application/json", ...CORS });
|
|
2968
|
-
return res.end(JSON.stringify(client));
|
|
2969
|
-
}
|
|
2970
|
-
|
|
2971
|
-
// ── Authorization endpoint — auto-approve ──────────────
|
|
2972
|
-
if (method === "GET" && reqPath === "/authorize") {
|
|
2973
|
-
const clientId = url.searchParams.get("client_id");
|
|
2974
|
-
const redirectUri = url.searchParams.get("redirect_uri");
|
|
2975
|
-
const state = url.searchParams.get("state");
|
|
2976
|
-
const codeChallenge = url.searchParams.get("code_challenge");
|
|
2977
|
-
|
|
2978
|
-
if (!clientId || !redirectUri) {
|
|
2979
|
-
res.writeHead(400, { "Content-Type": "text/plain", ...CORS });
|
|
2980
|
-
return res.end("Missing client_id or redirect_uri");
|
|
2981
|
-
}
|
|
2982
|
-
|
|
2983
|
-
const code = crypto.randomBytes(32).toString("hex");
|
|
2984
|
-
oauthCodes.set(code, {
|
|
2985
|
-
client_id: clientId, redirect_uri: redirectUri,
|
|
2986
|
-
code_challenge: codeChallenge,
|
|
2987
|
-
expires: Date.now() + 300_000,
|
|
2988
|
-
});
|
|
2989
|
-
|
|
2990
|
-
const redirect = new URL(redirectUri);
|
|
2991
|
-
redirect.searchParams.set("code", code);
|
|
2992
|
-
if (state) redirect.searchParams.set("state", state);
|
|
2993
|
-
|
|
2994
|
-
const logMsg = `[${new Date().toISOString()}] OAuth: authorize → code for ${clientId}, redirect to ${redirect.origin}\n`;
|
|
2995
|
-
try { fs.appendFileSync(LOG_FILE, logMsg); } catch {}
|
|
2996
|
-
res.writeHead(302, { Location: redirect.toString(), ...CORS });
|
|
2997
|
-
return res.end();
|
|
2998
|
-
}
|
|
2999
|
-
|
|
3000
|
-
// ── Token endpoint ──────────────────────────────────────
|
|
3001
|
-
if (method === "POST" && reqPath === "/token") {
|
|
3002
|
-
let body;
|
|
3003
|
-
const ct = req.headers["content-type"] || "";
|
|
3004
|
-
try {
|
|
3005
|
-
if (ct.includes("application/json")) {
|
|
3006
|
-
body = await readBody(req);
|
|
3007
|
-
} else {
|
|
3008
|
-
const raw = await readRawBody(req);
|
|
3009
|
-
body = Object.fromEntries(new URLSearchParams(raw));
|
|
3010
|
-
}
|
|
3011
|
-
} catch {
|
|
3012
|
-
res.writeHead(400, { "Content-Type": "application/json", ...CORS });
|
|
3013
|
-
return res.end(JSON.stringify({ error: "invalid_request" }));
|
|
3014
|
-
}
|
|
3015
|
-
|
|
3016
|
-
const tokenLog = (msg) => { try { fs.appendFileSync(LOG_FILE, `[${new Date().toISOString()}] OAuth /token: ${msg}\n`); } catch {} };
|
|
3017
|
-
tokenLog(`grant_type=${body.grant_type} code=${(body.code||"").slice(0,8)}… verifier=${body.code_verifier ? "present" : "missing"}`);
|
|
3018
|
-
|
|
3019
|
-
if (body.grant_type !== "authorization_code") {
|
|
3020
|
-
tokenLog(`REJECT: unsupported_grant_type (${body.grant_type})`);
|
|
3021
|
-
res.writeHead(400, { "Content-Type": "application/json", ...CORS });
|
|
3022
|
-
return res.end(JSON.stringify({ error: "unsupported_grant_type" }));
|
|
3023
|
-
}
|
|
3024
|
-
|
|
3025
|
-
const stored = oauthCodes.get(body.code);
|
|
3026
|
-
if (!stored || stored.expires < Date.now()) {
|
|
3027
|
-
tokenLog(`REJECT: invalid_grant (stored=${!!stored}, expired=${stored ? stored.expires < Date.now() : "n/a"}, codes_size=${oauthCodes.size})`);
|
|
3028
|
-
oauthCodes.delete(body.code);
|
|
3029
|
-
res.writeHead(400, { "Content-Type": "application/json", ...CORS });
|
|
3030
|
-
return res.end(JSON.stringify({ error: "invalid_grant" }));
|
|
3031
|
-
}
|
|
3032
|
-
|
|
3033
|
-
// PKCE verification
|
|
3034
|
-
if (stored.code_challenge && body.code_verifier) {
|
|
3035
|
-
const computed = sha256base64url(body.code_verifier);
|
|
3036
|
-
tokenLog(`PKCE: challenge=${stored.code_challenge.slice(0,12)}… computed=${computed.slice(0,12)}… match=${computed === stored.code_challenge}`);
|
|
3037
|
-
if (computed !== stored.code_challenge) {
|
|
3038
|
-
oauthCodes.delete(body.code);
|
|
3039
|
-
res.writeHead(400, { "Content-Type": "application/json", ...CORS });
|
|
3040
|
-
return res.end(JSON.stringify({ error: "invalid_grant", error_description: "PKCE failed" }));
|
|
3041
|
-
}
|
|
3042
|
-
} else {
|
|
3043
|
-
tokenLog(`PKCE: skipped (challenge=${!!stored.code_challenge}, verifier=${!!body.code_verifier})`);
|
|
3044
|
-
}
|
|
3045
|
-
|
|
3046
|
-
oauthCodes.delete(body.code);
|
|
3047
|
-
const accessToken = crypto.randomBytes(32).toString("hex");
|
|
3048
|
-
oauthTokens.add(accessToken);
|
|
3049
|
-
saveTokens(oauthTokens);
|
|
3050
|
-
|
|
3051
|
-
const logMsg = `[${new Date().toISOString()}] OAuth: token issued for ${stored.client_id} (token=${accessToken.slice(0,8)}…)\n`;
|
|
3052
|
-
try { fs.appendFileSync(LOG_FILE, logMsg); } catch {}
|
|
3053
|
-
res.writeHead(200, { "Content-Type": "application/json", ...CORS });
|
|
3054
|
-
return res.end(JSON.stringify({ access_token: accessToken, token_type: "Bearer", scope: "mcp:tools", expires_in: 86400 }));
|
|
3055
|
-
}
|
|
2947
|
+
// ── OAuth endpoints REMOVED ──────────────
|
|
2948
|
+
// claude.ai's OAuth is bugged (token issued, never used — anthropics/claude-code#46140).
|
|
2949
|
+
// regen-media works because it has NO OAuth handlers at all.
|
|
2950
|
+
// These paths fall through to the catch-all 404 at the bottom.
|
|
3056
2951
|
|
|
3057
2952
|
// ── MCP path helpers ──
|
|
3058
2953
|
const MCP_PATHS = ["/mcp", "/gws", "/clauth"];
|