Skip to content

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”).

Ra-core 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), ra-core delegates this logic to an authProvider.

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 ra-core and this authentication backend.

For example, when the user accesses a page component (<ListBase>, <EditBase>, <CreateBase>, <ShowBase>), ra-core 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 <Navigate 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.

Once you set a <CoreAdmin authProvider>, ra-core enables authentication automatically.

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

For page components (<ListBase>, <EditBase>, <CreateBase>, <ShowBase>) 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 { ListBase } from 'ra-core';
const PostList = () => (
<ListBase disableAuthentication>
...
</ListBase>
);

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 { CoreAdmin, Resource, CustomRoutes, Authenticated } from 'ra-core';
import { Route } from "react-router-dom";
import { MyCustomPage } from './MyCustomPage';
const App = () => (
<CoreAdmin authProvider={authProvider}>
...
<CustomRoutes>
<Route path="/my-custom-page" element={
<Authenticated>
<MyCustomPage />
</Authenticated>
} />
</CustomRoutes>
</CoreAdmin>
);

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

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

Check the Auth Provider Setup Guide for more details.

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 ra-core with popular authorization solutions like Okta, Casbin, Cerbos, and others.

Page components (<ListBase>, <CreateBase>, <EditBase>, <ShowBase>) have built-in access control. Before rendering them, ra-core 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 { CanAccess } from 'ra-core';
const CommentsToolbar = ({ record }) => (
<div style={{ display: 'flex', gap: '8px' }}>
<ApproveCommentButton record={record} />
<RejectCommentButton record={record} />
<CanAccess action="delete" resource="comments" record={record}>
<DeleteCommentButton record={record} />
</CanAccess>
</div>
);

Check the Authorization Guide for more details.

Ra-core displays a login page when the user is not authenticated. Since ra-core is headless, you need to provide your own login page implementation by setting the <CoreAdmin loginPage> prop.

You can create a custom login component, leveraging the useLogin hook to call authProvider.login():

// in src/MyLoginPage.js
import { useState } from 'react';
import { useLogin, useNotify } from 'ra-core';
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 { CoreAdmin } from "ra-core";
import { dataProvider } from "./dataProvider";
import { authProvider } from "./authProvider";
import MyLoginPage from "./MyLoginPage";
const App = () => (
<CoreAdmin loginPage={MyLoginPage} authProvider={authProvider} dataProvider={dataProvider}>
...
</CoreAdmin>
);

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 = () => (
<CoreAdmin authProvider={authProvider} loginPage={false}>
...
</CoreAdmin>
);

Ra-core 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.