fix: keep the other account out of the OIDC link conflict

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.
This commit is contained in:
Juan Castro committed 2026-10-06 11:14:19 -04:00
1 parent 3389d3f8a1
commit ac5fa20388
2 files changed
+19 -2

No files matched your search

+8 -1
View File
@@ -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' },
);
}
+11 -1
View File
@@ -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<void> => {
async (
_sql: string,
_params: readonly unknown[],
): Promise<void> => {
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');
});
});