UNPKG

ra-core

Version:

Core components of react-admin, a frontend Framework for building admin applications on top of REST services, using ES6, React

149 lines (135 loc) 5.24 kB
import { useCallback } from 'react'; import useAuthProvider from './useAuthProvider'; import useLogout from './useLogout'; import { useNotify } from '../notification'; import { useNavigate } from 'react-router'; let timer; /** * Returns a callback used to call the authProvider.checkError() method * and an error from the dataProvider. If the authProvider rejects the call, * the hook logs the user out and shows a logged out notification. * * Used in the useDataProvider hook to check for access denied responses * (e.g. 401 or 403 responses) and trigger a logout. * * @see useLogout * @see useDataProvider * * @returns {Function} logoutIfAccessDenied callback * * @example * * import { useLogoutIfAccessDenied, useNotify, DataProviderContext } from 'react-admin'; * * const FetchRestrictedResource = () => { * const dataProvider = useContext(DataProviderContext); * const logoutIfAccessDenied = useLogoutIfAccessDenied(); * const notify = useNotify() * useEffect(() => { * dataProvider.getOne('secret', { id: 123 }) * .catch(error => { * logoutIfAccessDenied(error); * notify('server error', { type: 'error' }); * }) * }, []); * // ... * } */ const useLogoutIfAccessDenied = (): LogoutIfAccessDenied => { const authProvider = useAuthProvider(); const logout = useLogout(); const notify = useNotify(); const navigate = useNavigate(); const handleRedirect = useCallback( (url: string) => { if (url.startsWith('http')) { window.location.href = url; } else { navigate(url); } }, [navigate] ); const logoutIfAccessDenied = useCallback( (error?: any) => { if (!authProvider) { return logoutIfAccessDeniedWithoutProvider(); } return authProvider .checkError(error) .then(() => false) .catch(async e => { const logoutUser = e?.logoutUser ?? true; //manual debounce if (timer) { // side effects already triggered in this tick, exit return true; } timer = setTimeout(() => { timer = undefined; }, 0); const redirectTo = e && e.redirectTo != null ? e.redirectTo : error && error.redirectTo ? error.redirectTo : undefined; const shouldNotify = !( (e && e.message === false) || (error && error.message === false) || redirectTo?.startsWith('http') ); if (shouldNotify) { // notify only if not yet logged out authProvider .checkAuth({}) .then(() => { if (logoutUser) { notify( getErrorMessage( e, 'ra.notification.logged_out' ), { type: 'error' } ); } else { notify( getErrorMessage( e, 'ra.notification.not_authorized' ), { type: 'error' } ); } }) .catch(() => {}); } if (logoutUser) { logout({}, redirectTo); } else if (redirectTo) { handleRedirect(redirectTo); } return true; }); }, [authProvider, logout, notify, handleRedirect] ); return logoutIfAccessDenied; }; const logoutIfAccessDeniedWithoutProvider = () => Promise.resolve(false); /** * Call the authProvider.authError() method, using the error passed as argument. * If the authProvider rejects the call, logs the user out and shows a logged out notification. * * @param {Error} error An Error object (usually returned by the dataProvider) * * @return {Promise} Resolved to true if there was a logout, false otherwise */ type LogoutIfAccessDenied = (error?: any) => Promise<boolean>; const getErrorMessage = (error, defaultMessage) => typeof error === 'string' ? error : typeof error === 'undefined' || !error.message ? defaultMessage : error.message; export default useLogoutIfAccessDenied;