fix: warn about mismatched OAuth callback URLs

This commit is contained in:
Timon_bed committed 2026-08-30 15:00:24 +02:00
1 parent f0ccd9db38
commit 3453bfe101
8 files changed
+74 -1

No files matched your search

+1
View File
@@ -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": {
+1
View File
@@ -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": {
@@ -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({
</p>
)}
</div>
<CallbackMismatchNotice
provider={t("settings.fitbit")}
callbackUrl={integrationCallbackUrl("fitbit")}
/>
</SettingsCard>
);
}
@@ -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({
</p>
)}
</div>
<CallbackMismatchNotice
provider={t("settings.googleHealth")}
callbackUrl={integrationCallbackUrl("google-health")}
/>
</SettingsCard>
);
}
@@ -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({
</p>
)}
</div>
<CallbackMismatchNotice
provider={t(i18nPrefix)}
callbackUrl={integrationCallbackUrl(provider)}
/>
</SettingsCard>
);
}
@@ -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 (
<span className="text-warning border-warning/30 bg-warning/5 block rounded-md border px-2 py-1 text-xs">
{t("settings.oauthCallbackPreflightItem", {
provider,
configuredOrigin: callbackOrigin,
currentOrigin,
})}
</span>
);
}
/**
* 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
@@ -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({
</p>
)}
</div>
<CallbackMismatchNotice
provider={t("settings.whoop")}
callbackUrl={integrationCallbackUrl("whoop")}
/>
</SettingsCard>
);
}
@@ -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({
</p>
)}
</div>
<CallbackMismatchNotice
provider={t("settings.withings")}
callbackUrl={integrationCallbackUrl("withings")}
/>
</SettingsCard>
);
}