From 3453bfe10139ef77557f1ead7c8625186c8a453f Mon Sep 17 00:00:00 2001 From: Timon_bed Date: Sun, 30 Aug 2026 15:00:24 +0200 Subject: [PATCH] fix: warn about mismatched OAuth callback URLs --- messages/de.json | 1 + messages/en.json | 1 + .../settings/integrations/fitbit-card.tsx | 6 +++ .../integrations/google-health-card.tsx | 10 ++++- .../integrations/oauth-provider-card.tsx | 6 +++ .../integrations/setup-guide-link.tsx | 39 +++++++++++++++++++ .../settings/integrations/whoop-card.tsx | 6 +++ .../settings/integrations/withings-card.tsx | 6 +++ 8 files changed, 74 insertions(+), 1 deletion(-) diff --git a/messages/de.json b/messages/de.json index b02ed86da..b0f681548 100644 --- a/messages/de.json +++ b/messages/de.json @@ -4636,6 +4636,7 @@ "remainingMany": "Noch {count} Messungen bis zum Trend." }, "settings": { + "oauthCallbackPreflightItem": "{provider}: Die Callback-Adresse {configuredOrigin} unterscheidet sich von der App-Adresse {currentOrigin}.", "shell": { "sectionsNav": "Einstellungs-Bereiche", "groups": { diff --git a/messages/en.json b/messages/en.json index a7472a8de..e9b91967f 100644 --- a/messages/en.json +++ b/messages/en.json @@ -4636,6 +4636,7 @@ "remainingMany": "{count} more readings to go." }, "settings": { + "oauthCallbackPreflightItem": "{provider}: callback address {configuredOrigin} differs from this app address {currentOrigin}.", "shell": { "sectionsNav": "Settings sections", "groups": { diff --git a/src/components/settings/integrations/fitbit-card.tsx b/src/components/settings/integrations/fitbit-card.tsx index 351ef2dd2..98e7c4056 100644 --- a/src/components/settings/integrations/fitbit-card.tsx +++ b/src/components/settings/integrations/fitbit-card.tsx @@ -65,8 +65,10 @@ import { type SyncOutcomeState, } from "./sync-outcome"; import { + CallbackMismatchNotice, IntegrationCardDescription, IntegrationRedirectGuide, + integrationCallbackUrl, } from "./setup-guide-link"; export function FitbitCard({ @@ -521,6 +523,10 @@ export function FitbitCard({

)} + ); } diff --git a/src/components/settings/integrations/google-health-card.tsx b/src/components/settings/integrations/google-health-card.tsx index c76e11e57..99b127409 100644 --- a/src/components/settings/integrations/google-health-card.tsx +++ b/src/components/settings/integrations/google-health-card.tsx @@ -80,7 +80,11 @@ import { type SyncOutcomeResource, type SyncOutcomeState, } from "./sync-outcome"; -import { IntegrationCardDescription } from "./setup-guide-link"; +import { + CallbackMismatchNotice, + IntegrationCardDescription, + integrationCallbackUrl, +} from "./setup-guide-link"; export function GoogleHealthCard({ viewModel, @@ -758,6 +762,10 @@ export function GoogleHealthCard({

)} + ); } diff --git a/src/components/settings/integrations/oauth-provider-card.tsx b/src/components/settings/integrations/oauth-provider-card.tsx index 3b15226f4..147fcc79a 100644 --- a/src/components/settings/integrations/oauth-provider-card.tsx +++ b/src/components/settings/integrations/oauth-provider-card.tsx @@ -65,9 +65,11 @@ import { pillStateForVerdict, } from "./shared"; import { + CallbackMismatchNotice, IntegrationCardDescription, IntegrationRedirectGuide, type IntegrationDocsProvider, + integrationCallbackUrl, } from "./setup-guide-link"; export interface OAuthProviderStatus { @@ -522,6 +524,10 @@ export function OAuthProviderCard({

)} + ); } diff --git a/src/components/settings/integrations/setup-guide-link.tsx b/src/components/settings/integrations/setup-guide-link.tsx index 96ac12b49..23889a30a 100644 --- a/src/components/settings/integrations/setup-guide-link.tsx +++ b/src/components/settings/integrations/setup-guide-link.tsx @@ -11,6 +11,7 @@ * destination now so a user who is mid-setup always knows where to go. */ +import { useSyncExternalStore } from "react"; import { ExternalLink } from "lucide-react"; import { useTranslations } from "@/lib/i18n/context"; @@ -51,6 +52,44 @@ export function integrationCallbackUrl( return `${base}/api/${provider}/callback`; } +/** Shows a compact warning when the callback origin differs from the app. */ +export function CallbackMismatchNotice({ + provider, + callbackUrl, +}: { + provider: string; + callbackUrl: string; +}) { + // Get the current origin of the app + const { t } = useTranslations(); + const currentOrigin = useSyncExternalStore( + () => () => {}, + () => window.location.origin, + () => null, + ); + // Get the origin of the callback URL + const callbackOrigin = (() => { + try { + return new URL(callbackUrl).origin; + } catch { + return null; + } + })(); + if (!currentOrigin || !callbackOrigin || callbackOrigin === currentOrigin) { + return null; + } + // If the callback origin differs from the app, show a warning + return ( + + {t("settings.oauthCallbackPreflightItem", { + provider, + configuredOrigin: callbackOrigin, + currentOrigin, + })} + + ); +} + /** * v1.29.x (UX audit H2) — compact 3-step BYO-developer-app guide for the * six providers that require a self-hoster to register their own vendor diff --git a/src/components/settings/integrations/whoop-card.tsx b/src/components/settings/integrations/whoop-card.tsx index 6abcec564..db02bd914 100644 --- a/src/components/settings/integrations/whoop-card.tsx +++ b/src/components/settings/integrations/whoop-card.tsx @@ -56,8 +56,10 @@ import { type SyncOutcomeState, } from "./sync-outcome"; import { + CallbackMismatchNotice, IntegrationCardDescription, IntegrationRedirectGuide, + integrationCallbackUrl, } from "./setup-guide-link"; export function WhoopCard({ @@ -503,6 +505,10 @@ export function WhoopCard({

)} + ); } diff --git a/src/components/settings/integrations/withings-card.tsx b/src/components/settings/integrations/withings-card.tsx index faf143687..b85c8b03b 100644 --- a/src/components/settings/integrations/withings-card.tsx +++ b/src/components/settings/integrations/withings-card.tsx @@ -55,8 +55,10 @@ import { type SyncOutcomeState, } from "./sync-outcome"; import { + CallbackMismatchNotice, IntegrationCardDescription, IntegrationRedirectGuide, + integrationCallbackUrl, } from "./setup-guide-link"; export function WithingsCard({ @@ -552,6 +554,10 @@ export function WithingsCard({

)} + ); }