Files
OmniRoute/tests/unit/kiro-multi-account-isolation.test.ts
Armin Anton” ∴ 8f390efffd feat(codex): self-contained codex app-server transport (executor + provider + sign-in) (#11205)
Merged after conflict resolution: the 5 conflicting test files were the base-red drains that #11201 already landed on the tip — kept the tip versions; the feature content is untouched. Validated on the combined batch board + this branch: codex-app-server + codex-gpt56-catalog 25/25, typecheck:core clean, docs-counts green (351 providers), provider-consistency 268/351/0. The opt-in codex-app-server transport (JSON-RPC-over-WS, turn/completed-awaited close, Responses SSE bridge) leaves the default codex path untouched. Thank you @arminanton — a 3.4k-line transport with the docs wave and tests to match!
2026-08-23 10:20:06 -03:00

194 lines
6.9 KiB
TypeScript

/**
* Tests for Kiro multi-account isolation (issue #2328).
*
* Each OmniRoute connection must own its own OIDC client registration
* (clientId + clientSecret) so that refreshing or re-authenticating one
* account does not invalidate another account's refresh token.
*/
import test from "node:test";
import assert from "node:assert/strict";
// KiroService.validateImportToken() reads cached OIDC client credentials from
// the real AWS SSO cache at `~/.aws/sso/cache/` (via os.homedir()) before it
// ever hits the mocked `/client/register` fetch. CI runs in a clean home with
// no such cache, so the mocked registration path is exercised. But this suite
// can run on a host/sandbox that DOES have `~/.aws/sso/cache/*.json` (e.g. a
// developer or agent machine with a live AWS SSO session), in which case the
// service adopts a real cached clientId and the assertions below (which expect
// the mocked "test-client-id") fail. Point HOME/USERPROFILE at an isolated,
// empty temp dir so os.homedir() resolves to a cache-free home and the test is
// hermetic regardless of the ambient machine.
import { mkdtempSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
const ISOLATED_HOME = mkdtempSync(join(tmpdir(), "omniroute-kiro-home-"));
process.env.HOME = ISOLATED_HOME;
process.env.USERPROFILE = ISOLATED_HOME;
import { KiroService } from "../../src/lib/oauth/services/kiro.ts";
// ── helpers ───────────────────────────────────────────────────────────────────
function withMockedFetch(impl: typeof fetch, fn: () => Promise<void>) {
const original = globalThis.fetch;
globalThis.fetch = impl;
return fn().finally(() => {
globalThis.fetch = original;
});
}
function jsonResponse(body: unknown, status = 200) {
return new Response(JSON.stringify(body), {
status,
headers: { "content-type": "application/json" },
});
}
/**
* Build a fetch mock that handles:
* - /token → returns a minimal token refresh response
* - /client/register → returns the given registration pair
*/
function buildFetchMock(registration: {
clientId: string;
clientSecret: string;
clientSecretExpiresAt?: number;
}) {
return (async (input: RequestInfo | URL) => {
const url = String(input);
if (url.endsWith("/client/register")) {
return jsonResponse(registration);
}
// Treat any other URL as a social-auth/refresh endpoint
return jsonResponse({
accessToken: "at-mock",
refreshToken: "rt-next-mock",
expiresIn: 3600,
});
}) as typeof fetch;
}
// A valid-looking Kiro refresh token (must start with "aorAAAAAG")
const VALID_REFRESH_TOKEN = "aorAAAAAG-mock-refresh-token-for-tests";
// ── tests ─────────────────────────────────────────────────────────────────────
test("validateImportToken registers a client and returns clientId + clientSecret", async () => {
const service = new KiroService();
const reg = {
clientId: "test-client-id",
clientSecret: "test-client-secret",
clientSecretExpiresAt: 9999999999,
};
await withMockedFetch(buildFetchMock(reg), async () => {
const result = await service.validateImportToken(VALID_REFRESH_TOKEN);
assert.equal(result.clientId, reg.clientId, "clientId should be returned");
assert.equal(result.clientSecret, reg.clientSecret, "clientSecret should be returned");
assert.equal(
result.clientSecretExpiresAt,
reg.clientSecretExpiresAt,
"clientSecretExpiresAt should be returned"
);
assert.equal(result.authMethod, "imported");
assert.equal(result.accessToken, "at-mock");
});
});
test("validateImportToken succeeds without clientId when registerClient fails", async () => {
const service = new KiroService();
let callCount = 0;
await withMockedFetch(
async (input) => {
const url = String(input);
callCount++;
if (url.endsWith("/client/register")) {
return new Response("Service Unavailable", { status: 503 });
}
return jsonResponse({
accessToken: "at-degraded",
refreshToken: "rt-degraded",
expiresIn: 3600,
});
},
async () => {
// Should not throw even though registerClient fails
const result = await service.validateImportToken(VALID_REFRESH_TOKEN);
assert.equal(
result.accessToken,
"at-degraded",
"import should succeed with a degraded token"
);
assert.equal(result.authMethod, "imported");
// clientId must not be set — the connection degrades to shared social-auth path
assert.equal(result.clientId, undefined, "clientId should be absent on degraded import");
assert.equal(
result.clientSecret,
undefined,
"clientSecret should be absent on degraded import"
);
}
);
assert.ok(callCount >= 1, "fetch should have been called at least once");
});
test("validateImportToken throws when token format is invalid", async () => {
const service = new KiroService();
await assert.rejects(
() => service.validateImportToken("invalid-token-does-not-start-correctly"),
/Invalid token format/
);
});
test("two validateImportToken calls return different clientIds when registerClient returns distinct pairs", async () => {
const service = new KiroService();
let registrationIndex = 0;
const registrations = [
{ clientId: "client-alpha", clientSecret: "secret-alpha" },
{ clientId: "client-beta", clientSecret: "secret-beta" },
];
const mockFetch: typeof fetch = async (input) => {
const url = String(input);
if (url.endsWith("/client/register")) {
return jsonResponse(registrations[registrationIndex++] ?? registrations[0]);
}
return jsonResponse({ accessToken: "at", refreshToken: "rt", expiresIn: 3600 });
};
await withMockedFetch(mockFetch, async () => {
const result1 = await service.validateImportToken(VALID_REFRESH_TOKEN);
const result2 = await service.validateImportToken(VALID_REFRESH_TOKEN);
assert.notEqual(
result1.clientId,
result2.clientId,
"each import call should receive a distinct clientId for session isolation"
);
assert.equal(result1.clientId, "client-alpha");
assert.equal(result2.clientId, "client-beta");
});
});
test("registerClient uses the provided region in the OIDC endpoint URL", async () => {
const service = new KiroService();
const calls: string[] = [];
await withMockedFetch(
async (input) => {
calls.push(String(input));
return jsonResponse({ clientId: "cid", clientSecret: "csec" });
},
async () => {
await service.registerClient("ap-southeast-1");
}
);
assert.ok(
calls.some((url) => url.includes("ap-southeast-1")),
"registerClient should call the OIDC endpoint for the specified region"
);
});