Security

Web applications often need to limit access to specific pages or resources to authenticated users (“authentication”) and ensure that users can only perform permitted actions (“authorization”).

React-admin supports both authentication and authorization, allowing you to secure your admin app with your preferred authentication strategy. Since there are many strategies (e.g., OAuth, MFA, passwordless, magic link), react-admin delegates this logic to an authProvider.

The Auth Provider

Authentication and authorization features rely on an authentication backend (e.g., OAuth server, API server, or SAML server). The authProvider acts as a bridge between react-admin and this authentication backend.

For example, when the user accesses a page component (<List>, <Edit>, <Create>, <Show>), react-admin checks if the user is authenticated by calling the authProvider.checkAuth() method. If the user is not authenticated, they are redirected to the login page:

try {
    await authProvider.checkAuth();
} catch (error) {
    // The user is not authenticated
    return <Redirect to="/login" />;
}

If you use JWT tokens, this method checks if the user token is valid and refreshes it if necessary.

An Auth Provider must implement the following methods:

const authProvider = {
    // Send username and password to the auth server and get back credentials
    async login(params) {/** ... **/},
    // Check if an error from the dataProvider indicates an authentication issue
    async checkError(error) {/** ... **/},
    // Verify that the user's credentials are still valid during navigation
    async checkAuth(params) {/** ... **/},
    // Remove local credentials and notify the auth server of the logout
    async logout() {/** ... **/},
    // Retrieve the user's profile
    async getIdentity() {/** ... **/},
    // (Optional) Check if the user has permission for a specific action on a resource
    async canAccess() {/** ... **/},
};

You can use an existing Auth Provider from the List of Available Auth Providers or create your own following the Building Your Own Auth Provider guide.

Authentication

Once you set an <Admin authProvider>, react-admin enables authentication automatically.

const App = () => (
    <Admin authProvider={authProvider}>
        ...
    </Admin>
);

For page components (<List>, <Edit>, <Create>, <Show>) and the dashboard, anonymous users are redirected to the login screen. To allow anonymous access on a page, use the disableAuthentication prop. For example, in a list view:

import { List } from 'react-admin';

const PostList = () => (
    <List disableAuthentication>
        ...
    </List>
);

For custom routes, anonymous users have access by default. To require authentication on a custom route, wrap the page component in an <Authenticated> component:

import { Admin, Resource, CustomRoutes, Authenticated } from 'react-admin';
import { Route } from "react-router-dom";
import { MyCustomPage } from './MyCustomPage';

const App = () => (
    <Admin authProvider={authProvider}>
        ...
        <CustomRoutes>
            <Route path="/my-custom-page" element={
                <Authenticated>
                    <MyCustomPage />
                </Authenticated>
            } />
        </CustomRoutes>
    </Admin>
);

If all your custom routes require authentication, use the <Admin requireAuth> prop instead of wrapping each route in <Authenticated>:

const App = () => (
    <Admin
        dataProvider={dataProvider}
        authProvider={authProvider}
        requireAuth
    >
        ...
    </Admin>
);

Check the Auth Provider Setup Guide for more details.

Authorization

After a user is authenticated, your application may need to check if the user has the right to access a specific resource or perform an action.

The authProvider.canAccess() method determines if the user can access a resource or perform an action. This flexibility allows you to implement various authorization strategies, such as:

  • Role-Based Access Control (RBAC)
  • Attribute-Based Access Control (ABAC)
  • Access Control List (ACL).

Since the auth logic is abstracted by the Auth Provider, you can integrate react-admin with popular authorization solutions like Okta, Casbin, Cerbos, and others.

Page components (<List>, <Create>, <Edit>, <Show>) have built-in access control. Before rendering them, react-admin calls authProvider.canAccess() with the relevant action and resource parameters.

<Resource
    name="posts"
    // Available if canAccess({ action: 'list', resource: 'posts' }) returns true
    list={PostList}
    // Available if canAccess({ action: 'create', resource: 'posts' }) returns true
    create={PostCreate}
    // Available if canAccess({ action: 'edit', resource: 'posts' }) returns true
    edit={PostEdit}
    // Available if canAccess({ action: 'show', resource: 'posts' }) returns true
    show={PostShow}
/>;

To control access in your own components, use the useCanAccess() hook or the <CanAccess> component.

In the following example, only users who can access the delete action on the comments resource can see the DeleteCommentButton:

import Stack from '@mui/material/Stack';
import { CanAccess } from 'react-admin';

const CommentsToolbar = ({ record }) => (
    <Stack direction="row" spacing={2}>
        <ApproveCommentButton record={record} />
        <RejectCommentButton record={record} />
        <CanAccess action="delete" resource="comments" record={record}>
            <DeleteCommentButton record={record} />
        </CanAccess>
    </Stack>
);

Check the Authorization Guide for more details.

Login Page

React-admin displays a login page when the user is not authenticated. The login page is a simple form with username and password fields.

Login form

You can customize the login page by setting the <Admin loginPage> prop.

For example, to use an email field instead of a username field, use the LoginWithEmail component:

import { Admin, LoginWithEmail } from 'react-admin';
import authProvider from './authProvider';

const App = () => (
    <Admin loginPage={LoginWithEmail} authProvider={authProvider}>
        ...
    </Admin>
);

If you need other login options (magic link, Email OTP, OAuth provider, etc), you can pass a custom login component, leveraging the useLogin hook to call authProvider.login()::

// in src/MyLoginPage.js
import { useState } from 'react';
import { useLogin, useNotify, Notification } from 'react-admin';

const MyLoginPage = ({ theme }) => {
    const [email, setEmail] = useState('');
    const [password, setPassword] = useState('');
    const login = useLogin();
    const notify = useNotify();

    const handleSubmit = e => {
        e.preventDefault();
        login({ email, password }).catch(() =>
            notify('Invalid email or password')
        );
    };

    return (
        <form onSubmit={handleSubmit}>
            <input
                name="email"
                type="email"
                value={email}
                onChange={e => setEmail(e.target.value)}
            />
            <input
                name="password"
                type="password"
                value={password}
                onChange={e => setPassword(e.target.value)}
            />
        </form>
    );
};

export default MyLoginPage;

// in src/App.js
import { Admin } from "react-admin";
import { dataProvider } from "./dataProvider";
import { authProvider } from "./authProvider";
import MyLoginPage from "./MyLoginPage";

const App = () => (
    <Admin loginPage={MyLoginPage} authProvider={authProvider} dataProvider={dataProvider}>
        ...
    </Admin>
);

You can also entirely turn off the /login route by passing false to this prop. In this case, the authProvider must handle redirecting unauthenticated users to a custom login page by returning a redirectTo field in response to checkAuth (see authProvider.checkAuth() for details). If you fail to customize the redirection, the app may end up in an infinite loop.

const authProvider = {
    // ...
    async checkAuth() {
        // ...
        if (!authenticated) {
            throw { redirectTo: '/no-access' };
        }
    },
};

const App = () => (
    <Admin authProvider={authProvider} loginPage={false}>
        ...
    </Admin>
);

Calling The Auth Provider

React-admin provides several ways to call authentication provider methods in your components:

  • useLogin: Calls the authProvider.login() method. Use it in custom login screens.
  • useLogout: Calls the authProvider.logout() method. Use it in custom logout buttons.
  • <Authenticated>: Redirects to the login page if the user is not authenticated. Use it to protect custom routes.
  • useAuthState: Calls the authProvider.checkAuth() method. Use it to display different UI elements based on the user’s authentication state.
  • useAuthenticated: Calls the authProvider.checkAuth() method and redirect to the login page if the user is not authenticated. Use it to protect custom routes.
  • useGetIdentity: Calls the authProvider.getIdentity() method. Use it to display the user’s profile information.
  • useCanAccess: Calls the authProvider.canAccess() method. Use it to display different UI elements based on the user’s permissions.
  • <CanAccess>: Renders its children only of authProvider.canAccess() method returns true.
  • useAuthProvider: Returns the authProvider instance. Use it to call other methods of the authProvider.