diff --git a/app/scenes/Login/Login.tsx b/app/scenes/Login/Login.tsx index 423ea1b7ab..9576208cd0 100644 --- a/app/scenes/Login/Login.tsx +++ b/app/scenes/Login/Login.tsx @@ -40,8 +40,9 @@ import { BackButton } from "./components/BackButton"; import { Background } from "./components/Background"; import { Centered } from "./components/Centered"; import { Notices } from "./components/Notices"; -import { getRedirectUrl, navigateToSubdomain } from "./urls"; +import { navigateToSubdomain } from "./urls"; import lazyWithRetry from "~/utils/lazyWithRetry"; +import { getRedirectUrl } from "~/utils/urls"; const WorkspaceSetup = lazyWithRetry( () => import("./components/WorkspaceSetup") diff --git a/app/scenes/Login/components/AuthenticationProvider.tsx b/app/scenes/Login/components/AuthenticationProvider.tsx index f23ff39262..00ad9cf7b2 100644 --- a/app/scenes/Login/components/AuthenticationProvider.tsx +++ b/app/scenes/Login/components/AuthenticationProvider.tsx @@ -11,7 +11,7 @@ import PluginIcon from "~/components/PluginIcon"; import Tooltip from "~/components/Tooltip"; import { client } from "~/utils/ApiClient"; import Desktop from "~/utils/Desktop"; -import { getRedirectUrl } from "../urls"; +import { getRedirectUrl } from "~/utils/urls"; import { CSRF } from "@shared/constants"; import { getCookie } from "tiny-cookie"; diff --git a/app/scenes/Login/urls.ts b/app/scenes/Login/urls.ts index 81a3fd92e1..fe1328a358 100644 --- a/app/scenes/Login/urls.ts +++ b/app/scenes/Login/urls.ts @@ -1,6 +1,3 @@ -import { Client } from "@shared/types"; -import { parseDomain } from "@shared/utils/domains"; -import env from "~/env"; import Desktop from "~/utils/Desktop"; function validateAndEncodeSubdomain(subdomain: string): string { @@ -12,29 +9,6 @@ function validateAndEncodeSubdomain(subdomain: string): string { return `https://${encodedSubdomain}.getoutline.com`; } -/** - * If we're on a custom domain or a subdomain then the auth must point to the - * apex (env.URL) for authentication so that the state cookie can be set and read. - * We pass the host into the auth URL so that the server can redirect on error - * and keep the user on the same page. - * - * @param authUrl The URL to redirect to after authentication - */ -export function getRedirectUrl(authUrl: string) { - const { custom, teamSubdomain, host } = parseDomain(window.location.origin); - const url = new URL(env.URL); - url.pathname = authUrl; - - if (custom || teamSubdomain) { - url.searchParams.set("host", host); - } - if (Desktop.isElectron()) { - url.searchParams.set("client", Client.Desktop); - } - - return url.toString(); -} - /** * Redirect to a subdomain, adding it to the custom hosts list on desktop first. * diff --git a/app/scenes/Settings/Authentication.tsx b/app/scenes/Settings/Authentication.tsx index 08c6c52c1f..07d92e3b41 100644 --- a/app/scenes/Settings/Authentication.tsx +++ b/app/scenes/Settings/Authentication.tsx @@ -19,6 +19,7 @@ import useRequest from "~/hooks/useRequest"; import useStores from "~/hooks/useStores"; import SettingRow from "./components/SettingRow"; import { setPostLoginPath } from "~/hooks/useLastVisitedPath"; +import { getRedirectUrl } from "~/utils/urls"; import { settingsPath } from "~/utils/routeHelpers"; import DomainManagement from "./components/DomainManagement"; import Button from "~/components/Button"; @@ -97,7 +98,7 @@ function Authentication() { const handleConnectProvider = React.useCallback((name: string) => { setPostLoginPath(settingsPath("authentication")); - window.location.href = `/auth/${name}?host=${window.location.host}`; + window.location.href = getRedirectUrl(`/auth/${name}`); }, []); const handleToggleGroupSync = React.useCallback( diff --git a/app/utils/urls.ts b/app/utils/urls.ts index fe4e3a548e..6aed3f9ee3 100644 --- a/app/utils/urls.ts +++ b/app/utils/urls.ts @@ -1,3 +1,32 @@ +import { Client } from "@shared/types"; +import { parseDomain } from "@shared/utils/domains"; +import env from "~/env"; +import Desktop from "~/utils/Desktop"; + +/** + * Builds an absolute auth redirect URL against the apex (env.URL). When the + * user is on a custom domain or team subdomain the auth flow must start on the + * apex so that the OAuth state cookie can be set and later read by the + * callback. The originating host is forwarded as a query param so the server + * can return the user to the same page on error or after sign-in. + * + * @param authUrl The auth endpoint path to redirect to (e.g. "/auth/google"). + */ +export function getRedirectUrl(authUrl: string) { + const { custom, teamSubdomain, host } = parseDomain(window.location.origin); + const url = new URL(env.URL); + url.pathname = authUrl; + + if (custom || teamSubdomain) { + url.searchParams.set("host", host); + } + if (Desktop.isElectron()) { + url.searchParams.set("client", Client.Desktop); + } + + return url.toString(); +} + export function isHash(href: string) { if (href[0] === "#") { return true;