/** * Kiro IDC Token Refresh Tool * 通过 refreshToken + clientId + clientSecret 获取 accessToken (基于 AWS OIDC/IDC) * * 使用方法: * 1. 位置参数模式: * node src/kiro-idc-token-refresh.js [authMethod] [provider] * 2. JSON 文件模式: * node src/kiro-idc-token-refresh.js ./config.json * 3. JSON 字符串模式: * node src/kiro-idc-token-refresh.js '{"refreshToken": "...", "clientId": "...", "clientSecret": "..."}' * * 参数: * refreshToken - Kiro 的 refresh token * clientId - AWS OIDC client ID * clientSecret - AWS OIDC client secret * authMethod - 认证方法 (可选,默认: IdC) * provider - 提供商 (可选,默认: BuilderId) * * 输出格式: * { * "accessToken": "aoaAAAA", * "refreshToken": "aorAAAAAGnTpTMP_mR", * "expiresAt": "2026-01-06T14:22:16.130Z", * "authMethod": "IdC", * "provider": "BuilderId", * "clientId": "e8pqSrALVjvbqaW", * "clientSecret": "eyJraWQiOiJrZXktMTU2NDAy", * "region": "us-east-1" * } */ import axios from 'axios'; import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; // 获取当前脚本所在目录 const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const KIRO_IDC_CONSTANTS = { REFRESH_IDC_URL: 'https://oidc.{{region}}.amazonaws.com/token', CONTENT_TYPE_JSON: 'application/json', DEFAULT_AUTH_METHOD: 'IdC', DEFAULT_PROVIDER: 'BuilderId', DEFAULT_REGION: 'us-east-1', AXIOS_TIMEOUT: 30000, // 30 seconds timeout }; /** * 通过 IDC (AWS OIDC) 刷新 token * @param {string} refreshToken - Kiro 的 refresh token * @param {string} clientId - AWS OIDC client ID * @param {string} clientSecret - AWS OIDC client secret * @param {Object} options - 可选参数 * @param {string} options.authMethod - 认证方法 (默认: IdC) * @param {string} options.provider - 提供商 (默认: BuilderId) * @param {string} options.region - AWS 区域 (默认: us-east-1) * @returns {Promise} 包含 accessToken 等信息的对象 */ async function refreshKiroIdcToken(refreshToken, clientId, clientSecret, options = {}) { const authMethod = options.authMethod || KIRO_IDC_CONSTANTS.DEFAULT_AUTH_METHOD; const provider = options.provider || KIRO_IDC_CONSTANTS.DEFAULT_PROVIDER; const region = options.region || KIRO_IDC_CONSTANTS.DEFAULT_REGION; const refreshUrl = KIRO_IDC_CONSTANTS.REFRESH_IDC_URL.replace('{{region}}', region); // IDC/OIDC 使用 form-urlencoded 格式 const requestBody = { grantType: 'refresh_token', refreshToken: refreshToken, clientId: clientId, clientSecret: clientSecret, }; const axiosConfig = { timeout: KIRO_IDC_CONSTANTS.AXIOS_TIMEOUT, headers: { 'Content-Type': KIRO_IDC_CONSTANTS.CONTENT_TYPE_JSON, 'User-Agent': 'KiroIDE' }, }; try { console.log(`[Kiro IDC Token Refresh] 正在请求: ${refreshUrl}`); const response = await axios.post(refreshUrl, requestBody, axiosConfig); // AWS OIDC 返回格式: { access_token, refresh_token, expires_in, token_type } if (response.data && response.data.accessToken) { const expiresIn = response.data.expiresIn; const expiresAt = new Date(Date.now() + expiresIn * 1000).toISOString(); const result = { accessToken: response.data.accessToken, refreshToken: response.data.refreshToken || refreshToken, expiresAt: expiresAt, authMethod: authMethod, provider: provider, clientId: clientId, clientSecret: clientSecret, region: region, }; return result; } else { throw new Error('Invalid refresh response: Missing access_token'); } } catch (error) { if (error.response) { console.error(`[Kiro IDC Token Refresh] 请求失败: HTTP ${error.response.status}`); console.error(`[Kiro IDC Token Refresh] 响应内容:`, error.response.data); } else if (error.request) { console.error(`[Kiro IDC Token Refresh] 请求失败: 无响应`); } else { console.error(`[Kiro IDC Token Refresh] 请求失败:`, error.message); } throw error; } } /** * 主函数 - 命令行入口 */ async function main() { const args = process.argv.slice(2); let refreshToken, clientId, clientSecret, authMethod, provider; // 1. 尝试解析第一个参数为 JSON 文件路径 if (args.length === 1 && args[0].toLowerCase().endsWith('.json')) { try { const jsonPath = path.isAbsolute(args[0]) ? args[0] : path.resolve(process.cwd(), args[0]); if (fs.existsSync(jsonPath)) { console.log(`[Kiro IDC Token Refresh] 正在从文件读取配置: ${jsonPath}`); const fileContent = fs.readFileSync(jsonPath, 'utf-8'); const parsed = JSON.parse(fileContent); refreshToken = parsed.refreshToken; clientId = parsed.clientId; clientSecret = parsed.clientSecret; authMethod = parsed.authMethod; provider = parsed.provider; } else { console.error(`错误: 找不到文件 ${jsonPath}`); process.exit(1); } } catch (e) { console.error(`错误: 读取或解析 JSON 文件失败: ${e.message}`); process.exit(1); } } // 2. 尝试解析第一个参数为 JSON 字符串 else if (args.length === 1 && args[0].trim().startsWith('{')) { try { const parsed = JSON.parse(args[0]); refreshToken = parsed.refreshToken; clientId = parsed.clientId; clientSecret = parsed.clientSecret; authMethod = parsed.authMethod; provider = parsed.provider; } catch (e) { // JSON 解析失败,将回退到位置参数处理 } } // 如果没有通过 JSON 成功获取参数,则尝试位置参数 if (!refreshToken) { if (args.length === 0 || args.length < 3) { console.log('Kiro IDC Token Refresh Tool'); console.log('============================'); console.log(''); console.log('使用方法:'); console.log(' 1. 位置参数模式:'); console.log(' node src/kiro-idc-token-refresh.js [authMethod] [provider]'); console.log(' 2. JSON 文件模式:'); console.log(' node src/kiro-idc-token-refresh.js ./config.json'); console.log(' 3. JSON 字符串模式:'); console.log(' node src/kiro-idc-token-refresh.js \'{"refreshToken": "...", "clientId": "...", "clientSecret": "...", "authMethod": "...", "provider": "..."}\''); console.log(''); console.log('参数:'); console.log(' refreshToken - Kiro 的 refresh token (必需)'); console.log(' clientId - AWS OIDC client ID (必需)'); console.log(' clientSecret - AWS OIDC client secret (必需)'); console.log(' authMethod - 认证方法 (可选,默认: IdC)'); console.log(' provider - 提供商 (可选,默认: BuilderId)'); console.log(''); console.log('示例:'); console.log(' node src/kiro-idc-token-refresh.js aorAxxxxxxxx e8pqSrALVjvbqaW eyJraWQiOiJrZXktMTU2NDAy'); console.log(' node src/kiro-idc-token-refresh.js aorAxxxxxxxx e8pqSrALVjvbqaW eyJraWQiOiJrZXktMTU2NDAy IdC Enterprise'); console.log(''); console.log('输出格式:'); console.log(JSON.stringify({ accessToken: "aoaAAAA...", refreshToken: "aorAAAAAGnTpTMP_mR...", expiresAt: "2026-01-06T14:22:16.130Z", authMethod: "IdC", provider: "BuilderId", clientId: "e8pqSrALVjvbqaW", clientSecret: "eyJraWQiOiJrZXktMTU2NDAy", region: "us-east-1" }, null, 2)); process.exit(0); } refreshToken = args[0]; clientId = args[1]; clientSecret = args[2]; authMethod = args[3]; provider = args[4]; } // 设置默认值 authMethod = authMethod || KIRO_IDC_CONSTANTS.DEFAULT_AUTH_METHOD; provider = provider || KIRO_IDC_CONSTANTS.DEFAULT_PROVIDER; if (!refreshToken) { console.error('错误: 请提供 refreshToken'); process.exit(1); } if (!clientId) { console.error('错误: 请提供 clientId'); process.exit(1); } if (!clientSecret) { console.error('错误: 请提供 clientSecret'); process.exit(1); } try { console.log(`[Kiro IDC Token Refresh] 开始刷新 token...`); console.log(`[Kiro IDC Token Refresh] 认证方法: ${authMethod}`); console.log(`[Kiro IDC Token Refresh] 提供商: ${provider}`); console.log(`[Kiro IDC Token Refresh] Client ID: ${clientId.substring(0, 8)}...`); const result = await refreshKiroIdcToken(refreshToken, clientId, clientSecret, { authMethod, provider, region: KIRO_IDC_CONSTANTS.DEFAULT_REGION }); console.log(''); console.log('=== Token 刷新成功 ==='); console.log(''); console.log(JSON.stringify(result, null, 2)); // 输出过期时间信息 const expiresDate = new Date(result.expiresAt); const now = new Date(); const diffMs = expiresDate - now; const diffMins = Math.floor(diffMs / 60000); const diffHours = Math.floor(diffMins / 60); console.log(''); console.log(`[Kiro IDC Token Refresh] Token 将在 ${diffHours} 小时 ${diffMins % 60} 分钟后过期`); console.log(`[Kiro IDC Token Refresh] 过期时间: ${result.expiresAt}`); // 写入 JSON 文件到脚本执行目录 const timestamp = Date.now(); const outputFileName = `kiro-idc-${timestamp}-auth-token.json`; const outputFilePath = path.join(__dirname, outputFileName); fs.writeFileSync(outputFilePath, JSON.stringify(result, null, 2), 'utf-8'); console.log(''); console.log(`[Kiro IDC Token Refresh] Token 已保存到文件: ${outputFilePath}`); } catch (error) { console.error(''); console.error('=== Token 刷新失败 ==='); console.error(`错误: ${error.message}`); process.exit(1); } } // 导出函数供其他模块使用 export { refreshKiroIdcToken }; // 如果直接运行此脚本,执行主函数 main();