Skip to content

Commit bc2b7ac

Browse files
committed
fix: match login fallback variants by identity
1 parent 5fb1733 commit bc2b7ac

2 files changed

Lines changed: 41 additions & 1 deletion

File tree

lib/auth/login-runner.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,19 @@ export function applyAccountSelectionFallbacks(
119119
fallbacks: AccountSelectionFallbacks,
120120
): AccountSelectionResult {
121121
const primary = { ...selection.primary };
122+
const primaryAccountId = selection.primary.accountIdOverride?.trim();
123+
const primaryOrganizationId = selection.primary.organizationIdOverride?.trim();
124+
const shouldReusePrimaryVariant =
125+
(primaryAccountId?.length ?? 0) > 0 || (primaryOrganizationId?.length ?? 0) > 0;
126+
// Callers may deep-clone `selection.primary`, so reuse the updated primary by
127+
// persisted account identity instead of relying on object aliasing.
122128
let variantsForPersistence = selection.variantsForPersistence.map((variant) =>
123-
variant === selection.primary ? primary : { ...variant },
129+
variant === selection.primary ||
130+
(shouldReusePrimaryVariant &&
131+
(variant.accountIdOverride?.trim() ?? "") === (primaryAccountId ?? "") &&
132+
(variant.organizationIdOverride?.trim() ?? "") === (primaryOrganizationId ?? ""))
133+
? primary
134+
: { ...variant },
124135
);
125136

126137
const accountIdOverride = fallbacks.accountIdOverride?.trim();

test/login-runner.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
persistResolvedAccountSelection,
99
resolveAccountSelection,
1010
resolveAndPersistAccountSelection,
11+
type AccountSelectionResult,
1112
type TokenSuccessWithAccount,
1213
} from "../lib/auth/login-runner.js";
1314
import { loadAccounts, setStoragePathDirect } from "../lib/storage.js";
@@ -125,6 +126,34 @@ describe("login-runner selection finalization", () => {
125126
expect(updated.variantsForPersistence).toHaveLength(selection.variantsForPersistence.length);
126127
});
127128

129+
it("updates cloned primary variants without relying on object identity", () => {
130+
const primary: TokenSuccessWithAccount = {
131+
type: "success",
132+
access: "access-token",
133+
refresh: "refresh-token",
134+
expires: Date.now() + 60_000,
135+
idToken: "id-token",
136+
accountIdOverride: "resolved-account",
137+
accountIdSource: "token",
138+
};
139+
const selection: AccountSelectionResult = {
140+
primary,
141+
variantsForPersistence: [{ ...primary }],
142+
};
143+
144+
const updated = applyAccountSelectionFallbacks(selection, {
145+
organizationIdOverride: "resolved-org",
146+
accountLabel: "Resolved label",
147+
});
148+
149+
expect(updated.primary.organizationIdOverride).toBe("resolved-org");
150+
expect(updated.primary.accountLabel).toBe("Resolved label");
151+
expect(updated.variantsForPersistence).toHaveLength(1);
152+
expect(updated.variantsForPersistence[0]).toBe(updated.primary);
153+
expect(updated.variantsForPersistence[0]?.organizationIdOverride).toBe("resolved-org");
154+
expect(updated.variantsForPersistence[0]?.accountLabel).toBe("Resolved label");
155+
});
156+
128157
it("resolves and persists the selected variants through the shared callback", async () => {
129158
const persistSelections = vi.fn(async () => {});
130159
const result = await resolveAndPersistAccountSelection(

0 commit comments

Comments
 (0)