CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-admin

A frontend Framework for building admin applications on top of REST services, using ES6, React and Material UI

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

auth.mddocs/

Authentication & Authorization

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.

Auth Provider Interface

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;
}

Auth Provider Methods

login

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

logout

Handles user logout and cleanup.

Parameters:

  • params: Optional logout parameters

Returns: Promise that resolves when logout is complete

checkAuth

Checks if the user is authenticated. Called on app startup and route changes.

Parameters:

  • params: Optional parameters for auth check

Returns: Promise that resolves if authenticated, rejects if not

checkError

Handles authentication errors from API responses.

Parameters:

  • error: Error object from failed API request

Returns: Promise that resolves to continue, rejects to logout user

getPermissions

Retrieves user permissions for authorization.

Parameters:

  • params: Optional parameters

Returns: Promise resolving to permissions object

getIdentity

Gets user identity information for display.

Returns: Promise resolving to UserIdentity object

Basic Auth Provider Example

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
    };
  }
};

Authentication Hooks

useAuthProvider

Access the auth provider instance directly.

import { useAuthProvider } from 'react-admin';

const useAuthProvider: () => AuthProvider | undefined;

useLogin

Get the login function for programmatic authentication.

import { useLogin } from 'react-admin';

type LoginFunction = (params: any, pathName?: string) => Promise<any>;

const useLogin: () => LoginFunction;

Usage Example

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>;
};

useLogout

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;

Usage Example

import { useLogout } from 'react-admin';

const CustomLogoutButton = () => {
  const logout = useLogout();
  
  const handleLogout = () => {
    logout({}, '/login');
  };
  
  return <button onClick={handleLogout}>Logout</button>;
};

useAuthState

Get the current authentication state.

import { useAuthState } from 'react-admin';

interface AuthStateResult {
  isLoading: boolean;
  authenticated?: boolean;
}

const useAuthState: () => AuthStateResult;

useAuthenticated

Hook that throws an error if user is not authenticated, useful for protecting components.

import { useAuthenticated } from 'react-admin';

const useAuthenticated: () => void;

Usage Example

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>;
};

useCheckAuth

Perform authentication checks programmatically.

import { useCheckAuth } from 'react-admin';

const useCheckAuth: () => (params?: any, logoutOnFailure?: boolean, redirectTo?: string) => Promise<any>;

useGetIdentity

Get the current user's identity information.

import { useGetIdentity } from 'react-admin';

const useGetIdentity: () => {
  data: UserIdentity | undefined;
  isLoading: boolean;
  error: any;
  refetch: () => void;
};

Usage Example

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>
  );
};

Authorization Hooks

usePermissions

Get the current user's permissions.

import { usePermissions } from 'react-admin';

const usePermissions: () => {
  permissions: any;
  isLoading: boolean;
  error: any;
  refetch: () => void;
};

Usage Example

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>
  );
};

usePermissionsOptimized

Optimized version of usePermissions that doesn't trigger re-renders.

import { usePermissionsOptimized } from 'react-admin';

const usePermissionsOptimized: () => any;

Authentication Components

Authenticated

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>;

Usage Example

import { Authenticated } from 'react-admin';

const App = () => (
  <div>
    <h1>Public Content</h1>
    <Authenticated>
      <div>This content requires authentication</div>
    </Authenticated>
  </div>
);

WithPermissions

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>;

Usage Example

import { WithPermissions } from 'react-admin';

const ConditionalButton = () => (
  <WithPermissions
    render={({ permissions }) => 
      permissions && permissions.canEdit ? 
        <button>Edit</button> : 
        <span>Read-only</span>
    }
  />
);

Login Components

Login

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>;

LoginForm

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>;

Custom Login Page Example

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

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>;

OAuth and External Auth

useHandleAuthCallback

Handle OAuth authentication callbacks.

import { useHandleAuthCallback } from 'react-admin';

const useHandleAuthCallback: () => {
  data: AuthRedirectResult | undefined;
  isLoading: boolean;
  error: any;
};

OAuth Example

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>;
};

Error Handling

useLogoutIfAccessDenied

Automatically logout users when access is denied.

import { useLogoutIfAccessDenied } from 'react-admin';

const useLogoutIfAccessDenied: () => (error?: any) => Promise<boolean>;

Usage Example

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>;
};

Advanced Authentication Examples

JWT Auth Provider

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);
};

Role-Based Access Control

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

docs

admin-core.md

advanced.md

auth.md

data-management.md

detail-views.md

forms-inputs.md

i18n.md

index.md

layout-navigation.md

lists-data-display.md

ui-components.md

tile.json