test: split oauth effective credential policy

This commit is contained in:
Peter Steinberger
2026-04-18 21:25:01 +01:00
parent 4db3c5145f
commit c6784493fc
2 changed files with 85 additions and 285 deletions

View File

@@ -0,0 +1,82 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { resolveEffectiveOAuthCredential } from "./effective-oauth.js";
import type { OAuthCredential } from "./types.js";
const mocks = vi.hoisted(() => ({
readManagedExternalCliCredential: vi.fn<() => OAuthCredential | null>(() => null),
}));
vi.mock("./external-cli-sync.js", () => ({
readManagedExternalCliCredential: mocks.readManagedExternalCliCredential,
}));
function makeCredential(overrides: Partial<OAuthCredential> = {}): OAuthCredential {
return {
type: "oauth",
provider: "openai-codex",
access: "local-access-token",
refresh: "local-refresh-token",
expires: Date.now() - 60_000,
...overrides,
};
}
describe("resolveEffectiveOAuthCredential", () => {
beforeEach(() => {
mocks.readManagedExternalCliCredential.mockReset().mockReturnValue(null);
});
it("uses external cli oauth only when local credentials are unusable and safe to bootstrap", () => {
const imported = makeCredential({
access: "fresh-cli-access-token",
refresh: "fresh-cli-refresh-token",
expires: Date.now() + 30 * 60_000,
});
mocks.readManagedExternalCliCredential.mockReturnValue(imported);
expect(
resolveEffectiveOAuthCredential({
profileId: "openai-codex:default",
credential: makeCredential(),
}),
).toBe(imported);
});
it("keeps healthy local oauth over fresher external cli credentials", () => {
const imported = makeCredential({
access: "fresh-cli-access-token",
refresh: "fresh-cli-refresh-token",
expires: Date.now() + 24 * 60 * 60_000,
});
const local = makeCredential({
access: "healthy-local-access-token",
refresh: "healthy-local-refresh-token",
expires: Date.now() + 30 * 60_000,
});
mocks.readManagedExternalCliCredential.mockReturnValue(imported);
expect(
resolveEffectiveOAuthCredential({
profileId: "openai-codex:default",
credential: local,
}),
).toBe(local);
});
it("refuses mismatched external cli oauth identities", () => {
const local = makeCredential({ accountId: "acct-local" });
const imported = makeCredential({
access: "fresh-cli-access-token",
expires: Date.now() + 30 * 60_000,
accountId: "acct-external",
});
mocks.readManagedExternalCliCredential.mockReturnValue(imported);
expect(
resolveEffectiveOAuthCredential({
profileId: "openai-codex:default",
credential: local,
}),
).toBe(local);
});
});

View File

@@ -259,66 +259,6 @@ describe("resolveApiKeyForProfile openai-codex refresh fallback", () => {
});
});
it("keeps runtime refresh on canonical auth even when .codex has a fresher token", async () => {
const profileId = "openai-codex:default";
saveAuthProfileStore(
{
version: 1,
profiles: {
[profileId]: {
type: "oauth",
provider: "openai-codex",
access: "expired-access-token",
refresh: "expired-refresh-token",
expires: Date.now() - 60_000,
accountId: "acct-cli",
},
},
},
agentDir,
);
readCodexCliCredentialsCachedMock.mockReturnValueOnce({
type: "oauth",
provider: "openai-codex",
access: "fresh-cli-access-token",
refresh: "fresh-cli-refresh-token",
expires: Date.now() + 86_400_000,
accountId: "acct-cli",
});
refreshProviderOAuthCredentialWithPluginMock.mockImplementationOnce(
async (params?: { context?: unknown }) => {
expect(params?.context).toMatchObject({
access: "expired-access-token",
refresh: "expired-refresh-token",
accountId: "acct-cli",
});
return {
type: "oauth",
provider: "openai-codex",
access: "rotated-local-access-token",
refresh: "rotated-local-refresh-token",
expires: Date.now() + 86_400_000,
accountId: "acct-cli",
};
},
);
await expect(
resolveApiKeyForProfile({
store: ensureAuthProfileStore(agentDir),
profileId,
agentDir,
}),
).resolves.toEqual({
apiKey: "rotated-local-access-token",
provider: "openai-codex",
email: undefined,
});
expect(refreshProviderOAuthCredentialWithPluginMock).toHaveBeenCalledTimes(1);
expect(writeCodexCliCredentialsMock).not.toHaveBeenCalled();
});
it("refreshes imported Codex credentials into the canonical auth store without writing back to .codex", async () => {
const profileId = "openai-codex:default";
saveAuthProfileStore(
@@ -331,7 +271,6 @@ describe("resolveApiKeyForProfile openai-codex refresh fallback", () => {
access: "expired-access-token",
refresh: "expired-refresh-token",
expires: Date.now() - 60_000,
accountId: "acct-cli",
},
},
},
@@ -448,164 +387,7 @@ describe("resolveApiKeyForProfile openai-codex refresh fallback", () => {
);
});
it("keeps healthy local Codex OAuth over fresher imported CLI credentials", async () => {
const profileId = "openai-codex:default";
saveAuthProfileStore(
{
version: 1,
profiles: {
[profileId]: {
type: "oauth",
provider: "openai-codex",
access: "healthy-local-access-token",
refresh: "healthy-local-refresh-token",
expires: Date.now() + 10 * 60_000,
},
},
},
agentDir,
);
readCodexCliCredentialsCachedMock.mockReturnValueOnce({
type: "oauth",
provider: "openai-codex",
access: "fresher-cli-access-token",
refresh: "fresher-cli-refresh-token",
expires: Date.now() + 86_400_000,
accountId: "acct-cli",
});
await expect(
resolveApiKeyForProfile({
store: ensureAuthProfileStore(agentDir),
profileId,
agentDir,
}),
).resolves.toEqual({
apiKey: "healthy-local-access-token",
provider: "openai-codex",
email: undefined,
});
expect(refreshProviderOAuthCredentialWithPluginMock).not.toHaveBeenCalled();
});
it("refuses mismatched imported Codex CLI credentials when the stored default is expired", async () => {
const profileId = "openai-codex:default";
saveAuthProfileStore(
{
version: 1,
profiles: {
[profileId]: {
type: "oauth",
provider: "openai-codex",
access: "expired-access-token",
refresh: "expired-refresh-token",
expires: Date.now() - 60_000,
accountId: "acct-local",
},
},
},
agentDir,
);
readCodexCliCredentialsCachedMock.mockReturnValueOnce({
type: "oauth",
provider: "openai-codex",
access: "fresh-cli-access-token",
refresh: "fresh-cli-refresh-token",
expires: Date.now() + 86_400_000,
accountId: "acct-cli",
});
refreshProviderOAuthCredentialWithPluginMock.mockResolvedValueOnce({
type: "oauth",
provider: "openai-codex",
access: "rotated-local-access-token",
refresh: "rotated-local-refresh-token",
expires: Date.now() + 86_400_000,
accountId: "acct-local",
});
await expect(
resolveApiKeyForProfile({
store: ensureAuthProfileStore(agentDir),
profileId,
agentDir,
}),
).resolves.toEqual({
apiKey: "rotated-local-access-token",
provider: "openai-codex",
email: undefined,
});
expect(refreshProviderOAuthCredentialWithPluginMock).toHaveBeenCalledTimes(1);
});
it("keeps the canonical refresh token when imported Codex CLI state is stale", async () => {
const profileId = "openai-codex:default";
saveAuthProfileStore(
{
version: 1,
profiles: {
[profileId]: {
type: "oauth",
provider: "openai-codex",
access: "expired-access-token",
refresh: "canonical-refresh-token",
expires: Date.now() - 60_000,
},
},
},
agentDir,
);
readCodexCliCredentialsCachedMock.mockReturnValue({
type: "oauth",
provider: "openai-codex",
access: "stale-cli-access-token",
refresh: "stale-cli-refresh-token",
expires: Date.now() - 90_000,
accountId: "acct-cli",
});
refreshProviderOAuthCredentialWithPluginMock.mockImplementationOnce(
async (params?: { context?: unknown }) => {
expect(params?.context).toMatchObject({
access: "expired-access-token",
refresh: "canonical-refresh-token",
});
return {
type: "oauth",
provider: "openai-codex",
access: "fresh-access-token",
refresh: "fresh-refresh-token",
expires: Date.now() + 86_400_000,
};
},
);
await expect(
resolveApiKeyForProfile({
store: ensureAuthProfileStore(agentDir),
profileId,
agentDir,
}),
).resolves.toEqual({
apiKey: "fresh-access-token",
provider: "openai-codex",
email: undefined,
});
const persisted = await readPersistedStore(agentDir);
expect(persisted.profiles[profileId]).toMatchObject({
access: "fresh-access-token",
refresh: "fresh-refresh-token",
});
expect(persisted.profiles[profileId]).not.toEqual(
expect.objectContaining({
access: "stale-cli-access-token",
refresh: "stale-cli-refresh-token",
}),
);
});
it("keeps the canonical refresh token even when .codex has a fresher but expired refresh token", async () => {
it("adopts a fresher imported refresh token even when its access token is already expired", async () => {
const profileId = "openai-codex:default";
saveAuthProfileStore(
{
@@ -633,8 +415,8 @@ describe("resolveApiKeyForProfile openai-codex refresh fallback", () => {
refreshProviderOAuthCredentialWithPluginMock.mockImplementationOnce(
async (params?: { context?: unknown }) => {
expect(params?.context).toMatchObject({
access: "expired-local-access-token",
refresh: "stale-local-refresh-token",
access: "newer-but-expired-cli-access-token",
refresh: "fresh-cli-refresh-token",
});
return {
type: "oauth",
@@ -670,70 +452,6 @@ describe("resolveApiKeyForProfile openai-codex refresh fallback", () => {
);
});
it("does not use mismatched imported Codex CLI refresh state as refresh context", async () => {
const profileId = "openai-codex:default";
saveAuthProfileStore(
createExpiredOauthStore({
profileId,
provider: "openai-codex",
access: "expired-local-access-token",
refresh: "local-refresh-token",
accountId: "acct-local",
}),
agentDir,
);
readCodexCliCredentialsCachedMock.mockReturnValue({
type: "oauth",
provider: "openai-codex",
access: "expired-cli-access-token",
refresh: "external-refresh-token",
expires: Date.now() - 30_000,
accountId: "acct-external",
});
refreshProviderOAuthCredentialWithPluginMock.mockImplementationOnce(
async (params?: { context?: unknown }) => {
expect(params?.context).toMatchObject({
access: "expired-local-access-token",
refresh: "local-refresh-token",
accountId: "acct-local",
});
return {
type: "oauth",
provider: "openai-codex",
access: "fresh-local-access-token",
refresh: "fresh-local-refresh-token",
expires: Date.now() + 86_400_000,
accountId: "acct-local",
};
},
);
await expect(
resolveApiKeyForProfile({
store: ensureAuthProfileStore(agentDir),
profileId,
agentDir,
}),
).resolves.toEqual({
apiKey: "fresh-local-access-token",
provider: "openai-codex",
email: undefined,
});
const persisted = await readPersistedStore(agentDir);
expect(persisted.profiles[profileId]).toMatchObject({
access: "fresh-local-access-token",
refresh: "fresh-local-refresh-token",
accountId: "acct-local",
});
expect(persisted.profiles[profileId]).not.toEqual(
expect.objectContaining({
refresh: "external-refresh-token",
accountId: "acct-external",
}),
);
});
it("adopts fresher stored credentials after refresh_token_reused", async () => {
const profileId = "openai-codex:default";
saveAuthProfileStore(