diff --git a/UPGRADE.md b/UPGRADE.md
index dc4e0a1a7d6..bcd89a0284c 100644
--- a/UPGRADE.md
+++ b/UPGRADE.md
@@ -965,3 +965,28 @@ const ExportButton = ({ sort, filter, maxResults = 1000, resource }) => {
);
};
```
+
+## The `authProvider` no longer receives the Location pathname in AUTH_GET_PERMISSIONS
+
+When calling the `authProvider` for permissions (with the `AUTH_GET_PERMISSIONS` verb), react-admin used to include the pathname as second parameter. That allowed you to return different permissions based on the page.
+
+We believe that permissions should not vary depending on where you are in the application ; it's up to components to decide to do something or not depending on permissions. So we've removed the pathname parameter from the calls - the `authProvider` doesn't receive it anymore.
+
+If you want to keep location-dependent permissions logic, red the current location from the `window` object direclty in your `authProvider`:
+
+```diff
+// in myauthProvider.js
+import { AUTH_LOGIN, AUTH_LOGOUT, AUTH_ERROR, AUTH_GET_PERMISSIONS } from 'react-admin';
+import decodeJwt from 'jwt-decode';
+
+export default (type, params) => {
+ // ...
+ if (type === AUTH_GET_PERMISSIONS) {
+- const { pathname } = params;
++ const pathname = window.location.pathname;
+ // pathname-dependent logic follows
+ // ...
+ }
+ return Promise.reject('Unknown method');
+};
+```
diff --git a/packages/ra-core/src/auth/useAuthState.ts b/packages/ra-core/src/auth/useAuthState.ts
index 115113a4725..e83509b5909 100644
--- a/packages/ra-core/src/auth/useAuthState.ts
+++ b/packages/ra-core/src/auth/useAuthState.ts
@@ -29,7 +29,7 @@ const emptyParams = {};
*
* @see useAuthenticated()
*
- * @param {Object} authParams Any params you want to pass to the authProvider
+ * @param {Object} params Any params you want to pass to the authProvider
*
* @returns The current auth check state. Destructure as { authenticated, error, loading, loaded }.
*
@@ -48,22 +48,22 @@ const emptyParams = {};
*
* );
*/
-const useAuthState = (authParams: any = emptyParams): State => {
+const useAuthState = (params: any = emptyParams): State => {
const [state, setState] = useSafeSetState({
loading: true,
loaded: false,
authenticated: true, // optimistic
});
- const checkAuth = useCheckAuth(authParams);
+ const checkAuth = useCheckAuth();
useEffect(() => {
- checkAuth(false)
+ checkAuth(params, false)
.then(() =>
setState({ loading: false, loaded: true, authenticated: true })
)
.catch(() =>
setState({ loading: false, loaded: true, authenticated: false })
);
- }, [checkAuth, setState]);
+ }, [checkAuth, params, setState]);
return state;
};
diff --git a/packages/ra-core/src/auth/useAuthenticated.spec.tsx b/packages/ra-core/src/auth/useAuthenticated.spec.tsx
index afabb44bb0f..d166010d8ef 100644
--- a/packages/ra-core/src/auth/useAuthenticated.spec.tsx
+++ b/packages/ra-core/src/auth/useAuthenticated.spec.tsx
@@ -24,9 +24,6 @@ describe('useAuthenticated', () => {
);
expect(authProvider).toBeCalledTimes(1);
expect(authProvider.mock.calls[0][0]).toBe('AUTH_CHECK');
- const payload = authProvider.mock.calls[0][1] as any;
- expect(payload.afterLoginUrl).toBe('/');
- expect(payload.loginUrl).toBe('/login');
expect(dispatch).toHaveBeenCalledTimes(0);
});
diff --git a/packages/ra-core/src/auth/useAuthenticated.tsx b/packages/ra-core/src/auth/useAuthenticated.tsx
index 4f57ac6865d..dd6f781acb8 100644
--- a/packages/ra-core/src/auth/useAuthenticated.tsx
+++ b/packages/ra-core/src/auth/useAuthenticated.tsx
@@ -1,6 +1,8 @@
import { useEffect } from 'react';
import useCheckAuth from './useCheckAuth';
+const emptyParams = {};
+
/**
* Restrict access to authenticated users.
* Redirect anonymous users to the login page.
@@ -26,9 +28,9 @@ import useCheckAuth from './useCheckAuth';
*
* );
*/
-export default authParams => {
- const checkAuth = useCheckAuth(authParams);
+export default (params: any = emptyParams) => {
+ const checkAuth = useCheckAuth();
useEffect(() => {
- checkAuth().catch(() => {});
- }, [checkAuth]);
+ checkAuth(params).catch(() => {});
+ }, [checkAuth, params]);
};
diff --git a/packages/ra-core/src/auth/useCheckAuth.ts b/packages/ra-core/src/auth/useCheckAuth.ts
index 7263900a876..ea20f87b505 100644
--- a/packages/ra-core/src/auth/useCheckAuth.ts
+++ b/packages/ra-core/src/auth/useCheckAuth.ts
@@ -16,8 +16,6 @@ import useNotify from '../sideEffect/useNotify';
* @see useAuthenticated
* @see useAuthState
*
- * @param {Object} authParams Any params you want to pass to the authProvider
- *
* @returns {Function} checkAuth callback
*
* @example
@@ -33,24 +31,28 @@ import useNotify from '../sideEffect/useNotify';
* } // tip: use useAuthenticated() hook instead
*
* const MyPage = () => {
- * const checkAuth = usecheckAuth();
+ * const checkAuth = useCheckAuth();
* const [authenticated, setAuthenticated] = useState(true); // optimistic auth
* useEffect(() => {
- * checkAuth(false)
+ * checkAuth({}, false)
* .then() => setAuthenticated(true))
* .catch(() => setAuthenticated(false));
* }, []);
* return authenticated ? : ;
* } // tip: use useAuthState() hook instead
*/
-const useCheckAuth = (authParams: any = defaultAuthParams): CheckAuth => {
+const useCheckAuth = (): CheckAuth => {
const authProvider = useAuthProvider();
const notify = useNotify();
- const logout = useLogout(authParams);
+ const logout = useLogout();
const checkAuth = useCallback(
- (logoutOnFailure = true, redirectTo = authParams.loginUrl) =>
- authProvider(AUTH_CHECK, authParams).catch(error => {
+ (
+ params: any = {},
+ logoutOnFailure = true,
+ redirectTo = defaultAuthParams.loginUrl
+ ) =>
+ authProvider(AUTH_CHECK, params).catch(error => {
if (logoutOnFailure) {
logout(redirectTo);
notify(
@@ -60,24 +62,26 @@ const useCheckAuth = (authParams: any = defaultAuthParams): CheckAuth => {
}
throw error;
}),
- [authParams, authProvider, logout, notify]
+ [authProvider, logout, notify]
);
return authProvider ? checkAuth : checkAuthWithoutAuthProvider;
};
-const checkAuthWithoutAuthProvider = (_, __) => Promise.resolve();
+const checkAuthWithoutAuthProvider = () => Promise.resolve();
/**
* Check if the current user is authenticated by calling the authProvider AUTH_CHECK verb.
* Logs the user out on failure.
*
+ * @param {Object} params The parameters to pass to the authProvider
* @param {boolean} logoutOnFailure Whether the user should be logged out if the authProvider fails to authenticatde them. True by default.
* @param {string} redirectTo The login form url. Defaults to '/login'
*
* @return {Promise} Resolved to the authProvider response if the user passes the check, or rejected with an error otherwise
*/
type CheckAuth = (
+ params?: any,
logoutOnFailure?: boolean,
redirectTo?: string
) => Promise;
diff --git a/packages/ra-core/src/auth/useGetPermissions.ts b/packages/ra-core/src/auth/useGetPermissions.ts
index 7512cd25c97..1070df66c22 100644
--- a/packages/ra-core/src/auth/useGetPermissions.ts
+++ b/packages/ra-core/src/auth/useGetPermissions.ts
@@ -1,15 +1,12 @@
import { useCallback } from 'react';
-import { useStore } from 'react-redux';
-import { Location } from 'history';
-import useAuthProvider, { defaultAuthParams } from './useAuthProvider';
+import useAuthProvider from './useAuthProvider';
import { AUTH_GET_PERMISSIONS } from './types';
/**
* Get a callback for calling the authProvider with the AUTH_GET_PERMISSIONS verb.
*
* @see useAuthProvider
- * @param {Object} authParams Any params you want to pass to the authProvider
*
* @returns {Function} getPermissions callback
*
@@ -37,31 +34,11 @@ import { AUTH_GET_PERMISSIONS } from './types';
* );
* }
*/
-const useGetPermissions = (
- authParams: any = defaultAuthParams
-): GetPermissions => {
+const useGetPermissions = (): GetPermissions => {
const authProvider = useAuthProvider();
- /**
- * We need the current location to pass to the authProvider for GET_PERMISSIONS.
- *
- * But if we used useSelector to read it from the store, the getPermissions function
- * would be rebuilt each time the user changes location. Consequently, that
- * would force a rerender of the enclosing component upon navigation.
- *
- * To avoid that, we don't subscribe to the store using useSelector;
- * instead, we get a pointer to the store, and determine the location only
- * after the getPermissions function was called.
- */
-
- const store = useStore();
-
const getPermissions = useCallback(
- (location?: Location) =>
- authProvider(AUTH_GET_PERMISSIONS, {
- location: location || store.getState().router.location,
- ...authParams,
- }),
- [authParams, authProvider, store]
+ (params: any = {}) => authProvider(AUTH_GET_PERMISSIONS, params),
+ [authProvider]
);
return authProvider ? getPermissions : getPermissionsWithoutProvider;
@@ -72,10 +49,10 @@ const getPermissionsWithoutProvider = () => Promise.resolve([]);
/**
* Ask the permissions to the authProvider using the AUTH_GET_PERMISSIONS verb
*
- * @param {Location} location the current location from history (optional)
+ * @param {Object} params The parameters to pass to the authProvider
*
* @return {Promise} The authProvider response
*/
-type GetPermissions = (location?: Location) => Promise;
+type GetPermissions = (params?: any) => Promise;
export default useGetPermissions;
diff --git a/packages/ra-core/src/auth/useLogin.ts b/packages/ra-core/src/auth/useLogin.ts
index b4296ae035f..d0d907eca6f 100644
--- a/packages/ra-core/src/auth/useLogin.ts
+++ b/packages/ra-core/src/auth/useLogin.ts
@@ -11,7 +11,6 @@ import { ReduxState } from '../types';
* and redirect to the previous authenticated page (or the home page) on success.
*
* @see useAuthProvider
- * @param {Object} authParams Any params you want to pass to the authProvider
*
* @returns {Function} login callback
*
@@ -30,7 +29,7 @@ import { ReduxState } from '../types';
* return ;
* }
*/
-const useLogin = (authParams: any = defaultAuthParams): Login => {
+const useLogin = (): Login => {
const authProvider = useAuthProvider();
const currentLocation = useSelector(
(state: ReduxState) => state.router.location
@@ -40,20 +39,20 @@ const useLogin = (authParams: any = defaultAuthParams): Login => {
const dispatch = useDispatch();
const login = useCallback(
- (params, pathName = authParams.afterLoginUrl) =>
- authProvider(AUTH_LOGIN, { ...params, ...authParams }).then(ret => {
+ (params: any = {}, pathName = defaultAuthParams.afterLoginUrl) =>
+ authProvider(AUTH_LOGIN, params).then(ret => {
dispatch(push(nextPathName || pathName));
return ret;
}),
- [authParams, authProvider, dispatch, nextPathName]
+ [authProvider, dispatch, nextPathName]
);
const loginWithoutProvider = useCallback(
(_, __) => {
- dispatch(push(authParams.afterLoginUrl));
+ dispatch(push(defaultAuthParams.afterLoginUrl));
return Promise.resolve();
},
- [authParams.afterLoginUrl, dispatch]
+ [dispatch]
);
return authProvider ? login : loginWithoutProvider;
diff --git a/packages/ra-core/src/auth/useLogout.ts b/packages/ra-core/src/auth/useLogout.ts
index 1e32e9215ad..71f6cd34bf1 100644
--- a/packages/ra-core/src/auth/useLogout.ts
+++ b/packages/ra-core/src/auth/useLogout.ts
@@ -11,7 +11,6 @@ import { clearState } from '../actions/clearActions';
* redirect to the login page, and clear the Redux state.
*
* @see useAuthProvider
- * @param {Object} authParams Any params you want to pass to the authProvider
*
* @returns {Function} logout callback
*
@@ -25,7 +24,7 @@ import { clearState } from '../actions/clearActions';
* return ;
* }
*/
-const useLogout = (authParams: any = defaultAuthParams): Logout => {
+const useLogout = (): Logout => {
const authProvider = useAuthProvider();
/**
* We need the current location to pass in the router state
@@ -44,24 +43,22 @@ const useLogout = (authParams: any = defaultAuthParams): Logout => {
const dispatch = useDispatch();
const logout = useCallback(
- (redirectTo = authParams.loginUrl) =>
- authProvider(AUTH_LOGOUT, authParams).then(
- redirectToFromProvider => {
- dispatch(clearState());
- const currentLocation = store.getState().router.location;
- dispatch(
- push({
- pathname: redirectToFromProvider || redirectTo,
- state: {
- nextPathname:
- currentLocation && currentLocation.pathname,
- },
- })
- );
- return redirectToFromProvider;
- }
- ),
- [authParams, authProvider, store, dispatch]
+ (params = {}, redirectTo = defaultAuthParams.loginUrl) =>
+ authProvider(AUTH_LOGOUT, params).then(redirectToFromProvider => {
+ dispatch(clearState());
+ const currentLocation = store.getState().router.location;
+ dispatch(
+ push({
+ pathname: redirectToFromProvider || redirectTo,
+ state: {
+ nextPathname:
+ currentLocation && currentLocation.pathname,
+ },
+ })
+ );
+ return redirectToFromProvider;
+ }),
+ [authProvider, store, dispatch]
);
const logoutWithoutProvider = useCallback(
@@ -69,7 +66,7 @@ const useLogout = (authParams: any = defaultAuthParams): Logout => {
const currentLocation = store.getState().router.location;
dispatch(
push({
- pathname: authParams.loginUrl,
+ pathname: defaultAuthParams.loginUrl,
state: {
nextPathname:
currentLocation && currentLocation.pathname,
@@ -79,7 +76,7 @@ const useLogout = (authParams: any = defaultAuthParams): Logout => {
dispatch(clearState());
return Promise.resolve();
},
- [authParams.loginUrl, store, dispatch]
+ [store, dispatch]
);
return authProvider ? logout : logoutWithoutProvider;
@@ -89,10 +86,11 @@ const useLogout = (authParams: any = defaultAuthParams): Logout => {
* Log the current user out by calling the authProvider AUTH_LOGOUT verb,
* and redirect them to the login screen.
*
+ * @param {Object} params The parameters to pass to the authProvider
* @param {string} redirectTo The path name to redirect the user to (optional, defaults to login)
*
* @return {Promise} The authProvider response
*/
-type Logout = (redirectTo?: string) => Promise;
+type Logout = (params?: any, redirectTo?: string) => Promise;
export default useLogout;
diff --git a/packages/ra-core/src/auth/usePermissions.ts b/packages/ra-core/src/auth/usePermissions.ts
index 14705c01f74..00253bad030 100644
--- a/packages/ra-core/src/auth/usePermissions.ts
+++ b/packages/ra-core/src/auth/usePermissions.ts
@@ -26,7 +26,7 @@ const emptyParams = {};
*
* Useful to enable features based on user permissions
*
- * @param {Object} authParams Any params you want to pass to the authProvider
+ * @param {Object} params Any params you want to pass to the authProvider
*
* @returns The current auth check state. Destructure as { permissions, error, loading, loaded }.
*
@@ -42,14 +42,14 @@ const emptyParams = {};
* }
* };
*/
-const usePermissions = (authParams = emptyParams) => {
+const usePermissions = (params = emptyParams) => {
const [state, setState] = useSafeSetState({
loading: true,
loaded: false,
});
- const getPermissions = useGetPermissions(authParams);
+ const getPermissions = useGetPermissions();
useEffect(() => {
- getPermissions()
+ getPermissions(params)
.then(permissions => {
setState({ loading: false, loaded: true, permissions });
})
@@ -60,7 +60,7 @@ const usePermissions = (authParams = emptyParams) => {
error,
});
});
- }, [authParams, getPermissions, setState]);
+ }, [getPermissions, params, setState]);
return state;
};