Auth Provider Setup
Web applications often need to limit access to specific pages or resources to authenticated users (“Authentication”) and ensure that users can only execute actions they are permitted to (“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 (OAuth, MFA, passwordless, magic link, etc.), react-admin delegates this logic to anauthProvider
.
Enabling Authentication
By default, react-admin apps do not require authentication. To restrict access to the admin, pass anauthProvider
to the<Admin>
component.
// in src/App.jsimportauthProviderfrom'./authProvider';constApp=()=>(<AdminauthProvider={authProvider}> ...</Admin>);
AnauthProvider
is an object that handles authentication and authorization logic, similar to adataProvider
. It exposes methods that react-admin calls when needed, and you can also call these methods manually through specialized hooks.
Once an admin has anauthProvider
, react-admin will restrict CRUD pages (thelist
,edit
,create
, andshow
components of yourResources
) to authenticated users and redirect anonymous users to the/login
page, displaying a login form for a username and password.
React-admin offers several built-inauthProvider
implementations for popular authentication services likeGoogle Identity,Microsoft Entra ID,AWS Cognito,Auth0,Keycloak, and others. Refer to theList of Available Auth Providers to find one that suits your requirements.
If you need to implement a custom authentication strategy, theBuilding Your Own Auth Provider offers a step-by-step guide. It boils down to implementing a few methods that react-admin calls when needed:
constauthProvider={// send username and password to the auth server and get back credentialsasynclogin(params){/** ... **/},// when the dataProvider returns an error, check if this is an authentication errorasynccheckError(error){/** ... **/},// when the user navigates, make sure that their credentials are still validasynccheckAuth(params){/** ... **/},// remove local credentials and notify the auth server that the user logged outasynclogout(){/** ... **/},// get the user's profileasyncgetIdentity(){/** ... **/},// check whether users have the right to perform an action on a resource (optional)asynccanAccess(){/** ... **/},};
Sending Credentials To The API
TheauthProvider
handles authentication logic, but thedataProvider
must include the user credentials in requests to the API.
As explained in theData providers documentation,simpleRestProvider
andjsonServerProvider
accept anhttpClient
as a second parameter. Here, you can customize request headers, cookies, etc.
For instance, if theauthProvider
stores an authentication token inlocalStorage
, you can tweak thedataProvider
to pass this token as anAuthorization
header:
import{fetchUtils,Admin,Resource}from'react-admin';importsimpleRestProviderfrom'ra-data-simple-rest';consthttpClient=(url,options={})=>{if(!options.headers){options.headers=newHeaders({Accept:'application/json'});}const{token}=JSON.parse(localStorage.getItem('auth'));options.headers.set('Authorization',`Bearer${token}`);returnfetchUtils.fetchJson(url,options);};constdataProvider=simpleRestProvider('http://localhost:3000',httpClient);constApp=()=>(<AdmindataProvider={dataProvider}authProvider={authProvider}> ...</Admin>);
Now the admin is secured: Authenticated users pass their credentials to the API.
If you have a custom REST client, don’t forget to add credentials yourself.
Restricting Access To Custom Pages
When you add custom pages, they are accessible to anonymous users by default. To make them accessible only to authenticated users, use theuseAuthenticated
hook in the custom page:
import{Admin,CustomRoutes,useAuthenticated}from'react-admin';import{Route}from'react-router-dom';constRestrictedPage=()=>{const{isPending}=useAuthenticated();// redirects to login if not authenticatedif(isPending)return<div>Checking auth...</div>;return(<div> ...</div>)};constAnonymousPage=()=>(<div> ...</div>);constApp=()=>(<AdminauthProvider={authProvider}><CustomRoutes><Routepath="/foo"element={<RestrictedPage/>}/><Routepath="/anonymous"element={<AnonymousPage/>}/></CustomRoutes></Admin>);
Alternatively, use the<Authenticated>
component to display its children only if the user is authenticated:
import{Admin,CustomRoutes,Authenticated}from'react-admin';import{Route}from'react-router-dom';constRestrictedPage=()=>(<Authenticated><div> ...</div></Authenticated>);constAnonymousPage=()=>(<div> ...</div>);constApp=()=>(<AdminauthProvider={authProvider}><CustomRoutes><Routepath="/restricted"element={<RestrictedPage/>}/><Routepath="/anonymous"element={<AnonymousPage/>}/></CustomRoutes></Admin>);
Disabling Anonymous Access
Securing custom pages one by one can be tedious. If your app will never accept anonymous access, you can force the app to wait forauthProvider.checkAuth()
to resolve before rendering the page layout by setting the<Admin requireAuth>
prop.
For example, the following app will require authentication to access all pages, including the/settings
and/profile
pages:
constApp=()=>(<AdmindataProvider={dataProvider}authProvider={authProvider}requireAuth><Resourcename="posts"{...posts}/><Resourcename="comments"{...comments}/><CustomRoutes><Routepath="/settings"element={<Settings/>}/><Routepath="/profile"element={<Profile/>}/></CustomRoutes></Admin>);
requireAuth
also hides the UI until the authentication check is complete, ensuring that no information (menu, resource names, etc.) is revealed to anonymous users.
requireAuth
doesn’t prevent users from accessing<CustomRoutes noLayout>
, as these routes are often used for public pages like the registration page or the password reset page.
constApp=()=>(<AdmindataProvider={dataProvider}authProvider={authProvider}requireAuth><CustomRoutesnoLayout>{/* These routes are public */}<Routepath="/register"element={<Register/>}/></CustomRoutes><CustomRoutes>{/* These routes are private */}<Routepath="/settings"element={<Settings/>}/><Routepath="/profile"element={<Profile/>}/></CustomRoutes></Admin>);
Allowing Anonymous Access
If you add anauthProvider
, react-admin restricts access to all pages declared in<Resource>
components. To allow anonymous access to some of these pages, set thedisableAuthentication
prop in the page component.
For example, to let anonymous users access the post list view:
constPostList=()=>(<ListdisableAuthentication> // ...</List>);constApp=()=>(<AdmindataProvider={dataProvider}authProvider={authProvider}><Resourcename="posts"list={PostList}/></Admin>);
disableAuthentication
is available on the following components and hooks:
<Create>
,<CreateBase>
,<CreateController>
anduseCreateController
<Edit>
,<EditBase>
,<EditController>
anduseEditController
<List>
,<ListBase>
,<ListController>
anduseListController
<Show>
,<ShowBase>
,<ShowController>
anduseShowController
Customizing The Login Component
Using anauthProvider
is enough to secure your app if authentication relies on a username and password. But for cases like using an email instead of a username, Single-Sign-On (SSO), or two-factor authentication, you can use a custom login page by setting the<Admin loginPage>
prop.
For example, to use an email field instead of a username field, use theLoginWithEmail
component:
import{Admin,LoginWithEmail}from'react-admin';importauthProviderfrom'./authProvider';constApp=()=>(<AdminloginPage={LoginWithEmail}authProvider={authProvider}> ...</Admin>);
The default login page component is theLogin
component, which delegates the rendering of the login form to its child, usually aLoginForm
component. This means you can create a custom login page by adding your own content to theLogin
component.
For instance, to add a “forgot password” link to the login page:
import{Box,Link}from'@mui/material';import{LinkasRouterLink}from'react-router-dom';import{Login,LoginForm}from'react-admin';constMyLogin=()=>(<Login><LoginForm/><BoxtextAlign="center"mb={1}><Linkcomponent={RouterLink}to="/forgot-password"> Forgot password?</Link></Box></Login>);
You can also customize the login form fields, by setting theLoginForm
children:
import{LinkasRouterLink}from'react-router-dom';import{Login,LoginForm,TextInput,PasswordInput,required}from'react-admin';constMyLogin=()=>(<Login><LoginForm><TextInputautoFocussource="email"label="Email"autoComplete="email"type="email"validate={required()}/><PasswordInputsource="password"label="Password"autoComplete="current-password"validate={required()}/></LoginForm></Login>);
By default, the login page displays a gradient background. To change it, use the default Login component and pass an image URL as thebackgroundImage
prop.
// in src/MyLoginPage.jsimport{Login}from'react-admin';constMyLoginPage=()=>(<LoginbackgroundImage="https://acme.com/img/background.png"/>);
You can also build your login page from scratch, leveraging theuseLogin
hook to handle the login form submission.
// in src/MyLoginPage.jsimport{useState}from'react';import{useLogin,useNotify,Notification}from'react-admin';constMyLoginPage=({theme})=>{const[email,setEmail]=useState('');const[password,setPassword]=useState('');constlogin=useLogin();constnotify=useNotify();consthandleSubmit=e=>{e.preventDefault();login({email,password}).catch(()=>notify('Invalid email or password'));};return(<formonSubmit={handleSubmit}><inputname="email"type="email"value={email}onChange={e=>setEmail(e.target.value)}/><inputname="password"type="password"value={password}onChange={e=>setPassword(e.target.value)}/></form>);};exportdefaultMyLoginPage;
Logging Out The User
Users can log out by clicking on the user menu in the AppBar. To allow log out from a custom button or under specific conditions, use theuseLogout
hook.
import{useLogout}from'react-admin';importButtonfrom'@mui/material/Button';constMyLogoutButton=()=>{constlogout=useLogout();consthandleClick=()=>logout();return<ButtononClick={handleClick}>Logout</Button>;};
Tip: By default, react-admin redirects to/login
after logout. This can be changed by passing a custom URL to thelogout()
function:
-const handleClick = () => logout();+const handleClick = () => logout('/custom-login');
Using External Authentication Providers
Instead of the built-in Login page, you can use an external authentication provider, like Auth0, Cognito, or any other OAuth-based service. These services require a callback URL to redirect users after login.
React-admin provides a default callback URL at/auth-callback
. This route calls theauthProvider.handleCallback
method on mount, which means it’s up to theauthProvider
to use the received params for authenticating future API calls.
For example, here’s a simple authProvider for Auth0:
import{Auth0Client}from'./Auth0Client';exportconstauthProvider={asynclogin(){/* This function will not be called */},asynccheckAuth(){constisAuthenticated=awaitAuth0Client.isAuthenticated();if(isAuthenticated){return;}// not authenticated: redirect the user to the Auth0 service,// where they will be redirected back to the app after loginAuth0Client.loginWithRedirect({authorizationParams:{redirect_uri:`${window.location.origin}/auth-callback`,},});},// A user logged successfully on the Auth0 service// and was redirected back to the /auth-callback route on the appasynchandleCallback(){constquery=window.location.search;if(query.includes('code=')&&query.includes('state=')){try{// get an access token based on the query paramatersawaitAuth0Client.handleRedirectCallback();return;}catch(error){console.log('error',error);throwerror;}}thrownewError('Failed to handle login callback.');},asynclogout(){constisAuthenticated=awaitclient.isAuthenticated();// need to check for this as react-admin calls logout in case checkAuth failedif(isAuthenticated){returnAuth0Client.logout({returnTo:window.location.origin,});}},...};
You can choose when to redirect users to the third-party authentication service, such as directly in theAuthProvider.checkAuth()
method or when they click a button on acustom login page.
Handling Refresh Tokens
Refresh tokens are crucial for maintaining secure sessions. To leverage them, decorate thedataProvider
and theauthProvider
to refresh authentication tokens as needed.
You can use theaddRefreshAuthToDataProvider
andaddRefreshAuthToAuthProvider
functions for this purpose:
// in src/refreshAuth.jsimport{getAuthTokensFromLocalStorage}from'./getAuthTokensFromLocalStorage';import{refreshAuthTokens}from'./refreshAuthTokens';exportconstrefreshAuth=()=>{const{accessToken,refreshToken}=getAuthTokensFromLocalStorage();if(accessToken.exp<Date.now().getTime()/1000){// This function will fetch the new tokens from the authentication service and update them in localStoragereturnrefreshAuthTokens(refreshToken);}returnPromise.resolve();}// in src/authProvider.jsimport{addRefreshAuthToAuthProvider}from'react-admin';import{refreshAuth}from'./refreshAuth';constmyAuthProvider={// ...AuthProvider methods};exportconstauthProvider=addRefreshAuthToAuthProvider(myAuthProvider,refreshAuth);// in src/dataProvider.jsimport{addRefreshAuthToDataProvider}from'react-admin';importsimpleRestProviderfrom'ra-data-simple-rest';import{refreshAuth}from'./refreshAuth';constbaseDataProvider=simpleRestProvider('http://path.to.my.api/');exportconstdataProvider=addRefreshAuthToDataProvider(baseDataProvider,refreshAuth);
Authorization
Access control and permissions allow you to restrict certain pages and features to specific users. React-admin provides powerful primitives for implementing authorization logic. For detailed guidance, check out theAuthorization documentation.