Skip to content

Commit

Permalink
Merge pull request #9117 from marmelab/check-permissions-error
Browse files Browse the repository at this point in the history
Call checkError when getPermissions fails
  • Loading branch information
slax57 authored Jul 21, 2023
2 parents 445272b + 082469b commit 656f1ba
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 5 deletions.
4 changes: 2 additions & 2 deletions docs/AuthProviderWriting.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).

Expand Down Expand Up @@ -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` |

23 changes: 21 additions & 2 deletions packages/ra-core/src/auth/usePermissions.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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(
<CoreAdminContext authProvider={authProvider}>
<UsePermissions>{stateInpector}</UsePermissions>
</CoreAdminContext>
);
await waitFor(() => {
expect(screen.queryByText('LOADING')).toBeNull();
});
expect(authProvider.logout).toHaveBeenCalled();
});
});
12 changes: 11 additions & 1 deletion packages/ra-core/src/auth/usePermissions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useMemo } from 'react';
import { useQuery, UseQueryOptions } from 'react-query';
import useAuthProvider from './useAuthProvider';
import useLogoutIfAccessDenied from './useLogoutIfAccessDenied';

const emptyParams = {};

Expand Down Expand Up @@ -41,13 +42,22 @@ const usePermissions = <Permissions = any, Error = any>(
}
) => {
const authProvider = useAuthProvider();
const logoutIfAccessDenied = useLogoutIfAccessDenied();

const result = useQuery(
['auth', 'getPermissions', params],
authProvider
? () => authProvider.getPermissions(params)
: async () => [],
queryParams
{
onError: error => {
if (process.env.NODE_ENV !== 'production') {
console.error(error);
}
logoutIfAccessDenied(error);
},
...queryParams,
}
);

return useMemo(
Expand Down

0 comments on commit 656f1ba

Please sign in to comment.