mirror of
https://github.com/Nezumi-2711/9router.git
synced 2026-09-22 13:38:31 +00:00
- zed live model discovery; codebuddy-intl handler; remove duplicate workbuddy - split oauth providers.js into per-provider files (facade re-export) - fold 5 standard refresh providers into config-driven generic - hide trae/windsurf from registry (no tool calling support) - fix login-CSRF + SSRF on trae/windsurf/zed local callback proxies via loopback-origin guard + strict state validation + apiOrigins allowlist - move zed RSA private key transit to POST body; redact proxy logs Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
63 lines
2.3 KiB
JavaScript
63 lines
2.3 KiB
JavaScript
import { CLINE_CONFIG } from "../constants/oauth.js";
|
|
|
|
const cline = {
|
|
config: CLINE_CONFIG,
|
|
flowType: "authorization_code",
|
|
buildAuthUrl: (config, redirectUri) => {
|
|
const params = new URLSearchParams({
|
|
client_type: "extension",
|
|
callback_url: redirectUri,
|
|
redirect_uri: redirectUri,
|
|
});
|
|
return `${config.authorizeUrl}?${params.toString()}`;
|
|
},
|
|
exchangeToken: async (config, code, redirectUri) => {
|
|
try {
|
|
// Cline encodes token data as base64 in the code param
|
|
let base64 = code;
|
|
const padding = 4 - (base64.length % 4);
|
|
if (padding !== 4) base64 += "=".repeat(padding);
|
|
const decoded = Buffer.from(base64, "base64").toString("utf-8");
|
|
const lastBrace = decoded.lastIndexOf("}");
|
|
if (lastBrace === -1) throw new Error("No JSON found in decoded code");
|
|
const tokenData = JSON.parse(decoded.substring(0, lastBrace + 1));
|
|
return {
|
|
access_token: tokenData.accessToken,
|
|
refresh_token: tokenData.refreshToken,
|
|
email: tokenData.email,
|
|
firstName: tokenData.firstName,
|
|
lastName: tokenData.lastName,
|
|
expires_at: tokenData.expiresAt,
|
|
};
|
|
} catch (e) {
|
|
const response = await fetch(config.tokenExchangeUrl, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json", Accept: "application/json" },
|
|
body: JSON.stringify({ grant_type: "authorization_code", code, client_type: "extension", redirect_uri: redirectUri }),
|
|
});
|
|
if (!response.ok) {
|
|
const error = await response.text();
|
|
throw new Error(`Cline token exchange failed: ${error}`);
|
|
}
|
|
const data = await response.json();
|
|
return {
|
|
access_token: data.data?.accessToken || data.accessToken,
|
|
refresh_token: data.data?.refreshToken || data.refreshToken,
|
|
email: data.data?.userInfo?.email || "",
|
|
expires_at: data.data?.expiresAt || data.expiresAt,
|
|
};
|
|
}
|
|
},
|
|
mapTokens: (tokens) => ({
|
|
accessToken: tokens.access_token,
|
|
refreshToken: tokens.refresh_token,
|
|
expiresIn: tokens.expires_at
|
|
? Math.floor((new Date(tokens.expires_at).getTime() - Date.now()) / 1000)
|
|
: 3600,
|
|
email: tokens.email,
|
|
providerSpecificData: { firstName: tokens.firstName, lastName: tokens.lastName },
|
|
}),
|
|
};
|
|
|
|
export default cline;
|