@esri/arcgis-rest-developer-credentials 1.1.0 → 2.1.0

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.
Files changed (65) hide show
  1. package/dist/bundled/developer-credentials.esm.js +254 -182
  2. package/dist/bundled/developer-credentials.esm.js.map +1 -1
  3. package/dist/bundled/developer-credentials.esm.min.js +3 -3
  4. package/dist/bundled/developer-credentials.esm.min.js.map +1 -1
  5. package/dist/bundled/developer-credentials.umd.js +257 -182
  6. package/dist/bundled/developer-credentials.umd.js.map +1 -1
  7. package/dist/bundled/developer-credentials.umd.min.js +3 -3
  8. package/dist/bundled/developer-credentials.umd.min.js.map +1 -1
  9. package/dist/cjs/createApiKey.js +37 -12
  10. package/dist/cjs/createApiKey.js.map +1 -1
  11. package/dist/cjs/index.js +1 -2
  12. package/dist/cjs/index.js.map +1 -1
  13. package/dist/cjs/invalidateApiKey.js +46 -0
  14. package/dist/cjs/invalidateApiKey.js.map +1 -0
  15. package/dist/cjs/shared/enum/privileges.js +0 -27
  16. package/dist/cjs/shared/enum/privileges.js.map +1 -1
  17. package/dist/cjs/shared/generateApiKeyToken.js +27 -0
  18. package/dist/cjs/shared/generateApiKeyToken.js.map +1 -0
  19. package/dist/cjs/shared/helpers.js +94 -16
  20. package/dist/cjs/shared/helpers.js.map +1 -1
  21. package/dist/cjs/shared/registerApp.js +1 -5
  22. package/dist/cjs/shared/registerApp.js.map +1 -1
  23. package/dist/cjs/shared/types/apiKeyType.js.map +1 -1
  24. package/dist/cjs/shared/types/appType.js.map +1 -1
  25. package/dist/cjs/updateApiKey.js +45 -26
  26. package/dist/cjs/updateApiKey.js.map +1 -1
  27. package/dist/esm/createApiKey.d.ts +9 -3
  28. package/dist/esm/createApiKey.js +39 -14
  29. package/dist/esm/createApiKey.js.map +1 -1
  30. package/dist/esm/index.d.ts +1 -2
  31. package/dist/esm/index.js +1 -2
  32. package/dist/esm/index.js.map +1 -1
  33. package/dist/esm/invalidateApiKey.d.ts +18 -0
  34. package/dist/esm/invalidateApiKey.js +42 -0
  35. package/dist/esm/invalidateApiKey.js.map +1 -0
  36. package/dist/esm/shared/enum/privileges.d.ts +2 -23
  37. package/dist/esm/shared/enum/privileges.js +1 -26
  38. package/dist/esm/shared/enum/privileges.js.map +1 -1
  39. package/dist/esm/shared/generateApiKeyToken.d.ts +11 -0
  40. package/dist/esm/shared/generateApiKeyToken.js +23 -0
  41. package/dist/esm/shared/generateApiKeyToken.js.map +1 -0
  42. package/dist/esm/shared/helpers.d.ts +42 -7
  43. package/dist/esm/shared/helpers.js +88 -14
  44. package/dist/esm/shared/helpers.js.map +1 -1
  45. package/dist/esm/shared/registerApp.d.ts +1 -1
  46. package/dist/esm/shared/registerApp.js +2 -6
  47. package/dist/esm/shared/registerApp.js.map +1 -1
  48. package/dist/esm/shared/types/apiKeyType.d.ts +54 -5
  49. package/dist/esm/shared/types/apiKeyType.js.map +1 -1
  50. package/dist/esm/shared/types/appType.d.ts +7 -3
  51. package/dist/esm/shared/types/appType.js.map +1 -1
  52. package/dist/esm/updateApiKey.d.ts +8 -2
  53. package/dist/esm/updateApiKey.js +47 -28
  54. package/dist/esm/updateApiKey.js.map +1 -1
  55. package/package.json +1 -1
  56. package/dist/cjs/deleteApiKey.js +0 -43
  57. package/dist/cjs/deleteApiKey.js.map +0 -1
  58. package/dist/cjs/deleteOAuthApp.js +0 -43
  59. package/dist/cjs/deleteOAuthApp.js.map +0 -1
  60. package/dist/esm/deleteApiKey.d.ts +0 -27
  61. package/dist/esm/deleteApiKey.js +0 -39
  62. package/dist/esm/deleteApiKey.js.map +0 -1
  63. package/dist/esm/deleteOAuthApp.d.ts +0 -27
  64. package/dist/esm/deleteOAuthApp.js +0 -39
  65. package/dist/esm/deleteOAuthApp.js.map +0 -1
@@ -1,43 +1,67 @@
1
1
  /* @preserve
2
- * @esri/arcgis-rest-developer-credentials - v1.0.1 - Apache-2.0
2
+ * @esri/arcgis-rest-developer-credentials - v2.0.0 - Apache-2.0
3
3
  * Copyright (c) 2017-2025 Esri, Inc.
4
- * Thu Jan 23 2025 20:05:00 GMT+0000 (Coordinated Universal Time)
4
+ * Wed Feb 26 2025 22:10:42 GMT+0000 (Coordinated Universal Time)
5
5
  */
6
- import { getPortalUrl, createItem, getItem, removeItem } from '@esri/arcgis-rest-portal';
7
- import { appendCustomParams, request } from '@esri/arcgis-rest-request';
6
+ import { getPortalUrl, createItem, updateItem, getItem } from '@esri/arcgis-rest-portal';
7
+ import { request, appendCustomParams } from '@esri/arcgis-rest-request';
8
8
 
9
+ /* Copyright (c) 2023 Environmental Systems Research Institute, Inc.
10
+ * Apache-2.0 */
9
11
  /**
10
- * Used to describe privilege list of an app.
12
+ * Used to retrieve registered app info. See the [REST Documentation](https://developers.arcgis.com/rest/users-groups-and-items/registered-app-info.htm) for more information.
13
+ *
14
+ * ```js
15
+ * import { getRegisteredAppInfo, IApp } from '@esri/arcgis-rest-developer-credentials';
16
+ * import { ArcGISIdentityManager } from "@esri/arcgis-rest-request";
17
+ *
18
+ * const authSession: ArcGISIdentityManager = await ArcGISIdentityManager.signIn({
19
+ * username: "xyz_usrName",
20
+ * password: "xyz_pw"
21
+ * });
22
+ *
23
+ * getRegisteredAppInfo({
24
+ * itemId: "xyz_itemId",
25
+ * authentication: authSession
26
+ * }).then((registeredApp: IApp) => {
27
+ * // => {client_id: "xyz_id", client_secret: "xyz_secret", ...}
28
+ * }).catch(e => {
29
+ * // => an exception object
30
+ * });
31
+ * ```
32
+ *
33
+ * @param requestOptions - Options for {@linkcode getRegisteredAppInfo | getRegisteredAppInfo()}, including an itemId of which app to retrieve and an {@linkcode ArcGISIdentityManager} authentication session.
34
+ * @returns A Promise that will resolve to an {@linkcode IApp} object representing successfully retrieved app.
11
35
  */
12
- var Privileges;
13
- (function (Privileges) {
14
- Privileges["Basemaps"] = "portal:apikey:basemaps";
15
- Privileges["Demographics"] = "premium:user:demographics";
16
- Privileges["Elevation"] = "premium:user:elevation";
17
- Privileges["FeatureReport"] = "premium:user:featurereport";
18
- Privileges["Geocode"] = "premium:user:geocode";
19
- Privileges["GeocodeStored"] = "premium:user:geocode:stored";
20
- Privileges["GeocodeTemporary"] = "premium:user:geocode:temporary";
21
- Privileges["GeoEnrichment"] = "premium:user:geoenrichment";
22
- Privileges["NetworkAnalysis"] = "premium:user:networkanalysis";
23
- Privileges["NetworkAnalysisRouting"] = "premium:user:networkanalysis:routing";
24
- Privileges["NetworkAnalysisOptimizedRouting"] = "premium:user:networkanalysis:optimizedrouting";
25
- Privileges["NetworkAnalysisClosestFacility"] = "premium:user:networkanalysis:closestfacility";
26
- Privileges["NetworkAnalysisServiceArea"] = "premium:user:networkanalysis:servicearea";
27
- Privileges["NetworkAnalysisLocationalLocation"] = "premium:user:networkanalysis:locationallocation";
28
- Privileges["NetworkAnalysisVehicleRouting"] = "premium:user:networkanalysis:vehiclerouting";
29
- Privileges["NetworkAnalysisOriginDestinationCostMatrix"] = "premium:user:networkanalysis:origindestinationcostmatrix";
30
- Privileges["Places"] = "premium:user:places";
31
- Privileges["SpatialAnalysis"] = "premium:user:spatialanalysis";
32
- Privileges["GeoAnalytics"] = "premium:publisher:geoanalytics";
33
- Privileges["RasterAnalysis"] = "premium:publisher:rasteranalysis";
34
- })(Privileges || (Privileges = {}));
36
+ async function getRegisteredAppInfo(requestOptions) {
37
+ const userName = await requestOptions.authentication.getUsername();
38
+ const url = getPortalUrl(requestOptions) +
39
+ `/content/users/${userName}/items/${requestOptions.itemId}/registeredAppInfo`;
40
+ requestOptions.httpMethod = "POST";
41
+ const registeredAppResponse = await request(url, Object.assign(Object.assign({}, requestOptions), { params: { f: "json" } }));
42
+ return registeredAppResponseToApp(registeredAppResponse);
43
+ }
44
+
45
+ async function generateApiKeyToken(options) {
46
+ const portal = getPortalUrl(options);
47
+ const url = `${portal}/oauth2/token`;
48
+ const appInfo = await getRegisteredAppInfo({
49
+ itemId: options.itemId,
50
+ authentication: options.authentication
51
+ });
52
+ const params = {
53
+ client_id: appInfo.client_id,
54
+ client_secret: appInfo.client_secret,
55
+ apiToken: options.apiKey,
56
+ regenerateApiToken: true,
57
+ grant_type: "client_credentials"
58
+ };
59
+ // authentication is not being passed to the request because client_secret acts as the auth
60
+ return request(url, {
61
+ params
62
+ });
63
+ }
35
64
 
36
- /**
37
- * @internal
38
- * Used to check privileges validity.
39
- */
40
- const arePrivilegesValid = (privileges) => privileges.every((element) => Object.values(Privileges).includes(element));
41
65
  /**
42
66
  * @internal
43
67
  * Encode special params value (e.g. array type...) in advance in order to make {@linkcode encodeParam} works correctly. Usage is case by case.
@@ -59,7 +83,8 @@ function registeredAppResponseToApp(response) {
59
83
  "apnsProdCert",
60
84
  "apnsSandboxCert",
61
85
  "gcmApiKey",
62
- "isBeta"
86
+ "isBeta",
87
+ "customAppLoginShowTriage"
63
88
  ];
64
89
  const dateKeys = ["modified", "registered"];
65
90
  return Object.keys(response)
@@ -79,13 +104,11 @@ function registeredAppResponseToApp(response) {
79
104
  * Used to convert {@linkcode IApp} to {@linkcode IApiKeyInfo} only if `appType` is "apikey".
80
105
  */
81
106
  function appToApiKeyProperties(response) {
82
- if (response.appType !== "apikey" || !("apiKey" in response)) {
83
- throw new Error("Item is not an API key.");
84
- }
85
- delete response.client_id;
86
107
  delete response.client_secret;
87
108
  delete response.redirect_uris;
88
109
  delete response.appType;
110
+ delete response.customAppLoginShowTriage;
111
+ delete response.apiKey;
89
112
  return response;
90
113
  }
91
114
  /**
@@ -93,12 +116,14 @@ function appToApiKeyProperties(response) {
93
116
  * Used to convert {@linkcode IApp} to {@linkcode IOAuthAppInfo}.
94
117
  */
95
118
  function appToOAuthAppProperties(response) {
96
- if (response.appType === "apikey") {
97
- throw new Error("Item is not an OAuth 2.0 app.");
98
- }
99
119
  delete response.appType;
100
120
  delete response.httpReferrers;
101
121
  delete response.privileges;
122
+ delete response.apiKey;
123
+ delete response.customAppLoginShowTriage;
124
+ delete response.isPersonalAPIToken;
125
+ delete response.apiToken1Active;
126
+ delete response.apiToken2Active;
102
127
  return response;
103
128
  }
104
129
  /**
@@ -131,6 +156,84 @@ function filterKeys(object, includedKeys) {
131
156
  return obj;
132
157
  }, {});
133
158
  }
159
+ /**
160
+ * Used to determine if a generated key is in slot 1 or slot 2 key. The full API key should be passed. `undefined` will be returned if the proper slot could not be identified.
161
+ */
162
+ function slotForKey(key) {
163
+ const slot = parseInt(key.substring(key.length - 10, key.length - 9));
164
+ if (slot === 1 || slot === 2) {
165
+ return slot;
166
+ }
167
+ return undefined;
168
+ }
169
+ /**
170
+ * @internal
171
+ * Used to determine which slot to invalidate a key in given a number or a full or patial key.
172
+ */
173
+ function slotForInvalidationKey(param) {
174
+ if (param === 1 || param === 2) {
175
+ return param;
176
+ }
177
+ if (typeof param !== "string") {
178
+ return undefined;
179
+ }
180
+ const fullKeySlot = slotForKey(param);
181
+ if (fullKeySlot) {
182
+ return fullKeySlot;
183
+ }
184
+ }
185
+ /**
186
+ * @internal
187
+ * Used to generate tokens in slot 1 and/or 2 of an API key.
188
+ */
189
+ function generateApiKeyTokens(itemId, slots, requestOptions) {
190
+ return Promise.all(slots.map((slot) => {
191
+ return generateApiKeyToken(Object.assign({ itemId, apiKey: slot }, requestOptions));
192
+ })).then((responses) => {
193
+ return responses
194
+ .map((responses) => responses.access_token)
195
+ .reduce((obj, token, index) => {
196
+ obj[`accessToken${slotForKey(token)}`] = token;
197
+ return obj;
198
+ }, {});
199
+ });
200
+ }
201
+ /**
202
+ * @internal
203
+ * Convert boolean flags to an array of slots for {@linkcode generateApiKeyTokens}.
204
+ */
205
+ function generateOptionsToSlots(generateToken1, generateToken2) {
206
+ const slots = [];
207
+ if (generateToken1) {
208
+ slots.push(1);
209
+ }
210
+ if (generateToken2) {
211
+ slots.push(2);
212
+ }
213
+ return slots;
214
+ }
215
+ /**
216
+ * @internal
217
+ * Build params for updating expiration dates
218
+ */
219
+ function buildExpirationDateParams(requestOptions, fillDefaults) {
220
+ const updateparams = {};
221
+ if (requestOptions.apiToken1ExpirationDate) {
222
+ updateparams.apiToken1ExpirationDate =
223
+ requestOptions.apiToken1ExpirationDate;
224
+ }
225
+ if (requestOptions.apiToken2ExpirationDate) {
226
+ updateparams.apiToken2ExpirationDate =
227
+ requestOptions.apiToken2ExpirationDate;
228
+ }
229
+ if (fillDefaults && !updateparams.apiToken1ExpirationDate) {
230
+ updateparams.apiToken1ExpirationDate = -1;
231
+ }
232
+ if (fillDefaults && !updateparams.apiToken2ExpirationDate) {
233
+ updateparams.apiToken2ExpirationDate = -1;
234
+ }
235
+ return updateparams;
236
+ }
134
237
 
135
238
  /* Copyright (c) 2023 Environmental Systems Research Institute, Inc.
136
239
  * Apache-2.0 */
@@ -158,7 +261,7 @@ function filterKeys(object, includedKeys) {
158
261
  * appType: "multiple",
159
262
  * redirect_uris: ["http://localhost:3000/"],
160
263
  * httpReferrers: ["http://localhost:3000/"],
161
- * privileges: [Privileges.Geocode, Privileges.FeatureReport],
264
+ * privileges: ["premium:user:geocode:temporary", Privileges.FeatureReport],
162
265
  * authentication: authSession
163
266
  * }).then((registeredApp: IApp) => {
164
267
  * // => {client_id: "xyz_id", client_secret: "xyz_secret", ...}
@@ -171,10 +274,6 @@ function filterKeys(object, includedKeys) {
171
274
  * @returns A Promise that will resolve to an {@linkcode IApp} object representing the newly registered app.
172
275
  */
173
276
  async function registerApp(requestOptions) {
174
- // privileges validation
175
- if (!arePrivilegesValid(requestOptions.privileges)) {
176
- throw new Error("The `privileges` option contains invalid privileges.");
177
- }
178
277
  // build params
179
278
  const options = appendCustomParams(requestOptions, [
180
279
  "itemId",
@@ -206,14 +305,20 @@ async function registerApp(requestOptions) {
206
305
  * password: "xyz_pw"
207
306
  * });
208
307
  *
308
+ * const threeDaysFromToday = new Date();
309
+ * threeDaysFromToday.setDate(threeDaysFromToday.getDate() + 3);
310
+ * threeDaysFromToday.setHours(23, 59, 59, 999);
311
+ *
209
312
  * createApiKey({
210
313
  * title: "xyz_title",
211
314
  * description: "xyz_desc",
212
315
  * tags: ["xyz_tag1", "xyz_tag2"],
213
- * privileges: [Privileges.Geocode, Privileges.FeatureReport],
214
- * authentication: authSession
316
+ * privileges: ["premium:user:networkanalysis:routing"],
317
+ * authentication: authSession,
318
+ * generateToken1: true, // optional,generate a new token
319
+ * apiToken1ExpirationDate: threeDaysFromToday // optional, update expiration date
215
320
  * }).then((registeredAPIKey: IApiKeyResponse) => {
216
- * // => {apiKey: "xyz_key", item: {tags: ["xyz_tag1", "xyz_tag2"], ...}, ...}
321
+ * // => {accessToken1: "xyz_key", item: {tags: ["xyz_tag1", "xyz_tag2"], ...}, ...}
217
322
  * }).catch(e => {
218
323
  * // => an exception object
219
324
  * });
@@ -223,9 +328,6 @@ async function registerApp(requestOptions) {
223
328
  * @returns A Promise that will resolve to an {@linkcode IApiKeyResponse} object representing the newly registered API key.
224
329
  */
225
330
  async function createApiKey(requestOptions) {
226
- if (!arePrivilegesValid(requestOptions.privileges)) {
227
- throw new Error("The `privileges` option contains invalid privileges.");
228
- }
229
331
  requestOptions.httpMethod = "POST";
230
332
  // filter param buckets:
231
333
  const baseRequestOptions = extractBaseRequestOptions(requestOptions); // snapshot of basic IRequestOptions before customized params being built into it
@@ -245,52 +347,37 @@ async function createApiKey(requestOptions) {
245
347
  "typeKeywords",
246
348
  "url"
247
349
  ];
248
- // step 1: add item
249
- const createItemOption = Object.assign(Object.assign({ item: Object.assign(Object.assign({}, filterKeys(requestOptions, itemAddProperties)), { type: "API Key" }) }, baseRequestOptions), { authentication: requestOptions.authentication, params: {
350
+ /**
351
+ * step 1: create item
352
+ */
353
+ const createItemOption = Object.assign(Object.assign({ item: Object.assign(Object.assign({}, filterKeys(requestOptions, itemAddProperties)), { type: "Application" }) }, baseRequestOptions), { authentication: requestOptions.authentication, params: {
250
354
  f: "json"
251
355
  } });
252
356
  const createItemResponse = await createItem(createItemOption);
253
- // step 2: register app
254
- const registerAppOption = Object.assign(Object.assign({ itemId: createItemResponse.id, appType: "apikey", redirect_uris: [], httpReferrers: requestOptions.httpReferrers || [], privileges: requestOptions.privileges }, baseRequestOptions), { authentication: requestOptions.authentication });
255
- const registeredAppResponse = await registerApp(registerAppOption);
357
+ /**
358
+ * getRegisteredAppInfoRoute
359
+ */
360
+ const registerAppOptions = Object.assign(Object.assign({ itemId: createItemResponse.id, appType: "multiple", redirect_uris: ["urn:ietf:wg:oauth:2.0:oob"], httpReferrers: requestOptions.httpReferrers || [], privileges: requestOptions.privileges }, baseRequestOptions), { authentication: requestOptions.authentication });
361
+ const registeredAppResponse = await registerApp(registerAppOptions);
362
+ /**
363
+ * step 3: update item with desired expiration dates
364
+ * you cannot set the expiration date propierties until you
365
+ * regiester the app so this has to be a seperate step
366
+ */
367
+ await updateItem(Object.assign(Object.assign({}, baseRequestOptions), { item: Object.assign({ id: createItemResponse.id }, buildExpirationDateParams(requestOptions, true)), authentication: requestOptions.authentication }));
368
+ /*
369
+ * step 4: get item info
370
+ */
256
371
  const itemInfo = await getItem(registeredAppResponse.itemId, Object.assign(Object.assign({}, baseRequestOptions), { authentication: requestOptions.authentication, params: { f: "json" } }));
257
- return Object.assign(Object.assign({}, appToApiKeyProperties(registeredAppResponse)), { item: itemInfo });
258
- }
259
-
260
- /* Copyright (c) 2023 Environmental Systems Research Institute, Inc.
261
- * Apache-2.0 */
262
- /**
263
- * Used to retrieve registered app info. See the [REST Documentation](https://developers.arcgis.com/rest/users-groups-and-items/registered-app-info.htm) for more information.
264
- *
265
- * ```js
266
- * import { getRegisteredAppInfo, IApp } from '@esri/arcgis-rest-developer-credentials';
267
- * import { ArcGISIdentityManager } from "@esri/arcgis-rest-request";
268
- *
269
- * const authSession: ArcGISIdentityManager = await ArcGISIdentityManager.signIn({
270
- * username: "xyz_usrName",
271
- * password: "xyz_pw"
272
- * });
273
- *
274
- * getRegisteredAppInfo({
275
- * itemId: "xyz_itemId",
276
- * authentication: authSession
277
- * }).then((registeredApp: IApp) => {
278
- * // => {client_id: "xyz_id", client_secret: "xyz_secret", ...}
279
- * }).catch(e => {
280
- * // => an exception object
281
- * });
282
- * ```
283
- *
284
- * @param requestOptions - Options for {@linkcode getRegisteredAppInfo | getRegisteredAppInfo()}, including an itemId of which app to retrieve and an {@linkcode ArcGISIdentityManager} authentication session.
285
- * @returns A Promise that will resolve to an {@linkcode IApp} object representing successfully retrieved app.
286
- */
287
- async function getRegisteredAppInfo(requestOptions) {
288
- const userName = await requestOptions.authentication.getUsername();
289
- const url = getPortalUrl(requestOptions) +
290
- `/content/users/${userName}/items/${requestOptions.itemId}/registeredAppInfo`;
291
- requestOptions.httpMethod = "POST";
292
- const registeredAppResponse = await request(url, Object.assign(Object.assign({}, requestOptions), { params: { f: "json" } }));
293
- return registeredAppResponseToApp(registeredAppResponse);
372
+ /**
373
+ * step 5: generate tokens if requested
374
+ */
375
+ const generatedTokens = await generateApiKeyTokens(itemInfo.id, generateOptionsToSlots(requestOptions.generateToken1, requestOptions.generateToken2), Object.assign(Object.assign({}, baseRequestOptions), { authentication: requestOptions.authentication }));
376
+ /**
377
+ * step 6: get registered app info to get updated active key status
378
+ */
379
+ const updatedRegisteredAppResponse = await getRegisteredAppInfo(Object.assign(Object.assign({}, baseRequestOptions), { itemId: itemInfo.id, authentication: requestOptions.authentication }));
380
+ return Object.assign(Object.assign(Object.assign({}, generatedTokens), appToApiKeyProperties(updatedRegisteredAppResponse)), { item: itemInfo });
294
381
  }
295
382
 
296
383
  /* Copyright (c) 2023 Environmental Systems Research Institute, Inc.
@@ -311,13 +398,19 @@ async function getRegisteredAppInfo(requestOptions) {
311
398
  * password: "xyz_pw"
312
399
  * });
313
400
  *
401
+ * const threeDaysFromToday = new Date();
402
+ * threeDaysFromToday.setDate(threeDaysFromToday.getDate() + 3);
403
+ * threeDaysFromToday.setHours(23, 59, 59, 999);
404
+ *
314
405
  * updateApiKey({
315
406
  * itemId: "xyz_itemId",
316
- * privileges: [Privileges.Geocode],
407
+ * privileges: ["premium:user:geocode:temporary"],
317
408
  * httpReferrers: [], // httpReferrers will be set to be empty
318
409
  * authentication: authSession
410
+ * generateToken1: true, // optional,generate a new token
411
+ * apiToken1ExpirationDate: threeDaysFromToday // optional, update expiration date
319
412
  * }).then((updatedAPIKey: IApiKeyResponse) => {
320
- * // => {apiKey: "xyz_key", item: {tags: ["xyz_tag1", "xyz_tag2"], ...}, ...}
413
+ * // => {accessToken1: "xyz_key", item: {tags: ["xyz_tag1", "xyz_tag2"], ...}, ...}
321
414
  * }).catch(e => {
322
415
  * // => an exception object
323
416
  * });
@@ -327,33 +420,46 @@ async function getRegisteredAppInfo(requestOptions) {
327
420
  * @returns A Promise that will resolve to an {@linkcode IApiKeyResponse} object representing updated API key.
328
421
  */
329
422
  async function updateApiKey(requestOptions) {
330
- // privileges validation
331
- if (requestOptions.privileges &&
332
- !arePrivilegesValid(requestOptions.privileges)) {
333
- throw new Error("The `privileges` option contains invalid privileges.");
334
- }
335
423
  requestOptions.httpMethod = "POST";
336
- // get app
337
424
  const baseRequestOptions = extractBaseRequestOptions(requestOptions); // get base requestOptions snapshot
338
- const getAppOption = Object.assign(Object.assign({}, baseRequestOptions), { authentication: requestOptions.authentication, itemId: requestOptions.itemId });
339
- const appResponse = await getRegisteredAppInfo(getAppOption);
340
- // appType must be APIKey to continue
341
- if (appResponse.appType !== "apikey" || !("apiKey" in appResponse)) {
342
- throw new Error("Item is not an API key.");
425
+ /**
426
+ * step 1: update expiration dates if provided. Build the object up to avoid overwriting any existing properties.
427
+ */
428
+ if (requestOptions.apiToken1ExpirationDate ||
429
+ requestOptions.apiToken2ExpirationDate) {
430
+ const updateParams = buildExpirationDateParams(requestOptions);
431
+ await updateItem(Object.assign(Object.assign({}, baseRequestOptions), { item: Object.assign({ id: requestOptions.itemId }, updateParams), authentication: requestOptions.authentication }));
343
432
  }
344
- const clientId = appResponse.client_id;
345
- const options = appendCustomParams(Object.assign(Object.assign({}, appResponse), requestOptions), // object with the custom params to look in
346
- ["privileges", "httpReferrers"] // keys you want copied to the params object
347
- );
348
- options.params.f = "json";
349
- // encode special params value (e.g. array type...) in advance in order to make encodeQueryString() works correctly
350
- stringifyArrays(options);
351
- const url = getPortalUrl(options) + `/oauth2/apps/${clientId}/update`;
352
- // Raw response from `/oauth2/apps/${clientId}/update`, apiKey not included because key is same.
353
- const updateResponse = await request(url, Object.assign(Object.assign({}, options), { authentication: requestOptions.authentication }));
354
- const app = registeredAppResponseToApp(Object.assign(Object.assign({}, updateResponse), { apiKey: appResponse.apiKey }));
355
- const itemInfo = await getItem(requestOptions.itemId, Object.assign(Object.assign({}, baseRequestOptions), { authentication: requestOptions.authentication, params: { f: "json" } }));
356
- return Object.assign(Object.assign({}, appToApiKeyProperties(app)), { item: itemInfo });
433
+ /**
434
+ * step 2: update privileges and httpReferrers if provided. Build the object up to avoid overwriting any existing properties.
435
+ */
436
+ if (requestOptions.privileges || requestOptions.httpReferrers) {
437
+ const getAppOption = Object.assign(Object.assign({}, baseRequestOptions), { authentication: requestOptions.authentication, itemId: requestOptions.itemId });
438
+ const appResponse = await getRegisteredAppInfo(getAppOption);
439
+ const clientId = appResponse.client_id;
440
+ const options = appendCustomParams(Object.assign(Object.assign({}, appResponse), requestOptions), // object with the custom params to look in
441
+ ["privileges", "httpReferrers"] // keys you want copied to the params object
442
+ );
443
+ options.params.f = "json";
444
+ // encode special params value (e.g. array type...) in advance in order to make encodeQueryString() works correctly
445
+ stringifyArrays(options);
446
+ const url = getPortalUrl(options) + `/oauth2/apps/${clientId}/update`;
447
+ // Raw response from `/oauth2/apps/${clientId}/update`, apiKey not included because key is same.
448
+ await request(url, Object.assign(Object.assign({}, options), { authentication: requestOptions.authentication }));
449
+ }
450
+ /**
451
+ * step 3: get the updated item info to return to the user.
452
+ */
453
+ const updatedItemInfo = await getItem(requestOptions.itemId, Object.assign(Object.assign({}, baseRequestOptions), { authentication: requestOptions.authentication, params: { f: "json" } }));
454
+ /**
455
+ * step 4: generate tokens if requested
456
+ */
457
+ const generatedTokens = await generateApiKeyTokens(requestOptions.itemId, generateOptionsToSlots(requestOptions.generateToken1, requestOptions.generateToken2), Object.assign(Object.assign({}, baseRequestOptions), { authentication: requestOptions.authentication }));
458
+ /**
459
+ * step 5: get updated registered app info
460
+ */
461
+ const updatedRegisteredAppResponse = await getRegisteredAppInfo(Object.assign(Object.assign({}, baseRequestOptions), { itemId: requestOptions.itemId, authentication: requestOptions.authentication }));
462
+ return Object.assign(Object.assign(Object.assign({}, generatedTokens), appToApiKeyProperties(updatedRegisteredAppResponse)), { item: updatedItemInfo });
357
463
  }
358
464
 
359
465
  /* Copyright (c) 2023 Environmental Systems Research Institute, Inc.
@@ -392,37 +498,39 @@ async function getApiKey(requestOptions) {
392
498
  /* Copyright (c) 2023 Environmental Systems Research Institute, Inc.
393
499
  * Apache-2.0 */
394
500
  /**
395
- * Used to delete the API Key with given `itemId`.
501
+ * Used to invalidate an API key.
396
502
  *
397
503
  * ```js
398
- * import { deleteApiKey, IDeleteApiKeyResponse } from '@esri/arcgis-rest-developer-credentials';
399
- * import { ArcGISIdentityManager } from "@esri/arcgis-rest-request";
400
- *
401
- * const authSession: ArcGISIdentityManager = await ArcGISIdentityManager.signIn({
402
- * username: "xyz_usrName",
403
- * password: "xyz_pw"
404
- * });
405
- *
406
- * deleteApiKey({
407
- * itemId: "xyz_itemId",
408
- * authentication: authSession
409
- * }).then((deletedApiKey: IDeleteApiKeyResponse) => {
410
- * // => {itemId: "xyz_itemId", success: true}
504
+ * import { invalidateApiKey } from "@esri/arcgis-rest-developer-credentials";
505
+ *
506
+ * invalidateApiKey({
507
+ * itemId: ITEM_ID,
508
+ * authentication,
509
+ * apiKey: 1, // invalidate the key in slot 1
510
+ * }).then((response) => {
511
+ * // => {success: true}
411
512
  * }).catch(e => {
412
513
  * // => an exception object
413
514
  * });
414
- * ```
415
- *
416
- * @param requestOptions - Options for {@linkcode deleteApiKey | deleteApiKey()}, including `itemId` of which API key to be deleted and an {@linkcode ArcGISIdentityManager} authentication session.
417
- * @returns A Promise that will resolve to an {@linkcode IDeleteApiKeyResponse} object representing deletion status.
418
515
  */
419
- async function deleteApiKey(requestOptions) {
420
- requestOptions.httpMethod = "POST";
421
- const baseRequestOptions = extractBaseRequestOptions(requestOptions);
422
- // validate provided itemId associates with API Key
423
- await getApiKey(Object.assign(Object.assign({}, baseRequestOptions), { itemId: requestOptions.itemId, authentication: requestOptions.authentication }));
424
- const removeItemResponse = await removeItem(Object.assign(Object.assign({}, baseRequestOptions), { id: requestOptions.itemId, authentication: requestOptions.authentication, params: { f: "json" } }));
425
- return removeItemResponse;
516
+ async function invalidateApiKey(requestOptions) {
517
+ const portal = getPortalUrl(requestOptions);
518
+ const url = `${portal}/oauth2/revokeToken`;
519
+ const appInfo = await getRegisteredAppInfo({
520
+ itemId: requestOptions.itemId,
521
+ authentication: requestOptions.authentication
522
+ });
523
+ const params = {
524
+ client_id: appInfo.client_id,
525
+ client_secret: appInfo.client_secret,
526
+ apiToken: slotForInvalidationKey(requestOptions.apiKey),
527
+ regenerateApiToken: true,
528
+ grant_type: "client_credentials"
529
+ };
530
+ // authentication is not being passed to the request because client_secret acts as the auth
531
+ return request(url, {
532
+ params
533
+ });
426
534
  }
427
535
 
428
536
  /* Copyright (c) 2023 Environmental Systems Research Institute, Inc.
@@ -458,42 +566,6 @@ async function getOAuthApp(requestOptions) {
458
566
  return Object.assign(Object.assign({}, appToOAuthAppProperties(appResponse)), { item: itemInfo });
459
567
  }
460
568
 
461
- /* Copyright (c) 2023 Environmental Systems Research Institute, Inc.
462
- * Apache-2.0 */
463
- /**
464
- * Used to delete the OAuth2.0 app with given `itemId`.
465
- *
466
- * ```js
467
- * import { deleteOAuthApp, IDeleteOAuthAppResponse } from '@esri/arcgis-rest-developer-credentials';
468
- * import { ArcGISIdentityManager } from "@esri/arcgis-rest-request";
469
- *
470
- * const authSession: ArcGISIdentityManager = await ArcGISIdentityManager.signIn({
471
- * username: "xyz_usrName",
472
- * password: "xyz_pw"
473
- * });
474
- *
475
- * deleteOAuthApp({
476
- * itemId: "xyz_itemId",
477
- * authentication: authSession
478
- * }).then((deletedOAuthApp: IDeleteOAuthAppResponse) => {
479
- * // => {itemId: "xyz_itemId", success: true}
480
- * }).catch(e => {
481
- * // => an exception object
482
- * });
483
- * ```
484
- *
485
- * @param requestOptions - Options for {@linkcode deleteOAuthApp | deleteOAuthApp()}, including `itemId` of which OAuth app to be deleted and an {@linkcode ArcGISIdentityManager} authentication session.
486
- * @returns A Promise that will resolve to an {@linkcode IDeleteOAuthAppResponse} object representing deletion status.
487
- */
488
- async function deleteOAuthApp(requestOptions) {
489
- requestOptions.httpMethod = "POST";
490
- const baseRequestOptions = extractBaseRequestOptions(requestOptions);
491
- // validate provided itemId associates with OAuth app
492
- await getOAuthApp(Object.assign(Object.assign({}, baseRequestOptions), { itemId: requestOptions.itemId, authentication: requestOptions.authentication }));
493
- const removeItemResponse = await removeItem(Object.assign(Object.assign({}, baseRequestOptions), { id: requestOptions.itemId, authentication: requestOptions.authentication, params: { f: "json" } }));
494
- return removeItemResponse;
495
- }
496
-
497
569
  /* Copyright (c) 2023 Environmental Systems Research Institute, Inc.
498
570
  * Apache-2.0 */
499
571
  /**
@@ -650,5 +722,5 @@ async function unregisterApp(requestOptions) {
650
722
  return unregisterAppResponse;
651
723
  }
652
724
 
653
- export { Privileges, appToApiKeyProperties, appToOAuthAppProperties, arePrivilegesValid, createApiKey, createOAuthApp, deleteApiKey, deleteOAuthApp, extractBaseRequestOptions, filterKeys, getApiKey, getOAuthApp, getRegisteredAppInfo, registerApp, registeredAppResponseToApp, stringifyArrays, unregisterApp, updateApiKey, updateOAuthApp };
725
+ export { appToApiKeyProperties, appToOAuthAppProperties, buildExpirationDateParams, createApiKey, createOAuthApp, extractBaseRequestOptions, filterKeys, generateApiKeyTokens, generateOptionsToSlots, getApiKey, getOAuthApp, getRegisteredAppInfo, invalidateApiKey, registerApp, registeredAppResponseToApp, slotForInvalidationKey, slotForKey, stringifyArrays, unregisterApp, updateApiKey, updateOAuthApp };
654
726
  //# sourceMappingURL=developer-credentials.esm.js.map