diff --git a/src/agents/auth-profiles/external-cli-sync.ts b/src/agents/auth-profiles/external-cli-sync.ts index 5353482c6fb8..b4df4373a845 100644 --- a/src/agents/auth-profiles/external-cli-sync.ts +++ b/src/agents/auth-profiles/external-cli-sync.ts @@ -16,6 +16,7 @@ import { OPENAI_CODEX_DEFAULT_PROFILE_ID, } from "./constants.js"; import { log } from "./constants.js"; +import { isSafeToCopyOAuthIdentity } from "./oauth-identity.js"; import { areOAuthCredentialsEquivalent, hasUsableOAuthCredential, @@ -61,15 +62,6 @@ type ExternalCliSyncProvider = { bootstrapOnly?: boolean; }; -function normalizeAuthIdentityToken(value: string | undefined): string | undefined { - const trimmed = value?.trim(); - return trimmed ? trimmed : undefined; -} - -function normalizeAuthEmailToken(value: string | undefined): string | undefined { - return normalizeAuthIdentityToken(value)?.toLowerCase(); -} - // Keep this gate aligned with the canonical identity-copy rule in oauth.ts. /** Return true when imported CLI credentials match an existing profile identity. */ export function isSafeToUseExternalCliCredential( @@ -82,24 +74,7 @@ export function isSafeToUseExternalCliCredential( if (existing.provider !== imported.provider) { return false; } - - const existingAccountId = normalizeAuthIdentityToken(existing.accountId); - const importedAccountId = normalizeAuthIdentityToken(imported.accountId); - const existingEmail = normalizeAuthEmailToken(existing.email); - const importedEmail = normalizeAuthEmailToken(imported.email); - - if (existingAccountId !== undefined && importedAccountId !== undefined) { - return existingAccountId === importedAccountId; - } - if (existingEmail !== undefined && importedEmail !== undefined) { - return existingEmail === importedEmail; - } - - const existingHasIdentity = existingAccountId !== undefined || existingEmail !== undefined; - if (existingHasIdentity) { - return false; - } - return true; + return isSafeToCopyOAuthIdentity(existing, imported); } const EXTERNAL_CLI_SYNC_PROVIDERS: ExternalCliSyncProvider[] = [ diff --git a/src/agents/auth-profiles/oauth-shared.ts b/src/agents/auth-profiles/oauth-shared.ts index 06378f2cc242..d556300f5bd4 100644 --- a/src/agents/auth-profiles/oauth-shared.ts +++ b/src/agents/auth-profiles/oauth-shared.ts @@ -6,8 +6,15 @@ import { asDateTimestampMs } from "../../shared/number-coercion.js"; import { cloneAuthProfileStore } from "./clone.js"; import { hasUsableOAuthCredential as hasUsableStoredOAuthCredential } from "./credential-state.js"; +import { + isSafeToCopyOAuthIdentity, + normalizeAuthEmailToken, + normalizeAuthIdentityToken, +} from "./oauth-identity.js"; import type { AuthProfileStore, OAuthCredential } from "./types.js"; +export { normalizeAuthEmailToken, normalizeAuthIdentityToken } from "./oauth-identity.js"; + /** OAuth profile imported from a runtime external CLI source. */ export type RuntimeExternalOAuthProfile = { profileId: string; @@ -74,17 +81,6 @@ export function hasUsableOAuthCredential( return hasUsableStoredOAuthCredential(credential, { now }); } -/** Normalizes account identity tokens for equality checks. */ -export function normalizeAuthIdentityToken(value: string | undefined): string | undefined { - const trimmed = value?.trim(); - return trimmed ? trimmed : undefined; -} - -/** Normalizes auth email identity tokens for equality checks. */ -export function normalizeAuthEmailToken(value: string | undefined): string | undefined { - return normalizeAuthIdentityToken(value)?.toLowerCase(); -} - /** Returns true when an OAuth credential has account or email identity. */ export function hasOAuthIdentity( credential: Pick, @@ -100,19 +96,7 @@ export function hasMatchingOAuthIdentity( existing: Pick, incoming: Pick, ): boolean { - const existingAccountId = normalizeAuthIdentityToken(existing.accountId); - const incomingAccountId = normalizeAuthIdentityToken(incoming.accountId); - if (existingAccountId !== undefined && incomingAccountId !== undefined) { - return existingAccountId === incomingAccountId; - } - - const existingEmail = normalizeAuthEmailToken(existing.email); - const incomingEmail = normalizeAuthEmailToken(incoming.email); - if (existingEmail !== undefined && incomingEmail !== undefined) { - return existingEmail === incomingEmail; - } - - return false; + return hasOAuthIdentity(existing) && isSafeToCopyOAuthIdentity(existing, incoming); } // Different adoption paths have different safety thresholds. Bootstrap can