A frontend Framework for building admin applications on top of REST services, using ES6, React and Material UI
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
React Admin provides a flexible authentication and authorization system that handles login/logout flows, session management, and permission-based access control. The system is backend-agnostic and works with any authentication service through auth providers.
The auth provider defines how your application handles authentication and authorization.
import { AuthProvider } from 'react-admin';
interface AuthProvider {
login: (params: any) => Promise<any>;
logout: (params?: any) => Promise<void>;
checkAuth: (params?: any) => Promise<void>;
checkError: (error: any) => Promise<void>;
getPermissions: (params?: any) => Promise<any>;
getIdentity?: () => Promise<UserIdentity>;
handleCallback?: () => Promise<AuthRedirectResult | void | any>;
}
interface UserIdentity {
id: string | number;
fullName?: string;
avatar?: string;
[key: string]: any;
}
interface AuthRedirectResult {
redirectTo?: string | false;
}Handles user authentication. Called when the login form is submitted.
Parameters:
params: Login credentials (usually { username, password })Returns: Promise that resolves on successful login, rejects on failure
Handles user logout and cleanup.
Parameters:
params: Optional logout parametersReturns: Promise that resolves when logout is complete
Checks if the user is authenticated. Called on app startup and route changes.
Parameters:
params: Optional parameters for auth checkReturns: Promise that resolves if authenticated, rejects if not
Handles authentication errors from API responses.
Parameters:
error: Error object from failed API requestReturns: Promise that resolves to continue, rejects to logout user
Retrieves user permissions for authorization.
Parameters:
params: Optional parametersReturns: Promise resolving to permissions object
Gets user identity information for display.
Returns: Promise resolving to UserIdentity object
import { AuthProvider } from 'react-admin';
const authProvider: AuthProvider = {
login: async ({ username, password }) => {
const request = new Request('/api/login', {
method: 'POST',
body: JSON.stringify({ username, password }),
headers: new Headers({ 'Content-Type': 'application/json' }),
});
const response = await fetch(request);
if (response.status < 200 || response.status >= 300) {
throw new Error(response.statusText);
}
const { token } = await response.json();
localStorage.setItem('token', token);
},
logout: async () => {
localStorage.removeItem('token');
},
checkAuth: async () => {
const token = localStorage.getItem('token');
if (!token) {
throw new Error('No token found');
}
},
checkError: async (error) => {
const status = error.status;
if (status === 401 || status === 403) {
localStorage.removeItem('token');
throw error;
}
},
getPermissions: async () => {
const token = localStorage.getItem('token');
if (!token) return null;
// Decode token or make API call to get permissions
return { role: 'admin' };
},
getIdentity: async () => {
const token = localStorage.getItem('token');
if (!token) throw new Error('No token');
const response = await fetch('/api/me', {
headers: { Authorization: `Bearer ${token}` }
});
const user = await response.json();
return {
id: user.id,
fullName: user.name,
avatar: user.avatar
};
}
};Access the auth provider instance directly.
import { useAuthProvider } from 'react-admin';
const useAuthProvider: () => AuthProvider | undefined;Get the login function for programmatic authentication.
import { useLogin } from 'react-admin';
type LoginFunction = (params: any, pathName?: string) => Promise<any>;
const useLogin: () => LoginFunction;import { useLogin, useNotify } from 'react-admin';
const CustomLoginButton = () => {
const login = useLogin();
const notify = useNotify();
const handleLogin = async () => {
try {
await login({ username: 'admin', password: 'password' });
notify('Login successful');
} catch (error) {
notify('Login failed', { type: 'error' });
}
};
return <button onClick={handleLogin}>Login</button>;
};Get the logout function for programmatic logout.
import { useLogout } from 'react-admin';
type LogoutFunction = (params?: any, redirectTo?: string, redirectToCurrentLocationAfterLogin?: boolean) => Promise<any>;
const useLogout: () => LogoutFunction;import { useLogout } from 'react-admin';
const CustomLogoutButton = () => {
const logout = useLogout();
const handleLogout = () => {
logout({}, '/login');
};
return <button onClick={handleLogout}>Logout</button>;
};Get the current authentication state.
import { useAuthState } from 'react-admin';
interface AuthStateResult {
isLoading: boolean;
authenticated?: boolean;
}
const useAuthState: () => AuthStateResult;Hook that throws an error if user is not authenticated, useful for protecting components.
import { useAuthenticated } from 'react-admin';
const useAuthenticated: () => void;import { useAuthenticated } from 'react-admin';
const ProtectedComponent = () => {
useAuthenticated(); // Redirects to login if not authenticated
return <div>This content is only visible to authenticated users</div>;
};Perform authentication checks programmatically.
import { useCheckAuth } from 'react-admin';
const useCheckAuth: () => (params?: any, logoutOnFailure?: boolean, redirectTo?: string) => Promise<any>;Get the current user's identity information.
import { useGetIdentity } from 'react-admin';
const useGetIdentity: () => {
data: UserIdentity | undefined;
isLoading: boolean;
error: any;
refetch: () => void;
};import { useGetIdentity } from 'react-admin';
const UserProfile = () => {
const { data: identity, isLoading } = useGetIdentity();
if (isLoading) return <div>Loading...</div>;
if (!identity) return <div>Not logged in</div>;
return (
<div>
<img src={identity.avatar} alt="Avatar" />
<h2>Welcome, {identity.fullName}</h2>
</div>
);
};Get the current user's permissions.
import { usePermissions } from 'react-admin';
const usePermissions: () => {
permissions: any;
isLoading: boolean;
error: any;
refetch: () => void;
};import { usePermissions } from 'react-admin';
const AdminPanel = () => {
const { permissions, isLoading } = usePermissions();
if (isLoading) return <div>Loading permissions...</div>;
const canDeletePosts = permissions && permissions.posts && permissions.posts.delete;
return (
<div>
<h2>Admin Panel</h2>
{canDeletePosts && <button>Delete Post</button>}
</div>
);
};Optimized version of usePermissions that doesn't trigger re-renders.
import { usePermissionsOptimized } from 'react-admin';
const usePermissionsOptimized: () => any;Component that renders children only if user is authenticated.
import { Authenticated } from 'react-admin';
interface AuthenticatedProps {
children: React.ReactNode;
loading?: React.ComponentType;
requireAuth?: boolean;
}
const Authenticated: React.FC<AuthenticatedProps>;import { Authenticated } from 'react-admin';
const App = () => (
<div>
<h1>Public Content</h1>
<Authenticated>
<div>This content requires authentication</div>
</Authenticated>
</div>
);Higher-order component for conditional rendering based on permissions.
import { WithPermissions } from 'react-admin';
interface WithPermissionsProps {
authParams?: any;
component?: React.ComponentType;
render?: (params: { permissions: any }) => React.ReactElement;
children?: (params: { permissions: any }) => React.ReactElement;
[key: string]: any;
}
const WithPermissions: React.FC<WithPermissionsProps>;import { WithPermissions } from 'react-admin';
const ConditionalButton = () => (
<WithPermissions
render={({ permissions }) =>
permissions && permissions.canEdit ?
<button>Edit</button> :
<span>Read-only</span>
}
/>
);Default login page component.
import { Login } from 'react-admin';
interface LoginProps {
backgroundImage?: string;
children?: React.ReactNode;
className?: string;
sx?: any;
}
const Login: React.FC<LoginProps>;Login form component that can be used in custom login pages.
import { LoginForm } from 'react-admin';
interface LoginFormProps {
redirectTo?: string;
children?: React.ReactNode;
className?: string;
sx?: any;
}
const LoginForm: React.FC<LoginFormProps>;import { Login, LoginForm } from 'react-admin';
const CustomLoginPage = () => (
<Login backgroundImage="/login-background.jpg">
<LoginForm>
<div style={{ marginTop: '1em' }}>
<a href="/forgot-password">Forgot password?</a>
</div>
</LoginForm>
</Login>
);Logout component for triggering logout.
import { Logout } from 'react-admin';
interface LogoutProps {
className?: string;
redirectTo?: string;
icon?: React.ReactElement;
sx?: any;
}
const Logout: React.FC<LogoutProps>;Handle OAuth authentication callbacks.
import { useHandleAuthCallback } from 'react-admin';
const useHandleAuthCallback: () => {
data: AuthRedirectResult | undefined;
isLoading: boolean;
error: any;
};import { useHandleAuthCallback } from 'react-admin';
import { useEffect } from 'react';
const AuthCallback = () => {
const { data, isLoading, error } = useHandleAuthCallback();
useEffect(() => {
if (data && data.redirectTo) {
window.location.href = data.redirectTo;
}
}, [data]);
if (isLoading) return <div>Processing authentication...</div>;
if (error) return <div>Authentication error</div>;
return <div>Authentication successful</div>;
};Automatically logout users when access is denied.
import { useLogoutIfAccessDenied } from 'react-admin';
const useLogoutIfAccessDenied: () => (error?: any) => Promise<boolean>;import { useLogoutIfAccessDenied, useDataProvider } from 'react-admin';
const SecureComponent = () => {
const dataProvider = useDataProvider();
const logoutIfAccessDenied = useLogoutIfAccessDenied();
const fetchSecureData = async () => {
try {
await dataProvider.getList('secure-resource', {});
} catch (error) {
// Automatically logout if 401/403
const shouldLogout = await logoutIfAccessDenied(error);
if (!shouldLogout) {
// Handle other errors
console.error('Fetch error:', error);
}
}
};
return <button onClick={fetchSecureData}>Load Secure Data</button>;
};import { AuthProvider } from 'react-admin';
import { jwtDecode } from 'jwt-decode';
const jwtAuthProvider: AuthProvider = {
login: async ({ username, password }) => {
const request = new Request('/api/auth/login', {
method: 'POST',
body: JSON.stringify({ username, password }),
headers: new Headers({ 'Content-Type': 'application/json' }),
});
const response = await fetch(request);
if (response.status < 200 || response.status >= 300) {
throw new Error(response.statusText);
}
const { token, refreshToken } = await response.json();
localStorage.setItem('token', token);
localStorage.setItem('refreshToken', refreshToken);
return { redirectTo: '/dashboard' };
},
logout: async () => {
const token = localStorage.getItem('token');
if (token) {
// Notify server of logout
await fetch('/api/auth/logout', {
method: 'POST',
headers: { Authorization: `Bearer ${token}` }
});
}
localStorage.removeItem('token');
localStorage.removeItem('refreshToken');
return { redirectTo: '/login' };
},
checkAuth: async () => {
const token = localStorage.getItem('token');
if (!token) {
throw new Error('No token found');
}
try {
const decodedToken = jwtDecode(token);
if (decodedToken.exp * 1000 < Date.now()) {
// Token expired, try to refresh
await refreshToken();
}
} catch (error) {
localStorage.removeItem('token');
throw new Error('Invalid token');
}
},
checkError: async (error) => {
const status = error.status;
if (status === 401 || status === 403) {
localStorage.removeItem('token');
localStorage.removeItem('refreshToken');
throw error;
}
},
getPermissions: async () => {
const token = localStorage.getItem('token');
if (!token) return null;
try {
const decodedToken = jwtDecode(token);
return decodedToken.permissions;
} catch (error) {
return null;
}
},
getIdentity: async () => {
const token = localStorage.getItem('token');
if (!token) throw new Error('No token');
const decodedToken = jwtDecode(token);
return {
id: decodedToken.sub,
fullName: decodedToken.name,
avatar: decodedToken.avatar
};
}
};
const refreshToken = async () => {
const refreshToken = localStorage.getItem('refreshToken');
if (!refreshToken) throw new Error('No refresh token');
const response = await fetch('/api/auth/refresh', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${refreshToken}`
}
});
if (!response.ok) throw new Error('Token refresh failed');
const { token } = await response.json();
localStorage.setItem('token', token);
};import { usePermissions, WithPermissions } from 'react-admin';
// Permission-based component rendering
const AdminOnlyButton = () => (
<WithPermissions
render={({ permissions }) =>
permissions?.role === 'admin' ?
<button>Admin Action</button> :
null
}
/>
);
// Hook-based permission checking
const useHasPermission = (resource: string, action: string) => {
const { permissions } = usePermissions();
return permissions &&
permissions[resource] &&
permissions[resource][action];
};
const PostActions = () => {
const canEdit = useHasPermission('posts', 'edit');
const canDelete = useHasPermission('posts', 'delete');
return (
<div>
{canEdit && <button>Edit</button>}
{canDelete && <button>Delete</button>}
</div>
);
};React Admin's authentication system provides flexible, secure user management that adapts to various backend authentication strategies while maintaining excellent user experience through optimistic updates and intelligent session management.
Install with Tessl CLI
npx tessl i tessl/npm-react-admin