Skip to content

Commit cc477d8

Browse files
authored
Merge pull request #10989 from marmelab/doc-authProvider-diagram
[Doc] Improve AuthProvider documentation
2 parents c27492d + a5a15cf commit cc477d8

File tree

6 files changed

+262
-132
lines changed

6 files changed

+262
-132
lines changed

docs/AuthProviderWriting.md

Lines changed: 111 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,35 @@ title: "Writing An Auth Provider"
77

88
React-admin can use any authentication backend, but you have to write an adapter for it. This adapter is called an `authProvider`. The `authProvider` is a simple object with methods that react-admin calls to handle authentication and authorization.
99

10-
## AuthProvider Interface Overview
10+
## Auth Provider Methods
1111

1212
React-admin expect an `authProvider` to implement the following methods:
1313

1414
```tsx
1515
const authProvider = {
16-
// required methods
16+
// REQUIRED
17+
// send username and password to the auth server and get back credentials
18+
// (for login / password flow)
1719
async login(params) {/* ... */},
20+
// when the dataProvider returns an error, check if this is an authentication error
1821
async checkError(error) {/* ... */},
22+
// when the user navigates, make sure that their credentials are still valid
1923
async checkAuth(params) {/* ... */},
24+
// remove local credentials and notify the auth server that the user logged out
2025
async logout() {/* ... */},
21-
// optional methods
26+
27+
// OPTIONAL
28+
// get the user's profile (id, fullName, avatar)
2229
async getIdentity() {/* ... */},
23-
async handleCallback() {/* ... */}, // for third-party authentication only
24-
async canAccess(params) {/* ... */}, // for authorization only
25-
async getPermissions() {/* ... */}, // for authorization only
30+
// process authentication callback from third-party providers
31+
// (for third-party authentication flow)
32+
async handleCallback() {/* ... */},
33+
// check authorization for an action over a resource
34+
// (for access-control style authorization)
35+
async canAccess(params) {/* ... */},
36+
// get the user's permissions
37+
// (for permission style authorization)
38+
async getPermissions() {/* ... */},
2639
};
2740
```
2841

@@ -36,7 +49,7 @@ const authProvider: AuthProvider = {
3649
};
3750
```
3851

39-
## Example
52+
## Simple Example
4053

4154
Here is a fictive but working implementation of an auth provider. It only accepts user "john" with password "123".
4255

@@ -71,19 +84,26 @@ const authProvider = {
7184
};
7285
```
7386

74-
## Step-By-Step
75-
76-
If you have to implement your own auth provider, here is a step-by-step guide to get you started.
87+
## `login`
7788

78-
### `login`
89+
| **Purpose** | Send username and password to the auth server and get back credentials |
90+
| **Required** | Yes |
91+
| **When to use** | For login / password flows |
92+
| **On resolve** | Redirects to previous page or admin index (customizable) |
93+
| **On reject** | Displays error message in a notification |
94+
| **Request format** | `Object` with values from the login form |
95+
| **Response format** | `void | { redirectTo?: string | boolean }` |
96+
| **Error format** | `string | { message?: string }` |
7997

8098
Once an admin has an `authProvider`, react-admin enables a new page on the `/login` route, which displays a login form.
8199

82100
![Default Login Form](./img/login-form.png)
83101

84102
Upon submission, the login page calls the `authProvider.login()` method with the login data as parameter. React-admin expects this async method to return if the login data is correct, and to throw an error if it's not.
85103

86-
For instance, to query an authentication route via HTTPS and store the user credentials (a token) in local storage, configure the `authProvider` as follows:
104+
**Tip:** The `login` method will never be called if you rely solely on [third-party authentication](./Authentication.md#using-external-authentication-providers) flows (e.g. Auth0, Cognito, or any other OAuth-based service). In this case, you can provide a dummy implementation that always resolves.
105+
106+
Below is an example showing how to configure the `authProvider` to query an authentication route via HTTPS and store the user credentials (a token) in local storage:
87107

88108
```tsx
89109
// in src/authProvider.js
@@ -129,7 +149,16 @@ const authProvider = {
129149
};
130150
```
131151

132-
### `checkError`
152+
## `checkError`
153+
154+
| **Purpose** | Check if a dataProvider error is an authentication error |
155+
| **Required** | Yes |
156+
| **When to use** | Always |
157+
| **On resolve** | - |
158+
| **On reject** | Logs the user out and redirects to the login page (customizable) |
159+
| **Request format** | `{ message: string, status: number, body: Object }` (error from the dataProvider) |
160+
| **Response format** | `void` |
161+
| **Error format** | `{ message?: string | boolean, redirectTo?: string | boolean, logoutUser?: boolean }` |
133162

134163
When the user credentials are missing or become invalid, a secure API usually responds with an HTTP error code 401 or 403.
135164

@@ -205,7 +234,16 @@ const authProvider = {
205234
};
206235
```
207236

208-
### `checkAuth`
237+
## `checkAuth`
238+
239+
| **Purpose** | Check if the user is authenticated (when navigating to an authenticated route) |
240+
| **Required** | Yes |
241+
| **When to use** | Always |
242+
| **On resolve** | - |
243+
| **On reject** | Logs the user out and redirects to the login page (customizable) |
244+
| **Request format** | Params passed to `useCheckAuth()` -- empty for react-admin default routes |
245+
| **Response format** | `void` |
246+
| **Error format** | `{ message?: string | boolean, redirectTo?: string | boolean }` |
209247

210248
Redirecting to the login page whenever a REST response uses a 401 status code is usually not enough. React-admin keeps data on the client side, and could briefly display stale data while contacting the server - even after the credentials are no longer valid.
211249

@@ -269,7 +307,18 @@ const authProvider = {
269307
};
270308
```
271309

272-
### `logout`
310+
**Tip:** `checkAuth` won't be called for routes [allowing anonymous access](./Authentication.md#allowing-anonymous-access).
311+
312+
## `logout`
313+
314+
| **Purpose** | Log out the user from the backend and clean up authentication data |
315+
| **Required** | Yes |
316+
| **When to use** | Always |
317+
| **On resolve** | Redirects to login page (customizable) |
318+
| **On reject** | - |
319+
| **Request format** | - |
320+
| **Response format** | `string | false | void` route to redirect to after logout, defaults to `/login` |
321+
| **Error format** | - |
273322

274323
If you enable authentication, react-admin adds a logout button in the user menu in the top bar (or in the sliding menu on mobile). When the user clicks on the logout button, this calls the `authProvider.logout()` method, and removes potentially sensitive data stored in [the react-admin Store](./Store.md). Then the user gets redirected to the login page. The two previous sections also illustrated that react-admin can call `authProvider.logout()` itself, when the API returns a 403 error or when the local credentials expire.
275324

@@ -303,9 +352,18 @@ const authProvider = {
303352
};
304353
```
305354

306-
### `getIdentity`
355+
**Tip**: If both `authProvider.checkAuth()` and `authProvider.logout()` return a redirect URL, the one from `authProvider.checkAuth()` takes precedence.
356+
357+
## `getIdentity`
307358

308-
Admin components often adapt their behavior based on the current user identity. For instance, a lock system may allow edition only if the lock owner is the current user. Another example is the user menu: it has to display the current user name and avatar.
359+
| **Purpose** | Get the current user identity |
360+
| **Required** | No |
361+
| **When to use** | Always |
362+
| **Request format** | - |
363+
| **Response format** | `{ id: string | number, fullName?: string, avatar?: string }` |
364+
| **Error format** | `Error` |
365+
366+
Admin components often adapt their behavior based on the current user identity. For instance, a [lock system](https://react-admin-ee.marmelab.com/documentation/ra-realtime#locks) may allow edition only if the lock owner is the current user. Another example is the [user menu](./AppBar.md#usermenu): it has to display the current user name and avatar.
309367

310368
React-admin delegates the storage of the connected user identity to the `authProvider`. If it exposes a `getIdentity()` method, react-admin will call it to read the user details.
311369

@@ -326,7 +384,7 @@ React-admin uses the `fullName` and the `avatar` (an image source, or a data-uri
326384

327385
![User identity](./img/identity.png)
328386

329-
**Tip**: You can use the `id` field to identify the current user in your code, by calling the `useGetIdentity` hook:
387+
**Tip**: You can use the `id` field to identify the current user in your code, by calling the [`useGetIdentity`](./useGetIdentity.md) hook:
330388

331389
```jsx
332390
import { useGetIdentity, useGetOne } from 'react-admin';
@@ -345,7 +403,16 @@ const PostDetail = ({ id }) => {
345403
}
346404
```
347405

348-
### `handleCallback`
406+
## `handleCallback`
407+
408+
| **Purpose** | Process authentication callback from third-party providers (Auth0, Cognito, ...) |
409+
| **Required** | No |
410+
| **When to use** | For third-party authentication flows |
411+
| **On resolve** | Redirects to previous page or admin index (customizable) |
412+
| **On reject** | Renders the error |
413+
| **Request format** | - |
414+
| **Response format** | `void | { redirectTo?: string | boolean }` |
415+
| **Error format** | `Error` |
349416

350417
This method is used when integrating a third-party authentication provider such as [Auth0](https://auth0.com/). React-admin provides a route at the `/auth-callback` path, to be used as the callback URL in the authentication service. After logging in using the authentication service, users will be redirected to this route. The `/auth-callback` route calls the `authProvider.handleCallback` method on mount.
351418

@@ -389,6 +456,8 @@ const authProvider = {
389456
}
390457
```
391458

459+
![Auth0 login flow diagram](./img/authProvider-OAuth-flow.png)
460+
392461
Once `handleCallback` returns, react-admin redirects the user to the home page, or to the location found in `localStorage.getItem(PreviousLocationStorageKey)`. In the above example, `authProvider.checkAuth()` sets this location to the page the user was trying to access.
393462

394463
You can override this behavior by returning an object with a `redirectTo` property, as follows:
@@ -408,7 +477,16 @@ const authProvider = {
408477
};
409478
```
410479

411-
### `canAccess`
480+
**Tip:** If you rely solely on third-party authentication flows, the `authProvider.login()` method will never be called. In this case you can simply provide a dummy implementation that always resolves.
481+
482+
## `canAccess`
483+
484+
| **Purpose** | Check authorization for an action over a resource |
485+
| **Required** | No |
486+
| **When to use** | For [Access Control](./Permissions.md#access-control) style Authorization |
487+
| **Request format** | `{ action: string, resource: string, record: object }` |
488+
| **Response format** | `boolean` |
489+
| **Error format** | `Error` |
412490

413491
React-admin has built-in [Access Control](./Permissions.md#access-control) features that you can enable by implementing the `authProvider.canAccess()` method. It receives a permissions object with the following properties:
414492

@@ -452,7 +530,14 @@ Check the [Access Control documentation](./Permissions.md#access-control) for mo
452530
**Tip**: [The Role-Based Access Control (RBAC) module](./AuthRBAC.md) allows fined-grained permissions in react-admin apps leveraging the `canAccess` method. Check [the RBAC documentation](./AuthRBAC.md) for more information.
453531

454532

455-
### `getPermissions`
533+
## `getPermissions`
534+
535+
| **Purpose** | Returns a boolean indicating whether the user can perform the provided action on the provided resource |
536+
| **Required** | No |
537+
| **When to use** | For [Permissions](./Permissions.md#permissions) style Authorization |
538+
| **Request format** | params passed to `usePermissions()` -- empty for react-admin default routes |
539+
| **Response format** | `any` |
540+
| **Error format** | `Error` |
456541

457542
As an alternative to `canAccess()`, `getPermissions()` lets you return an arbitrary permissions object. This object can be used by React components to enable or disable UI elements based on the user's role.
458543

@@ -472,50 +557,11 @@ React-admin doesn't use permissions by default, but it provides [the `usePermiss
472557

473558
Check the [Access Control documentation](./Permissions.md#permissions) for more information on how to use the `getPermissions` method.
474559

475-
## Request Format
476-
477-
React-admin calls the `authProvider` methods with the following params:
478-
479-
| Method | Usage | Parameters format |
480-
| ---------------- | ----------------------------------------------- | ------------------ |
481-
| `login` | Log a user in | `Object` whatever fields the login form contains |
482-
| `checkError` | Check if a dataProvider error is an authentication error | `{ message: string, status: number, body: Object }` the error returned by the `dataProvider` |
483-
| `checkAuth` | Check credentials before moving to a new route | `Object` whatever params passed to `useCheckAuth()` - empty for react-admin default routes |
484-
| `logout` | Log a user out | |
485-
| `getIdentity` | Get the current user identity | |
486-
| `handleCallback` | Validate users after third party authentication service redirection | |
487-
| `canAccess` | Check authorization for an action over a resource | `{ action: string, resource: string, record: object }` |
488-
| `getPermissions` | Get the current user credentials | `Object` whatever params passed to `usePermissions()` - empty for react-admin default routes |
489-
490-
## Response Format
491-
492-
`authProvider` methods must return a Promise. In case of success, the Promise should resolve to the following value:
493-
494-
| Method | Resolve if | Response format |
495-
| ---------------- | --------------------------------- | --------------- |
496-
| `login` | Login credentials were accepted | `void | { redirectTo?: string | boolean }` route to redirect to after login |
497-
| `checkError` | Error is not an auth error | `void` |
498-
| `checkAuth` | User is authenticated | `void` |
499-
| `logout` | Auth backend acknowledged logout | `string | false | void` route to redirect to after logout, defaults to `/login` |
500-
| `getIdentity` | Auth backend returned identity | `{ id: string | number, fullName?: string, avatar?: string }` |
501-
| `handleCallback` | User is authenticated | `void | { redirectTo?: string | boolean }` route to redirect to after login |
502-
| `canAccess` | Auth backend returned authorization | `boolean` |
503-
| `getPermissions` | Auth backend returned permissions | Free format. |
504-
505-
## Error Format
506-
507-
When the auth backend returns an error, the Auth Provider should return a rejected Promise, with the following value:
508-
509-
| Method | Reject if | Error format |
510-
| ---------------- | ----------------------------------------- | --------------- |
511-
| `login` | Login credentials weren't accepted | `string | { message?: string }` error message to display |
512-
| `checkError` | Error is an auth error | `void | { redirectTo?: string, message?: string | boolean }` route to redirect to after logout, message to notify the user or `false` to disable notification |
513-
| `checkAuth` | User is not authenticated | `void | { redirectTo?: string, message?: string }` route to redirect to after logout, message to notify the user |
514-
| `logout` | Auth backend failed to log the user out | `void` |
515-
| `getIdentity` | Auth backend failed to return identity | `Object` free format - returned as `error` when `useGetIdentity()` is called |
516-
| `handleCallback` | Failed to authenticate users after redirection | `void | { redirectTo?: string, logoutOnFailure?: boolean, message?: string }` |
517-
| `canAccess` | Auth backend failed to return authorization | `Object` free format - returned as `error` when `useCanAccess()` is called. |
518-
| `getPermissions` | Auth backend failed to return permissions | `Object` free format - returned as `error` when `usePermissions()` is called. The error will be passed to `checkError` |
560+
**Tip:** How to choose between `canAccess` and `getPermissions`? We recommend Access Control (i.e. `canAccess`) because it allows you to put the authorization logic in the `authProvider` rather than in the React code.
561+
562+
## Using External Authentication Providers
563+
564+
Instead of the built-in Login page, you can use an external authentication provider, like Auth0, Cognito, or any other OAuth-based service. See [Using External Authentication Providers](./Authentication.md#using-external-authentication-providers) for an example.
519565

520566
## Query Cancellation
521567

docs/Authentication.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,27 @@ export const authProvider = {
428428
};
429429
```
430430
431-
You can choose when to redirect users to the third-party authentication service, such as directly in the `AuthProvider.checkAuth()` method or when they click a button on a [custom login page](#customizing-the-login-component).
431+
![Auth0 login flow diagram](./img/authProvider-OAuth-flow.png)
432+
{% comment %}
433+
Diagram source:
434+
```mermaid
435+
sequenceDiagram
436+
autonumber
437+
participant U as User
438+
participant RA as React Admin<br/>(authProvider)
439+
participant A as Auth0 website
440+
U->>RA: Access /posts
441+
Note over RA: checkAuth()<br/>Auth0Client.isAuthenticated()
442+
RA->>A: Auth0Client.loginWithRedirect()
443+
Note over A: Login using Auth0 form
444+
A->>RA: Redirects to /auth-callback<br/>(with token)
445+
Note over RA: handleCallback()<br/>Auth0Client.handleRedirectCallback()
446+
RA->>U: Redirects to /posts
447+
```
448+
Edited with https://mermaid.live/edit
449+
{% endcomment %}
450+
451+
**Tip:** You can choose when to redirect users to the third-party authentication service, such as directly in the `AuthProvider.checkAuth()` method or when they click a button on a [custom login page](#customizing-the-login-component).
432452
433453
## Handling Refresh Tokens
434454
249 KB
Loading

0 commit comments

Comments
 (0)