Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Call checkError when getPermissions fails #9117

Merged
merged 2 commits into from
Jul 21, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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();
});
});
9 changes: 8 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,19 @@ const usePermissions = <Permissions = any, Error = any>(
}
) => {
const authProvider = useAuthProvider();
const logoutIfAccessDenied = useLogoutIfAccessDenied();

const result = useQuery(
['auth', 'getPermissions', params],
authProvider
? () => authProvider.getPermissions(params)
: async () => [],
queryParams
{
...queryParams,
onError: error => {
logoutIfAccessDenied(error);
},
slax57 marked this conversation as resolved.
Show resolved Hide resolved
}
);

return useMemo(
Expand Down
Loading