From ac5fa203882c859cb717d7ccfd2f17da6db10927 Mon Sep 17 00:00:00 2001 From: Juan Castro Date: Tue, 6 Oct 2026 11:14:19 -0400 Subject: [PATCH] fix: keep the other account out of the OIDC link conflict MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Linking a provider identity already bound elsewhere answered 409 with the raw provider subject and the other account's internal id in the message. The caller needs to know there is a conflict, not whose it is; both stay in the log for whoever is debugging it. Status and legacy code are unchanged — OIDCService branches on the 409. --- src/backend/stores/oidc/OIDCStore.js | 9 ++++++++- src/backend/stores/oidc/OIDCStore.test.ts | 12 +++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/backend/stores/oidc/OIDCStore.js b/src/backend/stores/oidc/OIDCStore.js index ca004fabc..96a33c47c 100644 --- a/src/backend/stores/oidc/OIDCStore.js +++ b/src/backend/stores/oidc/OIDCStore.js @@ -76,9 +76,16 @@ export class OIDCStore extends PuterStore { const existing = await this.getByProviderSub(provider, providerSub); if (!existing) return; if (existing.user_id !== userId) { + // The caller is told there is a conflict, not whose. + console.warn('[oidc] link conflict', { + provider, + providerSub, + boundTo: existing.user_id, + userId, + }); throw new HttpError( 409, - `OIDC link conflict: (${provider}, ${providerSub}) already bound to user ${existing.user_id}`, + `This ${provider} identity is already linked to another account`, { legacyCode: 'conflict' }, ); } diff --git a/src/backend/stores/oidc/OIDCStore.test.ts b/src/backend/stores/oidc/OIDCStore.test.ts index b792424ea..9a9c5feb2 100644 --- a/src/backend/stores/oidc/OIDCStore.test.ts +++ b/src/backend/stores/oidc/OIDCStore.test.ts @@ -19,7 +19,10 @@ const createPostgresUniqueError = (): Error & { code: string } => { const createStore = (rows: readonly OidcRow[]) => { const db = { write: vi.fn( - async (_sql: string, _params: readonly unknown[]): Promise => { + async ( + _sql: string, + _params: readonly unknown[], + ): Promise => { throw createPostgresUniqueError(); }, ), @@ -70,5 +73,12 @@ describe('OIDCStore', () => { statusCode: 409, legacyCode: 'conflict', }); + + // Neither the other account's id nor the raw provider subject. + const error = await store + .link(123, 'test-provider', 'subject-1') + .catch((e: Error) => e); + expect(error.message).not.toContain('456'); + expect(error.message).not.toContain('subject-1'); }); });