refactor kiro auth flow and share account password service

This commit is contained in:
QLHazyCoder
2026-05-18 12:49:49 +08:00
parent d081d47e1a
commit 12314e446a
21 changed files with 2155 additions and 108 deletions
@@ -23,6 +23,20 @@ function mergeUpdates(updatesList = []) {
return updatesList.reduce((acc, item) => Object.assign(acc, item), {});
}
function createChromeRecorder() {
const updates = [];
return {
updates,
chrome: {
tabs: {
update: async (tabId, update) => {
updates.push({ tabId, update });
},
},
},
};
}
test('kiro device auth module exposes a factory', () => {
const api = loadKiroDeviceAuthApi();
assert.equal(typeof api?.createKiroDeviceAuthExecutor, 'function');
@@ -30,19 +44,27 @@ test('kiro device auth module exposes a factory', () => {
assert.equal(typeof api?.uploadBuilderIdCredential, 'function');
});
test('kiro start device login registers client, opens auth tab, and completes with runtime payload', async () => {
test('kiro start device login opens the auth tab and waits for the email entry page', async () => {
const api = loadKiroDeviceAuthApi();
const fetchCalls = [];
const stateUpdates = [];
const registerCalls = [];
const reuseCalls = [];
const completeCalls = [];
const contentReadyCalls = [];
const contentMessages = [];
const stableWaitCalls = [];
const { chrome, updates: tabUpdates } = createChromeRecorder();
const executor = api.createKiroDeviceAuthExecutor({
addLog: async () => {},
chrome,
completeNodeFromBackground: async (nodeId, payload) => {
completeCalls.push({ nodeId, payload });
},
ensureContentScriptReadyOnTab: async (source, tabId, options = {}) => {
contentReadyCalls.push({ source, tabId, options });
},
fetchImpl: async (url, options = {}) => {
fetchCalls.push({
url,
@@ -75,20 +97,35 @@ test('kiro start device login registers client, opens auth tab, and completes wi
}
throw new Error(`Unexpected fetch URL: ${url}`);
},
getState: async () => ({
}),
getState: async () => ({}),
registerTab: async (source, tabId) => {
registerCalls.push({ source, tabId });
},
KIRO_DEVICE_AUTH_INJECT_FILES: [
'shared/source-registry.js',
'content/utils.js',
'content/kiro-device-auth-page.js',
],
reuseOrCreateTab: async (source, url) => {
reuseCalls.push({ source, url });
return 88;
},
sendToContentScriptResilient: async (source, message, options = {}) => {
contentMessages.push({ source, message, options });
return {
ok: true,
state: 'email_entry',
url: 'https://device.example.com/complete',
};
},
setState: async (updates) => {
stateUpdates.push(updates);
},
sleepWithStop: async () => {},
throwIfStopped: () => {},
waitForTabStableComplete: async (tabId, options = {}) => {
stableWaitCalls.push({ tabId, options });
},
});
await executor.executeKiroStartDeviceLogin({
@@ -111,6 +148,23 @@ test('kiro start device login registers client, opens auth tab, and completes wi
source: 'kiro-device-auth',
tabId: 88,
}]);
assert.deepEqual(tabUpdates, [{
tabId: 88,
update: { active: true },
}]);
assert.deepEqual(stableWaitCalls, [{
tabId: 88,
options: {
timeoutMs: 45000,
retryDelayMs: 300,
stableMs: 2500,
initialDelayMs: 300,
},
}]);
assert.equal(contentReadyCalls.length, 1);
assert.equal(contentMessages.length, 1);
assert.equal(contentMessages[0].message.type, 'ENSURE_KIRO_PAGE_STATE');
assert.deepEqual(contentMessages[0].message.payload.targetStates, ['email_entry']);
const finalState = mergeUpdates(stateUpdates);
assert.equal(finalState.kiroClientId, 'client-001');
@@ -122,6 +176,8 @@ test('kiro start device login registers client, opens auth tab, and completes wi
assert.equal(finalState.kiroAuthIntervalSeconds, 7);
assert.equal(finalState.kiroAuthStatus, 'waiting_user');
assert.equal(finalState.kiroUploadStatus, 'waiting_login');
assert.equal(finalState.kiroFullName, '');
assert.equal(finalState.kiroVerificationRequestedAt, 0);
assert.equal(completeCalls.length, 1);
assert.equal(completeCalls[0].nodeId, 'kiro-start-device-login');
@@ -129,19 +185,428 @@ test('kiro start device login registers client, opens auth tab, and completes wi
assert.equal(completeCalls[0].payload.kiroLoginUrl, 'https://device.example.com/complete');
});
test('kiro await device login polls until refresh token is captured', async () => {
test('kiro submit email resolves the signup email, reactivates the auth tab, and waits for the name page', async () => {
const api = loadKiroDeviceAuthApi();
const stateUpdates = [];
const completeCalls = [];
const resolvedEmails = [];
const contentMessages = [];
const stableWaitCalls = [];
const { chrome, updates: tabUpdates } = createChromeRecorder();
let ensureCallIndex = 0;
const executor = api.createKiroDeviceAuthExecutor({
addLog: async () => {},
chrome,
completeNodeFromBackground: async (nodeId, payload) => {
completeCalls.push({ nodeId, payload });
},
ensureContentScriptReadyOnTab: async () => {},
getState: async () => ({
kiroAuthTabId: 88,
kiroLoginUrl: 'https://device.example.com/complete',
email: '',
mailProvider: '163',
}),
isTabAlive: async (source) => source === 'kiro-device-auth',
KIRO_DEVICE_AUTH_INJECT_FILES: [
'shared/source-registry.js',
'content/utils.js',
'content/kiro-device-auth-page.js',
],
resolveSignupEmailForFlow: async (state, options = {}) => {
resolvedEmails.push({ state, options });
return 'user@example.com';
},
sendToContentScriptResilient: async (source, message, options = {}) => {
contentMessages.push({ source, message, options });
if (message.type === 'ENSURE_KIRO_PAGE_STATE') {
ensureCallIndex += 1;
return {
ok: true,
state: ensureCallIndex === 1 ? 'email_entry' : 'name_entry',
url: ensureCallIndex === 1
? 'https://device.example.com/complete'
: 'https://device.example.com/name',
};
}
if (message.type === 'EXECUTE_NODE') {
return {
ok: true,
submitted: true,
state: 'email_submitted',
url: 'https://device.example.com/complete',
};
}
throw new Error(`Unexpected content message: ${message.type}`);
},
setState: async (updates) => {
stateUpdates.push(updates);
},
sleepWithStop: async () => {},
throwIfStopped: () => {},
waitForTabStableComplete: async (tabId, options = {}) => {
stableWaitCalls.push({ tabId, options });
},
});
await executor.executeKiroSubmitEmail({
nodeId: 'kiro-submit-email',
kiroAuthTabId: 88,
kiroLoginUrl: 'https://device.example.com/complete',
email: '',
mailProvider: '163',
});
assert.equal(resolvedEmails.length, 1);
assert.equal(resolvedEmails[0].state.nodeId, 'kiro-submit-email');
assert.deepEqual(resolvedEmails[0].options, {
preserveAccountIdentity: true,
});
assert.deepEqual(tabUpdates, [
{ tabId: 88, update: { active: true } },
{ tabId: 88, update: { active: true } },
]);
assert.deepEqual(stableWaitCalls, [
{
tabId: 88,
options: {
timeoutMs: 45000,
retryDelayMs: 300,
stableMs: 2500,
initialDelayMs: 300,
},
},
{
tabId: 88,
options: {
timeoutMs: 45000,
retryDelayMs: 300,
stableMs: 1500,
initialDelayMs: 150,
},
},
]);
assert.equal(contentMessages.length, 3);
assert.deepEqual(contentMessages[0].message.payload.targetStates, ['email_entry']);
assert.equal(contentMessages[1].message.nodeId, 'kiro-submit-email');
assert.deepEqual(contentMessages[1].message.payload, { email: 'user@example.com' });
assert.deepEqual(contentMessages[2].message.payload.targetStates, ['name_entry']);
const finalState = mergeUpdates(stateUpdates);
assert.equal(finalState.kiroAuthorizedEmail, 'user@example.com');
assert.equal(finalState.kiroAuthError, '');
assert.equal(finalState.kiroAuthStatus, 'waiting_user');
assert.equal(finalState.kiroUploadError, '');
assert.equal(finalState.kiroUploadStatus, 'waiting_login');
assert.equal(finalState.kiroFullName, '');
assert.equal(finalState.kiroVerificationRequestedAt, 0);
assert.equal(completeCalls.length, 1);
assert.equal(completeCalls[0].nodeId, 'kiro-submit-email');
assert.equal(completeCalls[0].payload.email, 'user@example.com');
assert.equal(completeCalls[0].payload.accountIdentifierType, 'email');
assert.equal(completeCalls[0].payload.accountIdentifier, 'user@example.com');
assert.equal(completeCalls[0].payload.kiroNextState, 'name_entry');
assert.equal(completeCalls[0].payload.kiroNextUrl, 'https://device.example.com/name');
});
test('kiro submit name generates a full name and waits for the otp page', async () => {
const api = loadKiroDeviceAuthApi();
const stateUpdates = [];
const completeCalls = [];
const contentMessages = [];
const { chrome, updates: tabUpdates } = createChromeRecorder();
let ensureCallIndex = 0;
const executor = api.createKiroDeviceAuthExecutor({
addLog: async () => {},
chrome,
completeNodeFromBackground: async (nodeId, payload) => {
completeCalls.push({ nodeId, payload });
},
ensureContentScriptReadyOnTab: async () => {},
generateRandomName: () => ({ firstName: 'Ada', lastName: 'Lovelace' }),
getState: async () => ({
kiroAuthTabId: 88,
kiroAuthorizedEmail: 'user@example.com',
kiroLoginUrl: 'https://device.example.com/complete',
}),
isTabAlive: async (source) => source === 'kiro-device-auth',
sendToContentScriptResilient: async (_source, message) => {
contentMessages.push(message);
if (message.type === 'ENSURE_KIRO_PAGE_STATE') {
ensureCallIndex += 1;
return {
ok: true,
state: ensureCallIndex === 1 ? 'name_entry' : 'otp_page',
url: ensureCallIndex === 1
? 'https://device.example.com/name'
: 'https://device.example.com/verify',
};
}
if (message.type === 'EXECUTE_NODE') {
return {
ok: true,
submitted: true,
state: 'name_submitted',
url: 'https://device.example.com/name',
};
}
throw new Error(`Unexpected content message: ${message.type}`);
},
setState: async (updates) => {
stateUpdates.push(updates);
},
sleepWithStop: async () => {},
throwIfStopped: () => {},
waitForTabStableComplete: async () => {},
});
await executor.executeKiroSubmitName({
nodeId: 'kiro-submit-name',
kiroAuthTabId: 88,
kiroAuthorizedEmail: 'user@example.com',
kiroLoginUrl: 'https://device.example.com/complete',
});
assert.deepEqual(tabUpdates, [{
tabId: 88,
update: { active: true },
}]);
assert.equal(contentMessages.length, 3);
assert.deepEqual(contentMessages[0].payload.targetStates, ['name_entry']);
assert.equal(contentMessages[1].nodeId, 'kiro-submit-name');
assert.deepEqual(contentMessages[1].payload, { fullName: 'Ada Lovelace' });
assert.deepEqual(contentMessages[2].payload.targetStates, ['otp_page']);
const finalState = mergeUpdates(stateUpdates);
assert.equal(finalState.kiroFullName, 'Ada Lovelace');
assert.equal(finalState.kiroAuthError, '');
assert.equal(finalState.kiroUploadError, '');
assert.equal(typeof finalState.kiroVerificationRequestedAt, 'number');
assert.equal(finalState.kiroVerificationRequestedAt > 0, true);
assert.equal(completeCalls.length, 1);
assert.equal(completeCalls[0].nodeId, 'kiro-submit-name');
assert.equal(completeCalls[0].payload.kiroFullName, 'Ada Lovelace');
assert.equal(completeCalls[0].payload.kiroNextState, 'otp_page');
});
test('kiro submit verification code polls mail, returns to the auth tab, and waits for the password page', async () => {
const api = loadKiroDeviceAuthApi();
const stateUpdates = [];
const completeCalls = [];
const mailPollCalls = [];
const contentMessages = [];
const mailOpenCalls = [];
const { chrome, updates: tabUpdates } = createChromeRecorder();
let ensureCallIndex = 0;
const executor = api.createKiroDeviceAuthExecutor({
addLog: async () => {},
chrome,
completeNodeFromBackground: async (nodeId, payload) => {
completeCalls.push({ nodeId, payload });
},
ensureContentScriptReadyOnTab: async () => {},
getMailConfig: () => ({
source: 'mail-163',
url: 'https://mail.example.com/inbox',
label: '163 邮箱',
}),
getState: async () => ({
kiroAuthTabId: 88,
kiroAuthorizedEmail: 'user@example.com',
kiroLoginUrl: 'https://device.example.com/complete',
kiroVerificationRequestedAt: 1700000000000,
mailProvider: '163',
}),
isTabAlive: async (source) => source === 'kiro-device-auth',
reuseOrCreateTab: async (source, url) => {
mailOpenCalls.push({ source, url });
return 66;
},
sendToContentScriptResilient: async (_source, message) => {
contentMessages.push(message);
if (message.type === 'ENSURE_KIRO_PAGE_STATE') {
ensureCallIndex += 1;
return {
ok: true,
state: ensureCallIndex === 1 ? 'otp_page' : 'password_page',
url: ensureCallIndex === 1
? 'https://device.example.com/verify'
: 'https://device.example.com/password',
};
}
if (message.type === 'EXECUTE_NODE') {
return {
ok: true,
submitted: true,
state: 'verification_submitted',
url: 'https://device.example.com/verify',
};
}
throw new Error(`Unexpected content message: ${message.type}`);
},
sendToMailContentScriptResilient: async (mail, message, options = {}) => {
mailPollCalls.push({ mail, message, options });
return {
ok: true,
code: '654321',
emailTimestamp: 1700000005000,
mailId: 'mail-1',
};
},
setState: async (updates) => {
stateUpdates.push(updates);
},
sleepWithStop: async () => {},
throwIfStopped: () => {},
waitForTabStableComplete: async () => {},
});
await executor.executeKiroSubmitVerificationCode({
nodeId: 'kiro-submit-verification-code',
kiroAuthTabId: 88,
kiroAuthorizedEmail: 'user@example.com',
kiroLoginUrl: 'https://device.example.com/complete',
kiroVerificationRequestedAt: 1700000000000,
mailProvider: '163',
});
assert.deepEqual(mailOpenCalls, [{
source: 'mail-163',
url: 'https://mail.example.com/inbox',
}]);
assert.equal(mailPollCalls.length, 1);
assert.equal(mailPollCalls[0].message.type, 'POLL_EMAIL');
assert.equal(mailPollCalls[0].message.payload.targetEmail, 'user@example.com');
assert.equal(mailPollCalls[0].message.payload.filterAfterTimestamp, 1700000000000);
assert.deepEqual(tabUpdates, [
{ tabId: 88, update: { active: true } },
{ tabId: 88, update: { active: true } },
]);
assert.equal(contentMessages.length, 3);
assert.deepEqual(contentMessages[0].payload.targetStates, ['otp_page']);
assert.equal(contentMessages[1].nodeId, 'kiro-submit-verification-code');
assert.deepEqual(contentMessages[1].payload, { code: '654321' });
assert.deepEqual(contentMessages[2].payload.targetStates, ['password_page']);
const finalState = mergeUpdates(stateUpdates);
assert.equal(finalState.kiroAuthError, '');
assert.equal(finalState.kiroUploadError, '');
assert.equal(completeCalls.length, 1);
assert.equal(completeCalls[0].nodeId, 'kiro-submit-verification-code');
assert.equal(completeCalls[0].payload.code, '654321');
assert.equal(completeCalls[0].payload.mailId, 'mail-1');
assert.equal(completeCalls[0].payload.kiroNextState, 'password_page');
});
test('kiro fill password reuses the shared password state and waits for the page to leave password state', async () => {
const api = loadKiroDeviceAuthApi();
const stateUpdates = [];
const completeCalls = [];
const contentMessages = [];
const savedPasswords = [];
const { chrome, updates: tabUpdates } = createChromeRecorder();
const executor = api.createKiroDeviceAuthExecutor({
addLog: async () => {},
chrome,
completeNodeFromBackground: async (nodeId, payload) => {
completeCalls.push({ nodeId, payload });
},
ensureContentScriptReadyOnTab: async () => {},
getState: async () => ({
kiroAuthTabId: 88,
kiroLoginUrl: 'https://device.example.com/complete',
customPassword: 'SharedPass123!',
}),
isTabAlive: async (source) => source === 'kiro-device-auth',
sendToContentScriptResilient: async (_source, message) => {
contentMessages.push(message);
if (message.type === 'ENSURE_KIRO_PAGE_STATE') {
return {
ok: true,
state: 'password_page',
url: 'https://device.example.com/password',
};
}
if (message.type === 'ENSURE_KIRO_STATE_CHANGE') {
return {
ok: true,
state: 'authorization_page',
url: 'https://device.example.com/authorize',
};
}
if (message.type === 'EXECUTE_NODE') {
return {
ok: true,
submitted: true,
state: 'password_submitted',
url: 'https://device.example.com/password',
};
}
throw new Error(`Unexpected content message: ${message.type}`);
},
setPasswordState: async (password) => {
savedPasswords.push(password);
},
setState: async (updates) => {
stateUpdates.push(updates);
},
sleepWithStop: async () => {},
throwIfStopped: () => {},
waitForTabStableComplete: async () => {},
});
await executor.executeKiroFillPassword({
nodeId: 'kiro-fill-password',
kiroAuthTabId: 88,
kiroLoginUrl: 'https://device.example.com/complete',
customPassword: 'SharedPass123!',
});
assert.deepEqual(savedPasswords, ['SharedPass123!']);
assert.deepEqual(tabUpdates, [{
tabId: 88,
update: { active: true },
}]);
assert.equal(contentMessages.length, 3);
assert.deepEqual(contentMessages[0].payload.targetStates, ['password_page']);
assert.equal(contentMessages[1].nodeId, 'kiro-fill-password');
assert.deepEqual(contentMessages[1].payload, { password: 'SharedPass123!' });
assert.deepEqual(contentMessages[2].payload.fromStates, ['password_page']);
const finalState = mergeUpdates(stateUpdates);
assert.equal(finalState.kiroAuthError, '');
assert.equal(finalState.kiroUploadError, '');
assert.equal(completeCalls.length, 1);
assert.equal(completeCalls[0].nodeId, 'kiro-fill-password');
assert.equal(completeCalls[0].payload.kiroNextState, 'authorization_page');
assert.equal(completeCalls[0].payload.kiroNextUrl, 'https://device.example.com/authorize');
});
test('kiro confirm access completes the authorization page and then polls until refresh token is captured', async () => {
const api = loadKiroDeviceAuthApi();
const fetchCalls = [];
const stateUpdates = [];
const sleepCalls = [];
const completeCalls = [];
const contentMessages = [];
const { chrome, updates: tabUpdates } = createChromeRecorder();
let pollCount = 0;
const executor = api.createKiroDeviceAuthExecutor({
addLog: async () => {},
chrome,
completeNodeFromBackground: async (nodeId, payload) => {
completeCalls.push({ nodeId, payload });
},
ensureContentScriptReadyOnTab: async () => {},
fetchImpl: async (url, options = {}) => {
fetchCalls.push({
url,
@@ -167,13 +632,35 @@ test('kiro await device login polls until refresh token is captured', async () =
});
},
getState: async () => ({
kiroAuthTabId: 88,
kiroClientId: 'client-001',
kiroClientSecret: 'secret-001',
kiroDeviceAuthorizationCode: 'device-code-001',
kiroAuthRegion: 'us-east-1',
kiroAuthExpiresAt: Date.now() + 60000,
kiroAuthIntervalSeconds: 5,
kiroLoginUrl: 'https://device.example.com/complete',
}),
isTabAlive: async (source) => source === 'kiro-device-auth',
sendToContentScriptResilient: async (_source, message) => {
contentMessages.push(message);
if (message.type === 'ENSURE_KIRO_PAGE_STATE') {
return {
ok: true,
state: 'authorization_page',
url: 'https://device.example.com/authorize',
};
}
if (message.type === 'EXECUTE_NODE') {
return {
ok: true,
submitted: true,
state: 'success_page',
url: 'https://device.example.com/success',
};
}
throw new Error(`Unexpected content message: ${message.type}`);
},
setState: async (updates) => {
stateUpdates.push(updates);
},
@@ -181,18 +668,28 @@ test('kiro await device login polls until refresh token is captured', async () =
sleepCalls.push(ms);
},
throwIfStopped: () => {},
waitForTabStableComplete: async () => {},
});
await executor.executeKiroAwaitDeviceLogin({
nodeId: 'kiro-await-device-login',
await executor.executeKiroConfirmAccess({
nodeId: 'kiro-confirm-access',
kiroAuthTabId: 88,
kiroClientId: 'client-001',
kiroClientSecret: 'secret-001',
kiroDeviceAuthorizationCode: 'device-code-001',
kiroAuthRegion: 'us-east-1',
kiroAuthExpiresAt: Date.now() + 60000,
kiroAuthIntervalSeconds: 5,
kiroLoginUrl: 'https://device.example.com/complete',
});
assert.deepEqual(tabUpdates, [{
tabId: 88,
update: { active: true },
}]);
assert.equal(contentMessages.length, 2);
assert.deepEqual(contentMessages[0].payload.targetStates, ['authorization_page', 'success_page']);
assert.equal(contentMessages[1].nodeId, 'kiro-confirm-access');
assert.equal(fetchCalls.length, 2);
assert.equal(fetchCalls[0].url, 'https://oidc.us-east-1.amazonaws.com/token');
assert.deepEqual(fetchCalls[0].body, {
@@ -210,8 +707,10 @@ test('kiro await device login polls until refresh token is captured', async () =
assert.equal(finalState.kiroUploadStatus, 'ready_to_upload');
assert.equal(completeCalls.length, 1);
assert.equal(completeCalls[0].nodeId, 'kiro-await-device-login');
assert.equal(completeCalls[0].nodeId, 'kiro-confirm-access');
assert.equal(completeCalls[0].payload.kiroRefreshToken, 'refresh-001');
assert.equal(completeCalls[0].payload.kiroNextState, 'success_page');
assert.equal(completeCalls[0].payload.kiroNextUrl, 'https://device.example.com/success');
});
test('kiro upload credential checks connection and uploads builder id credential to kiro.rs', async () => {