@agility/management-sdk 0.1.37 → 0.1.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/npm-publish.yml +61 -0
- package/README.md +396 -371
- package/dist/apiMethods/instanceMethods.d.ts +13 -0
- package/dist/apiMethods/instanceMethods.js +48 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/models/exception.d.ts +4 -0
- package/dist/models/exception.js +17 -1
- package/dist/models/fetchApiStatus.d.ts +38 -0
- package/dist/models/fetchApiStatus.js +2 -0
- package/docs/asset-methods.md +381 -381
- package/docs/auth-component.md +360 -360
- package/docs/auth.md +593 -593
- package/docs/batch-methods.md +243 -243
- package/docs/cicd.md +606 -606
- package/docs/container-methods.md +267 -267
- package/docs/content-methods.md +491 -491
- package/docs/instance-methods.md +185 -105
- package/docs/instance-user-methods.md +230 -230
- package/docs/model-methods.md +433 -433
- package/docs/multi-instance-operations.md +191 -191
- package/docs/page-methods.md +737 -737
- package/docs/server-user-methods.md +279 -279
- package/docs/webhook-methods.md +451 -451
- package/license.txt +20 -20
- package/package.json +42 -41
- package/tsconfig.json +14 -14
package/docs/auth.md
CHANGED
|
@@ -1,594 +1,594 @@
|
|
|
1
|
-
# Agility CMS & Management API TypeScript SDK
|
|
2
|
-
|
|
3
|
-
## Authentication & Setup
|
|
4
|
-
|
|
5
|
-
This guide covers how to authenticate with the Agility CMS Management API and initialize the SDK for making API requests.
|
|
6
|
-
|
|
7
|
-
**🚀 NEW: Simplified Authentication**
|
|
8
|
-
We now provide a streamlined authentication experience with secure token storage and automatic token refresh. The new `ApiClient.auth()` method handles the entire OAuth flow internally.
|
|
9
|
-
|
|
10
|
-
### Prerequisites
|
|
11
|
-
1. Valid Agility CMS credentials
|
|
12
|
-
2. A configured OAuth application in your Agility CMS instance
|
|
13
|
-
3. A valid redirect URI for OAuth flow
|
|
14
|
-
4. **Node.js**: For secure token storage, keytar is required (`npm install keytar`)
|
|
15
|
-
|
|
16
|
-
### Setup Instructions
|
|
17
|
-
|
|
18
|
-
#### Install keytar for Secure Token Storage
|
|
19
|
-
```bash
|
|
20
|
-
npm install keytar
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
**Platform Requirements:**
|
|
24
|
-
- **macOS**: Uses Keychain (built-in)
|
|
25
|
-
- **Windows**: Uses Windows Credential Store (built-in)
|
|
26
|
-
- **Linux**: Requires `libsecret` development package
|
|
27
|
-
```bash
|
|
28
|
-
# Ubuntu/Debian
|
|
29
|
-
sudo apt-get install libsecret-1-dev
|
|
30
|
-
|
|
31
|
-
# Red Hat/CentOS
|
|
32
|
-
sudo yum install libsecret-devel
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
### Authentication Approaches
|
|
36
|
-
|
|
37
|
-
You have two options for authentication:
|
|
38
|
-
|
|
39
|
-
#### 1. **Recommended: Simple OAuth Flow**
|
|
40
|
-
Use the new `ApiClient.auth()` method for streamlined authentication:
|
|
41
|
-
|
|
42
|
-
```typescript
|
|
43
|
-
import * as mgmtApi from '@agility/management-sdk';
|
|
44
|
-
|
|
45
|
-
// Simple OAuth authentication
|
|
46
|
-
const apiClient = new mgmtApi.ApiClient();
|
|
47
|
-
await apiClient.auth();
|
|
48
|
-
|
|
49
|
-
// Client is now authenticated - make API calls directly
|
|
50
|
-
const guid = "your-website-guid";
|
|
51
|
-
const locale = "en-us";
|
|
52
|
-
const contentItem = await apiClient.contentMethods.getContentItem(22, guid, locale);
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
The `auth()` method:
|
|
56
|
-
- Generates OAuth authorization URL
|
|
57
|
-
- Handles token exchange automatically
|
|
58
|
-
- Stores tokens securely using keytar
|
|
59
|
-
- Provides automatic token refresh
|
|
60
|
-
|
|
61
|
-
#### 2. **Manual Token Management (Existing Pattern)**
|
|
62
|
-
For applications that need to handle tokens manually, the traditional approach is still fully supported:
|
|
63
|
-
|
|
64
|
-
```typescript
|
|
65
|
-
import * as mgmtApi from "@agility/management-sdk";
|
|
66
|
-
|
|
67
|
-
// Manual token approach (still supported)
|
|
68
|
-
const options = new mgmtApi.Options();
|
|
69
|
-
options.token = "your-access-token";
|
|
70
|
-
const apiClient = new mgmtApi.ApiClient(options);
|
|
71
|
-
|
|
72
|
-
const guid = "your-website-guid";
|
|
73
|
-
const locale = "en-us";
|
|
74
|
-
|
|
75
|
-
const contentItem = await apiClient.contentMethods.getContentItem(22, guid, locale);
|
|
76
|
-
console.log(JSON.stringify(contentItem));
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
For custom OAuth implementations, you can handle the OAuth flow manually:
|
|
80
|
-
|
|
81
|
-
Before using the SDK, you must authenticate against the Agility Management API to obtain a valid access token. This token is required for all subsequent API requests.
|
|
82
|
-
|
|
83
|
-
The authentication process uses OAuth 2.0 and requires multiple steps:
|
|
84
|
-
|
|
85
|
-
#### Step 1: Initiate Authorization Flow
|
|
86
|
-
|
|
87
|
-
First, initiate the authorization flow by making a GET request to the authorization endpoint:
|
|
88
|
-
|
|
89
|
-
```javascript
|
|
90
|
-
const authUrl = 'https://mgmt.aglty.io/oauth/authorize';
|
|
91
|
-
|
|
92
|
-
//if you wish to implement offline access using refresh tokens, use this URL (enables refresh tokens)
|
|
93
|
-
//const authUrl = 'https://mgmt.aglty.io/oauth/authorize?scope=offline-access ';
|
|
94
|
-
|
|
95
|
-
const params = new URLSearchParams({
|
|
96
|
-
response_type: 'code',
|
|
97
|
-
redirect_uri: 'YOUR_REDIRECT_URI',
|
|
98
|
-
state: 'YOUR_STATE',
|
|
99
|
-
scope: 'openid profile email offline_access'
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
// Redirect the user to the authorization URL
|
|
103
|
-
window.location.href = `${authUrl}?${params.toString()}`;
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
#### Step 2: Exchange Authorization Code for Access Token
|
|
107
|
-
|
|
108
|
-
After successful authentication, you'll receive an authorization code at your redirect URI. Use this code to obtain an access token:
|
|
109
|
-
|
|
110
|
-
```javascript
|
|
111
|
-
const response = await fetch('https://mgmt.aglty.io/oauth/token', {
|
|
112
|
-
method: 'POST',
|
|
113
|
-
headers: {
|
|
114
|
-
'Content-Type': 'application/x-www-form-urlencoded'
|
|
115
|
-
},
|
|
116
|
-
body: new URLSearchParams({
|
|
117
|
-
code: 'YOUR_AUTHORIZATION_CODE'
|
|
118
|
-
})
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
const { access_token, refresh_token, expires_in } = await response.json();
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
#### Step 3: Initialize the SDK
|
|
125
|
-
|
|
126
|
-
Use the obtained token to initialize the SDK:
|
|
127
|
-
|
|
128
|
-
```javascript
|
|
129
|
-
import * as mgmtApi from "@agility/management-sdk";
|
|
130
|
-
|
|
131
|
-
// Initialize the Options Class with your authentication token
|
|
132
|
-
let options = new mgmtApi.Options();
|
|
133
|
-
options.token = access_token; // Use the token obtained from authentication
|
|
134
|
-
|
|
135
|
-
// Initialize the APIClient Class
|
|
136
|
-
let apiClient = new mgmtApi.ApiClient(options);
|
|
137
|
-
|
|
138
|
-
let guid = "<<Provide the Guid of the Website>>";
|
|
139
|
-
let locale = "<<Provide the locale of the Website>>"; // Example: en-us
|
|
140
|
-
|
|
141
|
-
// Now you can make authenticated requests
|
|
142
|
-
var contentItem = await apiClient.contentMethods.getContentItem(22, guid, locale);
|
|
143
|
-
console.log(JSON.stringify(contentItem));
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
#### Step 4: Refresh Access Tokens
|
|
147
|
-
|
|
148
|
-
When the access token expires, use the refresh token to obtain a new access token:
|
|
149
|
-
|
|
150
|
-
```javascript
|
|
151
|
-
const response = await fetch('https://mgmt.aglty.io/oauth/refresh', {
|
|
152
|
-
method: 'POST',
|
|
153
|
-
headers: {
|
|
154
|
-
'Content-Type': 'application/json'
|
|
155
|
-
},
|
|
156
|
-
body: JSON.stringify({
|
|
157
|
-
refresh_token: 'YOUR_REFRESH_TOKEN'
|
|
158
|
-
})
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
const { access_token, refresh_token, expires_in } = await response.json();
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
### Important Authentication Notes
|
|
165
|
-
|
|
166
|
-
- **Token Lifetime**: The access token has a limited lifetime (typically 1 hour)
|
|
167
|
-
- **Refresh Tokens**: The refresh token can be used to obtain new access tokens
|
|
168
|
-
- **Security**: Store refresh tokens securely and never expose them in client-side code
|
|
169
|
-
- **Error Handling**: Implement proper error handling for authentication failures
|
|
170
|
-
|
|
171
|
-
### Making API Requests
|
|
172
|
-
|
|
173
|
-
Once authenticated, you can make requests using the SDK:
|
|
174
|
-
|
|
175
|
-
#### Using New Simple Authentication (Recommended)
|
|
176
|
-
```typescript
|
|
177
|
-
import * as mgmtApi from '@agility/management-sdk';
|
|
178
|
-
|
|
179
|
-
// Simple OAuth authentication
|
|
180
|
-
const apiClient = new mgmtApi.ApiClient();
|
|
181
|
-
await apiClient.auth();
|
|
182
|
-
|
|
183
|
-
// Client is now authenticated and handles token refresh automatically
|
|
184
|
-
const guid = "your-website-guid";
|
|
185
|
-
const locale = "en-us";
|
|
186
|
-
const contentItem = await apiClient.contentMethods.getContentItem(22, guid, locale);
|
|
187
|
-
console.log(JSON.stringify(contentItem));
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
#### Manual Client Creation (Existing Pattern)
|
|
191
|
-
```javascript
|
|
192
|
-
import * as mgmtApi from "@agility/management-sdk";
|
|
193
|
-
|
|
194
|
-
//initialize the Options Class
|
|
195
|
-
let options = new mgmtApi.Options();
|
|
196
|
-
|
|
197
|
-
options.token = "<<Provide Auth Token>>"
|
|
198
|
-
//Initialize the APIClient Class
|
|
199
|
-
let apiClient = new mgmtApi.ApiClient(options);
|
|
200
|
-
|
|
201
|
-
let guid = "<<Provide the Guid of the Website>>";
|
|
202
|
-
let locale = "<<Provide the locale of the Website>>"; //Example: en-us
|
|
203
|
-
|
|
204
|
-
//make the request: get a content item with the ID '22'
|
|
205
|
-
var contentItem = await apiClient.contentMethods.getContentItem(22,guid, locale);
|
|
206
|
-
|
|
207
|
-
//To log the response of the contentItem object in console.
|
|
208
|
-
console.log(JSON.stringify(contentItem));
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
### OAuth Flow Examples
|
|
212
|
-
|
|
213
|
-
#### For Web Applications
|
|
214
|
-
|
|
215
|
-
```javascript
|
|
216
|
-
// Redirect user to authorization URL
|
|
217
|
-
function initiateAuth() {
|
|
218
|
-
const authUrl = 'https://mgmt.aglty.io/oauth/authorize';
|
|
219
|
-
const params = new URLSearchParams({
|
|
220
|
-
response_type: 'code',
|
|
221
|
-
redirect_uri: window.location.origin + '/auth/callback',
|
|
222
|
-
state: generateRandomState(),
|
|
223
|
-
scope: 'openid profile email offline_access'
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
window.location.href = `${authUrl}?${params.toString()}`;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Handle callback in your redirect URI endpoint
|
|
230
|
-
async function handleAuthCallback(code) {
|
|
231
|
-
try {
|
|
232
|
-
const response = await fetch('https://mgmt.aglty.io/oauth/token', {
|
|
233
|
-
method: 'POST',
|
|
234
|
-
headers: {
|
|
235
|
-
'Content-Type': 'application/x-www-form-urlencoded'
|
|
236
|
-
},
|
|
237
|
-
body: new URLSearchParams({
|
|
238
|
-
code: code
|
|
239
|
-
})
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
const tokens = await response.json();
|
|
243
|
-
|
|
244
|
-
// Store tokens securely
|
|
245
|
-
localStorage.setItem('access_token', tokens.access_token);
|
|
246
|
-
localStorage.setItem('refresh_token', tokens.refresh_token);
|
|
247
|
-
|
|
248
|
-
return tokens;
|
|
249
|
-
} catch (error) {
|
|
250
|
-
console.error('Authentication failed:', error);
|
|
251
|
-
throw error;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
```
|
|
255
|
-
|
|
256
|
-
#### For Server-Side Applications
|
|
257
|
-
|
|
258
|
-
```javascript
|
|
259
|
-
// Express.js example
|
|
260
|
-
app.get('/auth/agility', (req, res) => {
|
|
261
|
-
const authUrl = 'https://mgmt.aglty.io/oauth/authorize';
|
|
262
|
-
const params = new URLSearchParams({
|
|
263
|
-
response_type: 'code',
|
|
264
|
-
redirect_uri: 'https://yourapp.com/auth/callback',
|
|
265
|
-
state: req.session.state,
|
|
266
|
-
scope: 'openid profile email offline_access'
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
res.redirect(`${authUrl}?${params.toString()}`);
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
app.get('/auth/callback', async (req, res) => {
|
|
273
|
-
const { code, state } = req.query;
|
|
274
|
-
|
|
275
|
-
// Verify state parameter
|
|
276
|
-
if (state !== req.session.state) {
|
|
277
|
-
return res.status(400).send('Invalid state parameter');
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
try {
|
|
281
|
-
const response = await fetch('https://mgmt.aglty.io/oauth/token', {
|
|
282
|
-
method: 'POST',
|
|
283
|
-
headers: {
|
|
284
|
-
'Content-Type': 'application/x-www-form-urlencoded'
|
|
285
|
-
},
|
|
286
|
-
body: new URLSearchParams({
|
|
287
|
-
code: code
|
|
288
|
-
})
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
const tokens = await response.json();
|
|
292
|
-
|
|
293
|
-
// Store tokens in session or database
|
|
294
|
-
req.session.tokens = tokens;
|
|
295
|
-
|
|
296
|
-
res.redirect('/dashboard');
|
|
297
|
-
} catch (error) {
|
|
298
|
-
res.status(500).send('Authentication failed');
|
|
299
|
-
}
|
|
300
|
-
});
|
|
301
|
-
```
|
|
302
|
-
|
|
303
|
-
### Token Management
|
|
304
|
-
|
|
305
|
-
#### Automatic Token Refresh
|
|
306
|
-
|
|
307
|
-
```javascript
|
|
308
|
-
class AgilityTokenManager {
|
|
309
|
-
constructor(refreshToken) {
|
|
310
|
-
this.refreshToken = refreshToken;
|
|
311
|
-
this.accessToken = null;
|
|
312
|
-
this.expiresAt = null;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
async getValidToken() {
|
|
316
|
-
if (!this.accessToken || Date.now() >= this.expiresAt) {
|
|
317
|
-
await this.refreshAccessToken();
|
|
318
|
-
}
|
|
319
|
-
return this.accessToken;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
async refreshAccessToken() {
|
|
323
|
-
try {
|
|
324
|
-
const response = await fetch('https://mgmt.aglty.io/oauth/refresh', {
|
|
325
|
-
method: 'POST',
|
|
326
|
-
headers: {
|
|
327
|
-
'Content-Type': 'application/json'
|
|
328
|
-
},
|
|
329
|
-
body: JSON.stringify({
|
|
330
|
-
refresh_token: this.refreshToken
|
|
331
|
-
})
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
const tokens = await response.json();
|
|
335
|
-
|
|
336
|
-
this.accessToken = tokens.access_token;
|
|
337
|
-
this.refreshToken = tokens.refresh_token; // Update refresh token if provided
|
|
338
|
-
this.expiresAt = Date.now() + (tokens.expires_in * 1000);
|
|
339
|
-
|
|
340
|
-
return tokens;
|
|
341
|
-
} catch (error) {
|
|
342
|
-
console.error('Token refresh failed:', error);
|
|
343
|
-
throw error;
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
// Usage
|
|
349
|
-
const tokenManager = new AgilityTokenManager('your-refresh-token');
|
|
350
|
-
const token = await tokenManager.getValidToken();
|
|
351
|
-
|
|
352
|
-
const options = new mgmtApi.Options();
|
|
353
|
-
options.token = token;
|
|
354
|
-
const apiClient = new mgmtApi.ApiClient(options);
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
### Error Handling
|
|
358
|
-
|
|
359
|
-
#### Common Authentication Errors
|
|
360
|
-
|
|
361
|
-
```javascript
|
|
362
|
-
async function makeAuthenticatedRequest() {
|
|
363
|
-
try {
|
|
364
|
-
const apiClient = new mgmtApi.ApiClient({ token: accessToken });
|
|
365
|
-
const result = await apiClient.contentMethods.getContentItem(123, guid, locale);
|
|
366
|
-
return result;
|
|
367
|
-
} catch (error) {
|
|
368
|
-
if (error.response?.status === 401) {
|
|
369
|
-
// Token expired or invalid
|
|
370
|
-
console.log('Authentication failed - refreshing token');
|
|
371
|
-
await refreshAccessToken();
|
|
372
|
-
|
|
373
|
-
// Retry the request
|
|
374
|
-
const apiClient = new mgmtApi.ApiClient({ token: newAccessToken });
|
|
375
|
-
return await apiClient.contentMethods.getContentItem(123, guid, locale);
|
|
376
|
-
} else if (error.response?.status === 403) {
|
|
377
|
-
// Insufficient permissions
|
|
378
|
-
console.error('Access denied - insufficient permissions');
|
|
379
|
-
throw new Error('User does not have permission for this operation');
|
|
380
|
-
} else {
|
|
381
|
-
// Other errors
|
|
382
|
-
console.error('API request failed:', error.message);
|
|
383
|
-
throw error;
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
### Migration Guide
|
|
390
|
-
|
|
391
|
-
#### Migrating from Manual OAuth to Simple Authentication
|
|
392
|
-
|
|
393
|
-
**Before (Manual OAuth):**
|
|
394
|
-
```typescript
|
|
395
|
-
// Old approach - manual OAuth handling
|
|
396
|
-
import * as mgmtApi from '@agility/management-sdk';
|
|
397
|
-
|
|
398
|
-
const options = new mgmtApi.Options();
|
|
399
|
-
options.token = 'manually-obtained-token';
|
|
400
|
-
const apiClient = new mgmtApi.ApiClient(options);
|
|
401
|
-
|
|
402
|
-
// Manual token refresh required
|
|
403
|
-
const contentItem = await apiClient.contentMethods.getContentItem(22, guid, locale);
|
|
404
|
-
```
|
|
405
|
-
|
|
406
|
-
**After (Simple Authentication):**
|
|
407
|
-
```typescript
|
|
408
|
-
// New approach - automatic OAuth handling
|
|
409
|
-
import * as mgmtApi from '@agility/management-sdk';
|
|
410
|
-
|
|
411
|
-
const apiClient = new mgmtApi.ApiClient();
|
|
412
|
-
await apiClient.auth();
|
|
413
|
-
|
|
414
|
-
// Automatic token refresh handled internally
|
|
415
|
-
const contentItem = await apiClient.contentMethods.getContentItem(22, guid, locale);
|
|
416
|
-
```
|
|
417
|
-
|
|
418
|
-
#### Migrating from AuthMethods Class
|
|
419
|
-
|
|
420
|
-
**Before (AuthMethods Class):**
|
|
421
|
-
```typescript
|
|
422
|
-
import { AuthMethods } from '@agility/management-sdk';
|
|
423
|
-
|
|
424
|
-
const auth = new AuthMethods();
|
|
425
|
-
const client = auth.createAuthenticatedClient('token');
|
|
426
|
-
```
|
|
427
|
-
|
|
428
|
-
**After (Simple Authentication):**
|
|
429
|
-
```typescript
|
|
430
|
-
import * as mgmtApi from '@agility/management-sdk';
|
|
431
|
-
|
|
432
|
-
const apiClient = new mgmtApi.ApiClient();
|
|
433
|
-
await apiClient.auth();
|
|
434
|
-
```
|
|
435
|
-
|
|
436
|
-
### Advanced Features
|
|
437
|
-
|
|
438
|
-
#### Secure Token Storage
|
|
439
|
-
The new authentication system uses OS-level secure storage:
|
|
440
|
-
|
|
441
|
-
- **macOS**: Keychain Access
|
|
442
|
-
- **Windows**: Windows Credential Store
|
|
443
|
-
- **Linux**: libsecret (GNOME Keyring)
|
|
444
|
-
|
|
445
|
-
Tokens are encrypted and stored securely, with automatic cleanup on logout.
|
|
446
|
-
|
|
447
|
-
#### Automatic Token Refresh
|
|
448
|
-
The SDK now handles token refresh automatically:
|
|
449
|
-
|
|
450
|
-
```typescript
|
|
451
|
-
const apiClient = new mgmtApi.ApiClient();
|
|
452
|
-
await apiClient.auth();
|
|
453
|
-
|
|
454
|
-
// Token refresh happens automatically on 401 errors
|
|
455
|
-
const contentItem = await apiClient.contentMethods.getContentItem(22, guid, locale);
|
|
456
|
-
// If token was expired, it gets refreshed transparently
|
|
457
|
-
```
|
|
458
|
-
|
|
459
|
-
#### Custom OAuth Configuration
|
|
460
|
-
You can customize the OAuth flow:
|
|
461
|
-
|
|
462
|
-
```typescript
|
|
463
|
-
const apiClient = new mgmtApi.ApiClient();
|
|
464
|
-
await apiClient.auth({
|
|
465
|
-
redirectUri: 'https://yourapp.com/custom-callback',
|
|
466
|
-
scope: 'openid profile email offline_access',
|
|
467
|
-
region: 'us' // or 'ca', 'eu', 'au', 'dev'
|
|
468
|
-
});
|
|
469
|
-
```
|
|
470
|
-
|
|
471
|
-
#### Manual Token Exchange
|
|
472
|
-
For custom OAuth implementations:
|
|
473
|
-
|
|
474
|
-
```typescript
|
|
475
|
-
const apiClient = new mgmtApi.ApiClient();
|
|
476
|
-
|
|
477
|
-
// Exchange authorization code for tokens
|
|
478
|
-
const tokens = await apiClient.exchangeCodeForToken({
|
|
479
|
-
code: 'authorization_code_from_callback',
|
|
480
|
-
redirectUri: 'https://yourapp.com/auth/callback'
|
|
481
|
-
});
|
|
482
|
-
|
|
483
|
-
// Tokens are automatically stored and used
|
|
484
|
-
```
|
|
485
|
-
|
|
486
|
-
### Best Practices
|
|
487
|
-
|
|
488
|
-
#### Security Considerations
|
|
489
|
-
|
|
490
|
-
1. **Use secure token storage (automatic with new auth)**
|
|
491
|
-
```typescript
|
|
492
|
-
// ✅ Recommended - automatic secure storage
|
|
493
|
-
const apiClient = new mgmtApi.ApiClient();
|
|
494
|
-
await apiClient.auth(); // Tokens stored securely in OS keychain
|
|
495
|
-
|
|
496
|
-
// ✅ CI/CD - manual token (for automated environments)
|
|
497
|
-
const options = new mgmtApi.Options();
|
|
498
|
-
options.token = process.env.AGILITY_ACCESS_TOKEN;
|
|
499
|
-
const apiClient = new mgmtApi.ApiClient(options);
|
|
500
|
-
|
|
501
|
-
// ❌ Bad - hardcoded tokens
|
|
502
|
-
options.token = 'your-secret-token'; // Never do this
|
|
503
|
-
```
|
|
504
|
-
|
|
505
|
-
2. **Leverage automatic token refresh**
|
|
506
|
-
```typescript
|
|
507
|
-
// ✅ Automatic token refresh (new approach)
|
|
508
|
-
const apiClient = new mgmtApi.ApiClient();
|
|
509
|
-
await apiClient.auth();
|
|
510
|
-
|
|
511
|
-
// Token refresh happens automatically on 401 errors
|
|
512
|
-
const contentItem = await apiClient.contentMethods.getContentItem(22, guid, locale);
|
|
513
|
-
```
|
|
514
|
-
|
|
515
|
-
3. **Handle authentication errors gracefully**
|
|
516
|
-
```typescript
|
|
517
|
-
const apiClient = new mgmtApi.ApiClient();
|
|
518
|
-
|
|
519
|
-
try {
|
|
520
|
-
await apiClient.auth();
|
|
521
|
-
const contentItem = await apiClient.contentMethods.getContentItem(22, guid, locale);
|
|
522
|
-
} catch (error) {
|
|
523
|
-
if (error.message.includes('OAuth flow requires manual implementation')) {
|
|
524
|
-
// Handle OAuth flow manually
|
|
525
|
-
console.log('Visit the OAuth URL to authenticate');
|
|
526
|
-
} else {
|
|
527
|
-
// Handle other auth errors
|
|
528
|
-
console.error('Authentication failed:', error);
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
```
|
|
532
|
-
|
|
533
|
-
4. **Clear tokens on logout**
|
|
534
|
-
```typescript
|
|
535
|
-
const apiClient = new mgmtApi.ApiClient();
|
|
536
|
-
await apiClient.auth();
|
|
537
|
-
|
|
538
|
-
// ... use API client ...
|
|
539
|
-
|
|
540
|
-
// Clear tokens securely on logout
|
|
541
|
-
await apiClient.clearToken();
|
|
542
|
-
```
|
|
543
|
-
|
|
544
|
-
#### Performance Optimization
|
|
545
|
-
|
|
546
|
-
1. **Reuse API client instances**
|
|
547
|
-
```javascript
|
|
548
|
-
// ✅ Good - single instance
|
|
549
|
-
const apiClient = new mgmtApi.ApiClient(options);
|
|
550
|
-
|
|
551
|
-
// Use the same instance for multiple requests
|
|
552
|
-
const content1 = await apiClient.contentMethods.getContentItem(1, guid, locale);
|
|
553
|
-
const content2 = await apiClient.contentMethods.getContentItem(2, guid, locale);
|
|
554
|
-
```
|
|
555
|
-
|
|
556
|
-
2. **Implement request caching**
|
|
557
|
-
```javascript
|
|
558
|
-
class CachedAgilityClient {
|
|
559
|
-
constructor(options) {
|
|
560
|
-
this.client = new mgmtApi.ApiClient(options);
|
|
561
|
-
this.cache = new Map();
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
async getContentItem(id, guid, locale) {
|
|
565
|
-
const cacheKey = `content-${id}-${guid}-${locale}`;
|
|
566
|
-
|
|
567
|
-
if (this.cache.has(cacheKey)) {
|
|
568
|
-
return this.cache.get(cacheKey);
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
const result = await this.client.contentMethods.getContentItem(id, guid, locale);
|
|
572
|
-
this.cache.set(cacheKey, result);
|
|
573
|
-
|
|
574
|
-
return result;
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
```
|
|
578
|
-
|
|
579
|
-
---
|
|
580
|
-
|
|
581
|
-
## Navigation
|
|
582
|
-
- [← Back to Main Documentation](../README.md)
|
|
583
|
-
- [CI/CD & Automated Environments](./cicd.md)
|
|
584
|
-
- [AssetMethods](./asset-methods.md)
|
|
585
|
-
- [BatchMethods](./batch-methods.md)
|
|
586
|
-
- [ContentMethods](./content-methods.md)
|
|
587
|
-
- [ContainerMethods](./container-methods.md)
|
|
588
|
-
- [InstanceMethods](./instance-methods.md)
|
|
589
|
-
- [InstanceUserMethods](./instance-user-methods.md)
|
|
590
|
-
- [ModelMethods](./model-methods.md)
|
|
591
|
-
- [PageMethods](./page-methods.md)
|
|
592
|
-
- [ServerUserMethods](./server-user-methods.md)
|
|
593
|
-
- [WebhookMethods](./webhook-methods.md)
|
|
1
|
+
# Agility CMS & Management API TypeScript SDK
|
|
2
|
+
|
|
3
|
+
## Authentication & Setup
|
|
4
|
+
|
|
5
|
+
This guide covers how to authenticate with the Agility CMS Management API and initialize the SDK for making API requests.
|
|
6
|
+
|
|
7
|
+
**🚀 NEW: Simplified Authentication**
|
|
8
|
+
We now provide a streamlined authentication experience with secure token storage and automatic token refresh. The new `ApiClient.auth()` method handles the entire OAuth flow internally.
|
|
9
|
+
|
|
10
|
+
### Prerequisites
|
|
11
|
+
1. Valid Agility CMS credentials
|
|
12
|
+
2. A configured OAuth application in your Agility CMS instance
|
|
13
|
+
3. A valid redirect URI for OAuth flow
|
|
14
|
+
4. **Node.js**: For secure token storage, keytar is required (`npm install keytar`)
|
|
15
|
+
|
|
16
|
+
### Setup Instructions
|
|
17
|
+
|
|
18
|
+
#### Install keytar for Secure Token Storage
|
|
19
|
+
```bash
|
|
20
|
+
npm install keytar
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**Platform Requirements:**
|
|
24
|
+
- **macOS**: Uses Keychain (built-in)
|
|
25
|
+
- **Windows**: Uses Windows Credential Store (built-in)
|
|
26
|
+
- **Linux**: Requires `libsecret` development package
|
|
27
|
+
```bash
|
|
28
|
+
# Ubuntu/Debian
|
|
29
|
+
sudo apt-get install libsecret-1-dev
|
|
30
|
+
|
|
31
|
+
# Red Hat/CentOS
|
|
32
|
+
sudo yum install libsecret-devel
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Authentication Approaches
|
|
36
|
+
|
|
37
|
+
You have two options for authentication:
|
|
38
|
+
|
|
39
|
+
#### 1. **Recommended: Simple OAuth Flow**
|
|
40
|
+
Use the new `ApiClient.auth()` method for streamlined authentication:
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
import * as mgmtApi from '@agility/management-sdk';
|
|
44
|
+
|
|
45
|
+
// Simple OAuth authentication
|
|
46
|
+
const apiClient = new mgmtApi.ApiClient();
|
|
47
|
+
await apiClient.auth();
|
|
48
|
+
|
|
49
|
+
// Client is now authenticated - make API calls directly
|
|
50
|
+
const guid = "your-website-guid";
|
|
51
|
+
const locale = "en-us";
|
|
52
|
+
const contentItem = await apiClient.contentMethods.getContentItem(22, guid, locale);
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The `auth()` method:
|
|
56
|
+
- Generates OAuth authorization URL
|
|
57
|
+
- Handles token exchange automatically
|
|
58
|
+
- Stores tokens securely using keytar
|
|
59
|
+
- Provides automatic token refresh
|
|
60
|
+
|
|
61
|
+
#### 2. **Manual Token Management (Existing Pattern)**
|
|
62
|
+
For applications that need to handle tokens manually, the traditional approach is still fully supported:
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
import * as mgmtApi from "@agility/management-sdk";
|
|
66
|
+
|
|
67
|
+
// Manual token approach (still supported)
|
|
68
|
+
const options = new mgmtApi.Options();
|
|
69
|
+
options.token = "your-access-token";
|
|
70
|
+
const apiClient = new mgmtApi.ApiClient(options);
|
|
71
|
+
|
|
72
|
+
const guid = "your-website-guid";
|
|
73
|
+
const locale = "en-us";
|
|
74
|
+
|
|
75
|
+
const contentItem = await apiClient.contentMethods.getContentItem(22, guid, locale);
|
|
76
|
+
console.log(JSON.stringify(contentItem));
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
For custom OAuth implementations, you can handle the OAuth flow manually:
|
|
80
|
+
|
|
81
|
+
Before using the SDK, you must authenticate against the Agility Management API to obtain a valid access token. This token is required for all subsequent API requests.
|
|
82
|
+
|
|
83
|
+
The authentication process uses OAuth 2.0 and requires multiple steps:
|
|
84
|
+
|
|
85
|
+
#### Step 1: Initiate Authorization Flow
|
|
86
|
+
|
|
87
|
+
First, initiate the authorization flow by making a GET request to the authorization endpoint:
|
|
88
|
+
|
|
89
|
+
```javascript
|
|
90
|
+
const authUrl = 'https://mgmt.aglty.io/oauth/authorize';
|
|
91
|
+
|
|
92
|
+
//if you wish to implement offline access using refresh tokens, use this URL (enables refresh tokens)
|
|
93
|
+
//const authUrl = 'https://mgmt.aglty.io/oauth/authorize?scope=offline-access ';
|
|
94
|
+
|
|
95
|
+
const params = new URLSearchParams({
|
|
96
|
+
response_type: 'code',
|
|
97
|
+
redirect_uri: 'YOUR_REDIRECT_URI',
|
|
98
|
+
state: 'YOUR_STATE',
|
|
99
|
+
scope: 'openid profile email offline_access'
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// Redirect the user to the authorization URL
|
|
103
|
+
window.location.href = `${authUrl}?${params.toString()}`;
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
#### Step 2: Exchange Authorization Code for Access Token
|
|
107
|
+
|
|
108
|
+
After successful authentication, you'll receive an authorization code at your redirect URI. Use this code to obtain an access token:
|
|
109
|
+
|
|
110
|
+
```javascript
|
|
111
|
+
const response = await fetch('https://mgmt.aglty.io/oauth/token', {
|
|
112
|
+
method: 'POST',
|
|
113
|
+
headers: {
|
|
114
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
115
|
+
},
|
|
116
|
+
body: new URLSearchParams({
|
|
117
|
+
code: 'YOUR_AUTHORIZATION_CODE'
|
|
118
|
+
})
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const { access_token, refresh_token, expires_in } = await response.json();
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
#### Step 3: Initialize the SDK
|
|
125
|
+
|
|
126
|
+
Use the obtained token to initialize the SDK:
|
|
127
|
+
|
|
128
|
+
```javascript
|
|
129
|
+
import * as mgmtApi from "@agility/management-sdk";
|
|
130
|
+
|
|
131
|
+
// Initialize the Options Class with your authentication token
|
|
132
|
+
let options = new mgmtApi.Options();
|
|
133
|
+
options.token = access_token; // Use the token obtained from authentication
|
|
134
|
+
|
|
135
|
+
// Initialize the APIClient Class
|
|
136
|
+
let apiClient = new mgmtApi.ApiClient(options);
|
|
137
|
+
|
|
138
|
+
let guid = "<<Provide the Guid of the Website>>";
|
|
139
|
+
let locale = "<<Provide the locale of the Website>>"; // Example: en-us
|
|
140
|
+
|
|
141
|
+
// Now you can make authenticated requests
|
|
142
|
+
var contentItem = await apiClient.contentMethods.getContentItem(22, guid, locale);
|
|
143
|
+
console.log(JSON.stringify(contentItem));
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
#### Step 4: Refresh Access Tokens
|
|
147
|
+
|
|
148
|
+
When the access token expires, use the refresh token to obtain a new access token:
|
|
149
|
+
|
|
150
|
+
```javascript
|
|
151
|
+
const response = await fetch('https://mgmt.aglty.io/oauth/refresh', {
|
|
152
|
+
method: 'POST',
|
|
153
|
+
headers: {
|
|
154
|
+
'Content-Type': 'application/json'
|
|
155
|
+
},
|
|
156
|
+
body: JSON.stringify({
|
|
157
|
+
refresh_token: 'YOUR_REFRESH_TOKEN'
|
|
158
|
+
})
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
const { access_token, refresh_token, expires_in } = await response.json();
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Important Authentication Notes
|
|
165
|
+
|
|
166
|
+
- **Token Lifetime**: The access token has a limited lifetime (typically 1 hour)
|
|
167
|
+
- **Refresh Tokens**: The refresh token can be used to obtain new access tokens
|
|
168
|
+
- **Security**: Store refresh tokens securely and never expose them in client-side code
|
|
169
|
+
- **Error Handling**: Implement proper error handling for authentication failures
|
|
170
|
+
|
|
171
|
+
### Making API Requests
|
|
172
|
+
|
|
173
|
+
Once authenticated, you can make requests using the SDK:
|
|
174
|
+
|
|
175
|
+
#### Using New Simple Authentication (Recommended)
|
|
176
|
+
```typescript
|
|
177
|
+
import * as mgmtApi from '@agility/management-sdk';
|
|
178
|
+
|
|
179
|
+
// Simple OAuth authentication
|
|
180
|
+
const apiClient = new mgmtApi.ApiClient();
|
|
181
|
+
await apiClient.auth();
|
|
182
|
+
|
|
183
|
+
// Client is now authenticated and handles token refresh automatically
|
|
184
|
+
const guid = "your-website-guid";
|
|
185
|
+
const locale = "en-us";
|
|
186
|
+
const contentItem = await apiClient.contentMethods.getContentItem(22, guid, locale);
|
|
187
|
+
console.log(JSON.stringify(contentItem));
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
#### Manual Client Creation (Existing Pattern)
|
|
191
|
+
```javascript
|
|
192
|
+
import * as mgmtApi from "@agility/management-sdk";
|
|
193
|
+
|
|
194
|
+
//initialize the Options Class
|
|
195
|
+
let options = new mgmtApi.Options();
|
|
196
|
+
|
|
197
|
+
options.token = "<<Provide Auth Token>>"
|
|
198
|
+
//Initialize the APIClient Class
|
|
199
|
+
let apiClient = new mgmtApi.ApiClient(options);
|
|
200
|
+
|
|
201
|
+
let guid = "<<Provide the Guid of the Website>>";
|
|
202
|
+
let locale = "<<Provide the locale of the Website>>"; //Example: en-us
|
|
203
|
+
|
|
204
|
+
//make the request: get a content item with the ID '22'
|
|
205
|
+
var contentItem = await apiClient.contentMethods.getContentItem(22,guid, locale);
|
|
206
|
+
|
|
207
|
+
//To log the response of the contentItem object in console.
|
|
208
|
+
console.log(JSON.stringify(contentItem));
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### OAuth Flow Examples
|
|
212
|
+
|
|
213
|
+
#### For Web Applications
|
|
214
|
+
|
|
215
|
+
```javascript
|
|
216
|
+
// Redirect user to authorization URL
|
|
217
|
+
function initiateAuth() {
|
|
218
|
+
const authUrl = 'https://mgmt.aglty.io/oauth/authorize';
|
|
219
|
+
const params = new URLSearchParams({
|
|
220
|
+
response_type: 'code',
|
|
221
|
+
redirect_uri: window.location.origin + '/auth/callback',
|
|
222
|
+
state: generateRandomState(),
|
|
223
|
+
scope: 'openid profile email offline_access'
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
window.location.href = `${authUrl}?${params.toString()}`;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Handle callback in your redirect URI endpoint
|
|
230
|
+
async function handleAuthCallback(code) {
|
|
231
|
+
try {
|
|
232
|
+
const response = await fetch('https://mgmt.aglty.io/oauth/token', {
|
|
233
|
+
method: 'POST',
|
|
234
|
+
headers: {
|
|
235
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
236
|
+
},
|
|
237
|
+
body: new URLSearchParams({
|
|
238
|
+
code: code
|
|
239
|
+
})
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
const tokens = await response.json();
|
|
243
|
+
|
|
244
|
+
// Store tokens securely
|
|
245
|
+
localStorage.setItem('access_token', tokens.access_token);
|
|
246
|
+
localStorage.setItem('refresh_token', tokens.refresh_token);
|
|
247
|
+
|
|
248
|
+
return tokens;
|
|
249
|
+
} catch (error) {
|
|
250
|
+
console.error('Authentication failed:', error);
|
|
251
|
+
throw error;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
#### For Server-Side Applications
|
|
257
|
+
|
|
258
|
+
```javascript
|
|
259
|
+
// Express.js example
|
|
260
|
+
app.get('/auth/agility', (req, res) => {
|
|
261
|
+
const authUrl = 'https://mgmt.aglty.io/oauth/authorize';
|
|
262
|
+
const params = new URLSearchParams({
|
|
263
|
+
response_type: 'code',
|
|
264
|
+
redirect_uri: 'https://yourapp.com/auth/callback',
|
|
265
|
+
state: req.session.state,
|
|
266
|
+
scope: 'openid profile email offline_access'
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
res.redirect(`${authUrl}?${params.toString()}`);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
app.get('/auth/callback', async (req, res) => {
|
|
273
|
+
const { code, state } = req.query;
|
|
274
|
+
|
|
275
|
+
// Verify state parameter
|
|
276
|
+
if (state !== req.session.state) {
|
|
277
|
+
return res.status(400).send('Invalid state parameter');
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
try {
|
|
281
|
+
const response = await fetch('https://mgmt.aglty.io/oauth/token', {
|
|
282
|
+
method: 'POST',
|
|
283
|
+
headers: {
|
|
284
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
285
|
+
},
|
|
286
|
+
body: new URLSearchParams({
|
|
287
|
+
code: code
|
|
288
|
+
})
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
const tokens = await response.json();
|
|
292
|
+
|
|
293
|
+
// Store tokens in session or database
|
|
294
|
+
req.session.tokens = tokens;
|
|
295
|
+
|
|
296
|
+
res.redirect('/dashboard');
|
|
297
|
+
} catch (error) {
|
|
298
|
+
res.status(500).send('Authentication failed');
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### Token Management
|
|
304
|
+
|
|
305
|
+
#### Automatic Token Refresh
|
|
306
|
+
|
|
307
|
+
```javascript
|
|
308
|
+
class AgilityTokenManager {
|
|
309
|
+
constructor(refreshToken) {
|
|
310
|
+
this.refreshToken = refreshToken;
|
|
311
|
+
this.accessToken = null;
|
|
312
|
+
this.expiresAt = null;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
async getValidToken() {
|
|
316
|
+
if (!this.accessToken || Date.now() >= this.expiresAt) {
|
|
317
|
+
await this.refreshAccessToken();
|
|
318
|
+
}
|
|
319
|
+
return this.accessToken;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
async refreshAccessToken() {
|
|
323
|
+
try {
|
|
324
|
+
const response = await fetch('https://mgmt.aglty.io/oauth/refresh', {
|
|
325
|
+
method: 'POST',
|
|
326
|
+
headers: {
|
|
327
|
+
'Content-Type': 'application/json'
|
|
328
|
+
},
|
|
329
|
+
body: JSON.stringify({
|
|
330
|
+
refresh_token: this.refreshToken
|
|
331
|
+
})
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
const tokens = await response.json();
|
|
335
|
+
|
|
336
|
+
this.accessToken = tokens.access_token;
|
|
337
|
+
this.refreshToken = tokens.refresh_token; // Update refresh token if provided
|
|
338
|
+
this.expiresAt = Date.now() + (tokens.expires_in * 1000);
|
|
339
|
+
|
|
340
|
+
return tokens;
|
|
341
|
+
} catch (error) {
|
|
342
|
+
console.error('Token refresh failed:', error);
|
|
343
|
+
throw error;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Usage
|
|
349
|
+
const tokenManager = new AgilityTokenManager('your-refresh-token');
|
|
350
|
+
const token = await tokenManager.getValidToken();
|
|
351
|
+
|
|
352
|
+
const options = new mgmtApi.Options();
|
|
353
|
+
options.token = token;
|
|
354
|
+
const apiClient = new mgmtApi.ApiClient(options);
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### Error Handling
|
|
358
|
+
|
|
359
|
+
#### Common Authentication Errors
|
|
360
|
+
|
|
361
|
+
```javascript
|
|
362
|
+
async function makeAuthenticatedRequest() {
|
|
363
|
+
try {
|
|
364
|
+
const apiClient = new mgmtApi.ApiClient({ token: accessToken });
|
|
365
|
+
const result = await apiClient.contentMethods.getContentItem(123, guid, locale);
|
|
366
|
+
return result;
|
|
367
|
+
} catch (error) {
|
|
368
|
+
if (error.response?.status === 401) {
|
|
369
|
+
// Token expired or invalid
|
|
370
|
+
console.log('Authentication failed - refreshing token');
|
|
371
|
+
await refreshAccessToken();
|
|
372
|
+
|
|
373
|
+
// Retry the request
|
|
374
|
+
const apiClient = new mgmtApi.ApiClient({ token: newAccessToken });
|
|
375
|
+
return await apiClient.contentMethods.getContentItem(123, guid, locale);
|
|
376
|
+
} else if (error.response?.status === 403) {
|
|
377
|
+
// Insufficient permissions
|
|
378
|
+
console.error('Access denied - insufficient permissions');
|
|
379
|
+
throw new Error('User does not have permission for this operation');
|
|
380
|
+
} else {
|
|
381
|
+
// Other errors
|
|
382
|
+
console.error('API request failed:', error.message);
|
|
383
|
+
throw error;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### Migration Guide
|
|
390
|
+
|
|
391
|
+
#### Migrating from Manual OAuth to Simple Authentication
|
|
392
|
+
|
|
393
|
+
**Before (Manual OAuth):**
|
|
394
|
+
```typescript
|
|
395
|
+
// Old approach - manual OAuth handling
|
|
396
|
+
import * as mgmtApi from '@agility/management-sdk';
|
|
397
|
+
|
|
398
|
+
const options = new mgmtApi.Options();
|
|
399
|
+
options.token = 'manually-obtained-token';
|
|
400
|
+
const apiClient = new mgmtApi.ApiClient(options);
|
|
401
|
+
|
|
402
|
+
// Manual token refresh required
|
|
403
|
+
const contentItem = await apiClient.contentMethods.getContentItem(22, guid, locale);
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
**After (Simple Authentication):**
|
|
407
|
+
```typescript
|
|
408
|
+
// New approach - automatic OAuth handling
|
|
409
|
+
import * as mgmtApi from '@agility/management-sdk';
|
|
410
|
+
|
|
411
|
+
const apiClient = new mgmtApi.ApiClient();
|
|
412
|
+
await apiClient.auth();
|
|
413
|
+
|
|
414
|
+
// Automatic token refresh handled internally
|
|
415
|
+
const contentItem = await apiClient.contentMethods.getContentItem(22, guid, locale);
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
#### Migrating from AuthMethods Class
|
|
419
|
+
|
|
420
|
+
**Before (AuthMethods Class):**
|
|
421
|
+
```typescript
|
|
422
|
+
import { AuthMethods } from '@agility/management-sdk';
|
|
423
|
+
|
|
424
|
+
const auth = new AuthMethods();
|
|
425
|
+
const client = auth.createAuthenticatedClient('token');
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
**After (Simple Authentication):**
|
|
429
|
+
```typescript
|
|
430
|
+
import * as mgmtApi from '@agility/management-sdk';
|
|
431
|
+
|
|
432
|
+
const apiClient = new mgmtApi.ApiClient();
|
|
433
|
+
await apiClient.auth();
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
### Advanced Features
|
|
437
|
+
|
|
438
|
+
#### Secure Token Storage
|
|
439
|
+
The new authentication system uses OS-level secure storage:
|
|
440
|
+
|
|
441
|
+
- **macOS**: Keychain Access
|
|
442
|
+
- **Windows**: Windows Credential Store
|
|
443
|
+
- **Linux**: libsecret (GNOME Keyring)
|
|
444
|
+
|
|
445
|
+
Tokens are encrypted and stored securely, with automatic cleanup on logout.
|
|
446
|
+
|
|
447
|
+
#### Automatic Token Refresh
|
|
448
|
+
The SDK now handles token refresh automatically:
|
|
449
|
+
|
|
450
|
+
```typescript
|
|
451
|
+
const apiClient = new mgmtApi.ApiClient();
|
|
452
|
+
await apiClient.auth();
|
|
453
|
+
|
|
454
|
+
// Token refresh happens automatically on 401 errors
|
|
455
|
+
const contentItem = await apiClient.contentMethods.getContentItem(22, guid, locale);
|
|
456
|
+
// If token was expired, it gets refreshed transparently
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
#### Custom OAuth Configuration
|
|
460
|
+
You can customize the OAuth flow:
|
|
461
|
+
|
|
462
|
+
```typescript
|
|
463
|
+
const apiClient = new mgmtApi.ApiClient();
|
|
464
|
+
await apiClient.auth({
|
|
465
|
+
redirectUri: 'https://yourapp.com/custom-callback',
|
|
466
|
+
scope: 'openid profile email offline_access',
|
|
467
|
+
region: 'us' // or 'ca', 'eu', 'au', 'dev'
|
|
468
|
+
});
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
#### Manual Token Exchange
|
|
472
|
+
For custom OAuth implementations:
|
|
473
|
+
|
|
474
|
+
```typescript
|
|
475
|
+
const apiClient = new mgmtApi.ApiClient();
|
|
476
|
+
|
|
477
|
+
// Exchange authorization code for tokens
|
|
478
|
+
const tokens = await apiClient.exchangeCodeForToken({
|
|
479
|
+
code: 'authorization_code_from_callback',
|
|
480
|
+
redirectUri: 'https://yourapp.com/auth/callback'
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
// Tokens are automatically stored and used
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
### Best Practices
|
|
487
|
+
|
|
488
|
+
#### Security Considerations
|
|
489
|
+
|
|
490
|
+
1. **Use secure token storage (automatic with new auth)**
|
|
491
|
+
```typescript
|
|
492
|
+
// ✅ Recommended - automatic secure storage
|
|
493
|
+
const apiClient = new mgmtApi.ApiClient();
|
|
494
|
+
await apiClient.auth(); // Tokens stored securely in OS keychain
|
|
495
|
+
|
|
496
|
+
// ✅ CI/CD - manual token (for automated environments)
|
|
497
|
+
const options = new mgmtApi.Options();
|
|
498
|
+
options.token = process.env.AGILITY_ACCESS_TOKEN;
|
|
499
|
+
const apiClient = new mgmtApi.ApiClient(options);
|
|
500
|
+
|
|
501
|
+
// ❌ Bad - hardcoded tokens
|
|
502
|
+
options.token = 'your-secret-token'; // Never do this
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
2. **Leverage automatic token refresh**
|
|
506
|
+
```typescript
|
|
507
|
+
// ✅ Automatic token refresh (new approach)
|
|
508
|
+
const apiClient = new mgmtApi.ApiClient();
|
|
509
|
+
await apiClient.auth();
|
|
510
|
+
|
|
511
|
+
// Token refresh happens automatically on 401 errors
|
|
512
|
+
const contentItem = await apiClient.contentMethods.getContentItem(22, guid, locale);
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
3. **Handle authentication errors gracefully**
|
|
516
|
+
```typescript
|
|
517
|
+
const apiClient = new mgmtApi.ApiClient();
|
|
518
|
+
|
|
519
|
+
try {
|
|
520
|
+
await apiClient.auth();
|
|
521
|
+
const contentItem = await apiClient.contentMethods.getContentItem(22, guid, locale);
|
|
522
|
+
} catch (error) {
|
|
523
|
+
if (error.message.includes('OAuth flow requires manual implementation')) {
|
|
524
|
+
// Handle OAuth flow manually
|
|
525
|
+
console.log('Visit the OAuth URL to authenticate');
|
|
526
|
+
} else {
|
|
527
|
+
// Handle other auth errors
|
|
528
|
+
console.error('Authentication failed:', error);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
4. **Clear tokens on logout**
|
|
534
|
+
```typescript
|
|
535
|
+
const apiClient = new mgmtApi.ApiClient();
|
|
536
|
+
await apiClient.auth();
|
|
537
|
+
|
|
538
|
+
// ... use API client ...
|
|
539
|
+
|
|
540
|
+
// Clear tokens securely on logout
|
|
541
|
+
await apiClient.clearToken();
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
#### Performance Optimization
|
|
545
|
+
|
|
546
|
+
1. **Reuse API client instances**
|
|
547
|
+
```javascript
|
|
548
|
+
// ✅ Good - single instance
|
|
549
|
+
const apiClient = new mgmtApi.ApiClient(options);
|
|
550
|
+
|
|
551
|
+
// Use the same instance for multiple requests
|
|
552
|
+
const content1 = await apiClient.contentMethods.getContentItem(1, guid, locale);
|
|
553
|
+
const content2 = await apiClient.contentMethods.getContentItem(2, guid, locale);
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
2. **Implement request caching**
|
|
557
|
+
```javascript
|
|
558
|
+
class CachedAgilityClient {
|
|
559
|
+
constructor(options) {
|
|
560
|
+
this.client = new mgmtApi.ApiClient(options);
|
|
561
|
+
this.cache = new Map();
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
async getContentItem(id, guid, locale) {
|
|
565
|
+
const cacheKey = `content-${id}-${guid}-${locale}`;
|
|
566
|
+
|
|
567
|
+
if (this.cache.has(cacheKey)) {
|
|
568
|
+
return this.cache.get(cacheKey);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
const result = await this.client.contentMethods.getContentItem(id, guid, locale);
|
|
572
|
+
this.cache.set(cacheKey, result);
|
|
573
|
+
|
|
574
|
+
return result;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
---
|
|
580
|
+
|
|
581
|
+
## Navigation
|
|
582
|
+
- [← Back to Main Documentation](../README.md)
|
|
583
|
+
- [CI/CD & Automated Environments](./cicd.md)
|
|
584
|
+
- [AssetMethods](./asset-methods.md)
|
|
585
|
+
- [BatchMethods](./batch-methods.md)
|
|
586
|
+
- [ContentMethods](./content-methods.md)
|
|
587
|
+
- [ContainerMethods](./container-methods.md)
|
|
588
|
+
- [InstanceMethods](./instance-methods.md)
|
|
589
|
+
- [InstanceUserMethods](./instance-user-methods.md)
|
|
590
|
+
- [ModelMethods](./model-methods.md)
|
|
591
|
+
- [PageMethods](./page-methods.md)
|
|
592
|
+
- [ServerUserMethods](./server-user-methods.md)
|
|
593
|
+
- [WebhookMethods](./webhook-methods.md)
|
|
594
594
|
- [Multi-Instance Operations](./multi-instance-operations.md)
|