Skip to content

Commit 656f1ba

Browse files
authored
Merge pull request #9117 from marmelab/check-permissions-error
Call checkError when getPermissions fails
2 parents 445272b + 082469b commit 656f1ba

3 files changed

Lines changed: 34 additions & 5 deletions

File tree

docs/AuthProviderWriting.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ If the login fails, `authProvider.login()` should return a rejected Promise with
157157

158158
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.
159159

160-
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.
160+
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.
161161

162162
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).
163163

@@ -502,5 +502,5 @@ When the auth backend returns an error, the Auth Provider should return a reject
502502
| `logout` | Auth backend failed to log the user out | `void` |
503503
| `getIdentity` | Auth backend failed to return identity | `Object` free format - returned as `error` when `useGetIdentity()` is called |
504504
| `handleCallback` | Failed to authenticate users after redirection | `void | { redirectTo?: string, logoutOnFailure?: boolean, message?: string }` |
505-
| `getPermissions` | Auth backend failed to return permissions | `Object` free format - returned as `error` when `usePermissions()` is called |
505+
| `getPermissions` | Auth backend failed to return permissions | `Object` free format - returned as `error` when `usePermissions()` is called. The error will be passed to `checkError` |
506506

packages/ra-core/src/auth/usePermissions.spec.tsx

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,12 @@ describe('usePermissions', () => {
6262
});
6363
});
6464

65-
it('should return an error after a tick if the auth call fails', async () => {
65+
it('should return an error after a tick if the auth.getPermissions call fails and checkError resolves', async () => {
6666
const authProvider = {
6767
login: () => Promise.reject('bad method'),
6868
logout: () => Promise.reject('bad method'),
6969
checkAuth: () => Promise.reject('bad method'),
70-
checkError: () => Promise.reject('bad method'),
70+
checkError: () => Promise.resolve(),
7171
getPermissions: () => Promise.reject('not good'),
7272
};
7373
render(
@@ -80,4 +80,23 @@ describe('usePermissions', () => {
8080
expect(screen.queryByText('ERROR')).not.toBeNull();
8181
});
8282
});
83+
84+
it('should call logout when the auth.getPermissions call fails and checkError rejects', async () => {
85+
const authProvider = {
86+
login: () => Promise.reject('bad method'),
87+
logout: jest.fn(() => Promise.resolve()),
88+
checkAuth: () => Promise.reject('bad method'),
89+
checkError: () => Promise.reject(),
90+
getPermissions: () => Promise.reject('not good'),
91+
};
92+
render(
93+
<CoreAdminContext authProvider={authProvider}>
94+
<UsePermissions>{stateInpector}</UsePermissions>
95+
</CoreAdminContext>
96+
);
97+
await waitFor(() => {
98+
expect(screen.queryByText('LOADING')).toBeNull();
99+
});
100+
expect(authProvider.logout).toHaveBeenCalled();
101+
});
83102
});

packages/ra-core/src/auth/usePermissions.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useMemo } from 'react';
22
import { useQuery, UseQueryOptions } from 'react-query';
33
import useAuthProvider from './useAuthProvider';
4+
import useLogoutIfAccessDenied from './useLogoutIfAccessDenied';
45

56
const emptyParams = {};
67

@@ -41,13 +42,22 @@ const usePermissions = <Permissions = any, Error = any>(
4142
}
4243
) => {
4344
const authProvider = useAuthProvider();
45+
const logoutIfAccessDenied = useLogoutIfAccessDenied();
4446

4547
const result = useQuery(
4648
['auth', 'getPermissions', params],
4749
authProvider
4850
? () => authProvider.getPermissions(params)
4951
: async () => [],
50-
queryParams
52+
{
53+
onError: error => {
54+
if (process.env.NODE_ENV !== 'production') {
55+
console.error(error);
56+
}
57+
logoutIfAccessDenied(error);
58+
},
59+
...queryParams,
60+
}
5161
);
5262

5363
return useMemo(

0 commit comments

Comments
 (0)