refactor kiro auth flow and share account password service
This commit is contained in:
@@ -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 () => {
|
||||
|
||||
Reference in New Issue
Block a user