diff --git a/docs/Authenticated.md b/docs/Authenticated.md
index 535f4a2c2e7..f31e01ba4e4 100644
--- a/docs/Authenticated.md
+++ b/docs/Authenticated.md
@@ -5,7 +5,7 @@ title: "The Authenticated Component"
# ``
-The `` component calls [the `useAuthenticated()` hook](./useAuthenticated.md), and renders its child component - unless the authentication check fails. Use it as an alternative to the `useAuthenticated()` hook when you can't use a hook, e.g. inside a `` commponent:
+The `` component calls [the `useAuthState()` hook](./useAuthState.md), and by default optimistically renders its child component - unless the authentication check fails. Use it as an alternative to the `useAuthenticated()` hook when you can't use a hook, or you want to support pessimistic mode by setting `requireAuth` prop, e.g. inside a `` commponent:
```jsx
import { Admin, CustomRoutes, Authenticated } from 'react-admin';
@@ -15,7 +15,7 @@ const App = () => (
} />
- } />
+ } />
} />
diff --git a/packages/ra-core/src/auth/Authenticated.tsx b/packages/ra-core/src/auth/Authenticated.tsx
index 95d8ccbc405..11fe00565ac 100644
--- a/packages/ra-core/src/auth/Authenticated.tsx
+++ b/packages/ra-core/src/auth/Authenticated.tsx
@@ -1,7 +1,7 @@
import * as React from 'react';
import { ReactNode } from 'react';
-import { useAuthenticated } from './useAuthenticated';
+import useAuthState from './useAuthState';
/**
* Restrict access to children to authenticated users.
@@ -10,36 +10,51 @@ import { useAuthenticated } from './useAuthenticated';
* Use it to decorate your custom page components to require
* authentication.
*
+ * By default this component is optimistic: it does not block
+ * rendering children when checking authentication, but this mode
+ * can be turned off by setting `requireAuth` to true.
+ *
* You can set additional `authParams` at will if your authProvider
* requires it.
*
- * @see useAuthenticated
+ * @see useAuthState
*
* @example
- * import { Authenticated } from 'react-admin';
+ * import { Admin, CustomRoutes, Authenticated } from 'react-admin';
*
- * const CustomRoutes = [
- *
- *
- *
- *
- * } />
+ * const customRoutes = [
+ *
+ *
+ *
+ * }
+ * />
* ];
* const App = () => (
- *
- * ...
+ *
+ * {customRoutes}
*
* );
*/
export const Authenticated = (props: AuthenticatedProps) => {
- const { authParams, children } = props;
- useAuthenticated({ params: authParams });
- // render the child even though the useAuthenticated() call isn't finished (optimistic rendering)
- // the above hook will log out if the authProvider doesn't validate that the user is authenticated
+ const { authParams, children, requireAuth = false } = props;
+
+ // this hook will log out if the authProvider doesn't validate that the user is authenticated
+ const { isLoading, authenticated } = useAuthState(authParams, true);
+
+ // in pessimistic mode don't render the children until authenticated
+ if ((requireAuth && isLoading) || !authenticated) {
+ return null;
+ }
+
+ // render the children in optimistic rendering or after authenticated
return <>{children}>;
};
export interface AuthenticatedProps {
children: ReactNode;
authParams?: object;
+ requireAuth?: boolean;
}
diff --git a/packages/ra-core/src/auth/useAuthState.ts b/packages/ra-core/src/auth/useAuthState.ts
index 1ef3263f8c4..635d6af4be4 100644
--- a/packages/ra-core/src/auth/useAuthState.ts
+++ b/packages/ra-core/src/auth/useAuthState.ts
@@ -29,6 +29,8 @@ const emptyParams = {};
*
* @param {Object} params Any params you want to pass to the authProvider
*
+ * @param {Boolean} logoutOnFailure: Optional. Whether the user should be logged out if the authProvider fails to authenticate them. False by default.
+ *
* @returns The current auth check state. Destructure as { authenticated, error, isLoading }.
*
* @example
@@ -45,17 +47,20 @@ const emptyParams = {};
* return ;
* };
*/
-const useAuthState = (params: any = emptyParams): State => {
+const useAuthState = (
+ params: any = emptyParams,
+ logoutOnFailure: boolean = false
+): State => {
const [state, setState] = useSafeSetState({
isLoading: true,
authenticated: true, // optimistic
});
const checkAuth = useCheckAuth();
useEffect(() => {
- checkAuth(params, false)
+ checkAuth(params, logoutOnFailure)
.then(() => setState({ isLoading: false, authenticated: true }))
.catch(() => setState({ isLoading: false, authenticated: false }));
- }, [checkAuth, params, setState]);
+ }, [checkAuth, params, logoutOnFailure, setState]);
return state;
};
diff --git a/packages/ra-core/src/auth/useAuthenticated.ts b/packages/ra-core/src/auth/useAuthenticated.ts
index 75fa3cfe605..83a3e1d573c 100644
--- a/packages/ra-core/src/auth/useAuthenticated.ts
+++ b/packages/ra-core/src/auth/useAuthenticated.ts
@@ -12,17 +12,17 @@ import { useCheckAuth } from './useCheckAuth';
* requires it.
*
* @example
- * import { useAuthenticated } from 'react-admin';
+ * import { Admin, CustomRoutes, useAuthenticated } from 'react-admin';
* const FooPage = () => {
* useAuthenticated();
* return ;
* }
- * const CustomRoutes = [
- * } />
+ * const customRoutes = [
+ * } />
* ];
* const App = () => (
- *
- * ...
+ *
+ * {customRoutes}
*
* );
*/