From 8d2c73b579fafb980ad8d42d2d5877392bb5cb6e Mon Sep 17 00:00:00 2001 From: Gildas Garcia <1122076+djhi@users.noreply.github.com> Date: Fri, 21 Jul 2023 10:34:13 +0200 Subject: [PATCH 1/2] Call checkError when getPermissions fails --- docs/AuthProviderWriting.md | 4 ++-- .../ra-core/src/auth/usePermissions.spec.tsx | 23 +++++++++++++++++-- packages/ra-core/src/auth/usePermissions.ts | 9 +++++++- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/docs/AuthProviderWriting.md b/docs/AuthProviderWriting.md index 7512d3b33e9..6081538c4ad 100644 --- a/docs/AuthProviderWriting.md +++ b/docs/AuthProviderWriting.md @@ -157,7 +157,7 @@ If the login fails, `authProvider.login()` should return a rejected Promise with When the user credentials are missing or become invalid, a secure API usually answers to the `dataProvider` with an HTTP error code 401 or 403. -Fortunately, each time the `dataProvider` returns an error, react-admin calls the `authProvider.checkError()` method. If it returns a rejected promise, react-admin calls the `authProvider.logout()` method immediately, and asks the user to log in again. +Fortunately, each time the `dataProvider` or the `authProvider.getPermissions` returns an error, react-admin calls the `authProvider.checkError()` method. If it returns a rejected promise, react-admin calls the `authProvider.logout()` method immediately, and asks the user to log in again. So it's up to you to decide which HTTP status codes should let the user continue (by returning a resolved promise) or log them out (by returning a rejected promise). @@ -502,5 +502,5 @@ When the auth backend returns an error, the Auth Provider should return a reject | `logout` | Auth backend failed to log the user out | `void` | | `getIdentity` | Auth backend failed to return identity | `Object` free format - returned as `error` when `useGetIdentity()` is called | | `handleCallback` | Failed to authenticate users after redirection | `void | { redirectTo?: string, logoutOnFailure?: boolean, message?: string }` | -| `getPermissions` | Auth backend failed to return permissions | `Object` free format - returned as `error` when `usePermissions()` is called | +| `getPermissions` | Auth backend failed to return permissions | `Object` free format - returned as `error` when `usePermissions()` is called. The error will be passed to `checkError` | diff --git a/packages/ra-core/src/auth/usePermissions.spec.tsx b/packages/ra-core/src/auth/usePermissions.spec.tsx index 32378343dd0..a3df64a2fee 100644 --- a/packages/ra-core/src/auth/usePermissions.spec.tsx +++ b/packages/ra-core/src/auth/usePermissions.spec.tsx @@ -62,12 +62,12 @@ describe('usePermissions', () => { }); }); - it('should return an error after a tick if the auth call fails', async () => { + it('should return an error after a tick if the auth.getPermissions call fails and checkError resolves', async () => { const authProvider = { login: () => Promise.reject('bad method'), logout: () => Promise.reject('bad method'), checkAuth: () => Promise.reject('bad method'), - checkError: () => Promise.reject('bad method'), + checkError: () => Promise.resolve(), getPermissions: () => Promise.reject('not good'), }; render( @@ -80,4 +80,23 @@ describe('usePermissions', () => { expect(screen.queryByText('ERROR')).not.toBeNull(); }); }); + + it('should call logout when the auth.getPermissions call fails and checkError rejects', async () => { + const authProvider = { + login: () => Promise.reject('bad method'), + logout: jest.fn(() => Promise.resolve()), + checkAuth: () => Promise.reject('bad method'), + checkError: () => Promise.reject(), + getPermissions: () => Promise.reject('not good'), + }; + render( + + {stateInpector} + + ); + await waitFor(() => { + expect(screen.queryByText('LOADING')).toBeNull(); + }); + expect(authProvider.logout).toHaveBeenCalled(); + }); }); diff --git a/packages/ra-core/src/auth/usePermissions.ts b/packages/ra-core/src/auth/usePermissions.ts index 04e955a7a18..8c450449a5a 100644 --- a/packages/ra-core/src/auth/usePermissions.ts +++ b/packages/ra-core/src/auth/usePermissions.ts @@ -1,6 +1,7 @@ import { useMemo } from 'react'; import { useQuery, UseQueryOptions } from 'react-query'; import useAuthProvider from './useAuthProvider'; +import useLogoutIfAccessDenied from './useLogoutIfAccessDenied'; const emptyParams = {}; @@ -41,13 +42,19 @@ const usePermissions = ( } ) => { const authProvider = useAuthProvider(); + const logoutIfAccessDenied = useLogoutIfAccessDenied(); const result = useQuery( ['auth', 'getPermissions', params], authProvider ? () => authProvider.getPermissions(params) : async () => [], - queryParams + { + ...queryParams, + onError: error => { + logoutIfAccessDenied(error); + }, + } ); return useMemo( From 082469b8bab9ed79598f23ab4da641554c471951 Mon Sep 17 00:00:00 2001 From: Gildas Garcia <1122076+djhi@users.noreply.github.com> Date: Fri, 21 Jul 2023 11:00:42 +0200 Subject: [PATCH 2/2] Fix breaking change --- packages/ra-core/src/auth/usePermissions.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/ra-core/src/auth/usePermissions.ts b/packages/ra-core/src/auth/usePermissions.ts index 8c450449a5a..02328540413 100644 --- a/packages/ra-core/src/auth/usePermissions.ts +++ b/packages/ra-core/src/auth/usePermissions.ts @@ -50,10 +50,13 @@ const usePermissions = ( ? () => authProvider.getPermissions(params) : async () => [], { - ...queryParams, onError: error => { + if (process.env.NODE_ENV !== 'production') { + console.error(error); + } logoutIfAccessDenied(error); }, + ...queryParams, } );