refactor kiro flow to web signin authorization

This commit is contained in:
QLHazyCoder
2026-05-19 12:16:59 +08:00
parent 98e776348e
commit ef700be3e2
22 changed files with 521 additions and 1391 deletions
@@ -83,6 +83,13 @@ test('kiro desktop authorize runner uses a shared 3-minute page-load timeout bud
assert.match(source, /finalizeDesktopAuthorizeCallback/);
});
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, /runtimeState\.register\?\.status/);
assert.match(source, /runtimeState\.webAuth\?\.status/);
assert.match(source, /Kiro Web 登录态尚未建立/);
});
test('executeKiroCompleteDesktopAuthorize finishes from callback page without waiting for tracker replay', async () => {
const api = loadDesktopAuthorizeRunnerApi();
let currentState = createDesktopAuthorizeState();
@@ -10,52 +10,18 @@ function loadRegisterRunnerApi() {
return globalScope.MultiPageBackgroundKiroRegisterRunner;
}
test('kiro register runner module exposes a factory and device login bootstrap helper', () => {
test('kiro register runner module exposes a factory and Kiro official sign-in entry', () => {
const api = loadRegisterRunnerApi();
assert.equal(typeof api?.createKiroRegisterRunner, 'function');
assert.equal(typeof api?.startBuilderIdDeviceLogin, 'function');
assert.equal(api?.KIRO_SIGNIN_URL, 'https://app.kiro.dev/signin');
});
test('startBuilderIdDeviceLogin registers Builder ID client and returns login bootstrap payload', async () => {
const api = loadRegisterRunnerApi();
const requests = [];
const result = await api.startBuilderIdDeviceLogin('us-east-1', async (url, options = {}) => {
requests.push({ url, options });
if (url.endsWith('/client/register')) {
return {
ok: true,
text: async () => JSON.stringify({
clientId: 'client-001',
clientSecret: 'secret-001',
}),
};
}
if (url.endsWith('/device_authorization')) {
return {
ok: true,
text: async () => JSON.stringify({
deviceCode: 'device-code-001',
userCode: 'ABCD-1234',
verificationUri: 'https://view.awsapps.com/start',
verificationUriComplete: 'https://view.awsapps.com/start?user_code=ABCD-1234',
interval: 7,
expiresIn: 600,
}),
};
}
throw new Error(`Unexpected request: ${url}`);
});
assert.equal(requests.length, 2);
assert.equal(requests[0].url, 'https://oidc.us-east-1.amazonaws.com/client/register');
assert.equal(requests[1].url, 'https://oidc.us-east-1.amazonaws.com/device_authorization');
assert.equal(result.clientId, 'client-001');
assert.equal(result.clientSecret, 'secret-001');
assert.equal(result.deviceCode, 'device-code-001');
assert.equal(result.userCode, 'ABCD-1234');
assert.equal(result.verificationUriComplete, 'https://view.awsapps.com/start?user_code=ABCD-1234');
assert.equal(result.interval, 7);
assert.equal(result.region, 'us-east-1');
test('kiro register runner removed the old AWS device authorization bootstrap', () => {
const source = fs.readFileSync('background/kiro/register-runner.js', 'utf8');
assert.doesNotMatch(source, /startBuilderIdDeviceLogin/);
assert.doesNotMatch(source, /device_authorization/);
assert.doesNotMatch(source, /verificationUriComplete/);
assert.match(source, /https:\/\/app\.kiro\.dev\/signin/);
});
test('kiro register runner uses a shared 3-minute page-load timeout budget', () => {
@@ -66,3 +32,10 @@ test('kiro register runner uses a shared 3-minute page-load timeout budget', ()
assert.match(source, /timeoutBudget\.getRemainingMs\(1000\)/);
assert.match(source, /onRetryableError: buildKiroRetryRecovery\(tabId, \{\s*\.\.\.options,\s*timeoutBudget,/);
});
test('kiro register consent step treats Kiro Web signed-in page as completion', () => {
const source = fs.readFileSync('background/kiro/register-runner.js', 'utf8');
assert.match(source, /targetStates: \['authorization_page', 'kiro_web_signed_in'\]/);
assert.match(source, /landingResult\?\.state !== 'kiro_web_signed_in'/);
assert.doesNotMatch(source, /landingResult\?\.state !== 'success_page'/);
});
+8 -3
View File
@@ -32,10 +32,14 @@ test('kiro state module exposes canonical nested kiroRuntime view', () => {
register: {
email: 'aws-user@example.com',
fullName: 'Ada Lovelace',
userCode: 'ABCD-1234',
loginUrl: 'https://device.example.com/complete',
loginUrl: 'https://app.kiro.dev/signin',
status: 'waiting_name',
},
webAuth: {
status: 'signin_started',
hasAccessToken: false,
hasSessionToken: false,
},
desktopAuth: {
clientId: 'client-001',
clientSecret: 'secret-001',
@@ -54,7 +58,8 @@ test('kiro state module exposes canonical nested kiroRuntime view', () => {
assert.equal(view.kiroRuntime.session.currentStage, 'desktop-authorize');
assert.equal(view.kiroRuntime.session.registerTabId, 88);
assert.equal(view.kiroRuntime.register.email, 'aws-user@example.com');
assert.equal(view.kiroRuntime.register.userCode, 'ABCD-1234');
assert.equal(view.kiroRuntime.register.loginUrl, 'https://app.kiro.dev/signin');
assert.equal(view.kiroRuntime.webAuth.status, 'signin_started');
assert.equal(view.kiroRuntime.desktopAuth.clientId, 'client-001');
assert.equal(view.kiroRuntime.desktopAuth.refreshToken, 'refresh-001');
assert.equal(view.kiroRuntime.upload.status, 'ready_to_upload');
@@ -146,6 +146,9 @@ const captured = [];
const PLUS_PAYMENT_METHOD_PAYPAL = 'paypal';
const PLUS_PAYMENT_METHOD_GOPAY = 'gopay';
const PLUS_PAYMENT_METHOD_GPC_HELPER = 'gpc-helper';
const PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH = 'oauth';
const PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION = 'sub2api_codex_session';
const DEFAULT_ACTIVE_FLOW_ID = 'openai';
const self = {
MultiPageStepDefinitions: {
getSteps(options) {
@@ -160,8 +163,10 @@ const self = {
};
${extractFunction('isPlusModeState')}
${extractFunction('normalizePlusPaymentMethod')}
${extractFunction('normalizePlusAccountAccessStrategy')}
${extractFunction('normalizeSignupMethod')}
${extractFunction('getSignupMethodForStepDefinitions')}
${extractFunction('buildResolvedStepDefinitionState')}
${extractFunction('getStepDefinitionsForState')}
return {
getCaptured: () => captured.slice(),
@@ -180,6 +185,7 @@ return {
activeFlowId: 'openai',
plusModeEnabled: true,
plusPaymentMethod: 'gopay',
plusAccountAccessStrategy: 'oauth',
signupMethod: 'phone',
phoneSignupReloginAfterBindEmailEnabled: false,
}]);
@@ -77,3 +77,16 @@ test('kiro register content does not misclassify the normal name page as a proxy
assert.equal(fatal, null);
});
test('kiro register content treats Kiro web success callback as signed in', () => {
const harness = createHarness({
href: 'https://app.kiro.dev/signin?auth_status=success&redirect_from=KiroIDE',
hostname: 'app.kiro.dev',
title: 'Kiro',
bodyText: 'Signed in',
});
const detected = harness.detectKiroRegisterPageState();
assert.equal(detected.state, 'kiro_web_signed_in');
});
@@ -195,6 +195,9 @@ test('collectSettingsPayload omits custom password and local sync settings in co
const api = new Function('normalizeIcloudTargetMailboxType', 'normalizeIcloudForwardMailProvider', `
let latestState = { contributionMode: true };
const window = {};
const PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH = 'oauth';
const PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION = 'sub2api_codex_session';
const DEFAULT_PLUS_ACCOUNT_ACCESS_STRATEGY = PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH;
let cloudflareDomainEditMode = false;
let cloudflareTempEmailDomainEditMode = false;
const selectCfDomain = { value: 'example.com' };
@@ -297,6 +300,7 @@ function normalizeAutoRunThreadIntervalMinutes(value) { return Number(value) ||
function normalizeAutoDelayMinutes(value) { return Number(value) || 30; }
function normalizeAutoStepDelaySeconds(value) { return value === '' ? null : Number(value); }
function normalizeVerificationResendCount(value, fallback) { return Number.isFinite(Number(value)) ? Number(value) : fallback; }
function normalizePlusAccountAccessStrategy(value = '') { return String(value || '').trim().toLowerCase() === PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION ? PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION : PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH; }
function normalizePhoneSmsProvider(value = '') { return String(value || '').trim().toLowerCase() === '5sim' ? '5sim' : 'hero-sms'; }
function getSelectedPhoneSmsProvider() { return normalizePhoneSmsProvider(selectPhoneSmsProvider?.value || latestState?.phoneSmsProvider); }
function normalizeFiveSimCountryId(value, fallback = DEFAULT_FIVE_SIM_COUNTRY_ID) { return String(value || '').trim().toLowerCase().replace(/[^a-z0-9_-]+/g, '') || fallback; }
+7 -1
View File
@@ -43,7 +43,7 @@ test('sidepanel html exposes flow selector and kiro source fields', () => {
'id="row-kiro-rs-key"',
'id="btn-test-kiro-rs"',
'id="row-kiro-rs-test-status"',
'id="row-kiro-device-code"',
'id="row-kiro-web-status"',
'id="row-kiro-login-url"',
'id="row-kiro-upload-status"',
].forEach((snippet) => {
@@ -73,12 +73,14 @@ const window = {
let latestState = { activeFlowId: 'openai' };
let currentPlusModeEnabled = false;
let currentPlusPaymentMethod = 'paypal';
let currentPlusAccountAccessStrategy = 'oauth';
let currentSignupMethod = 'email';
let currentPhoneSignupReloginAfterBindEmailEnabled = false;
let currentStepDefinitionFlowId = 'openai';
const DEFAULT_ACTIVE_FLOW_ID = 'openai';
const DEFAULT_SIGNUP_METHOD = 'email';
const DEFAULT_PLUS_PAYMENT_METHOD = 'paypal';
const DEFAULT_PLUS_ACCOUNT_ACCESS_STRATEGY = 'oauth';
let stepDefinitions = [{ id: 6, key: 'openai' }];
let STEP_IDS = [6];
let STEP_DEFAULT_STATUSES = { 6: 'pending' };
@@ -86,6 +88,9 @@ let SKIPPABLE_STEPS = new Set([6]);
function renderStepsList() {
calls.push({ type: 'render', stepIds: [...STEP_IDS] });
}
function normalizePlusAccountAccessStrategy(value = '') {
return String(value || DEFAULT_PLUS_ACCOUNT_ACCESS_STRATEGY).trim().toLowerCase() || DEFAULT_PLUS_ACCOUNT_ACCESS_STRATEGY;
}
${bundle}
return {
calls,
@@ -110,6 +115,7 @@ return {
activeFlowId: 'kiro',
plusModeEnabled: false,
plusPaymentMethod: 'paypal',
plusAccountAccessStrategy: 'oauth',
signupMethod: 'email',
phoneSignupReloginAfterBindEmailEnabled: false,
},
+8
View File
@@ -86,6 +86,9 @@ test('collectSettingsPayload persists icloud target mailbox settings', () => {
const api = new Function(`
let latestState = { contributionMode: false };
const window = {};
const PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH = 'oauth';
const PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION = 'sub2api_codex_session';
const DEFAULT_PLUS_ACCOUNT_ACCESS_STRATEGY = PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH;
let cloudflareDomainEditMode = false;
let cloudflareTempEmailDomainEditMode = false;
const selectCfDomain = { value: '' };
@@ -220,6 +223,7 @@ function normalizeAutoRunThreadIntervalMinutes(value) { return Number(value) ||
function normalizeAutoDelayMinutes(value) { return Number(value) || 30; }
function normalizeAutoStepDelaySeconds(value) { return value === '' ? null : Number(value); }
function normalizeVerificationResendCount(value, fallback) { return Number(value) || fallback; }
function normalizePlusAccountAccessStrategy(value = '') { return String(value || '').trim().toLowerCase() === PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION ? PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION : PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH; }
function normalizePhoneSmsProvider(value = '') { return String(value || '').trim().toLowerCase() === '5sim' ? '5sim' : 'hero-sms'; }
function setPhoneSmsProviderSelectValue(provider) {
const normalizedProvider = normalizePhoneSmsProvider(provider);
@@ -363,6 +367,9 @@ test('applySettingsState restores icloud forward mailbox settings before UI refr
const api = new Function('calls', `
let latestState = {};
const PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH = 'oauth';
const PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION = 'sub2api_codex_session';
const DEFAULT_PLUS_ACCOUNT_ACCESS_STRATEGY = PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH;
const inputEmail = { value: '' };
const inputVpsUrl = { value: '' };
const inputVpsPassword = { value: '' };
@@ -460,6 +467,7 @@ function normalizePanelMode(value = '') {
const normalized = String(value || '').trim().toLowerCase();
return normalized === 'sub2api' || normalized === 'codex2api' ? normalized : 'cpa';
}
function normalizePlusAccountAccessStrategy(value = '') { return String(value || '').trim().toLowerCase() === PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION ? PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION : PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH; }
function isCustomMailProvider() { return false; }
function setMail2925Mode() {}
function normalizeIcloudFetchMode(value) { return String(value || '') === 'always_new' ? 'always_new' : 'reuse_existing'; }
@@ -159,6 +159,9 @@ let latestState = {
currentMail2925AccountId: 'acc-2',
};
const window = {};
const PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH = 'oauth';
const PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION = 'sub2api_codex_session';
const DEFAULT_PLUS_ACCOUNT_ACCESS_STRATEGY = PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH;
let cloudflareDomainEditMode = false;
let cloudflareTempEmailDomainEditMode = false;
const selectCfDomain = { value: 'example.com' };
@@ -258,6 +261,7 @@ function normalizeAutoRunThreadIntervalMinutes(value) { return Number(value) ||
function normalizeAutoDelayMinutes(value) { return Number(value) || 30; }
function normalizeAutoStepDelaySeconds(value) { return value === '' ? null : Number(value); }
function normalizeVerificationResendCount(value, fallback) { return Number(value) || fallback; }
function normalizePlusAccountAccessStrategy(value = '') { return String(value || '').trim().toLowerCase() === PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION ? PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION : PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH; }
function normalizePhoneSmsProvider(value = '') { return String(value || '').trim().toLowerCase() === '5sim' ? '5sim' : 'hero-sms'; }
function getSelectedPhoneSmsProvider() { return normalizePhoneSmsProvider(selectPhoneSmsProvider?.value || latestState?.phoneSmsProvider); }
function normalizeFiveSimCountryId(value, fallback = DEFAULT_FIVE_SIM_COUNTRY_ID) { return String(value || '').trim().toLowerCase().replace(/[^a-z0-9_-]+/g, '') || fallback; }
@@ -873,6 +873,9 @@ return {
test('collectSettingsPayload keeps local helper sync enabled while persisting sms toggle state', () => {
const api = new Function('normalizeIcloudTargetMailboxType', 'normalizeIcloudForwardMailProvider', `
const window = {};
const PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH = 'oauth';
const PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION = 'sub2api_codex_session';
const DEFAULT_PLUS_ACCOUNT_ACCESS_STRATEGY = PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH;
let latestState = {
contributionMode: false,
mail2925UseAccountPool: false,
@@ -1025,6 +1028,7 @@ function normalizeAutoRunThreadIntervalMinutes(value) { return Number(value) ||
function normalizeAutoDelayMinutes(value) { return Number(value) || 30; }
function normalizeAutoStepDelaySeconds(value) { return value === '' ? null : Number(value); }
function normalizeVerificationResendCount(value, fallback) { return Number(value) || fallback; }
function normalizePlusAccountAccessStrategy(value = '') { return String(value || '').trim().toLowerCase() === PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION ? PLUS_ACCOUNT_ACCESS_STRATEGY_SUB2API_CODEX_SESSION : PLUS_ACCOUNT_ACCESS_STRATEGY_OAUTH; }
${extractFunction('normalizePhoneSmsProvider')}
${extractFunction('normalizePhoneSmsProviderValue')}
${extractFunction('normalizeFiveSimCountryCode')}
+11 -3
View File
@@ -70,8 +70,8 @@ test('shared source registry exposes canonical Kiro sources and drivers', () =>
);
assert.equal(
registry.detectSourceFromLocation({
url: 'https://view.awsapps.com/start',
hostname: 'view.awsapps.com',
url: 'https://app.kiro.dev/signin',
hostname: 'app.kiro.dev',
}),
'kiro-register-page'
);
@@ -91,11 +91,19 @@ test('shared source registry exposes canonical Kiro sources and drivers', () =>
'unknown-source'
);
assert.equal(
registry.matchesSourceUrlFamily(
'kiro-register-page',
'https://app.kiro.dev/signin',
'https://app.kiro.dev/signin'
),
true
);
assert.equal(
registry.matchesSourceUrlFamily(
'kiro-register-page',
'https://signin.aws/register',
'https://view.awsapps.com/start'
'https://app.kiro.dev/signin'
),
true
);