diff --git a/packages/console/src/containers/TenantAccess/index.tsx b/packages/console/src/containers/TenantAccess/index.tsx
index 781b225c9..b779d6026 100644
--- a/packages/console/src/containers/TenantAccess/index.tsx
+++ b/packages/console/src/containers/TenantAccess/index.tsx
@@ -1,12 +1,13 @@
import { useLogto } from '@logto/react';
import { useContext, useEffect } from 'react';
-import { Outlet } from 'react-router-dom';
+import { Outlet, useLocation } from 'react-router-dom';
import { useSWRConfig } from 'swr';
// Used in the docs
-// eslint-disable-next-line unused-imports/no-unused-imports
-import type ProtectedRoutes from '@/containers/ProtectedRoutes';
-import { TenantsContext } from '@/contexts/TenantsProvider';
+
+import { isCloud } from '@/consts/env';
+import { reservedTenantIdWildcard, TenantsContext } from '@/contexts/TenantsProvider';
+import useUserDefaultTenantId from '@/hooks/use-user-default-tenant-id';
/**
* The container that ensures the user has access to the current tenant. When the user is
@@ -43,6 +44,8 @@ export default function TenantAccess() {
const { isAuthenticated } = useLogto();
const { currentTenant, currentTenantId } = useContext(TenantsContext);
const { mutate } = useSWRConfig();
+ const { pathname } = useLocation();
+ const { defaultTenantId } = useUserDefaultTenantId();
// Clean the cache when the current tenant ID changes. This is required because the
// SWR cache key is not tenant-aware.
@@ -68,13 +71,20 @@ export default function TenantAccess() {
isAuthenticated &&
currentTenantId &&
// The current tenant is unavailable to the user, maybe a deleted tenant or a tenant that
- // the user has no access to. Fall back to the home page.
+ // the user has no access to. Try with prepended default tenant ID, or if it's not available,
+ // redirect to the home page.
!currentTenant
) {
+ if (isCloud && defaultTenantId && currentTenantId === reservedTenantIdWildcard) {
+ // eslint-disable-next-line @silverhand/fp/no-mutation
+ window.location.href = pathname.replace(reservedTenantIdWildcard, defaultTenantId);
+ return;
+ }
+
// eslint-disable-next-line @silverhand/fp/no-mutation
window.location.href = '/';
}
- }, [currentTenant, currentTenantId, isAuthenticated]);
+ }, [currentTenant, currentTenantId, isAuthenticated, pathname, defaultTenantId]);
return ;
}
diff --git a/packages/console/src/contexts/SubscriptionDataProvider/index.tsx b/packages/console/src/contexts/SubscriptionDataProvider/index.tsx
index b99af40d7..ea726657d 100644
--- a/packages/console/src/contexts/SubscriptionDataProvider/index.tsx
+++ b/packages/console/src/contexts/SubscriptionDataProvider/index.tsx
@@ -7,9 +7,6 @@ import {
defaultSubscriptionQuota,
defaultSubscriptionUsage,
} from '@/consts';
-// Used in the docs
-// eslint-disable-next-line unused-imports/no-unused-imports
-import TenantAccess from '@/containers/TenantAccess';
import { type FullContext } from './types';
diff --git a/packages/console/src/contexts/TenantsProvider.tsx b/packages/console/src/contexts/TenantsProvider.tsx
index 04fe0d02e..f7fdff34a 100644
--- a/packages/console/src/contexts/TenantsProvider.tsx
+++ b/packages/console/src/contexts/TenantsProvider.tsx
@@ -34,6 +34,13 @@ const reservedRoutes: Readonly = Object.freeze([
...Object.values(GlobalRoute),
]);
+/**
+ * The reserved tenant ID wildcard for the default tenant. Useful when specifying a console URL in
+ * the documentation or other places where the tenant ID is not known. Will be replaced with the
+ * actual default tenant ID in the runtime.
+ */
+export const reservedTenantIdWildcard = 'default';
+
/** @see {@link TenantsProvider} for why `useSWR()` is not applicable for this context. */
type Tenants = {
tenants: readonly TenantResponse[];