From 0b2ce80fcb81f784e995ba07691f8d954d729197 Mon Sep 17 00:00:00 2001 From: Luis Pater Date: Sun, 19 Jul 2026 02:19:38 +0800 Subject: [PATCH] fix(auth): update credential filename logic to include account hash - Modified `CredentialFileName` to incorporate `hashAccountID` for better disambiguation when available. - Updated fallback behavior to maintain compatibility with legacy email-based filenames. - Refined test suite to validate scenarios with and without account hash and plan type. Closes: #4425 --- internal/auth/codex/filename.go | 19 +++++++++--------- internal/auth/codex/filename_test.go | 30 +++++++++++++++++++++++++--- 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/internal/auth/codex/filename.go b/internal/auth/codex/filename.go index f56bdb67e..eba4d02ec 100644 --- a/internal/auth/codex/filename.go +++ b/internal/auth/codex/filename.go @@ -7,9 +7,8 @@ import ( ) // CredentialFileName returns the filename used to persist Codex OAuth credentials. -// When planType is available (e.g. "plus", "team"), it is appended after the email -// as a suffix to disambiguate subscriptions. Team-scoped plans include the account -// hash to avoid overwriting credentials for the same email across multiple teams. +// The account hash is included when available to keep accounts with the same email +// and plan distinct. The legacy email-based format remains the fallback. func CredentialFileName(email, planType, hashAccountID string, includeProviderPrefix bool) string { email = strings.TrimSpace(email) plan := normalizePlanTypeForFilename(planType) @@ -20,18 +19,18 @@ func CredentialFileName(email, planType, hashAccountID string, includeProviderPr prefix = "codex" } - if plan == "" { - return fmt.Sprintf("%s-%s.json", prefix, email) - } else if isTeamScopedPlan(plan) && hashAccountID != "" { + if hashAccountID != "" { + if plan == "" { + return fmt.Sprintf("%s-%s-%s.json", prefix, hashAccountID, email) + } return fmt.Sprintf("%s-%s-%s-%s.json", prefix, hashAccountID, email, plan) } + if plan == "" { + return fmt.Sprintf("%s-%s.json", prefix, email) + } return fmt.Sprintf("%s-%s-%s.json", prefix, email, plan) } -func isTeamScopedPlan(plan string) bool { - return plan == "team" || plan == "k12" -} - func normalizePlanTypeForFilename(planType string) string { planType = strings.TrimSpace(planType) if planType == "" { diff --git a/internal/auth/codex/filename_test.go b/internal/auth/codex/filename_test.go index 3dd26dc74..efa518944 100644 --- a/internal/auth/codex/filename_test.go +++ b/internal/auth/codex/filename_test.go @@ -36,10 +36,18 @@ func TestCredentialFileName(t *testing.T) { want: "codex-user@example.com-k12.json", }, { - name: "plus ignores account hash", + name: "plus includes account hash", email: " user@example.com ", planType: "Plus", - hashAccountID: "abc12345", + hashAccountID: " abc12345 ", + includeProviderPrefix: true, + want: "codex-abc12345-user@example.com-plus.json", + }, + { + name: "plus without account hash falls back to email and plan", + email: "user@example.com", + planType: "plus", + hashAccountID: "", includeProviderPrefix: true, want: "codex-user@example.com-plus.json", }, @@ -49,7 +57,23 @@ func TestCredentialFileName(t *testing.T) { planType: " Team Plan ", hashAccountID: "abc12345", includeProviderPrefix: true, - want: "codex-user@example.com-team-plan.json", + want: "codex-abc12345-user@example.com-team-plan.json", + }, + { + name: "account hash is used without plan", + email: "user@example.com", + planType: "", + hashAccountID: "abc12345", + includeProviderPrefix: true, + want: "codex-abc12345-user@example.com.json", + }, + { + name: "missing plan and account hash falls back to email", + email: "user@example.com", + planType: "", + hashAccountID: "", + includeProviderPrefix: true, + want: "codex-user@example.com.json", }, }