From 4d2a5efd12aa1606c5fda9fe17924b6dfdb7c59e Mon Sep 17 00:00:00 2001 From: diegosouzapw Date: Mon, 2 Mar 2026 00:37:50 -0300 Subject: [PATCH] fix: OAuth re-auth now updates existing connection instead of creating duplicates (#170) - Modified OAuth exchange route to use upsert logic at all 3 connection-save locations - Before creating a new connection, checks for existing connections with same provider+email+authType - If match found, calls updateProviderConnection() to refresh tokens instead of creating duplicate - Falls back to createProviderConnection() for genuinely new connections - Fixes: re-auth button creating new account entries instead of refreshing existing ones --- package-lock.json | 4 +- .../api/oauth/[provider]/[action]/route.ts | 113 +++++++++++++----- 2 files changed, 84 insertions(+), 33 deletions(-) diff --git a/package-lock.json b/package-lock.json index 013d95340d..764876f4be 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "omniroute", - "version": "1.7.3", + "version": "1.7.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "omniroute", - "version": "1.7.3", + "version": "1.7.4", "hasInstallScript": true, "license": "MIT", "workspaces": [ diff --git a/src/app/api/oauth/[provider]/[action]/route.ts b/src/app/api/oauth/[provider]/[action]/route.ts index 2fc980b355..d3596ec507 100644 --- a/src/app/api/oauth/[provider]/[action]/route.ts +++ b/src/app/api/oauth/[provider]/[action]/route.ts @@ -6,7 +6,7 @@ import { requestDeviceCode, pollForToken, } from "@/lib/oauth/providers"; -import { createProviderConnection, isCloudEnabled } from "@/models"; +import { createProviderConnection, updateProviderConnection, getProviderConnections, isCloudEnabled } from "@/models"; import { getConsistentMachineId } from "@/shared/utils/machineId"; import { syncToCloud } from "@/lib/cloudSync"; import { startLocalServer } from "@/lib/oauth/utils/server"; @@ -170,16 +170,33 @@ export async function POST( exchangeTokens(provider, code, redirectUri, codeVerifier, state) ); - // Save to database - const connection: any = await createProviderConnection({ - provider, - authType: "oauth", - ...tokenData, - expiresAt: tokenData.expiresIn - ? new Date(Date.now() + tokenData.expiresIn * 1000).toISOString() - : null, - testStatus: "active", - }); + // Upsert: update existing connection if same provider+email, else create new + const expiresAt = tokenData.expiresIn + ? new Date(Date.now() + tokenData.expiresIn * 1000).toISOString() + : null; + + let connection: any; + if (tokenData.email) { + const existing = await getProviderConnections({ provider }); + const match = existing.find((c: any) => c.email === tokenData.email && c.authType === "oauth"); + if (match) { + connection = await updateProviderConnection(match.id, { + ...tokenData, + expiresAt, + testStatus: "active", + isActive: true, + }); + } + } + if (!connection) { + connection = await createProviderConnection({ + provider, + authType: "oauth", + ...tokenData, + expiresAt, + testStatus: "active", + }); + } // Auto sync to Cloud if enabled await syncToCloudIfEnabled(); @@ -218,16 +235,33 @@ export async function POST( } if (result.success) { - // Save to database - const connection: any = await createProviderConnection({ - provider, - authType: "oauth", - ...result.tokens, - expiresAt: result.tokens.expiresIn - ? new Date(Date.now() + result.tokens.expiresIn * 1000).toISOString() - : null, - testStatus: "active", - }); + // Upsert: update existing connection if same provider+email, else create new + const expiresAt = result.tokens.expiresIn + ? new Date(Date.now() + result.tokens.expiresIn * 1000).toISOString() + : null; + + let connection: any; + if (result.tokens.email) { + const existing = await getProviderConnections({ provider }); + const match = existing.find((c: any) => c.email === result.tokens.email && c.authType === "oauth"); + if (match) { + connection = await updateProviderConnection(match.id, { + ...result.tokens, + expiresAt, + testStatus: "active", + isActive: true, + }); + } + } + if (!connection) { + connection = await createProviderConnection({ + provider, + authType: "oauth", + ...result.tokens, + expiresAt, + testStatus: "active", + }); + } // Auto sync to Cloud if enabled await syncToCloudIfEnabled(); @@ -312,16 +346,33 @@ export async function POST( exchangeTokens(provider, params.code, redirectUri, codeVerifier, params.state) ); - // Save to database - const connection: any = await createProviderConnection({ - provider, - authType: "oauth", - ...tokenData, - expiresAt: tokenData.expiresIn - ? new Date(Date.now() + tokenData.expiresIn * 1000).toISOString() - : null, - testStatus: "active", - }); + // Upsert: update existing connection if same provider+email, else create new + const expiresAt = tokenData.expiresIn + ? new Date(Date.now() + tokenData.expiresIn * 1000).toISOString() + : null; + + let connection: any; + if (tokenData.email) { + const existing = await getProviderConnections({ provider }); + const match = existing.find((c: any) => c.email === tokenData.email && c.authType === "oauth"); + if (match) { + connection = await updateProviderConnection(match.id, { + ...tokenData, + expiresAt, + testStatus: "active", + isActive: true, + }); + } + } + if (!connection) { + connection = await createProviderConnection({ + provider, + authType: "oauth", + ...tokenData, + expiresAt, + testStatus: "active", + }); + } await syncToCloudIfEnabled();