Merge pull request #216 from lixiangwuxian/main
fix: 修改oauth鉴权逻辑,支持调用回调认证url过程中使用代理
This commit is contained in:
commit
d1516abc4e
1 changed files with 64 additions and 29 deletions
|
|
@ -8,6 +8,7 @@ import open from 'open';
|
||||||
import { broadcastEvent } from '../services/ui-manager.js';
|
import { broadcastEvent } from '../services/ui-manager.js';
|
||||||
import { autoLinkProviderConfigs } from '../services/service-manager.js';
|
import { autoLinkProviderConfigs } from '../services/service-manager.js';
|
||||||
import { CONFIG } from '../core/config-manager.js';
|
import { CONFIG } from '../core/config-manager.js';
|
||||||
|
import { getGoogleAuthProxyConfig, getProxyConfigForProvider } from '../utils/proxy-utils.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OAuth 提供商配置
|
* OAuth 提供商配置
|
||||||
|
|
@ -135,6 +136,26 @@ const activeKiroServers = new Map();
|
||||||
*/
|
*/
|
||||||
const activeKiroPollingTasks = new Map();
|
const activeKiroPollingTasks = new Map();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建带代理支持的 fetch 请求
|
||||||
|
* @param {string} url - 请求 URL
|
||||||
|
* @param {Object} options - fetch 选项
|
||||||
|
* @param {string} providerType - 提供商类型,用于获取代理配置
|
||||||
|
* @returns {Promise<Response>}
|
||||||
|
*/
|
||||||
|
async function fetchWithProxy(url, options = {}, providerType) {
|
||||||
|
const proxyConfig = getProxyConfigForProvider(CONFIG, providerType);
|
||||||
|
|
||||||
|
if (proxyConfig) {
|
||||||
|
// 根据 URL 协议选择合适的 agent
|
||||||
|
const urlObj = new URL(url);
|
||||||
|
const agent = urlObj.protocol === 'https:' ? proxyConfig.httpsAgent : proxyConfig.httpAgent;
|
||||||
|
options.dispatcher = agent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fetch(url, options);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成 HTML 响应页面
|
* 生成 HTML 响应页面
|
||||||
* @param {boolean} isSuccess - 是否成功
|
* @param {boolean} isSuccess - 是否成功
|
||||||
|
|
@ -323,7 +344,21 @@ async function handleGoogleOAuth(providerKey, currentConfig, options = {}) {
|
||||||
const host = 'localhost';
|
const host = 'localhost';
|
||||||
const redirectUri = `http://${host}:${port}`;
|
const redirectUri = `http://${host}:${port}`;
|
||||||
|
|
||||||
const authClient = new OAuth2Client(config.clientId, config.clientSecret);
|
// 获取代理配置
|
||||||
|
const proxyConfig = getGoogleAuthProxyConfig(currentConfig, providerKey);
|
||||||
|
|
||||||
|
// 构建 OAuth2Client 选项
|
||||||
|
const oauth2Options = {
|
||||||
|
clientId: config.clientId,
|
||||||
|
clientSecret: config.clientSecret,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (proxyConfig) {
|
||||||
|
oauth2Options.transporterOptions = proxyConfig;
|
||||||
|
console.log(`${config.logPrefix} Using proxy for OAuth token exchange`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const authClient = new OAuth2Client(oauth2Options);
|
||||||
authClient.redirectUri = redirectUri;
|
authClient.redirectUri = redirectUri;
|
||||||
|
|
||||||
const authUrl = authClient.generateAuthUrl({
|
const authUrl = authClient.generateAuthUrl({
|
||||||
|
|
@ -451,14 +486,14 @@ async function pollQwenToken(deviceCode, codeVerifier, interval = 5, expiresIn =
|
||||||
.join('&');
|
.join('&');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(QWEN_OAUTH_CONFIG.tokenEndpoint, {
|
const response = await fetchWithProxy(QWEN_OAUTH_CONFIG.tokenEndpoint, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
'Accept': 'application/json'
|
'Accept': 'application/json'
|
||||||
},
|
},
|
||||||
body: formBody
|
body: formBody
|
||||||
});
|
}, 'openai-qwen-oauth');
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
|
|
@ -556,14 +591,14 @@ export async function handleQwenOAuth(currentConfig, options = {}) {
|
||||||
.join('&');
|
.join('&');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(QWEN_OAUTH_CONFIG.deviceCodeEndpoint, {
|
const response = await fetchWithProxy(QWEN_OAUTH_CONFIG.deviceCodeEndpoint, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
'Accept': 'application/json'
|
'Accept': 'application/json'
|
||||||
},
|
},
|
||||||
body: formBody
|
body: formBody
|
||||||
});
|
}, 'openai-qwen-oauth');
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(`Qwen OAuth请求失败: ${response.status} ${response.statusText}`);
|
throw new Error(`Qwen OAuth请求失败: ${response.status} ${response.statusText}`);
|
||||||
|
|
@ -706,7 +741,7 @@ async function handleKiroBuilderIDDeviceCode(currentConfig, options = {}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. 注册 OIDC 客户端
|
// 1. 注册 OIDC 客户端
|
||||||
const regResponse = await fetch(`${KIRO_OAUTH_CONFIG.ssoOIDCEndpoint}/client/register`, {
|
const regResponse = await fetchWithProxy(`${KIRO_OAUTH_CONFIG.ssoOIDCEndpoint}/client/register`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
|
@ -718,7 +753,7 @@ async function handleKiroBuilderIDDeviceCode(currentConfig, options = {}) {
|
||||||
scopes: KIRO_OAUTH_CONFIG.scopes,
|
scopes: KIRO_OAUTH_CONFIG.scopes,
|
||||||
grantTypes: ['urn:ietf:params:oauth:grant-type:device_code', 'refresh_token']
|
grantTypes: ['urn:ietf:params:oauth:grant-type:device_code', 'refresh_token']
|
||||||
})
|
})
|
||||||
});
|
}, 'claude-kiro-oauth');
|
||||||
|
|
||||||
if (!regResponse.ok) {
|
if (!regResponse.ok) {
|
||||||
throw new Error(`Kiro OAuth 客户端注册失败: ${regResponse.status}`);
|
throw new Error(`Kiro OAuth 客户端注册失败: ${regResponse.status}`);
|
||||||
|
|
@ -727,7 +762,7 @@ async function handleKiroBuilderIDDeviceCode(currentConfig, options = {}) {
|
||||||
const regData = await regResponse.json();
|
const regData = await regResponse.json();
|
||||||
|
|
||||||
// 2. 启动设备授权
|
// 2. 启动设备授权
|
||||||
const authResponse = await fetch(`${KIRO_OAUTH_CONFIG.ssoOIDCEndpoint}/device_authorization`, {
|
const authResponse = await fetchWithProxy(`${KIRO_OAUTH_CONFIG.ssoOIDCEndpoint}/device_authorization`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
|
@ -738,7 +773,7 @@ async function handleKiroBuilderIDDeviceCode(currentConfig, options = {}) {
|
||||||
clientSecret: regData.clientSecret,
|
clientSecret: regData.clientSecret,
|
||||||
startUrl: KIRO_OAUTH_CONFIG.builderIDStartURL
|
startUrl: KIRO_OAUTH_CONFIG.builderIDStartURL
|
||||||
})
|
})
|
||||||
});
|
}, 'claude-kiro-oauth');
|
||||||
|
|
||||||
if (!authResponse.ok) {
|
if (!authResponse.ok) {
|
||||||
throw new Error(`Kiro OAuth 设备授权失败: ${authResponse.status}`);
|
throw new Error(`Kiro OAuth 设备授权失败: ${authResponse.status}`);
|
||||||
|
|
@ -810,7 +845,7 @@ async function pollKiroBuilderIDToken(clientId, clientSecret, deviceCode, interv
|
||||||
attempts++;
|
attempts++;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(`${KIRO_OAUTH_CONFIG.ssoOIDCEndpoint}/token`, {
|
const response = await fetchWithProxy(`${KIRO_OAUTH_CONFIG.ssoOIDCEndpoint}/token`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
|
@ -822,7 +857,7 @@ async function pollKiroBuilderIDToken(clientId, clientSecret, deviceCode, interv
|
||||||
deviceCode,
|
deviceCode,
|
||||||
grantType: 'urn:ietf:params:oauth:grant-type:device_code'
|
grantType: 'urn:ietf:params:oauth:grant-type:device_code'
|
||||||
})
|
})
|
||||||
});
|
}, 'claude-kiro-oauth');
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
|
|
@ -986,7 +1021,7 @@ function createKiroHttpCallbackServer(port, codeVerifier, expectedState, options
|
||||||
}
|
}
|
||||||
|
|
||||||
// 交换 Code 获取 Token(使用动态的 redirect_uri)
|
// 交换 Code 获取 Token(使用动态的 redirect_uri)
|
||||||
const tokenResponse = await fetch(`${KIRO_OAUTH_CONFIG.authServiceEndpoint}/oauth/token`, {
|
const tokenResponse = await fetchWithProxy(`${KIRO_OAUTH_CONFIG.authServiceEndpoint}/oauth/token`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
|
@ -997,7 +1032,7 @@ function createKiroHttpCallbackServer(port, codeVerifier, expectedState, options
|
||||||
code_verifier: codeVerifier,
|
code_verifier: codeVerifier,
|
||||||
redirect_uri: redirectUri
|
redirect_uri: redirectUri
|
||||||
})
|
})
|
||||||
});
|
}, 'claude-kiro-oauth');
|
||||||
|
|
||||||
if (!tokenResponse.ok) {
|
if (!tokenResponse.ok) {
|
||||||
const errorText = await tokenResponse.text();
|
const errorText = await tokenResponse.text();
|
||||||
|
|
@ -1115,7 +1150,7 @@ async function exchangeIFlowCodeForTokens(code, redirectUri) {
|
||||||
// 生成 Basic Auth 头
|
// 生成 Basic Auth 头
|
||||||
const basicAuth = Buffer.from(`${IFLOW_OAUTH_CONFIG.clientId}:${IFLOW_OAUTH_CONFIG.clientSecret}`).toString('base64');
|
const basicAuth = Buffer.from(`${IFLOW_OAUTH_CONFIG.clientId}:${IFLOW_OAUTH_CONFIG.clientSecret}`).toString('base64');
|
||||||
|
|
||||||
const response = await fetch(IFLOW_OAUTH_CONFIG.tokenEndpoint, {
|
const response = await fetchWithProxy(IFLOW_OAUTH_CONFIG.tokenEndpoint, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
|
@ -1123,7 +1158,7 @@ async function exchangeIFlowCodeForTokens(code, redirectUri) {
|
||||||
'Authorization': `Basic ${basicAuth}`
|
'Authorization': `Basic ${basicAuth}`
|
||||||
},
|
},
|
||||||
body: form.toString()
|
body: form.toString()
|
||||||
});
|
}, 'openai-iflow');
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorText = await response.text();
|
const errorText = await response.text();
|
||||||
|
|
@ -1158,12 +1193,12 @@ async function fetchIFlowUserInfo(accessToken) {
|
||||||
|
|
||||||
const endpoint = `${IFLOW_OAUTH_CONFIG.userInfoEndpoint}?accessToken=${encodeURIComponent(accessToken)}`;
|
const endpoint = `${IFLOW_OAUTH_CONFIG.userInfoEndpoint}?accessToken=${encodeURIComponent(accessToken)}`;
|
||||||
|
|
||||||
const response = await fetch(endpoint, {
|
const response = await fetchWithProxy(endpoint, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
'Accept': 'application/json'
|
'Accept': 'application/json'
|
||||||
}
|
}
|
||||||
});
|
}, 'openai-iflow');
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorText = await response.text();
|
const errorText = await response.text();
|
||||||
|
|
@ -1446,7 +1481,7 @@ export async function refreshIFlowTokens(refreshToken) {
|
||||||
// 生成 Basic Auth 头
|
// 生成 Basic Auth 头
|
||||||
const basicAuth = Buffer.from(`${IFLOW_OAUTH_CONFIG.clientId}:${IFLOW_OAUTH_CONFIG.clientSecret}`).toString('base64');
|
const basicAuth = Buffer.from(`${IFLOW_OAUTH_CONFIG.clientId}:${IFLOW_OAUTH_CONFIG.clientSecret}`).toString('base64');
|
||||||
|
|
||||||
const response = await fetch(IFLOW_OAUTH_CONFIG.tokenEndpoint, {
|
const response = await fetchWithProxy(IFLOW_OAUTH_CONFIG.tokenEndpoint, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
|
@ -1454,7 +1489,7 @@ export async function refreshIFlowTokens(refreshToken) {
|
||||||
'Authorization': `Basic ${basicAuth}`
|
'Authorization': `Basic ${basicAuth}`
|
||||||
},
|
},
|
||||||
body: form.toString()
|
body: form.toString()
|
||||||
});
|
}, 'openai-iflow');
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
const errorText = await response.text();
|
const errorText = await response.text();
|
||||||
|
|
@ -1506,14 +1541,14 @@ async function refreshKiroToken(refreshToken, region = KIRO_REFRESH_CONSTANTS.DE
|
||||||
const timeoutId = setTimeout(() => controller.abort(), KIRO_REFRESH_CONSTANTS.REQUEST_TIMEOUT);
|
const timeoutId = setTimeout(() => controller.abort(), KIRO_REFRESH_CONSTANTS.REQUEST_TIMEOUT);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(refreshUrl, {
|
const response = await fetchWithProxy(refreshUrl, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': KIRO_REFRESH_CONSTANTS.CONTENT_TYPE_JSON
|
'Content-Type': KIRO_REFRESH_CONSTANTS.CONTENT_TYPE_JSON
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ refreshToken }),
|
body: JSON.stringify({ refreshToken }),
|
||||||
signal: controller.signal
|
signal: controller.signal
|
||||||
});
|
}, 'claude-kiro-oauth');
|
||||||
|
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
|
|
||||||
|
|
@ -1901,7 +1936,7 @@ export async function importAwsCredentials(credentials, skipDuplicateCheck = fal
|
||||||
const region = credentials.region || KIRO_REFRESH_CONSTANTS.DEFAULT_REGION;
|
const region = credentials.region || KIRO_REFRESH_CONSTANTS.DEFAULT_REGION;
|
||||||
const refreshUrl = KIRO_REFRESH_CONSTANTS.REFRESH_IDC_URL.replace('{{region}}', region);
|
const refreshUrl = KIRO_REFRESH_CONSTANTS.REFRESH_IDC_URL.replace('{{region}}', region);
|
||||||
|
|
||||||
const refreshResponse = await fetch(refreshUrl, {
|
const refreshResponse = await fetchWithProxy(refreshUrl, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
|
|
@ -1912,7 +1947,7 @@ export async function importAwsCredentials(credentials, skipDuplicateCheck = fal
|
||||||
clientSecret: credentials.clientSecret,
|
clientSecret: credentials.clientSecret,
|
||||||
grantType: 'refresh_token'
|
grantType: 'refresh_token'
|
||||||
})
|
})
|
||||||
});
|
}, 'claude-kiro-oauth');
|
||||||
|
|
||||||
if (refreshResponse.ok) {
|
if (refreshResponse.ok) {
|
||||||
const tokenData = await refreshResponse.json();
|
const tokenData = await refreshResponse.json();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue