diff --git a/packages/console/src/cloud/AppRoutes.tsx b/packages/console/src/cloud/AppRoutes.tsx
index ae6d71db3..20127127c 100644
--- a/packages/console/src/cloud/AppRoutes.tsx
+++ b/packages/console/src/cloud/AppRoutes.tsx
@@ -1,6 +1,7 @@
import { Route, Routes } from 'react-router-dom';
import ProtectedRoutes from '@/containers/ProtectedRoutes';
+import { GlobalAnonymousRoute } from '@/contexts/TenantsProvider';
import Callback from '@/pages/Callback';
import * as styles from './AppRoutes.module.scss';
@@ -12,11 +13,10 @@ function AppRoutes() {
return (
- } />
- } />
- } />
+ } />
+ } />
}>
- } />
+ } />
diff --git a/packages/console/src/containers/TenantAccess/index.tsx b/packages/console/src/containers/TenantAccess/index.tsx
index 460578f61..18045c4fc 100644
--- a/packages/console/src/containers/TenantAccess/index.tsx
+++ b/packages/console/src/containers/TenantAccess/index.tsx
@@ -59,7 +59,7 @@ export default function TenantAccess() {
* We need to exclude the `me` key because it's not tenant-aware. If don't, we
* need to manually revalidate the `me` key to make console work again.
*/
- void mutate((key) => key !== 'me', undefined, false);
+ void mutate((key) => key !== 'me', undefined, { rollbackOnError: false, throwOnError: false });
}, [mutate, currentTenantId]);
useEffect(() => {
diff --git a/packages/console/src/contexts/TenantsProvider.tsx b/packages/console/src/contexts/TenantsProvider.tsx
index 3c711b17f..122e4bb77 100644
--- a/packages/console/src/contexts/TenantsProvider.tsx
+++ b/packages/console/src/contexts/TenantsProvider.tsx
@@ -7,6 +7,23 @@ import { useMatch, useNavigate } from 'react-router-dom';
import { isCloud } from '@/consts/env';
+/**
+ * The routes don't start with a tenant ID.
+ *
+ * @remarks
+ * It's important to keep this single source of truth for all anonymous routes
+ * because we need to check if the current route is anonymous or not to decide
+ * if the current tenant ID is available.
+ *
+ * This should be more clear once we refactor the file structure and the routes.
+ */
+export enum GlobalAnonymousRoute {
+ Callback = '/callback',
+ SocialDemoCallback = '/social-demo-callback',
+}
+
+const anonymousRoutes: Readonly = Object.freeze(Object.values(GlobalAnonymousRoute));
+
/**
* The current tenant status of access validation. When it's `validated`, it indicates that a
* valid Access Token for the current tenant is available.
@@ -83,12 +100,19 @@ function TenantsProvider({ children }: Props) {
const [tenants, setTenants] = useState(initialTenants);
/** @see {@link initialTenants} */
const [isInitComplete, setIsInitComplete] = useState(!isCloud);
- const matched = useMatch('/:tenantId/*');
+ const match = useMatch('/:tenantId/*');
const navigate = useNavigate();
- const currentTenantId = useMemo(
- () => (isCloud ? matched?.params.tenantId ?? '' : defaultTenantId),
- [matched]
- );
+ const currentTenantId = useMemo(() => {
+ if (!isCloud) {
+ return defaultTenantId;
+ }
+
+ if (!match || anonymousRoutes.includes(match.pathname)) {
+ return '';
+ }
+
+ return match.params.tenantId ?? '';
+ }, [match]);
const [currentTenantStatus, setCurrentTenantStatus] = useState('pending');
const navigateTenant = useCallback(
diff --git a/packages/console/src/onboarding/pages/Congrats/index.tsx b/packages/console/src/onboarding/pages/Congrats/index.tsx
index eb87084e5..191c33850 100644
--- a/packages/console/src/onboarding/pages/Congrats/index.tsx
+++ b/packages/console/src/onboarding/pages/Congrats/index.tsx
@@ -23,9 +23,8 @@ function Congrats() {
const { update } = useUserOnboardingData();
const { navigateTenant, currentTenantId } = useContext(TenantsContext);
- const enterAdminConsole = () => {
- void update({ isOnboardingDone: true });
- // Note: navigate to the admin console page directly instead of using the router
+ const enterAdminConsole = async () => {
+ await update({ isOnboardingDone: true });
navigateTenant(currentTenantId);
};
diff --git a/packages/console/src/pages/Callback/index.tsx b/packages/console/src/pages/Callback/index.tsx
index 20c47154b..b82eb8f5a 100644
--- a/packages/console/src/pages/Callback/index.tsx
+++ b/packages/console/src/pages/Callback/index.tsx
@@ -19,7 +19,7 @@ function Callback() {
return;
}
- navigate(getTo('/'), { replace: true });
+ navigate('/', { replace: true });
});
return ;
diff --git a/packages/console/src/pages/ConsoleRoutes/index.tsx b/packages/console/src/pages/ConsoleRoutes/index.tsx
index 858220ac3..7eac25248 100644
--- a/packages/console/src/pages/ConsoleRoutes/index.tsx
+++ b/packages/console/src/pages/ConsoleRoutes/index.tsx
@@ -1,6 +1,7 @@
import { Component, GeneralEvent } from '@logto/app-insights/custom-event';
import { TrackOnce } from '@logto/app-insights/react';
-import { Outlet, Route, Routes } from 'react-router-dom';
+import { ossConsolePath } from '@logto/schemas';
+import { Navigate, Outlet, Route, Routes } from 'react-router-dom';
import { SWRConfig } from 'swr';
import { isCloud, isProduction } from '@/consts/env';
@@ -35,6 +36,12 @@ function Layout() {
export function ConsoleRoutes() {
return (
+ {/**
+ * OSS doesn't have a tenant concept nor root path handling component, but it may
+ * navigate to the root path in frontend. In this case, we redirect it to the OSS
+ * console path to trigger the console routes.
+ */}
+ {!isCloud && } />}
}>
} />
} />