Skip to content

Commit 0e29239

Browse files
slax57alanpoulain
andauthored
docs: Revamp API Platform Admin docs (#2141)
Co-authored-by: Alan Poulain <[email protected]>
1 parent f3702bd commit 0e29239

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1702
-663
lines changed

admin/advanced-customization.md

+462
Large diffs are not rendered by default.

admin/authentication-support.md

+82-55
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,24 @@
11
# Authentication Support
22

33
API Platform Admin delegates the authentication support to React Admin.
4-
Refer to [the chapter dedicated to authentication in the React Admin documentation](https://marmelab.com/react-admin/Authentication.html)
5-
for more information.
4+
5+
Refer to the [Auth Provider Setup](https://marmelab.com/react-admin/Authentication.html) documentation for more information.
6+
7+
**Tip:** Once you have set up the authentication, you can also configure React Admin to perform client-side Authorization checks. Refer to the [Authorization](https://marmelab.com/react-admin/Permissions.html) documentation for more information.
68

79
## HydraAdmin
810

9-
The authentication layer for [HydraAdmin component](https://api-platform.com/docs/admin/components/#hydra)
10-
consists of a few parts, which need to be integrated together.
11+
Enabling authentication support for [`<HydraAdmin>` component](./components.md#hydra) consists of a few parts, which need to be integrated together.
12+
13+
In the following steps, we will see how to:
14+
15+
- Make authenticated requests to the API (i.e. include the `Authorization` header)
16+
- Redirect users to the login page if they are not authenticated
17+
- Clear expired tokens when encountering unauthorized `401` response
1118

12-
### Authentication
19+
### Make Authenticated Requests
1320

14-
Add the Bearer token from `localStorage` to request headers.
21+
First, we need to implement a `getHeaders` function, that will add the Bearer token from `localStorage` (if there is one) to the `Authorization` header.
1522

1623
```typescript
1724
const getHeaders = () =>
@@ -20,9 +27,13 @@ const getHeaders = () =>
2027
: {};
2128
```
2229

23-
Extend the Hydra fetch function with custom headers for authentication.
30+
Then, extend the Hydra `fetch` function to use the `getHeaders` function to add the `Authorization` header to the requests.
2431

2532
```typescript
33+
import {
34+
fetchHydra as baseFetchHydra,
35+
} from "@api-platform/admin";
36+
2637
const fetchHydra = (url, options = {}) =>
2738
baseFetchHydra(url, {
2839
...options,
@@ -31,11 +42,14 @@ const fetchHydra = (url, options = {}) =>
3142

3243
```
3344

34-
### Login Redirection
45+
### Redirect To Login Page
3546

36-
Redirect users to a `/login` path, if no token is available in the `localStorage`.
47+
Then, we'll create a `<RedirectToLogin>` component, that will redirect users to the `/login` route if no token is available in the `localStorage`, and call the dataProvider's `introspect` function otherwise.
48+
49+
```tsx
50+
import { Navigate } from "react-router-dom";
51+
import { useIntrospection } from "@api-platform/admin";
3752

38-
```typescript
3953
const RedirectToLogin = () => {
4054
const introspect = useIntrospection();
4155

@@ -47,13 +61,16 @@ const RedirectToLogin = () => {
4761
};
4862
```
4963

50-
### API Documentation Parsing
64+
### Clear Expired Tokens
5165

52-
Extend the `parseHydraDocumentaion` function from the [API Doc Parser library](https://github.com/api-platform/api-doc-parser)
53-
to handle the documentation parsing. Customize it to clear
54-
expired tokens when encountering unauthorized `401` response.
66+
Now, we will extend the `parseHydraDocumentaion` function (imported from the [@api-platform/api-doc-parser](https://github.com/api-platform/api-doc-parser) library).
67+
68+
We will customize it to clear expired tokens when encountering unauthorized `401` response.
5569

5670
```typescript
71+
import { parseHydraDocumentation } from "@api-platform/api-doc-parser";
72+
import { ENTRYPOINT } from "config/entrypoint";
73+
5774
const apiDocumentationParser = (setRedirectToLogin) => async () => {
5875
try {
5976
setRedirectToLogin(false);
@@ -72,11 +89,16 @@ const apiDocumentationParser = (setRedirectToLogin) => async () => {
7289
};
7390
```
7491

75-
### Data Provider
92+
### Extend The Data Provider
7693

77-
Initialize the hydra data provider with custom headers and the documentation parser.
94+
Now, we can initialize the Hydra data provider with the custom `fetchHydra` (with custom headers) and `apiDocumentationParser` functions created earlier.
7895

7996
```typescript
97+
import {
98+
hydraDataProvider as baseHydraDataProvider,
99+
} from "@api-platform/admin";
100+
import { ENTRYPOINT } from "config/entrypoint";
101+
80102
const dataProvider = (setRedirectToLogin) =>
81103
baseHydraDataProvider({
82104
entrypoint: ENTRYPOINT,
@@ -85,12 +107,12 @@ const dataProvider = (setRedirectToLogin) =>
85107
});
86108
```
87109

88-
### Export Admin Component
110+
### Update The Admin Component
89111

90-
Export the Hydra admin component, and track the users' authentication status.
112+
Lastly, we can stitch everything together in the `Admin` component.
91113

92-
```typescript
93-
// components/admin/Admin.tsx
114+
```tsx
115+
// src/Admin.tsx
94116

95117
import Head from "next/head";
96118
import { useState } from "react";
@@ -106,14 +128,14 @@ import { parseHydraDocumentation } from "@api-platform/api-doc-parser";
106128
import authProvider from "utils/authProvider";
107129
import { ENTRYPOINT } from "config/entrypoint";
108130

109-
// Auth, Parser, Provider calls
131+
// Functions and components created in the previous steps:
110132
const getHeaders = () => {...};
111133
const fetchHydra = (url, options = {}) => {...};
112134
const RedirectToLogin = () => {...};
113135
const apiDocumentationParser = (setRedirectToLogin) => async () => {...};
114136
const dataProvider = (setRedirectToLogin) => {...};
115137

116-
const Admin = () => {
138+
export const Admin = () => {
117139
const [redirectToLogin, setRedirectToLogin] = useState(false);
118140

119141
return (
@@ -142,29 +164,32 @@ const Admin = () => {
142164
</>
143165
);
144166
};
145-
export default Admin;
146167
```
147168

148-
### Additional Notes
169+
### Example Implementation
149170

150171
For the implementation of the admin component, you can find a working example in the [API Platform's demo application](https://github.com/api-platform/demo/blob/4.0/pwa/components/admin/Admin.tsx).
151172

152173
## OpenApiAdmin
153174

154-
This section explains how to set up and customize the [OpenApiAdmin component](https://api-platform.com/docs/admin/components/#openapi) authentication layer.
155-
It covers:
156-
* Creating a custom HTTP Client
157-
* Data and rest data provider configuration
158-
* Implementation of an auth provider
175+
This section explains how to set up and customize the [`<OpenApiAdmin>` component](./components.md/#openapi) to enable authentication.
159176

160-
### Data Provider & HTTP Client
177+
In the following steps, we will see how to:
161178

162-
Create a custom HTTP client to add authentication tokens to request headers.
163-
Configure the `openApiDataProvider`, and
164-
inject the custom HTTP client into the [Simple REST Data Provider for React-Admin](https://github.com/Serind/ra-data-simple-rest).
179+
- Make authenticated requests to the API (i.e. include the `Authorization` header)
180+
- Implement an authProvider to redirect users to the login page if they are not authenticated, and clear expired tokens when encountering unauthorized `401` response
181+
182+
### Making Authenticated Requests
183+
184+
First, we need to create a custom `httpClient` to add authentication tokens (via the the `Authorization` HTTP header) to requests.
185+
186+
We will then configure `openApiDataProvider` to use [`ra-data-simple-rest`](https://github.com/marmelab/react-admin/blob/master/packages/ra-data-simple-rest/README.md), a simple REST dataProvider for React Admin, and make it use the `httpClient` we created earlier.
165187

166-
**File:** `src/components/jsonDataProvider.tsx`
167188
```typescript
189+
// src/dataProvider.ts
190+
191+
const getAccessToken = () => localStorage.getItem("token");
192+
168193
const httpClient = async (url: string, options: fetchUtils.Options = {}) => {
169194
options.headers = new Headers({
170195
...options.headers,
@@ -177,32 +202,31 @@ const httpClient = async (url: string, options: fetchUtils.Options = {}) => {
177202
return await fetchUtils.fetchJson(url, options);
178203
};
179204

180-
const jsonDataProvider = openApiDataProvider({
205+
const dataProvider = openApiDataProvider({
181206
dataProvider: simpleRestProvider(API_ENTRYPOINT_PATH, httpClient),
182207
entrypoint: API_ENTRYPOINT_PATH,
183208
docEntrypoint: API_DOCS_PATH,
184209
});
185210
```
186211

187-
> [!NOTE]
188-
> The `simpleRestProvider` provider expect the API to include a `Content-Range` header in the response.
189-
> You can find more about the header syntax in the [Mozilla’s MDN documentation: Content-Range](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Range).
190-
>
191-
> The `getAccessToken` function retrieves the JWT token stored in the browser.
212+
**Note:** The `simpleRestProvider` provider expect the API to include a `Content-Range` header in the response. You can find more about the header syntax in the [Mozilla’s MDN documentation: Content-Range](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Range).
213+
214+
**Note:** The `getAccessToken` function retrieves the JWT token stored in the browser's localStorage. Replace it with your own logic in case you don't store the token that way.
192215

193-
### Authentication and Authorization
216+
### Creating The AuthProvider
194217

195-
Create and export an `authProvider` object that handles authentication and authorization logic.
218+
Now let's create and export an `authProvider` object that handles authentication and authorization logic.
196219

197-
**File:** `src/components/authProvider.tsx`
198220
```typescript
221+
// src/authProvider.ts
222+
199223
interface JwtPayload {
200-
exp?: number;
201-
iat?: number;
202-
roles: string[];
224+
sub: string;
203225
username: string;
204226
}
205227

228+
const getAccessToken = () => localStorage.getItem("token");
229+
206230
const authProvider = {
207231
login: async ({username, password}: { username: string; password: string }) => {
208232
const request = new Request(API_AUTH_PATH, {
@@ -242,7 +266,7 @@ const authProvider = {
242266
const decoded = jwtDecode<JwtPayload>(token);
243267

244268
return Promise.resolve({
245-
id: "",
269+
id: decoded.sub,
246270
fullName: decoded.username,
247271
avatar: "",
248272
});
@@ -253,20 +277,23 @@ const authProvider = {
253277
export default authProvider;
254278
```
255279

256-
### Export OpenApiAdmin Component
280+
### Updating The Admin Component
257281

258-
**File:** `src/App.tsx`
259-
```typescript
260-
import {OpenApiAdmin} from '@api-platform/admin';
261-
import authProvider from "./components/authProvider";
262-
import jsonDataProvider from "./components/jsonDataProvider";
263-
import {API_DOCS_PATH, API_ENTRYPOINT_PATH} from "./config/api";
282+
Finally, we can update the `Admin` component to use the `authProvider` and `dataProvider` we created earlier.
283+
284+
```tsx
285+
// src/Admin.tsx
286+
287+
import { OpenApiAdmin } from '@api-platform/admin';
288+
import authProvider from "./authProvider";
289+
import dataProvider from "./dataProvider";
290+
import { API_DOCS_PATH, API_ENTRYPOINT_PATH } from "./config/api";
264291

265292
export default () => (
266293
<OpenApiAdmin
267294
entrypoint={API_ENTRYPOINT_PATH}
268295
docEntrypoint={API_DOCS_PATH}
269-
dataProvider={jsonDataProvider}
296+
dataProvider={dataProvider}
270297
authProvider={authProvider}
271298
/>
272299
);

0 commit comments

Comments
 (0)