Skip to content

Commit

Permalink
Merge pull request #7993 from smeng9/next
Browse files Browse the repository at this point in the history
Add `requireAuth` to <Authenticated /> component
  • Loading branch information
djhi committed Jul 21, 2022
2 parents ea8c82d + c5f6ba0 commit 8841616
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 25 deletions.
4 changes: 2 additions & 2 deletions docs/Authenticated.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ title: "The Authenticated Component"

# `<Authenticated>`

The `<Authenticated>` 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 `<Route element>` commponent:
The `<Authenticated>` 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 `<Route element>` commponent:

```jsx
import { Admin, CustomRoutes, Authenticated } from 'react-admin';
Expand All @@ -15,7 +15,7 @@ const App = () => (
<Admin authProvider={authProvider}>
<CustomRoutes>
<Route path="/foo" element={<Authenticated><Foo /></Authenticated>} />
<Route path="/bar" element={<Authenticated><Bar /></Authenticated>} />
<Route path="/bar" element={<Authenticated requireAuth><Bar /></Authenticated>} />
<Route path="/anoonymous" element={<Baz />} />
</CustomRoutes>
</Admin>
Expand Down
45 changes: 30 additions & 15 deletions packages/ra-core/src/auth/Authenticated.tsx
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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 = [
* <Route path="/foo" render={() =>
* <Authenticated authParams={{ foo: 'bar' }}>
* <Foo />
* </Authenticated>
* } />
* const customRoutes = [
* <Route
* path="/foo"
* element={
* <Authenticated authParams={{ foo: 'bar' }}>
* <Foo />
* </Authenticated>
* }
* />
* ];
* const App = () => (
* <Admin customRoutes={customRoutes}>
* ...
* <Admin>
* <CustomRoutes>{customRoutes}</CustomRoutes>
* </Admin>
* );
*/
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;
}
11 changes: 8 additions & 3 deletions packages/ra-core/src/auth/useAuthState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -45,17 +47,20 @@ const emptyParams = {};
* return <AnonymousContent />;
* };
*/
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;
};

Expand Down
10 changes: 5 additions & 5 deletions packages/ra-core/src/auth/useAuthenticated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 <Foo />;
* }
* const CustomRoutes = [
* <Route path="/foo" render={() => <FooPage />} />
* const customRoutes = [
* <Route path="/foo" element={<FooPage />} />
* ];
* const App = () => (
* <Admin customRoutes={customRoutes}>
* ...
* <Admin>
* <CustomRoutes>{customRoutes}</CustomRoutes>
* </Admin>
* );
*/
Expand Down

0 comments on commit 8841616

Please sign in to comment.