diff --git a/src/assets/icons/kiro.svg b/src/assets/icons/kiro.svg new file mode 100644 index 0000000..1b53e35 --- /dev/null +++ b/src/assets/icons/kiro.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json index b1a056e..4948e8c 100644 --- a/src/i18n/locales/en.json +++ b/src/i18n/locales/en.json @@ -675,7 +675,26 @@ "iflow_cookie_result_expired": "Expires At", "iflow_cookie_result_path": "Saved Path", "iflow_cookie_result_type": "Type", - "remote_access_disabled": "This login method is not available for remote access. Please access from localhost." + "remote_access_disabled": "This login method is not available for remote access. Please access from localhost.", + "kiro_oauth_title": "Kiro OAuth", + "kiro_oauth_hint": "Login to Kiro service via AWS SSO, supporting Builder ID and Identity Center (IDC), or import refreshToken from Kiro IDE directly.", + "kiro_builder_id_label": "AWS Builder ID Login", + "kiro_builder_id_hint": "Login with AWS Builder ID account, suitable for individual developers.", + "kiro_builder_id_button": "Login with Builder ID", + "kiro_idc_label": "AWS Identity Center (IDC) Login", + "kiro_idc_hint": "Login with enterprise AWS Identity Center, requires Start URL and Region.", + "kiro_idc_start_url_label": "Start URL", + "kiro_idc_start_url_placeholder": "https://your-org.awsapps.com/start", + "kiro_idc_region_label": "Region (Optional)", + "kiro_idc_region_placeholder": "us-east-1", + "kiro_idc_button": "Login with IDC", + "kiro_token_import_label": "Token Import", + "kiro_token_import_hint": "Import refreshToken from Kiro IDE, can be found in Kiro IDE's auth file.", + "kiro_token_placeholder": "Paste refreshToken", + "kiro_token_import_button": "Import Token", + "kiro_token_required": "Please enter refreshToken first", + "kiro_token_import_success": "Kiro Token imported successfully", + "kiro_token_import_error": "Kiro Token import failed:" }, "usage_stats": { "title": "Usage Statistics", diff --git a/src/i18n/locales/zh-CN.json b/src/i18n/locales/zh-CN.json index 9b526a5..5f74b0a 100644 --- a/src/i18n/locales/zh-CN.json +++ b/src/i18n/locales/zh-CN.json @@ -675,7 +675,26 @@ "iflow_cookie_result_expired": "过期时间", "iflow_cookie_result_path": "保存路径", "iflow_cookie_result_type": "类型", - "remote_access_disabled": "远程访问不支持此登录方式,请从本地 (localhost) 访问" + "remote_access_disabled": "远程访问不支持此登录方式,请从本地 (localhost) 访问", + "kiro_oauth_title": "Kiro OAuth", + "kiro_oauth_hint": "通过 AWS SSO 登录 Kiro 服务,支持 Builder ID 和 Identity Center (IDC) 两种方式,或直接导入 Kiro IDE 的 refreshToken。", + "kiro_builder_id_label": "AWS Builder ID 登录", + "kiro_builder_id_hint": "使用 AWS Builder ID 账号登录,适用于个人开发者。", + "kiro_builder_id_button": "使用 Builder ID 登录", + "kiro_idc_label": "AWS Identity Center (IDC) 登录", + "kiro_idc_hint": "使用企业 AWS Identity Center 登录,需要提供 Start URL 和 Region。", + "kiro_idc_start_url_label": "Start URL", + "kiro_idc_start_url_placeholder": "https://your-org.awsapps.com/start", + "kiro_idc_region_label": "Region (可选)", + "kiro_idc_region_placeholder": "us-east-1", + "kiro_idc_button": "使用 IDC 登录", + "kiro_token_import_label": "Token 导入", + "kiro_token_import_hint": "从 Kiro IDE 导入 refreshToken,可在 Kiro IDE 的认证文件中找到。", + "kiro_token_placeholder": "粘贴 refreshToken", + "kiro_token_import_button": "导入 Token", + "kiro_token_required": "请先填写 refreshToken", + "kiro_token_import_success": "Kiro Token 导入成功", + "kiro_token_import_error": "Kiro Token 导入失败:" }, "usage_stats": { "title": "使用统计", diff --git a/src/pages/OAuthPage.tsx b/src/pages/OAuthPage.tsx index 066a4b6..9a0f357 100644 --- a/src/pages/OAuthPage.tsx +++ b/src/pages/OAuthPage.tsx @@ -15,6 +15,7 @@ import iconGemini from '@/assets/icons/gemini.svg'; import iconQwen from '@/assets/icons/qwen.svg'; import iconIflow from '@/assets/icons/iflow.svg'; import iconVertex from '@/assets/icons/vertex.svg'; +import iconKiro from '@/assets/icons/kiro.svg'; interface ProviderState { url?: string; @@ -54,6 +55,19 @@ interface VertexImportState { result?: VertexImportResult; } +interface KiroOAuthState { + method: 'builder-id' | 'idc' | null; + startUrl: string; + region: string; +} + +interface KiroTokenImportState { + token: string; + loading: boolean; + error?: string; + success?: boolean; +} + const PROVIDERS: { id: OAuthProvider; titleKey: string; hintKey: string; urlLabelKey: string; icon: string | { light: string; dark: string } }[] = [ { id: 'codex', titleKey: 'auth_login.codex_oauth_title', hintKey: 'auth_login.codex_oauth_hint', urlLabelKey: 'auth_login.codex_oauth_url_label', icon: { light: iconCodexLight, dark: iconCodexDark } }, { id: 'anthropic', titleKey: 'auth_login.anthropic_oauth_title', hintKey: 'auth_login.anthropic_oauth_hint', urlLabelKey: 'auth_login.anthropic_oauth_url_label', icon: iconClaude }, @@ -82,6 +96,15 @@ export function OAuthPage() { location: '', loading: false }); + const [kiroOAuth, setKiroOAuth] = useState({ + method: null, + startUrl: '', + region: '' + }); + const [kiroTokenImport, setKiroTokenImport] = useState({ + token: '', + loading: false + }); const timers = useRef>({}); const vertexFileInputRef = useRef(null); @@ -303,6 +326,48 @@ export function OAuthPage() { } }; + const openKiroOAuth = (method: 'builder-id' | 'idc') => { + const baseUrl = window.location.origin; + let url = `${baseUrl}/v0/oauth/kiro/start?method=${method}`; + if (method === 'idc') { + const startUrl = kiroOAuth.startUrl.trim(); + const region = kiroOAuth.region.trim(); + if (startUrl) { + url += `&start_url=${encodeURIComponent(startUrl)}`; + } + if (region) { + url += `®ion=${encodeURIComponent(region)}`; + } + } + window.open(url, '_blank', 'noopener,noreferrer'); + }; + + const handleKiroTokenImport = async () => { + const token = kiroTokenImport.token.trim(); + if (!token) { + showNotification(t('auth_login.kiro_token_required'), 'warning'); + return; + } + setKiroTokenImport((prev) => ({ ...prev, loading: true, error: undefined, success: undefined })); + try { + const baseUrl = window.location.origin; + const response = await fetch(`${baseUrl}/v0/oauth/kiro/import`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ token }) + }); + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); + throw new Error(errorData.error || `HTTP ${response.status}`); + } + setKiroTokenImport((prev) => ({ ...prev, loading: false, success: true })); + showNotification(t('auth_login.kiro_token_import_success'), 'success'); + } catch (err: any) { + setKiroTokenImport((prev) => ({ ...prev, loading: false, error: err?.message })); + showNotification(`${t('auth_login.kiro_token_import_error')} ${err?.message || ''}`, 'error'); + } + }; + return (

{t('nav.oauth', { defaultValue: 'OAuth' })}

@@ -574,6 +639,95 @@ export function OAuthPage() {
)} + + {/* Kiro OAuth 登录 */} + + + {t('auth_login.kiro_oauth_title')} + + } + > +
{t('auth_login.kiro_oauth_hint')}
+ + {/* AWS Builder ID 登录 */} +
+ +
{t('auth_login.kiro_builder_id_hint')}
+ +
+ + {/* AWS Identity Center (IDC) 登录 */} +
+ +
{t('auth_login.kiro_idc_hint')}
+ setKiroOAuth((prev) => ({ ...prev, startUrl: e.target.value }))} + placeholder={t('auth_login.kiro_idc_start_url_placeholder')} + /> + setKiroOAuth((prev) => ({ ...prev, region: e.target.value }))} + placeholder={t('auth_login.kiro_idc_region_placeholder')} + /> + +
+ + {/* Token 导入 */} +
+ +
{t('auth_login.kiro_token_import_hint')}
+ + setKiroTokenImport((prev) => ({ + ...prev, + token: e.target.value, + error: undefined, + success: undefined + })) + } + placeholder={t('auth_login.kiro_token_placeholder')} + /> + + {kiroTokenImport.success && ( +
+ {t('auth_login.kiro_token_import_success')} +
+ )} + {kiroTokenImport.error && ( +
+ {t('auth_login.kiro_token_import_error')} {kiroTokenImport.error} +
+ )} +
+
);