diff --git a/frontend/src/lib/auth.ts b/frontend/src/lib/auth.ts index 34c7ea5e13..b8b06873f3 100644 --- a/frontend/src/lib/auth.ts +++ b/frontend/src/lib/auth.ts @@ -1,4 +1,4 @@ -import { notFound, redirect } from "@tanstack/react-router"; +import { redirect } from "@tanstack/react-router"; import { oauthProviderClient } from "@better-auth/oauth-provider/client"; import { adminClient, organizationClient } from "better-auth/client/plugins"; import { createAuthClient } from "better-auth/react"; @@ -40,34 +40,25 @@ export const redirectToOrganization = async ({ throw redirect({ to: from }); } - if (session.data.session.activeOrganizationId) { - const org = await authClient.organization.getFullOrganization({ - query: { - organizationId: session.data.session.activeOrganizationId, - }, - }); - - if (org.error) { - throw notFound(); - } - - throw redirect({ - to: "/orgs/$organization", - params: { organization: org.data.slug }, - }); - } + const activeOrganizationId = session.data.session.activeOrganizationId; const orgs = await authClient.organization.list(); + const org = + orgs.data?.find((o) => o.id === activeOrganizationId) ?? + orgs.data?.[0]; - if (!orgs.data?.[0]) { + if (!org) { return false; } - await authClient.organization.setActive({ - organizationId: orgs.data[0].id, - }); + if (org.id !== activeOrganizationId) { + await authClient.organization.setActive({ + organizationId: org.id, + }); + } + throw redirect({ to: "/orgs/$organization", - params: { organization: orgs.data[0].slug }, + params: { organization: org.slug }, }); } diff --git a/frontend/src/routes/_context/index.tsx b/frontend/src/routes/_context/index.tsx index 9771bc0022..fa18a5d1a2 100644 --- a/frontend/src/routes/_context/index.tsx +++ b/frontend/src/routes/_context/index.tsx @@ -10,7 +10,7 @@ export const Route = createFileRoute("/_context/")({ beforeLoad: async ({ context, search }) => { if (features.platform) { if (!(await redirectToOrganization(search))) { - throw redirect({ to: "/login", search: true }); + throw redirect({ to: "/new-org" }); } return; } diff --git a/frontend/src/routes/_context/orgs.$organization.tsx b/frontend/src/routes/_context/orgs.$organization.tsx index 447b9af043..69a2136af7 100644 --- a/frontend/src/routes/_context/orgs.$organization.tsx +++ b/frontend/src/routes/_context/orgs.$organization.tsx @@ -20,19 +20,23 @@ export const Route = createFileRoute("/_context/orgs/$organization")({ query: { organizationSlug: params.organization }, }); - // If the slug is unknown to the auth backend (stale URL, deleted org, - // user not a member anymore), redirect to root rather than throwing - // notFound(). notFound() leaves descendant matches stuck in `pending` - // while their layout components keep rendering, which crashes - // useCloudDataProvider() / useCloudProjectDataProvider() consumers. + // Redirect instead of throwing notFound(). notFound() leaves descendant + // matches stuck in `pending` while their layout components keep + // rendering, which crashes useCloudDataProvider() / + // useCloudProjectDataProvider() consumers. The destination must not + // resolve an organization itself, or an unresolvable slug ping-pongs + // between the two routes forever. if (org.error) { - throw redirect({ to: "/" }); + if (org.error.status === 403 || org.error.status === 404) { + throw redirect({ to: "/new-org" }); + } + throw new Error(org.error.message ?? "Failed to load organization"); } const session = await authClient.getSession(); if (session.data?.session.activeOrganizationId !== org.data.id) { await authClient.organization.setActive({ - organizationSlug: params.organization, + organizationId: org.data.id, }); }