From 85a6cc4157e2b7ec432f89b1d2395edacdab8bc5 Mon Sep 17 00:00:00 2001 From: QLHazyCoder <2825305047@qq.com> Date: Tue, 19 May 2026 14:58:31 +0800 Subject: [PATCH] Fix Kiro web session detection for desktop auth --- background.js | 2 +- background/kiro/desktop-authorize-runner.js | 67 ++++++++-- ...ro-desktop-authorize-runner-module.test.js | 126 ++++++++++++++++++ 3 files changed, 186 insertions(+), 9 deletions(-) diff --git a/background.js b/background.js index 5a56c28..bddd89f 100644 --- a/background.js +++ b/background.js @@ -13295,7 +13295,6 @@ const kiroRegisterRunner = self.MultiPageBackgroundKiroRegisterRunner?.createKir getState, HOTMAIL_PROVIDER, isTabAlive, - KIRO_REGISTER_INJECT_FILES, LUCKMAIL_PROVIDER, CLOUDFLARE_TEMP_EMAIL_PROVIDER, CLOUD_MAIL_PROVIDER, @@ -13333,6 +13332,7 @@ const kiroDesktopAuthorizeRunner = self.MultiPageBackgroundKiroDesktopAuthorizeR getState, HOTMAIL_PROVIDER, isTabAlive, + KIRO_REGISTER_INJECT_FILES, LUCKMAIL_PROVIDER, CLOUDFLARE_TEMP_EMAIL_PROVIDER, CLOUD_MAIL_PROVIDER, diff --git a/background/kiro/desktop-authorize-runner.js b/background/kiro/desktop-authorize-runner.js index 2857ba5..8bf0bf6 100644 --- a/background/kiro/desktop-authorize-runner.js +++ b/background/kiro/desktop-authorize-runner.js @@ -9,6 +9,7 @@ const MAIL_2925_FILTER_LOOKBACK_MS = 10 * 60 * 1000; const KIRO_REGISTER_PAGE_SOURCE_ID = 'kiro-register-page'; const KIRO_DESKTOP_SOURCE_ID = 'kiro-desktop-authorize'; + const KIRO_WEB_ACCOUNT_URL = 'https://app.kiro.dev/settings/account'; const KIRO_WEB_TAB_URL_PATTERNS = Object.freeze([ 'https://app.kiro.dev/*', 'https://kiro.dev/*', @@ -704,6 +705,32 @@ return candidates; } + async function openKiroWebAccountSessionTab() { + let tabId = null; + let tabUrl = KIRO_WEB_ACCOUNT_URL; + if (chrome?.tabs?.create) { + const tab = await chrome.tabs.create({ + url: KIRO_WEB_ACCOUNT_URL, + active: true, + }); + tabId = Number(tab?.id); + tabUrl = cleanString(tab?.url || KIRO_WEB_ACCOUNT_URL); + } else { + tabId = await reuseOrCreateTab(KIRO_REGISTER_PAGE_SOURCE_ID, KIRO_WEB_ACCOUNT_URL, { + inject: Array.isArray(KIRO_REGISTER_INJECT_FILES) ? KIRO_REGISTER_INJECT_FILES : null, + injectSource: KIRO_REGISTER_PAGE_SOURCE_ID, + }); + } + if (!Number.isInteger(tabId)) { + throw new Error('无法打开 Kiro 账号页,请手动打开 app.kiro.dev/settings/account 后重试步骤 7。'); + } + await registerTab(KIRO_REGISTER_PAGE_SOURCE_ID, tabId); + return { + id: tabId, + url: tabUrl || KIRO_WEB_ACCOUNT_URL, + }; + } + async function readKiroWebSessionStateFromTab(tabId, options = {}) { const timeoutBudget = resolveTimeoutBudget(options); if (typeof waitForTabStableComplete === 'function') { @@ -756,21 +783,27 @@ }; } - const tabs = await collectKiroWebSessionTabs(currentState); + const attemptedTabIds = new Set(); let detectedSignedInWithoutEmail = false; - for (const tab of tabs) { + let lastRecoveryError = ''; + const tryRestoreFromTab = async (tab) => { + const tabId = Number(tab?.id); + if (!Number.isInteger(tabId) || attemptedTabIds.has(tabId)) { + return null; + } + attemptedTabIds.add(tabId); try { - const pageState = await readKiroWebSessionStateFromTab(tab.id, { + const pageState = await readKiroWebSessionStateFromTab(tabId, { timeoutMs: DEFAULT_KIRO_PAGE_LOAD_TIMEOUT_MS, injectLogMessage: '步骤 7:Kiro Web 页面内容脚本未就绪,正在等待页面恢复...', }); if (pageState?.state !== 'kiro_web_signed_in') { - continue; + return null; } const detectedEmail = cleanString(pageState.accountEmail || pageState.email || existingEmail); if (!detectedEmail) { detectedSignedInWithoutEmail = true; - continue; + return null; } const restoredAt = Date.now(); @@ -811,18 +844,36 @@ restored: true, }; } catch (error) { + lastRecoveryError = getErrorMessage(error); console.warn('[MultiPage:kiro-desktop-authorize] restore web session failed', { - tabId: tab?.id, + tabId, url: tab?.url, - message: getErrorMessage(error), + message: lastRecoveryError, }); } + return null; + }; + + const tabs = await collectKiroWebSessionTabs(currentState); + for (const tab of tabs) { + const restoredSession = await tryRestoreFromTab(tab); + if (restoredSession) { + return restoredSession; + } + } + + await log('步骤 7:未能从已打开页面确认 Kiro Web 登录态,正在打开 Kiro 账号页重新确认...', 'info', nodeId); + const accountTab = await openKiroWebAccountSessionTab(); + const restoredSession = await tryRestoreFromTab(accountTab); + if (restoredSession) { + return restoredSession; } if (detectedSignedInWithoutEmail) { throw new Error('已检测到 Kiro Web 登录态,但未能识别账号邮箱。请打开 Kiro 账号设置页后重试步骤 7。'); } - throw new Error('Kiro Web 登录态尚未建立。请先完成步骤 6,或打开已登录的 Kiro Web 页面后从步骤 7 继续。'); + const detail = lastRecoveryError ? `最后一次检测错误:${lastRecoveryError}` : ''; + throw new Error(`Kiro Web 登录态尚未建立。请在自动打开的 Kiro 账号页登录后,从步骤 7 继续。${detail}`); } function buildDesktopOtpPollPayload(step, state = {}, mail = {}, filterAfterTimestamp = 0) { diff --git a/tests/background-kiro-desktop-authorize-runner-module.test.js b/tests/background-kiro-desktop-authorize-runner-module.test.js index f05bc4e..efee324 100644 --- a/tests/background-kiro-desktop-authorize-runner-module.test.js +++ b/tests/background-kiro-desktop-authorize-runner-module.test.js @@ -83,6 +83,24 @@ test('kiro desktop authorize runner uses a shared 3-minute page-load timeout bud assert.match(source, /finalizeDesktopAuthorizeCallback/); }); +test('background wires the Kiro register injector into desktop authorization runner', () => { + const source = fs.readFileSync('background.js', 'utf8'); + const start = source.indexOf('const kiroDesktopAuthorizeRunner = self.MultiPageBackgroundKiroDesktopAuthorizeRunner?.createKiroDesktopAuthorizeRunner({'); + const end = source.indexOf('const kiroPublisher = self.MultiPageBackgroundKiroPublisherKiroRs?.createKiroRsPublisher({'); + assert.notEqual(start, -1); + assert.notEqual(end, -1); + const block = source.slice(start, end); + assert.match(block, /KIRO_REGISTER_INJECT_FILES/); + assert.match(block, /KIRO_DESKTOP_AUTHORIZE_INJECT_FILES/); +}); + +test('kiro desktop authorization opens account page when no web session tab is tracked', () => { + const source = fs.readFileSync('background/kiro/desktop-authorize-runner.js', 'utf8'); + assert.match(source, /KIRO_WEB_ACCOUNT_URL = 'https:\/\/app\.kiro\.dev\/settings\/account'/); + assert.match(source, /chrome\.tabs\.create\(\{\s*url: KIRO_WEB_ACCOUNT_URL,\s*active: true,/); + assert.match(source, /未能从已打开页面确认 Kiro Web 登录态,正在打开 Kiro 账号页重新确认/); +}); + test('kiro desktop authorization is gated by completed Kiro Web sign-in', () => { const source = fs.readFileSync('background/kiro/desktop-authorize-runner.js', 'utf8'); assert.match(source, /restoreKiroWebSessionFromOpenTabs/); @@ -206,6 +224,114 @@ test('executeKiroStartDesktopAuthorize restores existing Kiro Web session before assert.equal(setStateCalls.length >= 2, true); }); +test('executeKiroStartDesktopAuthorize opens Kiro account page to restore login state', async () => { + const api = loadDesktopAuthorizeRunnerApi(); + let currentState = { + kiroRuntime: { + session: {}, + register: {}, + webAuth: {}, + desktopAuth: {}, + upload: {}, + }, + }; + const logs = []; + const registeredTabs = []; + let createdTabUrl = ''; + let completedPayload = null; + + const runner = api.createKiroDesktopAuthorizeRunner({ + addLog: async (message, level) => { + logs.push({ message, level }); + }, + chrome: { + tabs: { + create: async (payload) => { + createdTabUrl = payload.url; + return { + id: 91, + status: 'loading', + url: payload.url, + }; + }, + get: async (tabId) => ({ + id: tabId, + status: 'complete', + url: tabId === 91 + ? 'https://app.kiro.dev/settings/account' + : 'https://oidc.us-east-1.amazonaws.com/authorize', + }), + query: async () => [], + update: async () => ({}), + }, + webNavigation: { + onBeforeNavigate: { addListener: () => {} }, + onCommitted: { addListener: () => {} }, + }, + webRequest: { + onBeforeRequest: { addListener: () => {} }, + }, + }, + completeNodeFromBackground: async (_nodeId, payload) => { + completedPayload = payload; + }, + ensureContentScriptReadyOnTab: async (source, tabId, options = {}) => { + assert.equal(source, 'kiro-register-page'); + assert.equal(tabId, 91); + assert.deepEqual(options.inject, ['content/kiro/register-page.js']); + assert.equal(options.injectSource, 'kiro-register-page'); + }, + fetchImpl: async () => ({ + ok: true, + text: async () => JSON.stringify({ + clientId: 'opened-client-id', + clientSecret: 'opened-client-secret', + }), + }), + getState: async () => currentState, + getTabId: async () => null, + isTabAlive: async () => false, + KIRO_REGISTER_INJECT_FILES: ['content/kiro/register-page.js'], + registerTab: async (source, tabId) => { + registeredTabs.push({ source, tabId }); + }, + reuseOrCreateTab: async (_source, url) => { + assert.match(url, /oidc\.us-east-1\.amazonaws\.com\/authorize/); + return 92; + }, + sendToContentScriptResilient: async (source, message) => { + assert.equal(source, 'kiro-register-page'); + assert.equal(message.type, 'GET_KIRO_REGISTER_PAGE_STATE'); + return { + ok: true, + state: 'kiro_web_signed_in', + url: 'https://app.kiro.dev/settings/account', + accountEmail: 'opened@duck.com', + }; + }, + setState: async (patch) => { + currentState = { ...currentState, ...patch }; + }, + sleepWithStop: async () => {}, + throwIfStopped: () => {}, + waitForTabStableComplete: async () => ({ status: 'complete' }), + }); + + await runner.executeKiroStartDesktopAuthorize(currentState); + + assert.equal(createdTabUrl, 'https://app.kiro.dev/settings/account'); + assert.equal(currentState.kiroRuntime.register.email, 'opened@duck.com'); + assert.equal(completedPayload?.kiroRuntime?.desktopAuth?.clientId, 'opened-client-id'); + assert.deepEqual(registeredTabs, [ + { source: 'kiro-register-page', tabId: 91 }, + { source: 'kiro-desktop-authorize', tabId: 92 }, + ]); + assert.equal( + logs.some(({ message }) => message.includes('正在打开 Kiro 账号页重新确认')), + true + ); +}); + test('executeKiroCompleteDesktopAuthorize finishes from callback page without waiting for tracker replay', async () => { const api = loadDesktopAuthorizeRunnerApi(); let currentState = createDesktopAuthorizeState();