autnam 1.0.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 (490) hide show
  1. package/.env.example +2 -0
  2. package/.eslintrc.json +37 -0
  3. package/.github/workflows/deploy-docs.yml +27 -0
  4. package/.github/workflows/pre-release-tests.yml +49 -0
  5. package/.github/workflows/pull-request-tests.yml +49 -0
  6. package/.github/workflows/release.yml +75 -0
  7. package/.prettierignore +2 -0
  8. package/.prettierrc +19 -0
  9. package/.vscode/launch.json +25 -0
  10. package/.vscode/settings.json +5 -0
  11. package/codecov.yml +7 -0
  12. package/commitlint.config.cjs +41 -0
  13. package/demos/.eslintrc.js +13 -0
  14. package/demos/ago-node-cli/README.md +30 -0
  15. package/demos/ago-node-cli/ago.js +31 -0
  16. package/demos/ago-node-cli/index.js +11 -0
  17. package/demos/ago-node-cli/lib/item-export-command.js +48 -0
  18. package/demos/ago-node-cli/lib/item-search-command.js +32 -0
  19. package/demos/ago-node-cli/package.json +29 -0
  20. package/demos/attachments-browser/README.md +7 -0
  21. package/demos/attachments-browser/index.html +165 -0
  22. package/demos/attachments-browser/package.json +15 -0
  23. package/demos/attachments-node/README.md +4 -0
  24. package/demos/attachments-node/image.jpg +0 -0
  25. package/demos/attachments-node/index.js +89 -0
  26. package/demos/attachments-node/package.json +19 -0
  27. package/demos/batch-geocoder-node/NYC_Restaurant_Inspection_Results.csv +100 -0
  28. package/demos/batch-geocoder-node/README.md +13 -0
  29. package/demos/batch-geocoder-node/batch-geocode.js +112 -0
  30. package/demos/batch-geocoder-node/config-template.js +18 -0
  31. package/demos/batch-geocoder-node/package.json +34 -0
  32. package/demos/browser-es-modules/README.md +7 -0
  33. package/demos/browser-es-modules/index.html +38 -0
  34. package/demos/browser-es-modules/package.json +15 -0
  35. package/demos/deno/README.md +1 -0
  36. package/demos/deno/import_map.json +8 -0
  37. package/demos/deno/index.js +5 -0
  38. package/demos/express/README.md +16 -0
  39. package/demos/express/config.json.template +3 -0
  40. package/demos/express/package.json +15 -0
  41. package/demos/express/server.js +37 -0
  42. package/demos/feature-service-browser/README.md +8 -0
  43. package/demos/feature-service-browser/index.html +122 -0
  44. package/demos/feature-service-browser/package.json +15 -0
  45. package/demos/geocoder-browser/README.md +12 -0
  46. package/demos/geocoder-browser/authenticate.html +29 -0
  47. package/demos/geocoder-browser/config.js.template +1 -0
  48. package/demos/geocoder-browser/index.html +139 -0
  49. package/demos/geocoder-browser/package.json +15 -0
  50. package/demos/jsapi-integration/README.md +26 -0
  51. package/demos/jsapi-integration/authenticate.html +29 -0
  52. package/demos/jsapi-integration/config.js +6 -0
  53. package/demos/jsapi-integration/index.html +91 -0
  54. package/demos/jsapi-integration/package.json +15 -0
  55. package/demos/node-cli-item-management/README.md +11 -0
  56. package/demos/node-cli-item-management/index.js +229 -0
  57. package/demos/node-cli-item-management/package.json +24 -0
  58. package/demos/node-cli-item-management/screenshot.png +0 -0
  59. package/demos/node-common-js/README.md +4 -0
  60. package/demos/node-common-js/index.js +5 -0
  61. package/demos/node-common-js/package.json +15 -0
  62. package/demos/node-es-modules/README.md +4 -0
  63. package/demos/node-es-modules/index.js +5 -0
  64. package/demos/node-es-modules/package.json +16 -0
  65. package/demos/oauth2-browser/README.md +17 -0
  66. package/demos/oauth2-browser/authenticate.html +29 -0
  67. package/demos/oauth2-browser/config.js.template +6 -0
  68. package/demos/oauth2-browser/index.html +223 -0
  69. package/demos/oauth2-browser/logo.svg +4 -0
  70. package/demos/oauth2-browser/package.json +15 -0
  71. package/demos/oauth2-browser/style.css +36 -0
  72. package/demos/oauth2-browser-retry/README.md +32 -0
  73. package/demos/oauth2-browser-retry/authenticate.html +21 -0
  74. package/demos/oauth2-browser-retry/index.html +114 -0
  75. package/demos/oauth2-browser-retry/logo.svg +4 -0
  76. package/demos/oauth2-browser-retry/package.json +15 -0
  77. package/demos/parcel/README.md +8 -0
  78. package/demos/parcel/index.html +16 -0
  79. package/demos/parcel/index.js +8 -0
  80. package/demos/parcel/package.json +20 -0
  81. package/demos/snowpack/README.md +8 -0
  82. package/demos/snowpack/index.html +16 -0
  83. package/demos/snowpack/index.js +8 -0
  84. package/demos/snowpack/package.json +20 -0
  85. package/demos/snowpack/snowpack.config.js +3 -0
  86. package/demos/stream-response-to-file/README.md +7 -0
  87. package/demos/stream-response-to-file/index.js +36 -0
  88. package/demos/stream-response-to-file/output/.gitkeep +0 -0
  89. package/demos/stream-response-to-file/package.json +31 -0
  90. package/demos/tree-shaking-rollup/.babelrc +3 -0
  91. package/demos/tree-shaking-rollup/README.md +9 -0
  92. package/demos/tree-shaking-rollup/index.html +11 -0
  93. package/demos/tree-shaking-rollup/package.json +22 -0
  94. package/demos/tree-shaking-rollup/rollup.config.js +13 -0
  95. package/demos/tree-shaking-rollup/src/index.js +8 -0
  96. package/demos/tree-shaking-webpack/README.md +9 -0
  97. package/demos/tree-shaking-webpack/index.html +11 -0
  98. package/demos/tree-shaking-webpack/package.json +26 -0
  99. package/demos/tree-shaking-webpack/src/index.js +8 -0
  100. package/demos/tree-shaking-webpack/webpack.config.js +29 -0
  101. package/demos/vite/README.md +6 -0
  102. package/demos/vite/favicon.svg +15 -0
  103. package/demos/vite/index.html +13 -0
  104. package/demos/vite/main.js +8 -0
  105. package/demos/vite/package.json +18 -0
  106. package/demos/vue/.env.example +11 -0
  107. package/demos/vue/.eslintrc.js +17 -0
  108. package/demos/vue/.postcssrc.js +5 -0
  109. package/demos/vue/README.md +17 -0
  110. package/demos/vue/babel.config.js +3 -0
  111. package/demos/vue/package.json +35 -0
  112. package/demos/vue/public/favicon.ico +0 -0
  113. package/demos/vue/public/index.html +24 -0
  114. package/demos/vue/src/assets/logo.svg +29 -0
  115. package/demos/vue/src/components/App.vue +303 -0
  116. package/demos/vue/src/components/Authenticate.vue +65 -0
  117. package/demos/vue/src/components/Loader.vue +230 -0
  118. package/demos/vue/src/main.js +92 -0
  119. package/demos/webmap-checker-sapper/.env.example +5 -0
  120. package/demos/webmap-checker-sapper/README.md +124 -0
  121. package/demos/webmap-checker-sapper/appveyor.yml +18 -0
  122. package/demos/webmap-checker-sapper/cypress/fixtures/example.json +5 -0
  123. package/demos/webmap-checker-sapper/cypress/integration/spec.js +19 -0
  124. package/demos/webmap-checker-sapper/cypress/plugins/index.js +17 -0
  125. package/demos/webmap-checker-sapper/cypress/support/commands.js +25 -0
  126. package/demos/webmap-checker-sapper/cypress/support/index.js +20 -0
  127. package/demos/webmap-checker-sapper/cypress.json +4 -0
  128. package/demos/webmap-checker-sapper/package.json +47 -0
  129. package/demos/webmap-checker-sapper/rollup.config.js +87 -0
  130. package/demos/webmap-checker-sapper/src/client.js +20 -0
  131. package/demos/webmap-checker-sapper/src/components/LayerStatus.html +108 -0
  132. package/demos/webmap-checker-sapper/src/components/Nav.html +21 -0
  133. package/demos/webmap-checker-sapper/src/components/WebMap.html +62 -0
  134. package/demos/webmap-checker-sapper/src/routes/_error.html +41 -0
  135. package/demos/webmap-checker-sapper/src/routes/_layout.html +21 -0
  136. package/demos/webmap-checker-sapper/src/routes/auth/authorize.js +18 -0
  137. package/demos/webmap-checker-sapper/src/routes/auth/exchange-token.js +20 -0
  138. package/demos/webmap-checker-sapper/src/routes/auth/post-sign-in.js +24 -0
  139. package/demos/webmap-checker-sapper/src/routes/auth/sign-out.js +10 -0
  140. package/demos/webmap-checker-sapper/src/routes/index.html +20 -0
  141. package/demos/webmap-checker-sapper/src/routes/webmaps/[webmapId].html +83 -0
  142. package/demos/webmap-checker-sapper/src/routes/webmaps/index.html +59 -0
  143. package/demos/webmap-checker-sapper/src/server.js +102 -0
  144. package/demos/webmap-checker-sapper/src/service-worker.js +82 -0
  145. package/demos/webmap-checker-sapper/src/template.html +33 -0
  146. package/demos/webmap-checker-sapper/src/userInfoMiddleware.js +21 -0
  147. package/demos/webmap-checker-sapper/src/utils.js +33 -0
  148. package/demos/webmap-checker-sapper/static/favicon.png +0 -0
  149. package/demos/webmap-checker-sapper/static/global.css +36 -0
  150. package/demos/webmap-checker-sapper/static/manifest.json +20 -0
  151. package/demos/webmap-checker-sapper/static/svelte-logo-192.png +0 -0
  152. package/demos/webmap-checker-sapper/static/svelte-logo-512.png +0 -0
  153. package/docs/.eslintrc.js +12 -0
  154. package/docs/FAQ.md +48 -0
  155. package/docs/HISTORY.md +62 -0
  156. package/docs/acetate.config.js +262 -0
  157. package/docs/build-typedoc.js +434 -0
  158. package/docs/generate-srihashes.js +61 -0
  159. package/docs/package.json +3 -0
  160. package/docs/src/_layout.html +86 -0
  161. package/docs/src/api/_declaration.html +600 -0
  162. package/docs/src/api/_layout.html +204 -0
  163. package/docs/src/api/_package.html +38 -0
  164. package/docs/src/api/index.html +16 -0
  165. package/docs/src/guides/_layout.html +24 -0
  166. package/docs/src/guides/amd-requirejs-dojo.md +40 -0
  167. package/docs/src/guides/browser-authentication.md +39 -0
  168. package/docs/src/guides/bundlers.md +52 -0
  169. package/docs/src/guides/cli-authentication.md +9 -0
  170. package/docs/src/guides/client-server-authentication.md +9 -0
  171. package/docs/src/guides/embedded-apps.md +106 -0
  172. package/docs/src/guides/from-a-cdn.md +38 -0
  173. package/docs/src/guides/index.md +59 -0
  174. package/docs/src/guides/node.md +84 -0
  175. package/docs/src/guides/package-overview.md +111 -0
  176. package/docs/src/guides/server-authentication.md +9 -0
  177. package/docs/src/guides/whats-new-v2-0.md +305 -0
  178. package/docs/src/img/icons.png +0 -0
  179. package/docs/src/img/icons@2x.png +0 -0
  180. package/docs/src/img/oauth-browser.png +0 -0
  181. package/docs/src/index.html +12 -0
  182. package/docs/src/js/api-search.js +112 -0
  183. package/docs/src/js/nav-toggle.js +41 -0
  184. package/docs/src/sass/_highlight.scss +96 -0
  185. package/docs/src/sass/_icons.scss +157 -0
  186. package/docs/src/sass/style.scss +242 -0
  187. package/jasmine.json +7 -0
  188. package/jasmine.live.json +7 -0
  189. package/karma.conf.cjs +128 -0
  190. package/package.json +87 -0
  191. package/packages/arcgis-rest-auth/CHANGELOG.md +80 -0
  192. package/packages/arcgis-rest-auth/README.md +71 -0
  193. package/packages/arcgis-rest-auth/package.json +66 -0
  194. package/packages/arcgis-rest-auth/src/index.ts +34 -0
  195. package/packages/arcgis-rest-auth/tsconfig.json +6 -0
  196. package/packages/arcgis-rest-demographics/CHANGELOG.md +14 -0
  197. package/packages/arcgis-rest-demographics/README.md +74 -0
  198. package/packages/arcgis-rest-demographics/package.json +69 -0
  199. package/packages/arcgis-rest-demographics/src/getAvailableCountries.ts +113 -0
  200. package/packages/arcgis-rest-demographics/src/getAvailableDataCollections.ts +161 -0
  201. package/packages/arcgis-rest-demographics/src/getAvailableGeographyLevels.ts +86 -0
  202. package/packages/arcgis-rest-demographics/src/getGeography.ts +152 -0
  203. package/packages/arcgis-rest-demographics/src/helpers.ts +27 -0
  204. package/packages/arcgis-rest-demographics/src/index.ts +16 -0
  205. package/packages/arcgis-rest-demographics/src/queryDemographicData.ts +108 -0
  206. package/packages/arcgis-rest-demographics/test/getAvailableCountries.test.ts +89 -0
  207. package/packages/arcgis-rest-demographics/test/getAvailableDataCollections.test.ts +114 -0
  208. package/packages/arcgis-rest-demographics/test/getAvailableGeographyLevels.test.ts +69 -0
  209. package/packages/arcgis-rest-demographics/test/getGeography.test.ts +150 -0
  210. package/packages/arcgis-rest-demographics/test/mocks/responses.ts +4 -0
  211. package/packages/arcgis-rest-demographics/test/queryDemographicData.test.live.ts +50 -0
  212. package/packages/arcgis-rest-demographics/test/queryDemographicData.test.ts +127 -0
  213. package/packages/arcgis-rest-demographics/tsconfig.json +6 -0
  214. package/packages/arcgis-rest-feature-service/CHANGELOG.md +44 -0
  215. package/packages/arcgis-rest-feature-service/README.md +75 -0
  216. package/packages/arcgis-rest-feature-service/package.json +70 -0
  217. package/packages/arcgis-rest-feature-service/src/add.ts +55 -0
  218. package/packages/arcgis-rest-feature-service/src/addAttachment.ts +53 -0
  219. package/packages/arcgis-rest-feature-service/src/addToServiceDefinition.ts +74 -0
  220. package/packages/arcgis-rest-feature-service/src/applyEdits.ts +94 -0
  221. package/packages/arcgis-rest-feature-service/src/createFeatureService.ts +191 -0
  222. package/packages/arcgis-rest-feature-service/src/decodeValues.ts +126 -0
  223. package/packages/arcgis-rest-feature-service/src/delete.ts +62 -0
  224. package/packages/arcgis-rest-feature-service/src/deleteAttachments.ts +52 -0
  225. package/packages/arcgis-rest-feature-service/src/getAllLayersAndTables.ts +30 -0
  226. package/packages/arcgis-rest-feature-service/src/getAttachments.ts +55 -0
  227. package/packages/arcgis-rest-feature-service/src/getLayer.ts +23 -0
  228. package/packages/arcgis-rest-feature-service/src/getService.ts +25 -0
  229. package/packages/arcgis-rest-feature-service/src/getServiceAdminInfo.ts +33 -0
  230. package/packages/arcgis-rest-feature-service/src/getViewSources.ts +19 -0
  231. package/packages/arcgis-rest-feature-service/src/helpers.ts +768 -0
  232. package/packages/arcgis-rest-feature-service/src/index.ts +37 -0
  233. package/packages/arcgis-rest-feature-service/src/query.ts +200 -0
  234. package/packages/arcgis-rest-feature-service/src/queryRelated.ts +86 -0
  235. package/packages/arcgis-rest-feature-service/src/update.ts +65 -0
  236. package/packages/arcgis-rest-feature-service/src/updateAttachment.ts +59 -0
  237. package/packages/arcgis-rest-feature-service/src/updateServiceDefinition.ts +53 -0
  238. package/packages/arcgis-rest-feature-service/test/addToServiceDefinition.test.ts +350 -0
  239. package/packages/arcgis-rest-feature-service/test/attachments.test.ts +188 -0
  240. package/packages/arcgis-rest-feature-service/test/createFeatureService.test.ts +279 -0
  241. package/packages/arcgis-rest-feature-service/test/crud.test.ts +196 -0
  242. package/packages/arcgis-rest-feature-service/test/decodeValues.test.ts +66 -0
  243. package/packages/arcgis-rest-feature-service/test/getAllLayersAndTables.test.ts +27 -0
  244. package/packages/arcgis-rest-feature-service/test/getLayer.test.ts +30 -0
  245. package/packages/arcgis-rest-feature-service/test/getService.test.ts +30 -0
  246. package/packages/arcgis-rest-feature-service/test/getServiceAdminInfo.test.ts +41 -0
  247. package/packages/arcgis-rest-feature-service/test/getViewSources.test.ts +45 -0
  248. package/packages/arcgis-rest-feature-service/test/helpers.test.ts +27 -0
  249. package/packages/arcgis-rest-feature-service/test/mocks/allLayersAndTablesResponse.ts +950 -0
  250. package/packages/arcgis-rest-feature-service/test/mocks/cvdQueryResponse.ts +225 -0
  251. package/packages/arcgis-rest-feature-service/test/mocks/feature.ts +302 -0
  252. package/packages/arcgis-rest-feature-service/test/mocks/fields.ts +779 -0
  253. package/packages/arcgis-rest-feature-service/test/mocks/foo.txt +1 -0
  254. package/packages/arcgis-rest-feature-service/test/mocks/layerDefinition.ts +80 -0
  255. package/packages/arcgis-rest-feature-service/test/mocks/service.ts +476 -0
  256. package/packages/arcgis-rest-feature-service/test/query.test.ts +166 -0
  257. package/packages/arcgis-rest-feature-service/test/updateServiceDefinition.test.ts +103 -0
  258. package/packages/arcgis-rest-feature-service/tsconfig.json +9 -0
  259. package/packages/arcgis-rest-fetch/README.md +17 -0
  260. package/packages/arcgis-rest-fetch/browser-ponyfill.js +8 -0
  261. package/packages/arcgis-rest-fetch/browser-ponyfill.mjs +8 -0
  262. package/packages/arcgis-rest-fetch/index.types.d.ts +8 -0
  263. package/packages/arcgis-rest-fetch/node-ponyfill.js +10 -0
  264. package/packages/arcgis-rest-fetch/node-ponyfill.mjs +10 -0
  265. package/packages/arcgis-rest-fetch/package.json +21 -0
  266. package/packages/arcgis-rest-form-data/README.md +15 -0
  267. package/packages/arcgis-rest-form-data/browser-ponyfill.js +5 -0
  268. package/packages/arcgis-rest-form-data/browser-ponyfill.mjs +3 -0
  269. package/packages/arcgis-rest-form-data/index.types.d.ts +9 -0
  270. package/packages/arcgis-rest-form-data/node-ponyfill.js +7 -0
  271. package/packages/arcgis-rest-form-data/node-ponyfill.mjs +5 -0
  272. package/packages/arcgis-rest-form-data/package.json +21 -0
  273. package/packages/arcgis-rest-geocoding/CHANGELOG.md +26 -0
  274. package/packages/arcgis-rest-geocoding/README.md +85 -0
  275. package/packages/arcgis-rest-geocoding/package.json +69 -0
  276. package/packages/arcgis-rest-geocoding/src/bulk.ts +106 -0
  277. package/packages/arcgis-rest-geocoding/src/geocode.ts +169 -0
  278. package/packages/arcgis-rest-geocoding/src/helpers.ts +56 -0
  279. package/packages/arcgis-rest-geocoding/src/index.ts +17 -0
  280. package/packages/arcgis-rest-geocoding/src/reverse.ts +82 -0
  281. package/packages/arcgis-rest-geocoding/src/suggest.ts +45 -0
  282. package/packages/arcgis-rest-geocoding/test/bulk.test.ts +193 -0
  283. package/packages/arcgis-rest-geocoding/test/geocode.test.ts +254 -0
  284. package/packages/arcgis-rest-geocoding/test/helpers.test.ts +85 -0
  285. package/packages/arcgis-rest-geocoding/test/mocks/responses.ts +1031 -0
  286. package/packages/arcgis-rest-geocoding/test/reverse.test.ts +124 -0
  287. package/packages/arcgis-rest-geocoding/test/suggest.test.ts +53 -0
  288. package/packages/arcgis-rest-geocoding/tsconfig.json +6 -0
  289. package/packages/arcgis-rest-portal/CHANGELOG.md +73 -0
  290. package/packages/arcgis-rest-portal/README.md +72 -0
  291. package/packages/arcgis-rest-portal/package.json +68 -0
  292. package/packages/arcgis-rest-portal/src/groups/add-users.ts +140 -0
  293. package/packages/arcgis-rest-portal/src/groups/create.ts +42 -0
  294. package/packages/arcgis-rest-portal/src/groups/get.ts +187 -0
  295. package/packages/arcgis-rest-portal/src/groups/helpers.ts +14 -0
  296. package/packages/arcgis-rest-portal/src/groups/invite-users.ts +141 -0
  297. package/packages/arcgis-rest-portal/src/groups/join.ts +57 -0
  298. package/packages/arcgis-rest-portal/src/groups/notification.ts +77 -0
  299. package/packages/arcgis-rest-portal/src/groups/protect.ts +56 -0
  300. package/packages/arcgis-rest-portal/src/groups/remove-users.ts +84 -0
  301. package/packages/arcgis-rest-portal/src/groups/remove.ts +32 -0
  302. package/packages/arcgis-rest-portal/src/groups/search.ts +48 -0
  303. package/packages/arcgis-rest-portal/src/groups/update-user-membership.ts +63 -0
  304. package/packages/arcgis-rest-portal/src/groups/update.ts +39 -0
  305. package/packages/arcgis-rest-portal/src/helpers.ts +83 -0
  306. package/packages/arcgis-rest-portal/src/index.ts +68 -0
  307. package/packages/arcgis-rest-portal/src/items/add.ts +145 -0
  308. package/packages/arcgis-rest-portal/src/items/content.ts +70 -0
  309. package/packages/arcgis-rest-portal/src/items/create.ts +149 -0
  310. package/packages/arcgis-rest-portal/src/items/export.ts +92 -0
  311. package/packages/arcgis-rest-portal/src/items/get.ts +444 -0
  312. package/packages/arcgis-rest-portal/src/items/helpers.ts +272 -0
  313. package/packages/arcgis-rest-portal/src/items/protect.ts +41 -0
  314. package/packages/arcgis-rest-portal/src/items/reassign.ts +59 -0
  315. package/packages/arcgis-rest-portal/src/items/remove.ts +139 -0
  316. package/packages/arcgis-rest-portal/src/items/search.ts +25 -0
  317. package/packages/arcgis-rest-portal/src/items/update.ts +188 -0
  318. package/packages/arcgis-rest-portal/src/items/upload.ts +124 -0
  319. package/packages/arcgis-rest-portal/src/orgs/notification.ts +148 -0
  320. package/packages/arcgis-rest-portal/src/services/get-unique-service-name.ts +35 -0
  321. package/packages/arcgis-rest-portal/src/services/is-service-name-available.ts +29 -0
  322. package/packages/arcgis-rest-portal/src/sharing/access.ts +84 -0
  323. package/packages/arcgis-rest-portal/src/sharing/helpers.ts +80 -0
  324. package/packages/arcgis-rest-portal/src/sharing/is-item-shared-with-group.ts +42 -0
  325. package/packages/arcgis-rest-portal/src/sharing/share-item-with-group.ts +335 -0
  326. package/packages/arcgis-rest-portal/src/sharing/unshare-item-with-group.ts +114 -0
  327. package/packages/arcgis-rest-portal/src/users/get-user-tags.ts +52 -0
  328. package/packages/arcgis-rest-portal/src/users/get-user-url.ts +18 -0
  329. package/packages/arcgis-rest-portal/src/users/get-user.ts +60 -0
  330. package/packages/arcgis-rest-portal/src/users/invitation.ts +157 -0
  331. package/packages/arcgis-rest-portal/src/users/notification.ts +67 -0
  332. package/packages/arcgis-rest-portal/src/users/search-users.ts +35 -0
  333. package/packages/arcgis-rest-portal/src/users/update.ts +63 -0
  334. package/packages/arcgis-rest-portal/src/util/SearchQueryBuilder.ts +391 -0
  335. package/packages/arcgis-rest-portal/src/util/array.ts +16 -0
  336. package/packages/arcgis-rest-portal/src/util/generic-search.ts +111 -0
  337. package/packages/arcgis-rest-portal/src/util/get-portal-settings.ts +44 -0
  338. package/packages/arcgis-rest-portal/src/util/get-portal-url.ts +32 -0
  339. package/packages/arcgis-rest-portal/src/util/get-portal.ts +53 -0
  340. package/packages/arcgis-rest-portal/src/util/get-subscription-info.ts +43 -0
  341. package/packages/arcgis-rest-portal/src/util/scrub-control-chars.ts +13 -0
  342. package/packages/arcgis-rest-portal/src/util/search.ts +48 -0
  343. package/packages/arcgis-rest-portal/test/groups/add-users.test.ts +238 -0
  344. package/packages/arcgis-rest-portal/test/groups/crud.test.ts +183 -0
  345. package/packages/arcgis-rest-portal/test/groups/get.test.ts +174 -0
  346. package/packages/arcgis-rest-portal/test/groups/invite-users.test.ts +146 -0
  347. package/packages/arcgis-rest-portal/test/groups/join.test.ts +71 -0
  348. package/packages/arcgis-rest-portal/test/groups/notification.test.ts +111 -0
  349. package/packages/arcgis-rest-portal/test/groups/protect.test.ts +71 -0
  350. package/packages/arcgis-rest-portal/test/groups/remove-users.test.ts +140 -0
  351. package/packages/arcgis-rest-portal/test/groups/search.test.ts +151 -0
  352. package/packages/arcgis-rest-portal/test/groups/update-user-membership.test.ts +64 -0
  353. package/packages/arcgis-rest-portal/test/items/add.test.ts +320 -0
  354. package/packages/arcgis-rest-portal/test/items/content.test.ts +161 -0
  355. package/packages/arcgis-rest-portal/test/items/create.test.ts +404 -0
  356. package/packages/arcgis-rest-portal/test/items/export.test.ts +115 -0
  357. package/packages/arcgis-rest-portal/test/items/get.test.ts +606 -0
  358. package/packages/arcgis-rest-portal/test/items/helpers.test.ts +60 -0
  359. package/packages/arcgis-rest-portal/test/items/protect.test.ts +120 -0
  360. package/packages/arcgis-rest-portal/test/items/reassign.test.ts +130 -0
  361. package/packages/arcgis-rest-portal/test/items/remove.test.ts +259 -0
  362. package/packages/arcgis-rest-portal/test/items/search.test.ts +274 -0
  363. package/packages/arcgis-rest-portal/test/items/update.test.ts +549 -0
  364. package/packages/arcgis-rest-portal/test/items/upload.test.ts +286 -0
  365. package/packages/arcgis-rest-portal/test/mocks/groups/responses.ts +208 -0
  366. package/packages/arcgis-rest-portal/test/mocks/items/foo.zip +0 -0
  367. package/packages/arcgis-rest-portal/test/mocks/items/item.ts +526 -0
  368. package/packages/arcgis-rest-portal/test/mocks/items/resources.ts +38 -0
  369. package/packages/arcgis-rest-portal/test/mocks/items/search.ts +121 -0
  370. package/packages/arcgis-rest-portal/test/mocks/portal/response.ts +126 -0
  371. package/packages/arcgis-rest-portal/test/mocks/portal/settings-response.ts +56 -0
  372. package/packages/arcgis-rest-portal/test/mocks/sharing/sharing.ts +18 -0
  373. package/packages/arcgis-rest-portal/test/mocks/users/invitation.ts +70 -0
  374. package/packages/arcgis-rest-portal/test/mocks/users/notification.ts +34 -0
  375. package/packages/arcgis-rest-portal/test/mocks/users/user-search.ts +388 -0
  376. package/packages/arcgis-rest-portal/test/mocks/users/user-tags.ts +5 -0
  377. package/packages/arcgis-rest-portal/test/mocks/users/user.ts +174 -0
  378. package/packages/arcgis-rest-portal/test/orgs/notification.test.ts +142 -0
  379. package/packages/arcgis-rest-portal/test/services/get-unique-service-name.test.ts +64 -0
  380. package/packages/arcgis-rest-portal/test/services/is-service-name-available.test.ts +41 -0
  381. package/packages/arcgis-rest-portal/test/sharing/access.test.ts +162 -0
  382. package/packages/arcgis-rest-portal/test/sharing/helpers.test.ts +55 -0
  383. package/packages/arcgis-rest-portal/test/sharing/share-item-with-group.test.ts +1328 -0
  384. package/packages/arcgis-rest-portal/test/sharing/unshare-item-with-group.test.ts +288 -0
  385. package/packages/arcgis-rest-portal/test/users/get-user-tags.test.ts +71 -0
  386. package/packages/arcgis-rest-portal/test/users/get-user-url.test.ts +40 -0
  387. package/packages/arcgis-rest-portal/test/users/get-user.test.ts +90 -0
  388. package/packages/arcgis-rest-portal/test/users/invitation.test.ts +126 -0
  389. package/packages/arcgis-rest-portal/test/users/notification.test.ts +76 -0
  390. package/packages/arcgis-rest-portal/test/users/search.test.ts +41 -0
  391. package/packages/arcgis-rest-portal/test/users/update.test.ts +150 -0
  392. package/packages/arcgis-rest-portal/test/util/SearchQueryBuilder.test.ts +295 -0
  393. package/packages/arcgis-rest-portal/test/util/array.test.ts +33 -0
  394. package/packages/arcgis-rest-portal/test/util/get-portal-settings.test.ts +57 -0
  395. package/packages/arcgis-rest-portal/test/util/get-portal-url.test.ts +37 -0
  396. package/packages/arcgis-rest-portal/test/util/portal.test.ts +137 -0
  397. package/packages/arcgis-rest-portal/test/util/scrub-control-chars.test.ts +25 -0
  398. package/packages/arcgis-rest-portal/tsconfig.json +6 -0
  399. package/packages/arcgis-rest-request/README.md +72 -0
  400. package/packages/arcgis-rest-request/package.json +69 -0
  401. package/packages/arcgis-rest-request/post-message-auth-spec.md +70 -0
  402. package/packages/arcgis-rest-request/src/ApiKeyManager.ts +60 -0
  403. package/packages/arcgis-rest-request/src/ApplicationCredentialsManager.ts +141 -0
  404. package/packages/arcgis-rest-request/src/ArcGISIdentityManager.ts +1470 -0
  405. package/packages/arcgis-rest-request/src/app-tokens.ts +132 -0
  406. package/packages/arcgis-rest-request/src/authenticated-request-options.ts +23 -0
  407. package/packages/arcgis-rest-request/src/federation-utils.ts +88 -0
  408. package/packages/arcgis-rest-request/src/fetch-token.ts +48 -0
  409. package/packages/arcgis-rest-request/src/generate-token.ts +32 -0
  410. package/packages/arcgis-rest-request/src/index.ts +46 -0
  411. package/packages/arcgis-rest-request/src/request.ts +446 -0
  412. package/packages/arcgis-rest-request/src/revoke-token.ts +73 -0
  413. package/packages/arcgis-rest-request/src/types/feature.ts +42 -0
  414. package/packages/arcgis-rest-request/src/types/geometry.ts +239 -0
  415. package/packages/arcgis-rest-request/src/types/group.ts +72 -0
  416. package/packages/arcgis-rest-request/src/types/service.ts +87 -0
  417. package/packages/arcgis-rest-request/src/types/symbol.ts +21 -0
  418. package/packages/arcgis-rest-request/src/types/user.ts +49 -0
  419. package/packages/arcgis-rest-request/src/utils/ArcGISRequestError.ts +82 -0
  420. package/packages/arcgis-rest-request/src/utils/ErrorTypes.ts +29 -0
  421. package/packages/arcgis-rest-request/src/utils/GrantTypes.ts +5 -0
  422. package/packages/arcgis-rest-request/src/utils/HTTPMethods.ts +6 -0
  423. package/packages/arcgis-rest-request/src/utils/IAuthenticationManager.ts +22 -0
  424. package/packages/arcgis-rest-request/src/utils/IFetchTokenParams.ts +11 -0
  425. package/packages/arcgis-rest-request/src/utils/IGenerateTokenParams.ts +9 -0
  426. package/packages/arcgis-rest-request/src/utils/IParamBuilder.ts +3 -0
  427. package/packages/arcgis-rest-request/src/utils/IParams.ts +6 -0
  428. package/packages/arcgis-rest-request/src/utils/IParamsBuilder.ts +5 -0
  429. package/packages/arcgis-rest-request/src/utils/IRequestOptions.ts +59 -0
  430. package/packages/arcgis-rest-request/src/utils/ITokenRequestOptions.ts +9 -0
  431. package/packages/arcgis-rest-request/src/utils/ResponseFormats.ts +10 -0
  432. package/packages/arcgis-rest-request/src/utils/append-custom-params.ts +46 -0
  433. package/packages/arcgis-rest-request/src/utils/clean-url.ts +20 -0
  434. package/packages/arcgis-rest-request/src/utils/decode-query-string.ts +27 -0
  435. package/packages/arcgis-rest-request/src/utils/encode-form-data.ts +41 -0
  436. package/packages/arcgis-rest-request/src/utils/encode-query-string.ts +37 -0
  437. package/packages/arcgis-rest-request/src/utils/process-params.ts +109 -0
  438. package/packages/arcgis-rest-request/src/utils/retryAuthError.ts +10 -0
  439. package/packages/arcgis-rest-request/src/utils/warn.ts +11 -0
  440. package/packages/arcgis-rest-request/src/utils/with-options.ts +48 -0
  441. package/packages/arcgis-rest-request/src/validate-app-access.ts +69 -0
  442. package/packages/arcgis-rest-request/test/ApiKey.test.ts +53 -0
  443. package/packages/arcgis-rest-request/test/ApplicationSession.test.ts +148 -0
  444. package/packages/arcgis-rest-request/test/ArcGISIdentityManager.test.ts +2579 -0
  445. package/packages/arcgis-rest-request/test/app-tokens.test.ts +91 -0
  446. package/packages/arcgis-rest-request/test/federation-utils.test.ts +323 -0
  447. package/packages/arcgis-rest-request/test/fetchToken.test.ts +110 -0
  448. package/packages/arcgis-rest-request/test/generateToken.test.ts +40 -0
  449. package/packages/arcgis-rest-request/test/mocks/errors.ts +76 -0
  450. package/packages/arcgis-rest-request/test/mocks/geojson-feature-collection.ts +13 -0
  451. package/packages/arcgis-rest-request/test/mocks/param-builder.ts +7 -0
  452. package/packages/arcgis-rest-request/test/mocks/sharing-rest-info.ts +41 -0
  453. package/packages/arcgis-rest-request/test/mocks/webmap.ts +41 -0
  454. package/packages/arcgis-rest-request/test/request.test.ts +563 -0
  455. package/packages/arcgis-rest-request/test/revoke-token.test.ts +55 -0
  456. package/packages/arcgis-rest-request/test/utils/ArcGISAuthError.test.ts +196 -0
  457. package/packages/arcgis-rest-request/test/utils/ArcGISRequestError.test.ts +51 -0
  458. package/packages/arcgis-rest-request/test/utils/check-for-errors.test.ts +111 -0
  459. package/packages/arcgis-rest-request/test/utils/clean-url.test.ts +50 -0
  460. package/packages/arcgis-rest-request/test/utils/encode-form-data.test.ts +146 -0
  461. package/packages/arcgis-rest-request/test/utils/encode-query-string.test.ts +22 -0
  462. package/packages/arcgis-rest-request/test/utils/process-params.test.ts +205 -0
  463. package/packages/arcgis-rest-request/test/utils/with-options.test.ts +135 -0
  464. package/packages/arcgis-rest-request/test/validate-app-access.test.ts +44 -0
  465. package/packages/arcgis-rest-request/tsconfig.json +4 -0
  466. package/packages/arcgis-rest-routing/README.md +74 -0
  467. package/packages/arcgis-rest-routing/package.json +69 -0
  468. package/packages/arcgis-rest-routing/src/closestFacility.ts +223 -0
  469. package/packages/arcgis-rest-routing/src/helpers.ts +104 -0
  470. package/packages/arcgis-rest-routing/src/index.ts +20 -0
  471. package/packages/arcgis-rest-routing/src/originDestinationMatrix.ts +219 -0
  472. package/packages/arcgis-rest-routing/src/serviceArea.ts +169 -0
  473. package/packages/arcgis-rest-routing/src/solveRoute.ts +179 -0
  474. package/packages/arcgis-rest-routing/test/closestFacility.test.ts +686 -0
  475. package/packages/arcgis-rest-routing/test/mocks/inputs.ts +132 -0
  476. package/packages/arcgis-rest-routing/test/mocks/responses.ts +13316 -0
  477. package/packages/arcgis-rest-routing/test/originDestinationMatrix.test.ts +795 -0
  478. package/packages/arcgis-rest-routing/test/serviceArea.test.ts +601 -0
  479. package/packages/arcgis-rest-routing/test/solveRoute.test.ts +681 -0
  480. package/packages/arcgis-rest-routing/tsconfig.json +6 -0
  481. package/prettier-package-json.config.cjs +81 -0
  482. package/rollup.js +152 -0
  483. package/scripts/create-dist-package-jsons.js +17 -0
  484. package/scripts/deploy-doc-site.js +16 -0
  485. package/scripts/generate-sri-hashes.js +52 -0
  486. package/scripts/get-package-json.js +27 -0
  487. package/scripts/run-demo-server.js +21 -0
  488. package/scripts/test-helpers.ts +20 -0
  489. package/tsconfig.json +62 -0
  490. package/typedoc.json +14 -0
@@ -0,0 +1,2579 @@
1
+ /* Copyright (c) 2018 Environmental Systems Research Institute, Inc.
2
+ * Apache-2.0 */
3
+
4
+ /* tslint:disable:no-empty */
5
+ import fetchMock from "fetch-mock";
6
+ import {
7
+ ArcGISIdentityManager,
8
+ ICredential,
9
+ ArcGISAuthError,
10
+ request,
11
+ ArcGISRequestError,
12
+ ErrorTypes
13
+ } from "../src/index.js";
14
+ import { FormData } from "@esri/arcgis-rest-form-data";
15
+ import { YESTERDAY, TOMORROW } from "../../../scripts/test-helpers.js";
16
+ import { DESTRUCTION } from "dns";
17
+
18
+ describe("ArcGISIdentityManager", () => {
19
+ afterEach(fetchMock.restore);
20
+
21
+ describe(".serialize() and ArcGISIdentityManager.deserialize", () => {
22
+ it("should serialize to and from JSON", () => {
23
+ const session = new ArcGISIdentityManager({
24
+ clientId: "clientId",
25
+ redirectUri: "https://example-app.com/redirect-uri",
26
+ token: "token",
27
+ tokenExpires: TOMORROW,
28
+ refreshToken: "refreshToken",
29
+ refreshTokenExpires: TOMORROW,
30
+ refreshTokenTTL: 1440,
31
+ username: "c@sey",
32
+ password: "123456"
33
+ });
34
+
35
+ const session2 = ArcGISIdentityManager.deserialize(session.serialize());
36
+
37
+ expect(session2.clientId).toEqual("clientId");
38
+ expect(session2.redirectUri).toEqual(
39
+ "https://example-app.com/redirect-uri"
40
+ );
41
+ expect(session2.ssl).toBe(undefined);
42
+ expect(session2.token).toEqual("token");
43
+ expect(session2.tokenExpires).toEqual(TOMORROW);
44
+ expect(session2.refreshToken).toEqual("refreshToken");
45
+ expect(session2.refreshTokenExpires).toEqual(TOMORROW);
46
+ expect(session2.username).toEqual("c@sey");
47
+ expect(session2.password).toEqual("123456");
48
+ expect(session2.tokenDuration).toEqual(20160);
49
+ expect(session2.refreshTokenTTL).toEqual(1440);
50
+ });
51
+
52
+ it("should serialize to and from JSON with a server", () => {
53
+ const session = new ArcGISIdentityManager({
54
+ portal: undefined,
55
+ server: "https://myserver.com/",
56
+ token: "token",
57
+ tokenExpires: TOMORROW
58
+ });
59
+
60
+ const session2 = ArcGISIdentityManager.deserialize(session.serialize());
61
+
62
+ expect(session2.server).toEqual("https://myserver.com/");
63
+ expect(session2.token).toEqual("token");
64
+ expect(session2.tokenExpires).toEqual(TOMORROW);
65
+ });
66
+ });
67
+
68
+ describe(".getToken()", () => {
69
+ it("should return unexpired tokens for trusted arcgis.com domains", (done) => {
70
+ const session = new ArcGISIdentityManager({
71
+ clientId: "id",
72
+ token: "token",
73
+ tokenExpires: TOMORROW
74
+ });
75
+
76
+ Promise.all([
77
+ session.getToken("https://www.arcgis.com/sharing/rest/portals/self"),
78
+ session.getToken(
79
+ "https://services1.arcgis.com/MOCK_ORG/arcgis/rest/services/Private_Service/FeatureServer"
80
+ )
81
+ ])
82
+ .then(([token1, token2]) => {
83
+ expect(token1).toBe("token");
84
+ expect(token2).toBe("token");
85
+ done();
86
+ })
87
+ .catch((e) => {
88
+ fail(e);
89
+ });
90
+ });
91
+
92
+ it("should return unexpired tokens when an org url is passed", (done) => {
93
+ const session = new ArcGISIdentityManager({
94
+ clientId: "id",
95
+ token: "token",
96
+ tokenExpires: TOMORROW,
97
+ portal: "https://custom.maps.arcgis.com/sharing/rest"
98
+ });
99
+
100
+ session
101
+ .getToken(
102
+ "https://services1.arcgis.com/MOCK_ORG/arcgis/rest/services/Private_Service/FeatureServer"
103
+ )
104
+ .then((token) => {
105
+ expect(token).toBe("token");
106
+ done();
107
+ })
108
+ .catch((e) => {
109
+ fail(e);
110
+ });
111
+ });
112
+
113
+ it("should return unexpired tokens when an org url is passed on other ArcGIS Online environments", (done) => {
114
+ const session = new ArcGISIdentityManager({
115
+ clientId: "id",
116
+ token: "token",
117
+ tokenExpires: TOMORROW,
118
+ portal: "https://custom.mapsdev.arcgis.com/sharing/rest"
119
+ });
120
+
121
+ session
122
+ .getToken(
123
+ "https://services1dev.arcgis.com/MOCK_ORG/arcgis/rest/services/Private_Service/FeatureServer"
124
+ )
125
+ .then((token) => {
126
+ expect(token).toBe("token");
127
+ done();
128
+ })
129
+ .catch((e) => {
130
+ fail(e);
131
+ });
132
+ });
133
+
134
+ it("should return unexpired tokens when there is an http/https mismatch", (done) => {
135
+ const session = new ArcGISIdentityManager({
136
+ clientId: "id",
137
+ token: "token",
138
+ tokenExpires: TOMORROW,
139
+ portal: "http://custom.mapsdev.arcgis.com/sharing/rest"
140
+ });
141
+
142
+ session
143
+ .getToken(
144
+ "https://services1dev.arcgis.com/MOCK_ORG/arcgis/rest/services/Private_Service/FeatureServer"
145
+ )
146
+ .then((token) => {
147
+ expect(token).toBe("token");
148
+ done();
149
+ })
150
+ .catch((e) => {
151
+ fail(e);
152
+ });
153
+ });
154
+
155
+ it("should return unexpired tokens for the configured portal domain", (done) => {
156
+ const session = new ArcGISIdentityManager({
157
+ clientId: "id",
158
+ token: "token",
159
+ tokenExpires: TOMORROW,
160
+ portal: "https://gis.city.gov/sharing/rest"
161
+ });
162
+
163
+ session
164
+ .getToken("https://gis.city.gov/sharing/rest/portals/self")
165
+ .then((token) => {
166
+ expect(token).toBe("token");
167
+ done();
168
+ })
169
+ .catch((e) => {
170
+ fail(e);
171
+ });
172
+ });
173
+
174
+ it("should return unexpired tokens for the configured portal domain, regardless of CASING", (done) => {
175
+ // This was a real configuration discovered on a portal instance
176
+ const session = new ArcGISIdentityManager({
177
+ clientId: "id",
178
+ token: "token",
179
+ tokenExpires: TOMORROW,
180
+ portal: "https://pnp00035.esri.com/sharing/rest"
181
+ });
182
+
183
+ session
184
+ .getToken("https://PNP00035.esri.com/sharing/rest/portals/self")
185
+ .then((token) => {
186
+ expect(token).toBe("token");
187
+ done();
188
+ })
189
+ .catch((e) => {
190
+ fail(e);
191
+ });
192
+ });
193
+
194
+ it("should fetch token when contacting a server that is federated, even if on same domain, regardless of domain casing", (done) => {
195
+ // This was a real configuration discovered on a portal instance
196
+ // apparently when federating servers, the UI does not force the
197
+ // server url to lowercase, and this any feature service items generated
198
+ // will have the server name using the casing the admin entered.
199
+ // this is just a test to ensure that the mis-matched casing does not
200
+ // break the federation flow.
201
+ const session = new ArcGISIdentityManager({
202
+ clientId: "id",
203
+ token: "existing-session-token",
204
+ refreshToken: "refresh",
205
+ tokenExpires: TOMORROW,
206
+ portal: "https://pnp00035.esri.com/portal/sharing/rest"
207
+ });
208
+
209
+ fetchMock.postOnce("https://pnp00035.esri.com/server/rest/info", {
210
+ currentVersion: 10.61,
211
+ fullVersion: "10.6.1",
212
+ owningSystemUrl: "https://pnp00035.esri.com/portal",
213
+ authInfo: {
214
+ isTokenBasedSecurity: true,
215
+ tokenServicesUrl:
216
+ "https://pnp00035.esri.com/portal/sharing/rest/generateToken"
217
+ }
218
+ });
219
+
220
+ fetchMock.getOnce(
221
+ "https://pnp00035.esri.com/portal/sharing/rest/portals/self?f=json&token=existing-session-token",
222
+ {
223
+ authorizedCrossOriginDomains: []
224
+ }
225
+ );
226
+
227
+ fetchMock.postOnce("https://pnp00035.esri.com/portal/sharing/rest/info", {
228
+ owningSystemUrl: "https://pnp00035.esri.com/portal",
229
+ authInfo: {
230
+ tokenServicesUrl:
231
+ "https://pnp00035.esri.com/portal/sharing/rest/generateToken",
232
+ isTokenBasedSecurity: true
233
+ }
234
+ });
235
+
236
+ fetchMock.postOnce(
237
+ "https://pnp00035.esri.com/portal/sharing/rest/generateToken",
238
+ {
239
+ token: "new-server-token",
240
+ expires: TOMORROW
241
+ }
242
+ );
243
+
244
+ // request the token twice, for the same domain, but with different casing
245
+ // and we expect a single POST to generate a token once
246
+ session
247
+ .getToken(
248
+ "https://PNP00035.esri.com/server/rest/services/Hosted/perimeters_dd83/FeatureServer"
249
+ )
250
+ .then((token) => {
251
+ expect(token).toBe("new-server-token");
252
+ return session.getToken(
253
+ "https://pnp00035.esri.com/server/rest/services/Hosted/otherService/FeatureServer"
254
+ );
255
+ })
256
+ .then((token) => {
257
+ expect(token).toBe("new-server-token");
258
+ done();
259
+ })
260
+ .catch((e) => {
261
+ fail(e);
262
+ });
263
+ });
264
+
265
+ it("should fetch new tokens when tokens for trusted arcgis.com domains are expired", (done) => {
266
+ const session = new ArcGISIdentityManager({
267
+ clientId: "id",
268
+ token: "token",
269
+ refreshToken: "refresh",
270
+ tokenExpires: YESTERDAY
271
+ });
272
+
273
+ fetchMock.mock(
274
+ "https://www.arcgis.com/sharing/rest/oauth2/token",
275
+ {
276
+ access_token: "new",
277
+ expires_in: 1800,
278
+ username: "c@sey"
279
+ },
280
+ { repeat: 2, method: "POST" }
281
+ );
282
+
283
+ Promise.all([
284
+ session.getToken("https://www.arcgis.com/sharing/rest/portals/self"),
285
+ session.getToken(
286
+ "https://services1.arcgis.com/MOCK_ORG/arcgis/rest/services/Private_Service/FeatureServer"
287
+ )
288
+ ])
289
+ .then(([token1, token2]) => {
290
+ expect(token1).toBe("new");
291
+ expect(token2).toBe("new");
292
+ done();
293
+ })
294
+ .catch((e) => {
295
+ fail(e);
296
+ });
297
+ });
298
+
299
+ it("should pass through a token when no token expiration is present", (done) => {
300
+ const session = new ArcGISIdentityManager({
301
+ token: "token"
302
+ });
303
+
304
+ session
305
+ .getToken("https://www.arcgis.com/sharing/rest/portals/self")
306
+ .then((token1) => {
307
+ expect(token1).toBe("token");
308
+ done();
309
+ })
310
+ .catch((e) => {
311
+ fail(e);
312
+ });
313
+ });
314
+
315
+ it("should generate a token for an untrusted, federated server", (done) => {
316
+ const session = new ArcGISIdentityManager({
317
+ clientId: "id",
318
+ token: "token",
319
+ refreshToken: "refresh",
320
+ tokenExpires: TOMORROW,
321
+ portal: "https://gis.city.gov/sharing/rest"
322
+ });
323
+
324
+ fetchMock.postOnce("https://gisservices.city.gov/public/rest/info", {
325
+ currentVersion: 10.51,
326
+ fullVersion: "10.5.1.120",
327
+ owningSystemUrl: "https://gis.city.gov",
328
+ authInfo: {
329
+ isTokenBasedSecurity: true,
330
+ tokenServicesUrl: "https://gis.city.gov/sharing/generateToken"
331
+ }
332
+ });
333
+
334
+ fetchMock.getOnce(
335
+ "https://gis.city.gov/sharing/rest/portals/self?f=json&token=token",
336
+ {
337
+ authorizedCrossOriginDomains: []
338
+ }
339
+ );
340
+
341
+ fetchMock.postOnce("https://gis.city.gov/sharing/rest/info", {
342
+ owningSystemUrl: "http://gis.city.gov",
343
+ authInfo: {
344
+ tokenServicesUrl: "https://gis.city.gov/sharing/generateToken",
345
+ isTokenBasedSecurity: true
346
+ }
347
+ });
348
+
349
+ fetchMock.postOnce("https://gis.city.gov/sharing/generateToken", {
350
+ token: "serverToken",
351
+ expires: TOMORROW
352
+ });
353
+
354
+ session
355
+ .getToken(
356
+ "https://gisservices.city.gov/public/rest/services/trees/FeatureServer/0/query"
357
+ )
358
+ .then((token) => {
359
+ expect(token).toBe("serverToken");
360
+ return session.getToken(
361
+ "https://gisservices.city.gov/public/rest/services/trees/FeatureServer/0/query"
362
+ );
363
+ })
364
+ .then((token) => {
365
+ expect(token).toBe("serverToken");
366
+ done();
367
+ })
368
+ .catch((e) => {
369
+ fail(e);
370
+ });
371
+ });
372
+
373
+ it("should generate a token for an untrusted, federated server admin call", (done) => {
374
+ const session = new ArcGISIdentityManager({
375
+ clientId: "id",
376
+ token: "token",
377
+ refreshToken: "refresh",
378
+ tokenExpires: TOMORROW,
379
+ portal: "https://gis.city.gov/sharing/rest"
380
+ });
381
+
382
+ fetchMock.postOnce("https://gisservices.city.gov/public/rest/info", {
383
+ currentVersion: 10.51,
384
+ fullVersion: "10.5.1.120",
385
+ owningSystemUrl: "https://gis.city.gov",
386
+ authInfo: {
387
+ isTokenBasedSecurity: true,
388
+ tokenServicesUrl: "https://gis.city.gov/sharing/generateToken"
389
+ }
390
+ });
391
+
392
+ fetchMock.getOnce(
393
+ "https://gis.city.gov/sharing/rest/portals/self?f=json&token=token",
394
+ {
395
+ authorizedCrossOriginDomains: []
396
+ }
397
+ );
398
+
399
+ fetchMock.postOnce("https://gis.city.gov/sharing/rest/info", {
400
+ owningSystemUrl: "http://gis.city.gov",
401
+ authInfo: {
402
+ tokenServicesUrl: "https://gis.city.gov/sharing/generateToken",
403
+ isTokenBasedSecurity: true
404
+ }
405
+ });
406
+
407
+ fetchMock.postOnce("https://gis.city.gov/sharing/generateToken", {
408
+ token: "serverToken",
409
+ expires: TOMORROW
410
+ });
411
+
412
+ session
413
+ .getToken(
414
+ "https://gisservices.city.gov/public/rest/admin/services/trees/FeatureServer/addToDefinition"
415
+ )
416
+ .then((token) => {
417
+ expect(token).toBe("serverToken");
418
+ return session.getToken(
419
+ "https://gisservices.city.gov/public/rest/admin/services/trees/FeatureServer/addToDefinition"
420
+ );
421
+ })
422
+ .then((token) => {
423
+ expect(token).toBe("serverToken");
424
+ done();
425
+ })
426
+ .catch((e) => {
427
+ fail(e);
428
+ });
429
+ });
430
+
431
+ it("should generate a token for an untrusted, federated server with user credentials", (done) => {
432
+ const session = new ArcGISIdentityManager({
433
+ username: "c@sey",
434
+ password: "jones",
435
+ portal: "https://gis.city.gov/sharing/rest"
436
+ });
437
+
438
+ fetchMock.postOnce("https://gisservices.city.gov/public/rest/info", {
439
+ currentVersion: 10.51,
440
+ fullVersion: "10.5.1.120",
441
+ owningSystemUrl: "https://gis.city.gov",
442
+ authInfo: {
443
+ isTokenBasedSecurity: true,
444
+ tokenServicesUrl: "https://gis.city.gov/sharing/generateToken"
445
+ }
446
+ });
447
+
448
+ fetchMock.postOnce("https://gis.city.gov/sharing/rest/generateToken", {
449
+ token: "portalToken"
450
+ });
451
+
452
+ fetchMock.getOnce(
453
+ "https://gis.city.gov/sharing/rest/portals/self?f=json&token=portalToken",
454
+ {
455
+ authorizedCrossOriginDomains: []
456
+ }
457
+ );
458
+
459
+ fetchMock.postOnce("https://gis.city.gov/sharing/rest/info", {
460
+ owningSystemUrl: "http://gis.city.gov",
461
+ authInfo: {
462
+ tokenServicesUrl: "https://gis.city.gov/sharing/generateToken",
463
+ isTokenBasedSecurity: true
464
+ }
465
+ });
466
+
467
+ fetchMock.postOnce("https://gis.city.gov/sharing/generateToken", {
468
+ token: "serverToken",
469
+ expires: TOMORROW
470
+ });
471
+
472
+ session
473
+ .getToken(
474
+ "https://gisservices.city.gov/public/rest/services/trees/FeatureServer/0/query"
475
+ )
476
+ .then((token) => {
477
+ expect(token).toBe("serverToken");
478
+ done();
479
+ })
480
+ .catch((e) => {
481
+ fail(e);
482
+ });
483
+ });
484
+
485
+ it("should only make 1 token request to an untrusted portal for similar URLs", (done) => {
486
+ const session = new ArcGISIdentityManager({
487
+ clientId: "id",
488
+ token: "token",
489
+ refreshToken: "refresh",
490
+ tokenExpires: TOMORROW,
491
+ portal: "https://gis.city.gov/sharing/rest"
492
+ });
493
+
494
+ fetchMock.mock(
495
+ "https://gisservices.city.gov/public/rest/info",
496
+ {
497
+ currentVersion: 10.51,
498
+ fullVersion: "10.5.1.120",
499
+ owningSystemUrl: "https://gis.city.gov",
500
+ authInfo: {
501
+ isTokenBasedSecurity: true,
502
+ tokenServicesUrl: "https://gis.city.gov/sharing/generateToken"
503
+ }
504
+ },
505
+ { repeat: 1, method: "POST" }
506
+ );
507
+
508
+ fetchMock.getOnce(
509
+ "https://gis.city.gov/sharing/rest/portals/self?f=json&token=token",
510
+ {
511
+ authorizedCrossOriginDomains: []
512
+ }
513
+ );
514
+
515
+ fetchMock.mock(
516
+ "https://gis.city.gov/sharing/rest/info",
517
+ {
518
+ owningSystemUrl: "http://gis.city.gov",
519
+ authInfo: {
520
+ tokenServicesUrl: "https://gis.city.gov/sharing/generateToken",
521
+ isTokenBasedSecurity: true
522
+ }
523
+ },
524
+ { repeat: 1, method: "POST" }
525
+ );
526
+
527
+ fetchMock.mock(
528
+ "https://gis.city.gov/sharing/generateToken",
529
+ {
530
+ token: "serverToken",
531
+ expires: TOMORROW
532
+ },
533
+ { repeat: 1, method: "POST" }
534
+ );
535
+
536
+ Promise.all([
537
+ session.getToken(
538
+ "https://gisservices.city.gov/public/rest/services/trees/FeatureServer/0/query"
539
+ ),
540
+ session.getToken(
541
+ "https://gisservices.city.gov/public/rest/services/trees/FeatureServer/0/query"
542
+ )
543
+ ])
544
+ .then(([token1, token2]) => {
545
+ expect(token1).toBe("serverToken");
546
+ expect(token2).toBe("serverToken");
547
+ expect(
548
+ fetchMock.calls("https://gis.city.gov/sharing/generateToken").length
549
+ ).toBe(1);
550
+ done();
551
+ })
552
+ .catch((e) => {
553
+ fail(e);
554
+ });
555
+ });
556
+
557
+ it("should throw an ArcGISAuthError when the owning system doesn't match", (done) => {
558
+ const session = new ArcGISIdentityManager({
559
+ clientId: "id",
560
+ token: "token",
561
+ refreshToken: "refresh",
562
+ tokenExpires: YESTERDAY
563
+ });
564
+
565
+ // similates refreshing the token with the refresh token
566
+ fetchMock.postOnce("https://www.arcgis.com/sharing/rest/oauth2/token", {
567
+ access_token: "newToken",
568
+ expires_in: 60,
569
+ username: " c@sey"
570
+ });
571
+
572
+ fetchMock.getOnce(
573
+ "https://www.arcgis.com/sharing/rest/portals/self?f=json&token=newToken",
574
+ {
575
+ authorizedCrossOriginDomains: []
576
+ }
577
+ );
578
+
579
+ fetchMock.post("https://gisservices.city.gov/public/rest/info", {
580
+ currentVersion: 10.51,
581
+ fullVersion: "10.5.1.120",
582
+ owningSystemUrl: "https://gis.city.gov",
583
+ authInfo: {
584
+ isTokenBasedSecurity: true,
585
+ tokenServicesUrl: "https://gis.city.gov/sharing/generateToken"
586
+ }
587
+ });
588
+
589
+ session
590
+ .getToken(
591
+ "https://gisservices.city.gov/public/rest/services/trees/FeatureServer/0/query"
592
+ )
593
+ .catch((e) => {
594
+ expect(e.name).toEqual(ErrorTypes.ArcGISAuthError);
595
+ expect(e.code).toEqual("NOT_FEDERATED");
596
+ expect(e.message).toEqual(
597
+ "NOT_FEDERATED: https://gisservices.city.gov/public/rest/services/trees/FeatureServer/0/query is not federated with https://www.arcgis.com/sharing/rest."
598
+ );
599
+ done();
600
+ });
601
+ });
602
+
603
+ it("should throw a fully hydrated ArcGISAuthError when no owning system is advertised", (done) => {
604
+ const session = new ArcGISIdentityManager({
605
+ clientId: "id",
606
+ token: "token",
607
+ refreshToken: "refresh",
608
+ tokenExpires: YESTERDAY
609
+ });
610
+
611
+ fetchMock.postOnce("https://www.arcgis.com/sharing/rest/oauth2/token", {
612
+ access_token: "newToken",
613
+ expires_in: 60,
614
+ username: " c@sey"
615
+ });
616
+
617
+ fetchMock.getOnce(
618
+ "https://www.arcgis.com/sharing/rest/portals/self?f=json&token=newToken",
619
+ {
620
+ authorizedCrossOriginDomains: []
621
+ }
622
+ );
623
+
624
+ fetchMock.post("https://gisservices.city.gov/public/rest/info", {
625
+ currentVersion: 10.51,
626
+ fullVersion: "10.5.1.120"
627
+ });
628
+
629
+ fetchMock.post(
630
+ "https://gisservices.city.gov/public/rest/services/trees/FeatureServer/0/query",
631
+ {
632
+ error: {
633
+ code: 499,
634
+ message: "Token Required",
635
+ details: []
636
+ }
637
+ }
638
+ );
639
+
640
+ request(
641
+ "https://gisservices.city.gov/public/rest/services/trees/FeatureServer/0/query",
642
+ {
643
+ authentication: session,
644
+ params: {
645
+ foo: "bar"
646
+ }
647
+ }
648
+ ).catch((e) => {
649
+ expect(e.name).toEqual(ErrorTypes.ArcGISAuthError);
650
+ expect(e.code).toEqual("NOT_FEDERATED");
651
+ expect(e.message).toEqual(
652
+ "NOT_FEDERATED: https://gisservices.city.gov/public/rest/services/trees/FeatureServer/0/query is not federated with any portal and is not explicitly trusted."
653
+ );
654
+ expect(e.url).toEqual(
655
+ "https://gisservices.city.gov/public/rest/services/trees/FeatureServer/0/query"
656
+ );
657
+ expect(e.options.params.foo).toEqual("bar");
658
+ done();
659
+ });
660
+ });
661
+
662
+ it("should not throw an ArcGISAuthError when the unfederated service is public", (done) => {
663
+ const session = new ArcGISIdentityManager({
664
+ clientId: "id",
665
+ token: "token",
666
+ refreshToken: "refresh",
667
+ tokenExpires: YESTERDAY
668
+ });
669
+
670
+ fetchMock.post("https://gisservices.city.gov/public/rest/info", {
671
+ currentVersion: 10.51,
672
+ fullVersion: "10.5.1.120"
673
+ });
674
+
675
+ fetchMock.postOnce("https://www.arcgis.com/sharing/rest/oauth2/token", {
676
+ access_token: "newToken",
677
+ expires_in: 60,
678
+ username: " c@sey"
679
+ });
680
+
681
+ fetchMock.getOnce(
682
+ "https://www.arcgis.com/sharing/rest/portals/self?f=json&token=newToken",
683
+ {
684
+ authorizedCrossOriginDomains: []
685
+ }
686
+ );
687
+
688
+ fetchMock.post(
689
+ "https://gisservices.city.gov/public/rest/services/trees/FeatureServer/0/query",
690
+ {
691
+ count: 123
692
+ }
693
+ );
694
+
695
+ request(
696
+ "https://gisservices.city.gov/public/rest/services/trees/FeatureServer/0/query",
697
+ {
698
+ authentication: session,
699
+ params: {
700
+ returnCount: true
701
+ }
702
+ }
703
+ )
704
+ .then((res) => {
705
+ expect(res.count).toEqual(123);
706
+ done();
707
+ })
708
+ .catch((e) => {
709
+ fail(e);
710
+ });
711
+ });
712
+ });
713
+
714
+ describe(".refreshSession()", () => {
715
+ it("should refresh with a username and password if expired", (done) => {
716
+ const session = new ArcGISIdentityManager({
717
+ username: "c@sey",
718
+ password: "123456"
719
+ });
720
+
721
+ expect(session.canRefresh).toBe(true);
722
+
723
+ fetchMock.postOnce("https://www.arcgis.com/sharing/rest/generateToken", {
724
+ token: "token",
725
+ expires: TOMORROW.getTime(),
726
+ username: " c@sey"
727
+ });
728
+
729
+ session
730
+ .refreshSession()
731
+ .then((s) => {
732
+ expect(s.token).toBe("token");
733
+ expect(s.tokenExpires).toEqual(TOMORROW);
734
+ done();
735
+ })
736
+ .catch((e) => {
737
+ fail(e);
738
+ });
739
+ });
740
+
741
+ it("should refresh with an unexpired refresh token", (done) => {
742
+ const session = new ArcGISIdentityManager({
743
+ clientId: "clientId",
744
+ token: "token",
745
+ username: "c@sey",
746
+ refreshToken: "refreshToken",
747
+ refreshTokenExpires: TOMORROW
748
+ });
749
+
750
+ expect(session.canRefresh).toBe(true);
751
+
752
+ fetchMock.postOnce("https://www.arcgis.com/sharing/rest/oauth2/token", {
753
+ access_token: "newToken",
754
+ expires_in: 60,
755
+ username: " c@sey"
756
+ });
757
+
758
+ session
759
+ .refreshSession()
760
+ .then((s) => {
761
+ expect(s.token).toBe("newToken");
762
+ expect(s.tokenExpires.getTime()).toBeGreaterThan(Date.now());
763
+ done();
764
+ })
765
+ .catch((e) => {
766
+ fail(e);
767
+ });
768
+ });
769
+
770
+ it("should refresh with an expired refresh token", (done) => {
771
+ const session = new ArcGISIdentityManager({
772
+ clientId: "clientId",
773
+ token: "token",
774
+ username: "c@sey",
775
+ refreshToken: "refreshToken",
776
+ refreshTokenExpires: YESTERDAY,
777
+ redirectUri: "https://example-app.com/redirect-uri"
778
+ });
779
+
780
+ fetchMock.postOnce("https://www.arcgis.com/sharing/rest/oauth2/token", {
781
+ access_token: "newToken",
782
+ expires_in: 60,
783
+ username: " c@sey",
784
+ refresh_token: "newRefreshToken"
785
+ });
786
+
787
+ session
788
+ .refreshSession()
789
+ .then((s) => {
790
+ expect(s.token).toBe("newToken");
791
+ expect(s.tokenExpires.getTime()).toBeGreaterThan(Date.now());
792
+ expect(s.refreshToken).toBe("newRefreshToken");
793
+ expect(s.refreshTokenExpires.getTime()).toBeGreaterThan(Date.now());
794
+ done();
795
+ })
796
+ .catch((e) => {
797
+ fail(e);
798
+ });
799
+ });
800
+
801
+ it("should reject if we cannot refresh the token", (done) => {
802
+ const session = new ArcGISIdentityManager({
803
+ clientId: "clientId",
804
+ token: "token",
805
+ username: "c@sey"
806
+ });
807
+
808
+ expect(session.canRefresh).toBe(false);
809
+
810
+ session.refreshSession().catch((e) => {
811
+ expect(e instanceof ArcGISAuthError).toBeTruthy();
812
+ expect(e.name).toBe("ArcGISAuthError");
813
+ expect(e.message).toBe("Unable to refresh token.");
814
+ done();
815
+ });
816
+ });
817
+
818
+ it("should only make 1 token request to the portal for similar URLs", (done) => {
819
+ const session = new ArcGISIdentityManager({
820
+ clientId: "id",
821
+ token: "token",
822
+ refreshToken: "refresh",
823
+ tokenExpires: YESTERDAY
824
+ });
825
+
826
+ fetchMock.mock(
827
+ "https://www.arcgis.com/sharing/rest/oauth2/token",
828
+ {
829
+ access_token: "new",
830
+ expires_in: 1800,
831
+ username: "c@sey"
832
+ },
833
+ { repeat: 1, method: "POST" }
834
+ );
835
+
836
+ Promise.all([
837
+ session.getToken("https://www.arcgis.com/sharing/rest/portals/self"),
838
+ session.getToken("https://www.arcgis.com/sharing/rest/portals/self")
839
+ ])
840
+ .then(([token1, token2]) => {
841
+ expect(token1).toBe("new");
842
+ expect(token2).toBe("new");
843
+ expect(
844
+ fetchMock.calls("https://www.arcgis.com/sharing/rest/oauth2/token")
845
+ .length
846
+ ).toBe(1);
847
+ done();
848
+ })
849
+ .catch((e) => {
850
+ fail(e);
851
+ });
852
+ });
853
+ });
854
+
855
+ describe(".beginOAuth2()", () => {
856
+ it("should authorize via a popup", (done) => {
857
+ const MockWindow: any = {
858
+ open: jasmine.createSpy("spy")
859
+ };
860
+
861
+ ArcGISIdentityManager.beginOAuth2(
862
+ {
863
+ clientId: "clientId123",
864
+ redirectUri: "http://example-app.com/redirect",
865
+ state: "abc123",
866
+ style: "light"
867
+ },
868
+ MockWindow
869
+ )
870
+ .then((session) => {
871
+ expect(session.token).toBe("token");
872
+ expect(session.username).toBe("c@sey");
873
+ expect(session.ssl).toBe(true);
874
+ expect(session.tokenExpires).toEqual(TOMORROW);
875
+ done();
876
+ })
877
+ .catch((e) => {
878
+ fail(e);
879
+ });
880
+
881
+ expect(MockWindow.open).toHaveBeenCalledWith(
882
+ "https://www.arcgis.com/sharing/rest/oauth2/authorize?client_id=clientId123&response_type=token&expiration=20160&redirect_uri=http%3A%2F%2Fexample-app.com%2Fredirect&state=abc123&locale=&style=light",
883
+ "oauth-window",
884
+ "height=400,width=600,menubar=no,location=yes,resizable=yes,scrollbars=yes,status=yes"
885
+ );
886
+
887
+ MockWindow.__ESRI_REST_AUTH_HANDLER_clientId123(
888
+ JSON.stringify(undefined),
889
+ JSON.stringify({
890
+ token: "token",
891
+ expires: TOMORROW,
892
+ username: "c@sey",
893
+ ssl: true
894
+ })
895
+ );
896
+ });
897
+
898
+ it("should reject the promise if there is an error", (done) => {
899
+ const MockWindow: any = {
900
+ open: jasmine.createSpy("spy")
901
+ };
902
+
903
+ ArcGISIdentityManager.beginOAuth2(
904
+ {
905
+ clientId: "clientId123",
906
+ redirectUri: "http://example-app.com/redirect",
907
+ locale: "fr"
908
+ },
909
+ MockWindow
910
+ ).catch((e) => {
911
+ done();
912
+ });
913
+
914
+ expect(MockWindow.open).toHaveBeenCalledWith(
915
+ "https://www.arcgis.com/sharing/rest/oauth2/authorize?client_id=clientId123&response_type=token&expiration=20160&redirect_uri=http%3A%2F%2Fexample-app.com%2Fredirect&state=clientId123&locale=fr&style=",
916
+ "oauth-window",
917
+ "height=400,width=600,menubar=no,location=yes,resizable=yes,scrollbars=yes,status=yes"
918
+ );
919
+
920
+ MockWindow.__ESRI_REST_AUTH_HANDLER_clientId123(
921
+ JSON.stringify({
922
+ errorMessage: "unable to sign in",
923
+ error: "SIGN_IN_FAILED"
924
+ })
925
+ );
926
+ });
927
+
928
+ it("should authorize in the same window/tab", () => {
929
+ const MockWindow: any = {
930
+ location: {
931
+ href: ""
932
+ }
933
+ };
934
+
935
+ // https://github.com/palantir/tslint/issues/3056
936
+ void ArcGISIdentityManager.beginOAuth2(
937
+ {
938
+ clientId: "clientId123",
939
+ redirectUri: "http://example-app.com/redirect",
940
+ popup: false
941
+ },
942
+ MockWindow
943
+ );
944
+
945
+ expect(MockWindow.location.href).toBe(
946
+ "https://www.arcgis.com/sharing/rest/oauth2/authorize?client_id=clientId123&response_type=token&expiration=20160&redirect_uri=http%3A%2F%2Fexample-app.com%2Fredirect&state=clientId123&locale=&style="
947
+ );
948
+ });
949
+
950
+ it("should authorize using a social media provider", () => {
951
+ const MockWindow: any = {
952
+ location: {
953
+ href: ""
954
+ }
955
+ };
956
+
957
+ // https://github.com/palantir/tslint/issues/3056
958
+ void ArcGISIdentityManager.beginOAuth2(
959
+ {
960
+ clientId: "clientId123",
961
+ redirectUri: "http://example-app.com/redirect",
962
+ popup: false,
963
+ provider: "facebook"
964
+ },
965
+ MockWindow
966
+ );
967
+
968
+ expect(MockWindow.location.href).toBe(
969
+ "https://www.arcgis.com/sharing/rest/oauth2/social/authorize?client_id=clientId123&socialLoginProviderName=facebook&autoAccountCreateForSocial=true&response_type=token&expiration=20160&redirect_uri=http%3A%2F%2Fexample-app.com%2Fredirect&state=clientId123&locale=&style="
970
+ );
971
+ });
972
+
973
+ it("should authorize using the other social media provider", () => {
974
+ const MockWindow: any = {
975
+ location: {
976
+ href: ""
977
+ }
978
+ };
979
+
980
+ // https://github.com/palantir/tslint/issues/3056
981
+ void ArcGISIdentityManager.beginOAuth2(
982
+ {
983
+ clientId: "clientId123",
984
+ redirectUri: "http://example-app.com/redirect",
985
+ popup: false,
986
+ provider: "google"
987
+ },
988
+ MockWindow
989
+ );
990
+
991
+ expect(MockWindow.location.href).toBe(
992
+ "https://www.arcgis.com/sharing/rest/oauth2/social/authorize?client_id=clientId123&socialLoginProviderName=google&autoAccountCreateForSocial=true&response_type=token&expiration=20160&redirect_uri=http%3A%2F%2Fexample-app.com%2Fredirect&state=clientId123&locale=&style="
993
+ );
994
+ });
995
+
996
+ it("should pass custom expiration", () => {
997
+ const MockWindow: any = {
998
+ location: {
999
+ href: ""
1000
+ }
1001
+ };
1002
+
1003
+ // https://github.com/palantir/tslint/issues/3056
1004
+ void ArcGISIdentityManager.beginOAuth2(
1005
+ {
1006
+ clientId: "clientId123",
1007
+ redirectUri: "http://example-app.com/redirect",
1008
+ popup: false,
1009
+ expiration: 9000
1010
+ },
1011
+ MockWindow
1012
+ );
1013
+
1014
+ expect(MockWindow.location.href).toBe(
1015
+ "https://www.arcgis.com/sharing/rest/oauth2/authorize?client_id=clientId123&response_type=token&expiration=9000&redirect_uri=http%3A%2F%2Fexample-app.com%2Fredirect&state=clientId123&locale=&style="
1016
+ );
1017
+ });
1018
+
1019
+ it("should pass custom duration (DEPRECATED)", () => {
1020
+ const MockWindow: any = {
1021
+ location: {
1022
+ href: ""
1023
+ }
1024
+ };
1025
+
1026
+ // https://github.com/palantir/tslint/issues/3056
1027
+ void ArcGISIdentityManager.beginOAuth2(
1028
+ {
1029
+ clientId: "clientId123",
1030
+ redirectUri: "http://example-app.com/redirect",
1031
+ popup: false,
1032
+ duration: 9001
1033
+ },
1034
+ MockWindow
1035
+ );
1036
+
1037
+ expect(MockWindow.location.href).toBe(
1038
+ "https://www.arcgis.com/sharing/rest/oauth2/authorize?client_id=clientId123&response_type=token&expiration=9001&redirect_uri=http%3A%2F%2Fexample-app.com%2Fredirect&state=clientId123&locale=&style="
1039
+ );
1040
+ });
1041
+ });
1042
+
1043
+ describe(".completeOAuth2()", () => {
1044
+ it("should return a new user session if it cannot find a valid parent", () => {
1045
+ const MockWindow = {
1046
+ location: {
1047
+ hash: "#access_token=token&expires_in=1209600&username=c%40sey&ssl=true&persist=true"
1048
+ },
1049
+ get parent() {
1050
+ return this;
1051
+ }
1052
+ };
1053
+
1054
+ const session = ArcGISIdentityManager.completeOAuth2(
1055
+ {
1056
+ clientId: "clientId",
1057
+ redirectUri: "https://example-app.com/redirect-uri"
1058
+ },
1059
+ MockWindow
1060
+ );
1061
+
1062
+ expect(session.token).toBe("token");
1063
+ expect(session.tokenExpires.getTime()).toBeGreaterThan(Date.now());
1064
+ expect(session.username).toBe("c@sey");
1065
+ expect(session.ssl).toBe(true);
1066
+ });
1067
+
1068
+ it("should return a new user session with ssl as false when callback hash does not have ssl parameter", () => {
1069
+ const MockWindow = {
1070
+ location: {
1071
+ hash: "#access_token=token&expires_in=1209600&username=c%40sey&persist=true"
1072
+ },
1073
+ get parent() {
1074
+ return this;
1075
+ }
1076
+ };
1077
+
1078
+ const session = ArcGISIdentityManager.completeOAuth2(
1079
+ {
1080
+ clientId: "clientId",
1081
+ redirectUri: "https://example-app.com/redirect-uri"
1082
+ },
1083
+ MockWindow
1084
+ );
1085
+ expect(session.ssl).toBe(false);
1086
+ });
1087
+
1088
+ it("should callback to create a new user session if finds a valid opener.parent", (done) => {
1089
+ const MockWindow = {
1090
+ opener: {
1091
+ parent: {
1092
+ __ESRI_REST_AUTH_HANDLER_clientId(
1093
+ errorString: string,
1094
+ oauthInfoString: string
1095
+ ) {
1096
+ const oauthInfo = JSON.parse(oauthInfoString);
1097
+ expect(oauthInfo.token).toBe("token");
1098
+ expect(oauthInfo.username).toBe("c@sey");
1099
+ expect(oauthInfo.ssl).toBe(true);
1100
+ expect(new Date(oauthInfo.expires).getTime()).toBeGreaterThan(
1101
+ Date.now()
1102
+ );
1103
+ }
1104
+ }
1105
+ },
1106
+ close() {
1107
+ done();
1108
+ },
1109
+ location: {
1110
+ hash: "#access_token=token&expires_in=1209600&username=c%40sey&ssl=true"
1111
+ }
1112
+ };
1113
+
1114
+ ArcGISIdentityManager.completeOAuth2(
1115
+ {
1116
+ clientId: "clientId",
1117
+ redirectUri: "https://example-app.com/redirect-uri"
1118
+ },
1119
+ MockWindow
1120
+ );
1121
+ });
1122
+
1123
+ it("should callback to create a new user session if finds a valid opener (Iframe support)", (done) => {
1124
+ const MockWindow = {
1125
+ opener: {
1126
+ __ESRI_REST_AUTH_HANDLER_clientId(
1127
+ errorString: string,
1128
+ oauthInfoString: string
1129
+ ) {
1130
+ const oauthInfo = JSON.parse(oauthInfoString);
1131
+ expect(oauthInfo.token).toBe("token");
1132
+ expect(oauthInfo.username).toBe("c@sey");
1133
+ expect(oauthInfo.ssl).toBe(true);
1134
+ expect(new Date(oauthInfo.expires).getTime()).toBeGreaterThan(
1135
+ Date.now()
1136
+ );
1137
+ }
1138
+ },
1139
+ close() {
1140
+ done();
1141
+ },
1142
+ location: {
1143
+ hash: "#access_token=token&expires_in=1209600&username=c%40sey&ssl=true"
1144
+ }
1145
+ };
1146
+
1147
+ ArcGISIdentityManager.completeOAuth2(
1148
+ {
1149
+ clientId: "clientId",
1150
+ redirectUri: "https://example-app.com/redirect-uri"
1151
+ },
1152
+ MockWindow
1153
+ );
1154
+ });
1155
+
1156
+ it("should callback to create a new user session if finds a valid parent", (done) => {
1157
+ const MockWindow = {
1158
+ parent: {
1159
+ __ESRI_REST_AUTH_HANDLER_clientId(
1160
+ errorString: string,
1161
+ oauthInfoString: string
1162
+ ) {
1163
+ const oauthInfo = JSON.parse(oauthInfoString);
1164
+ expect(oauthInfo.token).toBe("token");
1165
+ expect(oauthInfo.username).toBe("c@sey");
1166
+ expect(oauthInfo.ssl).toBe(true);
1167
+ expect(new Date(oauthInfo.expires).getTime()).toBeGreaterThan(
1168
+ Date.now()
1169
+ );
1170
+ }
1171
+ },
1172
+ close() {
1173
+ done();
1174
+ },
1175
+ location: {
1176
+ hash: "#access_token=token&expires_in=1209600&username=c%40sey&ssl=true"
1177
+ }
1178
+ };
1179
+
1180
+ ArcGISIdentityManager.completeOAuth2(
1181
+ {
1182
+ clientId: "clientId",
1183
+ redirectUri: "https://example-app.com/redirect-uri"
1184
+ },
1185
+ MockWindow
1186
+ );
1187
+ });
1188
+
1189
+ it("should throw an error from the authorization window", () => {
1190
+ const MockWindow = {
1191
+ location: {
1192
+ hash: "#error=Invalid_Signin&error_description=Invalid_Signin"
1193
+ },
1194
+ get parent() {
1195
+ return this;
1196
+ }
1197
+ };
1198
+
1199
+ expect(function () {
1200
+ ArcGISIdentityManager.completeOAuth2(
1201
+ {
1202
+ clientId: "clientId",
1203
+ redirectUri: "https://example-app.com/redirect-uri"
1204
+ },
1205
+ MockWindow
1206
+ );
1207
+ }).toThrowError(ArcGISRequestError, "Invalid_Signin: Invalid_Signin");
1208
+ });
1209
+
1210
+ it("should throw an error if the handler or parent window cannot be accessed", () => {
1211
+ const MockParent = {
1212
+ get parent() {
1213
+ throw new Error(
1214
+ "This window isn't where auth started, but was opened from somewhere else via window.open() perhaps from another domain which would cause a cross domain error when read."
1215
+ );
1216
+ }
1217
+ };
1218
+
1219
+ const MockWindow = {
1220
+ location: {
1221
+ hash: "#error=Invalid_Signin&error_description=Invalid_Signin"
1222
+ },
1223
+ get opener() {
1224
+ return MockParent;
1225
+ }
1226
+ };
1227
+
1228
+ expect(function () {
1229
+ ArcGISIdentityManager.completeOAuth2(
1230
+ {
1231
+ clientId: "clientId",
1232
+ redirectUri: "https://example-app.com/redirect-uri"
1233
+ },
1234
+ MockWindow
1235
+ );
1236
+ }).toThrowError(ArcGISAuthError);
1237
+ });
1238
+ });
1239
+
1240
+ describe("postmessage auth :: ", () => {
1241
+ const MockWindow = {
1242
+ addEventListener: () => {},
1243
+ removeEventListener: () => {},
1244
+ parent: {
1245
+ postMessage: () => {}
1246
+ }
1247
+ };
1248
+
1249
+ const cred = {
1250
+ expires: TOMORROW.getTime(),
1251
+ server: "https://www.arcgis.com/sharing/rest",
1252
+ ssl: false,
1253
+ token: "token",
1254
+ userId: "jsmith"
1255
+ };
1256
+
1257
+ it(".disablePostMessageAuth removes event listener", () => {
1258
+ const removeSpy = spyOn(MockWindow, "removeEventListener");
1259
+ const session = ArcGISIdentityManager.fromCredential(cred);
1260
+ session.disablePostMessageAuth(MockWindow);
1261
+ expect(removeSpy.calls.count()).toBe(
1262
+ 1,
1263
+ "should call removeEventListener"
1264
+ );
1265
+ });
1266
+ it(".enablePostMessageAuth adds event listener", () => {
1267
+ const addSpy = spyOn(MockWindow, "addEventListener");
1268
+ const session = ArcGISIdentityManager.fromCredential(cred);
1269
+ session.enablePostMessageAuth(
1270
+ ["https://storymaps.arcgis.com"],
1271
+ MockWindow
1272
+ );
1273
+ expect(addSpy.calls.count()).toBe(1, "should call addEventListener");
1274
+ });
1275
+
1276
+ it(".enablePostMessage handler returns credential to origin in list", () => {
1277
+ // ok, this gets kinda gnarly...
1278
+
1279
+ // create a mock window object
1280
+ // that will hold the passed in event handler so we can fire it manually
1281
+ const Win = {
1282
+ _fn: (evt: any) => {},
1283
+ addEventListener(evt: any, fn: any) {
1284
+ // enablePostMessageAuth passes in the handler, which is what we're actually testing
1285
+ Win._fn = fn;
1286
+ },
1287
+ removeEventListener() {}
1288
+ };
1289
+ // Create the session
1290
+ const session = ArcGISIdentityManager.fromCredential(cred);
1291
+ // enable postMessageAuth allowing storymaps.arcgis.com to recieve creds
1292
+ session.enablePostMessageAuth(["https://storymaps.arcgis.com"], Win);
1293
+ // create an event object, with a matching origin
1294
+ // an a source.postMessage fn that we can spy on
1295
+ const event = {
1296
+ origin: "https://storymaps.arcgis.com",
1297
+ source: {
1298
+ postMessage(msg: any, origin: string) {}
1299
+ },
1300
+ data: {
1301
+ type: "arcgis:auth:requestCredential"
1302
+ }
1303
+ };
1304
+ // create the spy
1305
+ const sourceSpy = spyOn(event.source, "postMessage");
1306
+ // Now, fire the handler, simulating what happens when a postMessage event comes
1307
+ // from an embedded iframe
1308
+ Win._fn(event);
1309
+ // Expectations...
1310
+ expect(sourceSpy.calls.count()).toBe(
1311
+ 1,
1312
+ "souce.postMessage should be called in handler"
1313
+ );
1314
+ const args = sourceSpy.calls.argsFor(0);
1315
+ expect(args[0].type).toBe(
1316
+ "arcgis:auth:credential",
1317
+ "should send credential type"
1318
+ );
1319
+ expect(args[0].credential.userId).toBe(
1320
+ "jsmith",
1321
+ "should send credential"
1322
+ );
1323
+ expect(args[0].credential.server).toBe(
1324
+ "https://www.arcgis.com",
1325
+ "sends server url without /sharing/rest"
1326
+ );
1327
+ // now the case where it's not a valid origin
1328
+ event.origin = "https://evil.com";
1329
+ Win._fn(event);
1330
+ expect(sourceSpy.calls.count()).toBe(
1331
+ 1,
1332
+ "souce.postMessage should not be called in handler for invalid origin"
1333
+ );
1334
+ });
1335
+
1336
+ it(".enablePostMessage handler returns error if session is expired", () => {
1337
+ // ok, this gets kinda gnarly...
1338
+ const expiredCred = {
1339
+ expires: YESTERDAY.getTime(),
1340
+ server: "https://www.arcgis.com/sharing/rest",
1341
+ ssl: false,
1342
+ token: "token",
1343
+ userId: "jsmith"
1344
+ };
1345
+ // create a mock window object
1346
+ // that will hold the passed in event handler so we can fire it manually
1347
+ const Win = {
1348
+ _fn: (evt: any) => {},
1349
+ addEventListener(evt: any, fn: any) {
1350
+ // enablePostMessageAuth passes in the handler, which is what we're actually testing
1351
+ Win._fn = fn;
1352
+ },
1353
+ removeEventListener() {}
1354
+ };
1355
+ // Create the session
1356
+ const session = ArcGISIdentityManager.fromCredential(expiredCred);
1357
+ // enable postMessageAuth allowing storymaps.arcgis.com to recieve creds
1358
+ session.enablePostMessageAuth(["https://storymaps.arcgis.com"], Win);
1359
+ // create an event object, with a matching origin
1360
+ // an a source.postMessage fn that we can spy on
1361
+ const event = {
1362
+ origin: "https://storymaps.arcgis.com",
1363
+ source: {
1364
+ postMessage(msg: any, origin: string) {}
1365
+ },
1366
+ data: {
1367
+ type: "arcgis:auth:requestCredential"
1368
+ }
1369
+ };
1370
+ // create the spy
1371
+ const sourceSpy = spyOn(event.source, "postMessage");
1372
+ // Now, fire the handler, simulating what happens when a postMessage event comes
1373
+ // from an embedded iframe
1374
+ Win._fn(event);
1375
+ // Expectations...
1376
+ expect(sourceSpy.calls.count()).toBe(
1377
+ 1,
1378
+ "souce.postMessage should be called in handler"
1379
+ );
1380
+ const args = sourceSpy.calls.argsFor(0);
1381
+ expect(args[0].type).toBe("arcgis:auth:error", "should send error type");
1382
+ expect(args[0].credential).not.toBeDefined();
1383
+ expect(args[0].error.name).toBe(
1384
+ "tokenExpiredError",
1385
+ "should recieve tokenExpiredError"
1386
+ );
1387
+ });
1388
+
1389
+ it(".fromParent happy path", () => {
1390
+ // create a mock window that will fire the handler
1391
+ const Win = {
1392
+ _fn: (evt: any) => {},
1393
+ addEventListener(evt: any, fn: any) {
1394
+ Win._fn = fn;
1395
+ },
1396
+ removeEventListener() {},
1397
+ parent: {
1398
+ postMessage(msg: any, origin: string) {
1399
+ Win._fn({
1400
+ origin: "https://origin.com",
1401
+ data: { type: "arcgis:auth:credential", credential: cred },
1402
+ source: Win.parent
1403
+ });
1404
+ }
1405
+ }
1406
+ };
1407
+
1408
+ return ArcGISIdentityManager.fromParent("https://origin.com", Win).then(
1409
+ (session) => {
1410
+ expect(session.username).toBe(
1411
+ "jsmith",
1412
+ "should use the cred wired throu the mock window"
1413
+ );
1414
+ }
1415
+ );
1416
+ });
1417
+
1418
+ it(".fromParent ignores other messages, then intercepts the correct one", async () => {
1419
+ // create a mock window that will fire the handler
1420
+ const Win = {
1421
+ _fn: (evt: any) => {},
1422
+ addEventListener(evt: any, fn: any) {
1423
+ Win._fn = fn;
1424
+ },
1425
+ removeEventListener() {},
1426
+ parent: {
1427
+ postMessage(msg: any, origin: string) {
1428
+ // fire one we intend to ignore
1429
+ Win._fn({
1430
+ origin: "https://notorigin.com",
1431
+ data: { type: "other:random", foo: { bar: "baz" } },
1432
+ source: "Not Parent Object"
1433
+ });
1434
+ // fire a second we want to intercept
1435
+ Win._fn({
1436
+ origin: "https://origin.com",
1437
+ data: { type: "arcgis:auth:credential", credential: cred },
1438
+ source: Win.parent
1439
+ });
1440
+ }
1441
+ }
1442
+ };
1443
+
1444
+ return ArcGISIdentityManager.fromParent("https://origin.com", Win).then(
1445
+ (resp) => {
1446
+ expect(resp.username).toBe(
1447
+ "jsmith",
1448
+ "should use the cred wired throu the mock window"
1449
+ );
1450
+ }
1451
+ );
1452
+ });
1453
+
1454
+ it(".fromParent rejects if invlid cred", () => {
1455
+ // create a mock window that will fire the handler
1456
+ const Win = {
1457
+ _fn: (evt: any) => {},
1458
+ addEventListener(evt: any, fn: any) {
1459
+ Win._fn = fn;
1460
+ },
1461
+ removeEventListener() {},
1462
+ parent: {
1463
+ postMessage(msg: any, origin: string) {
1464
+ Win._fn({
1465
+ origin: "https://origin.com",
1466
+ data: {
1467
+ type: "arcgis:auth:credential",
1468
+ credential: { foo: "bar" }
1469
+ },
1470
+ source: Win.parent
1471
+ });
1472
+ }
1473
+ }
1474
+ };
1475
+
1476
+ return ArcGISIdentityManager.fromParent("https://origin.com", Win).catch(
1477
+ (err) => {
1478
+ expect(err).toBeDefined("Should reject");
1479
+ }
1480
+ );
1481
+ });
1482
+
1483
+ it(".fromParent rejects if auth error recieved", () => {
1484
+ // create a mock window that will fire the handler
1485
+ const Win = {
1486
+ _fn: (evt: any) => {},
1487
+ addEventListener(evt: any, fn: any) {
1488
+ Win._fn = fn;
1489
+ },
1490
+ removeEventListener() {},
1491
+ parent: {
1492
+ postMessage(msg: any, origin: string) {
1493
+ Win._fn({
1494
+ origin: "https://origin.com",
1495
+ data: {
1496
+ type: "arcgis:auth:error",
1497
+ error: { message: "Rejected authentication request." }
1498
+ },
1499
+ source: Win.parent
1500
+ });
1501
+ }
1502
+ }
1503
+ };
1504
+
1505
+ return ArcGISIdentityManager.fromParent("https://origin.com", Win).catch(
1506
+ (err) => {
1507
+ expect(err).toBeDefined("Should reject");
1508
+ }
1509
+ );
1510
+ });
1511
+
1512
+ it(".fromParent rejects if auth unknown message", () => {
1513
+ // create a mock window that will fire the handler
1514
+ const Win = {
1515
+ _fn: (evt: any) => {},
1516
+ addEventListener(evt: any, fn: any) {
1517
+ Win._fn = fn;
1518
+ },
1519
+ removeEventListener() {},
1520
+ parent: {
1521
+ postMessage(msg: any, origin: string) {
1522
+ Win._fn({
1523
+ origin: "https://origin.com",
1524
+ data: { type: "arcgis:auth:other" },
1525
+ source: Win.parent
1526
+ });
1527
+ }
1528
+ }
1529
+ };
1530
+
1531
+ return ArcGISIdentityManager.fromParent("https://origin.com", Win).catch(
1532
+ (err) => {
1533
+ expect(err.message).toBe("Unknown message type.", "Should reject");
1534
+ }
1535
+ );
1536
+ });
1537
+ });
1538
+
1539
+ describe("validateAppAccess: ", () => {
1540
+ it("makes a request to /oauth2/validateAppAccess passing params", () => {
1541
+ const VERIFYAPPACCESS_URL =
1542
+ "https://www.arcgis.com/sharing/rest/oauth2/validateAppAccess";
1543
+ fetchMock.postOnce(VERIFYAPPACCESS_URL, {
1544
+ valid: true,
1545
+ viewOnlyUserTypeApp: false
1546
+ });
1547
+ const session = new ArcGISIdentityManager({
1548
+ clientId: "clientId",
1549
+ redirectUri: "https://example-app.com/redirect-uri",
1550
+ token: "FAKE-TOKEN",
1551
+ tokenExpires: TOMORROW,
1552
+ refreshToken: "refreshToken",
1553
+ refreshTokenExpires: TOMORROW,
1554
+ refreshTokenTTL: 1440,
1555
+ username: "jsmith",
1556
+ password: "123456"
1557
+ });
1558
+ return session
1559
+ .validateAppAccess("abc123")
1560
+ .then((response) => {
1561
+ const [url, options]: [string, RequestInit] =
1562
+ fetchMock.lastCall(VERIFYAPPACCESS_URL);
1563
+ expect(url).toEqual(VERIFYAPPACCESS_URL);
1564
+ expect(options.body).toContain("f=json");
1565
+ expect(options.body).toContain("token=FAKE-TOKEN");
1566
+ expect(options.body).toContain("client_id=abc123");
1567
+ expect(response.valid).toEqual(true);
1568
+ expect(response.viewOnlyUserTypeApp).toBe(false);
1569
+ })
1570
+ .catch((e) => fail(e));
1571
+ });
1572
+ });
1573
+
1574
+ it("should throw an unknown error if the url has no error or access_token", () => {
1575
+ const MockWindow = {
1576
+ location: {
1577
+ hash: ""
1578
+ },
1579
+ get opener() {
1580
+ return this;
1581
+ }
1582
+ };
1583
+
1584
+ expect(function () {
1585
+ ArcGISIdentityManager.completeOAuth2(
1586
+ {
1587
+ clientId: "clientId",
1588
+ redirectUri: "https://example-app.com/redirect-uri"
1589
+ },
1590
+ MockWindow
1591
+ );
1592
+ }).toThrowError(ArcGISRequestError, "Unknown error");
1593
+ });
1594
+
1595
+ describe(".authorize()", () => {
1596
+ it("should redirect the request to the authorization page", (done) => {
1597
+ const spy = jasmine.createSpy("spy");
1598
+ const MockResponse: any = {
1599
+ writeHead: spy,
1600
+ end() {
1601
+ expect(spy.calls.mostRecent().args[0]).toBe(301);
1602
+ expect(spy.calls.mostRecent().args[1].Location).toBe(
1603
+ "https://arcgis.com/sharing/rest/oauth2/authorize?client_id=clientId&expiration=20160&response_type=code&redirect_uri=https%3A%2F%2Fexample-app.com%2Fredirect-uri"
1604
+ );
1605
+ done();
1606
+ }
1607
+ };
1608
+
1609
+ ArcGISIdentityManager.authorize(
1610
+ {
1611
+ clientId: "clientId",
1612
+ redirectUri: "https://example-app.com/redirect-uri"
1613
+ },
1614
+ MockResponse
1615
+ );
1616
+ });
1617
+
1618
+ it("should redirect the request to the authorization page with custom expiration", (done) => {
1619
+ const spy = jasmine.createSpy("spy");
1620
+ const MockResponse: any = {
1621
+ writeHead: spy,
1622
+ end() {
1623
+ expect(spy.calls.mostRecent().args[0]).toBe(301);
1624
+ expect(spy.calls.mostRecent().args[1].Location).toBe(
1625
+ "https://arcgis.com/sharing/rest/oauth2/authorize?client_id=clientId&expiration=10000&response_type=code&redirect_uri=https%3A%2F%2Fexample-app.com%2Fredirect-uri"
1626
+ );
1627
+ done();
1628
+ }
1629
+ };
1630
+
1631
+ ArcGISIdentityManager.authorize(
1632
+ {
1633
+ clientId: "clientId",
1634
+ redirectUri: "https://example-app.com/redirect-uri",
1635
+ expiration: 10000
1636
+ },
1637
+ MockResponse
1638
+ );
1639
+ });
1640
+
1641
+ it("should redirect the request to the authorization page with custom duration (DEPRECATED)", (done) => {
1642
+ const spy = jasmine.createSpy("spy");
1643
+ const MockResponse: any = {
1644
+ writeHead: spy,
1645
+ end() {
1646
+ expect(spy.calls.mostRecent().args[0]).toBe(301);
1647
+ expect(spy.calls.mostRecent().args[1].Location).toBe(
1648
+ "https://arcgis.com/sharing/rest/oauth2/authorize?client_id=clientId&expiration=10001&response_type=code&redirect_uri=https%3A%2F%2Fexample-app.com%2Fredirect-uri"
1649
+ );
1650
+ done();
1651
+ }
1652
+ };
1653
+
1654
+ ArcGISIdentityManager.authorize(
1655
+ {
1656
+ clientId: "clientId",
1657
+ redirectUri: "https://example-app.com/redirect-uri",
1658
+ duration: 10001
1659
+ },
1660
+ MockResponse
1661
+ );
1662
+ });
1663
+ });
1664
+
1665
+ describe(".exchangeAuthorizationCode()", () => {
1666
+ let paramsSpy: jasmine.Spy;
1667
+
1668
+ beforeEach(() => {
1669
+ paramsSpy = spyOn(FormData.prototype, "append").and.callThrough();
1670
+ });
1671
+
1672
+ afterAll(() => {
1673
+ paramsSpy.calls.reset();
1674
+ });
1675
+
1676
+ it("should exchange an authorization code for a new ArcGISIdentityManager", (done) => {
1677
+ fetchMock.postOnce("https://www.arcgis.com/sharing/rest/oauth2/token", {
1678
+ access_token: "token",
1679
+ expires_in: 1800,
1680
+ refresh_token: "refreshToken",
1681
+ username: "Casey",
1682
+ ssl: true
1683
+ });
1684
+
1685
+ ArcGISIdentityManager.exchangeAuthorizationCode(
1686
+ {
1687
+ clientId: "clientId",
1688
+ redirectUri: "https://example-app.com/redirect-uri"
1689
+ },
1690
+ "code"
1691
+ )
1692
+ .then((session) => {
1693
+ expect(session.token).toBe("token");
1694
+ expect(session.tokenExpires.getTime()).toBeGreaterThan(Date.now());
1695
+ expect(session.username).toBe("Casey");
1696
+ expect(session.refreshToken).toBe("refreshToken");
1697
+ expect(session.ssl).toBe(true);
1698
+ done();
1699
+ })
1700
+ .catch((e) => {
1701
+ fail(e);
1702
+ });
1703
+ });
1704
+
1705
+ it("should return a ArcGISIdentityManager where refreshTokenExpires is 2 weeks from now (within 10 ms)", (done) => {
1706
+ fetchMock.postOnce("https://www.arcgis.com/sharing/rest/oauth2/token", {
1707
+ access_token: "token",
1708
+ refresh_token: "refreshToken",
1709
+ username: "Casey",
1710
+ ssl: true
1711
+ });
1712
+
1713
+ ArcGISIdentityManager.exchangeAuthorizationCode(
1714
+ {
1715
+ clientId: "clientId",
1716
+ redirectUri: "https://example-app.com/redirect-uri"
1717
+ },
1718
+ "code"
1719
+ )
1720
+ .then((session) => {
1721
+ const twoWeeksFromNow = new Date(
1722
+ Date.now() + (20160 - 1) * 60 * 1000
1723
+ );
1724
+ expect(session.refreshTokenExpires.getTime()).toBeGreaterThan(
1725
+ twoWeeksFromNow.getTime() - 10
1726
+ );
1727
+ expect(session.refreshTokenExpires.getTime()).toBeLessThan(
1728
+ twoWeeksFromNow.getTime() + 10
1729
+ );
1730
+ done();
1731
+ })
1732
+ .catch((e) => {
1733
+ fail(e);
1734
+ });
1735
+ });
1736
+ });
1737
+
1738
+ describe(".getUser()", () => {
1739
+ afterEach(fetchMock.restore);
1740
+
1741
+ it("should cache metadata about the user", (done) => {
1742
+ // we intentionally only mock one response
1743
+ fetchMock.once(
1744
+ "https://www.arcgis.com/sharing/rest/community/self?f=json&token=token",
1745
+ {
1746
+ username: "jsmith",
1747
+ fullName: "John Smith",
1748
+ role: "org_publisher"
1749
+ }
1750
+ );
1751
+
1752
+ const session = new ArcGISIdentityManager({
1753
+ clientId: "clientId",
1754
+ redirectUri: "https://example-app.com/redirect-uri",
1755
+ token: "token",
1756
+ tokenExpires: TOMORROW,
1757
+ refreshToken: "refreshToken",
1758
+ refreshTokenExpires: TOMORROW,
1759
+ refreshTokenTTL: 1440,
1760
+ username: "jsmith",
1761
+ password: "123456"
1762
+ });
1763
+
1764
+ session
1765
+ .getUser()
1766
+ .then((response) => {
1767
+ expect(response.role).toEqual("org_publisher");
1768
+ session
1769
+ .getUser()
1770
+ .then((cachedResponse) => {
1771
+ expect(cachedResponse.fullName).toEqual("John Smith");
1772
+ done();
1773
+ })
1774
+ .catch((e) => {
1775
+ fail(e);
1776
+ });
1777
+ })
1778
+ .catch((e) => {
1779
+ fail(e);
1780
+ });
1781
+ });
1782
+
1783
+ it("should never make more then 1 request", (done) => {
1784
+ // we intentionally only mock one response
1785
+ fetchMock.once(
1786
+ "https://www.arcgis.com/sharing/rest/community/self?f=json&token=token",
1787
+ {
1788
+ username: "jsmith",
1789
+ fullName: "John Smith",
1790
+ role: "org_publisher"
1791
+ }
1792
+ );
1793
+
1794
+ const session = new ArcGISIdentityManager({
1795
+ clientId: "clientId",
1796
+ redirectUri: "https://example-app.com/redirect-uri",
1797
+ token: "token",
1798
+ tokenExpires: TOMORROW,
1799
+ refreshToken: "refreshToken",
1800
+ refreshTokenExpires: TOMORROW,
1801
+ refreshTokenTTL: 1440,
1802
+ username: "jsmith",
1803
+ password: "123456"
1804
+ });
1805
+
1806
+ Promise.all([session.getUser(), session.getUser()])
1807
+ .then(() => {
1808
+ done();
1809
+ })
1810
+ .catch((e) => {
1811
+ fail(e);
1812
+ });
1813
+ });
1814
+ });
1815
+
1816
+ describe(".getUsername()", () => {
1817
+ afterEach(fetchMock.restore);
1818
+
1819
+ it("should fetch the username via getUser()", (done) => {
1820
+ // we intentionally only mock one response
1821
+ fetchMock.once(
1822
+ "https://www.arcgis.com/sharing/rest/community/self?f=json&token=token",
1823
+ {
1824
+ username: "jsmith"
1825
+ }
1826
+ );
1827
+
1828
+ const session = new ArcGISIdentityManager({
1829
+ token: "token"
1830
+ });
1831
+
1832
+ session
1833
+ .getUsername()
1834
+ .then((response) => {
1835
+ expect(response).toEqual("jsmith");
1836
+
1837
+ // also test getting it from the cache.
1838
+ session
1839
+ .getUsername()
1840
+ .then((username) => {
1841
+ done();
1842
+
1843
+ expect(username).toEqual("jsmith");
1844
+ })
1845
+ .catch((e) => {
1846
+ fail(e);
1847
+ });
1848
+ })
1849
+ .catch((e) => {
1850
+ fail(e);
1851
+ });
1852
+ });
1853
+
1854
+ it("should use a username if passed in the session", (done) => {
1855
+ const session = new ArcGISIdentityManager({
1856
+ username: "jsmith"
1857
+ });
1858
+
1859
+ session
1860
+ .getUsername()
1861
+ .then((response) => {
1862
+ expect(response).toEqual("jsmith");
1863
+ done();
1864
+ })
1865
+ .catch((e) => {
1866
+ fail(e);
1867
+ });
1868
+ });
1869
+ });
1870
+
1871
+ describe("to/fromCredential()", () => {
1872
+ const MOCK_CREDENTIAL: ICredential = {
1873
+ expires: TOMORROW.getTime(),
1874
+ server: "https://www.arcgis.com",
1875
+ ssl: false,
1876
+ token: "token",
1877
+ userId: "jsmith"
1878
+ };
1879
+
1880
+ const MOCK_USER_SESSION = new ArcGISIdentityManager({
1881
+ clientId: "clientId",
1882
+ redirectUri: "https://example-app.com/redirect-uri",
1883
+ token: "token",
1884
+ ssl: false,
1885
+ tokenExpires: TOMORROW,
1886
+ refreshToken: "refreshToken",
1887
+ refreshTokenExpires: TOMORROW,
1888
+ refreshTokenTTL: 1440,
1889
+ username: "jsmith",
1890
+ password: "123456"
1891
+ });
1892
+
1893
+ it("should create a credential object from a session", () => {
1894
+ const creds = MOCK_USER_SESSION.toCredential();
1895
+ expect(creds.userId).toEqual("jsmith");
1896
+ expect(creds.server).toEqual("https://www.arcgis.com/sharing/rest");
1897
+ expect(creds.ssl).toEqual(false);
1898
+ expect(creds.token).toEqual("token");
1899
+ expect(creds.expires).toEqual(TOMORROW.getTime());
1900
+ });
1901
+
1902
+ it("should create a ArcGISIdentityManager from a credential", () => {
1903
+ const session = ArcGISIdentityManager.fromCredential(MOCK_CREDENTIAL);
1904
+ expect(session.username).toEqual("jsmith");
1905
+ expect(session.portal).toEqual("https://www.arcgis.com/sharing/rest");
1906
+ expect(session.ssl).toEqual(false);
1907
+ expect(session.token).toEqual("token");
1908
+ expect(session.tokenExpires).toEqual(new Date(TOMORROW));
1909
+ });
1910
+
1911
+ it("should create a ArcGISIdentityManager from a credential that came from a ArcGISIdentityManager", () => {
1912
+ const creds = MOCK_USER_SESSION.toCredential();
1913
+ const credSession = ArcGISIdentityManager.fromCredential(creds);
1914
+ expect(credSession.username).toEqual("jsmith");
1915
+ expect(credSession.portal).toEqual("https://www.arcgis.com/sharing/rest");
1916
+ expect(credSession.ssl).toEqual(false);
1917
+ expect(credSession.token).toEqual("token");
1918
+ expect(credSession.tokenExpires).toEqual(new Date(TOMORROW));
1919
+ });
1920
+ });
1921
+
1922
+ describe("fromCredential() when credential doesn't have an expiration date or ssl", () => {
1923
+ const MOCK_CREDENTIAL: ICredential = {
1924
+ expires: undefined,
1925
+ server: "https://www.arcgis.com",
1926
+ ssl: undefined,
1927
+ token: "token",
1928
+ userId: "jsmith"
1929
+ };
1930
+
1931
+ it("should create a ArcGISIdentityManager from a credential", () => {
1932
+ jasmine.clock().install();
1933
+ jasmine.clock().mockDate();
1934
+
1935
+ const session = ArcGISIdentityManager.fromCredential(MOCK_CREDENTIAL);
1936
+ expect(session.username).toEqual("jsmith");
1937
+ expect(session.portal).toEqual("https://www.arcgis.com/sharing/rest");
1938
+ expect(session.ssl).toBeTruthy();
1939
+ expect(session.token).toEqual("token");
1940
+ expect(session.tokenExpires).toEqual(
1941
+ new Date(Date.now() + 7200000 /* 2 hours */)
1942
+ );
1943
+
1944
+ jasmine.clock().uninstall();
1945
+ });
1946
+ });
1947
+
1948
+ describe("getServerRootUrl()", () => {
1949
+ it("should lowercase domain names", () => {
1950
+ const session = new ArcGISIdentityManager({
1951
+ clientId: "id",
1952
+ token: "token",
1953
+ tokenExpires: TOMORROW
1954
+ });
1955
+
1956
+ const root = session.getServerRootUrl(
1957
+ "https://PNP00035.esri.com/server/rest/services/Hosted/perimeters_dd83/FeatureServer"
1958
+ );
1959
+ expect(root).toEqual("https://pnp00035.esri.com/server");
1960
+ });
1961
+
1962
+ it("should not lowercase path names", () => {
1963
+ const session = new ArcGISIdentityManager({
1964
+ clientId: "id",
1965
+ token: "token",
1966
+ tokenExpires: TOMORROW
1967
+ });
1968
+
1969
+ const root = session.getServerRootUrl(
1970
+ "https://pnp00035.esri.com/tiles/LkFyxb9zDq7vAOAm/arcgis/rest/services/NB_Stereographic/VectorTileServer"
1971
+ );
1972
+ expect(root).toEqual(
1973
+ "https://pnp00035.esri.com/tiles/LkFyxb9zDq7vAOAm/arcgis"
1974
+ );
1975
+ });
1976
+
1977
+ it("should respect the original https/http protocol", () => {
1978
+ const session = new ArcGISIdentityManager({
1979
+ clientId: "id",
1980
+ token: "token",
1981
+ tokenExpires: TOMORROW
1982
+ });
1983
+
1984
+ const root = session.getServerRootUrl(
1985
+ "http://pnp00035.esri.com/tiles/LkFyxb9zDq7vAOAm/arcgis/rest/services/NB_Stereographic/VectorTileServer"
1986
+ );
1987
+ expect(root).toEqual(
1988
+ "http://pnp00035.esri.com/tiles/LkFyxb9zDq7vAOAm/arcgis"
1989
+ );
1990
+ });
1991
+ });
1992
+
1993
+ describe("non-federated server", () => {
1994
+ it("shouldnt fetch a fresh token if the current one isn't expired.", (done) => {
1995
+ const MOCK_USER_SESSION = new ArcGISIdentityManager({
1996
+ username: "c@sey",
1997
+ password: "123456",
1998
+ token: "token",
1999
+ tokenExpires: TOMORROW,
2000
+ server: "https://fakeserver.com/arcgis"
2001
+ });
2002
+
2003
+ MOCK_USER_SESSION.getToken(
2004
+ "https://fakeserver.com/arcgis/rest/services/Fake/MapServer/"
2005
+ )
2006
+ .then((token) => {
2007
+ expect(token).toBe("token");
2008
+ done();
2009
+ })
2010
+ .catch((err) => {
2011
+ fail(err);
2012
+ });
2013
+ });
2014
+
2015
+ it("should fetch a fresh token if the current one is expired.", (done) => {
2016
+ const MOCK_USER_SESSION = new ArcGISIdentityManager({
2017
+ username: "jsmith",
2018
+ password: "123456",
2019
+ token: "token",
2020
+ tokenExpires: YESTERDAY,
2021
+ server: "https://fakeserver.com/arcgis"
2022
+ });
2023
+
2024
+ fetchMock.postOnce("https://fakeserver.com/arcgis/rest/info", {
2025
+ currentVersion: 10.61,
2026
+ fullVersion: "10.6.1",
2027
+ authInfo: {
2028
+ isTokenBasedSecurity: true,
2029
+ tokenServicesUrl: "https://fakeserver.com/arcgis/tokens/"
2030
+ }
2031
+ });
2032
+
2033
+ fetchMock.postOnce("https://fakeserver.com/arcgis/tokens/", {
2034
+ token: "fresh-token",
2035
+ expires: TOMORROW.getTime(),
2036
+ username: " jsmith"
2037
+ });
2038
+
2039
+ MOCK_USER_SESSION.getToken(
2040
+ "https://fakeserver.com/arcgis/rest/services/Fake/MapServer/"
2041
+ )
2042
+ .then((token) => {
2043
+ expect(token).toBe("fresh-token");
2044
+ const [url, options]: [string, RequestInit] = fetchMock.lastCall(
2045
+ "https://fakeserver.com/arcgis/tokens/"
2046
+ );
2047
+ expect(options.method).toBe("POST");
2048
+ expect(options.body).toContain("f=json");
2049
+ expect(options.body).toContain("username=jsmith");
2050
+ expect(options.body).toContain("password=123456");
2051
+ expect(options.body).toContain("client=referer");
2052
+ done();
2053
+ })
2054
+ .catch((err) => {
2055
+ fail(err);
2056
+ });
2057
+ });
2058
+
2059
+ it("should trim down the server url if necessary.", (done) => {
2060
+ const MOCK_USER_SESSION = new ArcGISIdentityManager({
2061
+ username: "jsmith",
2062
+ password: "123456",
2063
+ token: "token",
2064
+ tokenExpires: YESTERDAY,
2065
+ server: "https://fakeserver.com/arcgis/rest/services/blah/"
2066
+ });
2067
+
2068
+ fetchMock.postOnce("https://fakeserver.com/arcgis/rest/info", {
2069
+ currentVersion: 10.61,
2070
+ fullVersion: "10.6.1",
2071
+ authInfo: {
2072
+ isTokenBasedSecurity: true,
2073
+ tokenServicesUrl: "https://fakeserver.com/arcgis/tokens/"
2074
+ }
2075
+ });
2076
+
2077
+ fetchMock.postOnce("https://fakeserver.com/arcgis/tokens/", {
2078
+ token: "fresh-token",
2079
+ expires: TOMORROW.getTime(),
2080
+ username: " jsmith"
2081
+ });
2082
+
2083
+ MOCK_USER_SESSION.getToken(
2084
+ "https://fakeserver.com/arcgis/rest/services/Fake/MapServer/"
2085
+ )
2086
+ .then((token) => {
2087
+ expect(token).toBe("fresh-token");
2088
+ done();
2089
+ })
2090
+ .catch((err) => {
2091
+ fail(err);
2092
+ });
2093
+ });
2094
+
2095
+ it("should throw an error if the server isnt trusted.", (done) => {
2096
+ fetchMock.postOnce("https://fakeserver2.com/arcgis/rest/info", {
2097
+ currentVersion: 10.61,
2098
+ fullVersion: "10.6.1",
2099
+ authInfo: {
2100
+ isTokenBasedSecurity: true,
2101
+ tokenServicesUrl: "https://fakeserver2.com/arcgis/tokens/"
2102
+ }
2103
+ });
2104
+ const MOCK_USER_SESSION = new ArcGISIdentityManager({
2105
+ username: "c@sey",
2106
+ password: "123456",
2107
+ token: "token",
2108
+ tokenExpires: TOMORROW,
2109
+ server: "https://fakeserver.com/arcgis"
2110
+ });
2111
+
2112
+ MOCK_USER_SESSION.getToken(
2113
+ "https://fakeserver2.com/arcgis/rest/services/Fake/MapServer/"
2114
+ )
2115
+ .then((token) => {
2116
+ fail(token);
2117
+ })
2118
+ .catch((err) => {
2119
+ expect(err.code).toBe("NOT_FEDERATED");
2120
+ expect(err.originalMessage).toEqual(
2121
+ "https://fakeserver2.com/arcgis/rest/services/Fake/MapServer/ is not federated with any portal and is not explicitly trusted."
2122
+ );
2123
+ done();
2124
+ });
2125
+ });
2126
+ });
2127
+
2128
+ describe(".getPortal()", () => {
2129
+ afterEach(fetchMock.restore);
2130
+
2131
+ it("should cache metadata about the portal", (done) => {
2132
+ // we intentionally only mock one response
2133
+ fetchMock.once(
2134
+ "https://www.arcgis.com/sharing/rest/portals/self?f=json&token=token",
2135
+ {
2136
+ authorizedCrossOriginDomains: ["gis.city.com"]
2137
+ }
2138
+ );
2139
+
2140
+ const session = new ArcGISIdentityManager({
2141
+ clientId: "clientId",
2142
+ redirectUri: "https://example-app.com/redirect-uri",
2143
+ token: "token",
2144
+ tokenExpires: TOMORROW,
2145
+ refreshToken: "refreshToken",
2146
+ refreshTokenExpires: TOMORROW,
2147
+ refreshTokenTTL: 1440,
2148
+ username: "jsmith",
2149
+ password: "123456"
2150
+ });
2151
+
2152
+ session
2153
+ .getPortal()
2154
+ .then((response) => {
2155
+ expect(response.authorizedCrossOriginDomains).toEqual([
2156
+ "gis.city.com"
2157
+ ]);
2158
+ session
2159
+ .getPortal()
2160
+ .then((cachedResponse) => {
2161
+ expect(cachedResponse.authorizedCrossOriginDomains).toEqual([
2162
+ "gis.city.com"
2163
+ ]);
2164
+ done();
2165
+ })
2166
+ .catch((e) => {
2167
+ fail(e);
2168
+ });
2169
+ })
2170
+ .catch((e) => {
2171
+ fail(e);
2172
+ });
2173
+ });
2174
+
2175
+ it("should never make more then 1 request", (done) => {
2176
+ // we intentionally only mock one response
2177
+ fetchMock.once(
2178
+ "https://www.arcgis.com/sharing/rest/portals/self?f=json&token=token",
2179
+ {
2180
+ authorizedCrossOriginDomains: ["gis.city.com"]
2181
+ }
2182
+ );
2183
+
2184
+ const session = new ArcGISIdentityManager({
2185
+ clientId: "clientId",
2186
+ redirectUri: "https://example-app.com/redirect-uri",
2187
+ token: "token",
2188
+ tokenExpires: TOMORROW,
2189
+ refreshToken: "refreshToken",
2190
+ refreshTokenExpires: TOMORROW,
2191
+ refreshTokenTTL: 1440,
2192
+ username: "jsmith",
2193
+ password: "123456"
2194
+ });
2195
+
2196
+ Promise.all([session.getPortal(), session.getPortal()])
2197
+ .then(() => {
2198
+ done();
2199
+ })
2200
+ .catch((e) => {
2201
+ fail(e);
2202
+ });
2203
+ });
2204
+ });
2205
+
2206
+ describe("fetchAuthorizedDomains/getDomainCredentials", () => {
2207
+ it("should default to same-origin credentials when no domains are listed in authorizedCrossOriginDomains", (done) => {
2208
+ const session = new ArcGISIdentityManager({
2209
+ clientId: "id",
2210
+ token: "token",
2211
+ refreshToken: "refresh",
2212
+ tokenExpires: TOMORROW,
2213
+ portal: "https://gis.city.gov/sharing/rest"
2214
+ });
2215
+
2216
+ fetchMock.postOnce("https://gisservices.city.gov/public/rest/info", {
2217
+ currentVersion: 10.51,
2218
+ fullVersion: "10.5.1.120",
2219
+ owningSystemUrl: "https://gis.city.gov",
2220
+ authInfo: {
2221
+ isTokenBasedSecurity: true,
2222
+ tokenServicesUrl: "https://gis.city.gov/sharing/generateToken"
2223
+ }
2224
+ });
2225
+
2226
+ fetchMock.getOnce(
2227
+ "https://gis.city.gov/sharing/rest/portals/self?f=json&token=token",
2228
+ {
2229
+ authorizedCrossOriginDomains: []
2230
+ }
2231
+ );
2232
+
2233
+ fetchMock.postOnce("https://gis.city.gov/sharing/rest/info", {
2234
+ owningSystemUrl: "http://gis.city.gov",
2235
+ authInfo: {
2236
+ tokenServicesUrl: "https://gis.city.gov/sharing/generateToken",
2237
+ isTokenBasedSecurity: true
2238
+ }
2239
+ });
2240
+
2241
+ fetchMock.postOnce("https://gis.city.gov/sharing/generateToken", {
2242
+ token: "serverToken",
2243
+ expires: TOMORROW
2244
+ });
2245
+
2246
+ fetchMock.post(
2247
+ "https://gisservices.city.gov/public/rest/services/trees/FeatureServer/0/query",
2248
+ {
2249
+ count: 123
2250
+ }
2251
+ );
2252
+
2253
+ request(
2254
+ "https://gisservices.city.gov/public/rest/services/trees/FeatureServer/0/query",
2255
+ {
2256
+ authentication: session
2257
+ }
2258
+ )
2259
+ .then((response) => {
2260
+ const { credentials } = fetchMock.lastOptions(
2261
+ "https://gisservices.city.gov/public/rest/services/trees/FeatureServer/0/query"
2262
+ );
2263
+ expect(credentials).toEqual("same-origin");
2264
+
2265
+ done();
2266
+ })
2267
+ .catch((e) => {
2268
+ fail(e);
2269
+ });
2270
+ });
2271
+
2272
+ it("should set the credentials option to include when a server is listed in authorizedCrossOriginDomains", (done) => {
2273
+ const session = new ArcGISIdentityManager({
2274
+ clientId: "id",
2275
+ token: "token",
2276
+ refreshToken: "refresh",
2277
+ tokenExpires: TOMORROW,
2278
+ portal: "https://gis.city.gov/sharing/rest"
2279
+ });
2280
+
2281
+ fetchMock.postOnce("https://gisservices.city.gov/public/rest/info", {
2282
+ currentVersion: 10.51,
2283
+ fullVersion: "10.5.1.120",
2284
+ owningSystemUrl: "https://gis.city.gov",
2285
+ authInfo: {
2286
+ isTokenBasedSecurity: true,
2287
+ tokenServicesUrl: "https://gis.city.gov/sharing/generateToken"
2288
+ }
2289
+ });
2290
+
2291
+ fetchMock.getOnce(
2292
+ "https://gis.city.gov/sharing/rest/portals/self?f=json&token=token",
2293
+ {
2294
+ authorizedCrossOriginDomains: ["https://gisservices.city.gov"]
2295
+ }
2296
+ );
2297
+
2298
+ fetchMock.postOnce("https://gis.city.gov/sharing/rest/info", {
2299
+ owningSystemUrl: "http://gis.city.gov",
2300
+ authInfo: {
2301
+ tokenServicesUrl: "https://gis.city.gov/sharing/generateToken",
2302
+ isTokenBasedSecurity: true
2303
+ }
2304
+ });
2305
+
2306
+ fetchMock.postOnce("https://gis.city.gov/sharing/generateToken", {
2307
+ token: "serverToken",
2308
+ expires: TOMORROW
2309
+ });
2310
+
2311
+ fetchMock.post(
2312
+ "https://gisservices.city.gov/public/rest/services/trees/FeatureServer/0/query",
2313
+ {
2314
+ count: 123
2315
+ }
2316
+ );
2317
+
2318
+ request(
2319
+ "https://gisservices.city.gov/public/rest/services/trees/FeatureServer/0/query",
2320
+ {
2321
+ authentication: session
2322
+ }
2323
+ )
2324
+ .then((response) => {
2325
+ const { credentials } = fetchMock.lastOptions(
2326
+ "https://gisservices.city.gov/public/rest/services/trees/FeatureServer/0/query"
2327
+ );
2328
+ expect(credentials).toEqual("include");
2329
+
2330
+ done();
2331
+ })
2332
+ .catch((e) => {
2333
+ fail(e);
2334
+ });
2335
+ });
2336
+ });
2337
+
2338
+ it("should still send same-origin credentials even if another domain is listed in authorizedCrossOriginDomains", (done) => {
2339
+ const session = new ArcGISIdentityManager({
2340
+ clientId: "id",
2341
+ token: "token",
2342
+ refreshToken: "refresh",
2343
+ tokenExpires: TOMORROW,
2344
+ portal: "https://gis.city.gov/sharing/rest"
2345
+ });
2346
+
2347
+ fetchMock.postOnce("https://gisservices.city.gov/public/rest/info", {
2348
+ currentVersion: 10.51,
2349
+ fullVersion: "10.5.1.120",
2350
+ owningSystemUrl: "https://gis.city.gov",
2351
+ authInfo: {
2352
+ isTokenBasedSecurity: true,
2353
+ tokenServicesUrl: "https://gis.city.gov/sharing/generateToken"
2354
+ }
2355
+ });
2356
+
2357
+ fetchMock.getOnce(
2358
+ "https://gis.city.gov/sharing/rest/portals/self?f=json&token=token",
2359
+ {
2360
+ authorizedCrossOriginDomains: ["https://other.city.gov"]
2361
+ }
2362
+ );
2363
+
2364
+ fetchMock.postOnce("https://gis.city.gov/sharing/rest/info", {
2365
+ owningSystemUrl: "http://gis.city.gov",
2366
+ authInfo: {
2367
+ tokenServicesUrl: "https://gis.city.gov/sharing/generateToken",
2368
+ isTokenBasedSecurity: true
2369
+ }
2370
+ });
2371
+
2372
+ fetchMock.postOnce("https://gis.city.gov/sharing/generateToken", {
2373
+ token: "serverToken",
2374
+ expires: TOMORROW
2375
+ });
2376
+
2377
+ fetchMock.post(
2378
+ "https://gisservices.city.gov/public/rest/services/trees/FeatureServer/0/query",
2379
+ {
2380
+ count: 123
2381
+ }
2382
+ );
2383
+
2384
+ request(
2385
+ "https://gisservices.city.gov/public/rest/services/trees/FeatureServer/0/query",
2386
+ {
2387
+ authentication: session
2388
+ }
2389
+ )
2390
+ .then((response) => {
2391
+ const { credentials } = fetchMock.lastOptions(
2392
+ "https://gisservices.city.gov/public/rest/services/trees/FeatureServer/0/query"
2393
+ );
2394
+ expect(credentials).toEqual("same-origin");
2395
+
2396
+ done();
2397
+ })
2398
+ .catch((e) => {
2399
+ fail(e);
2400
+ });
2401
+ });
2402
+
2403
+ it("should normalize optional protocols in authorizedCrossOriginDomains", (done) => {
2404
+ const session = new ArcGISIdentityManager({
2405
+ clientId: "id",
2406
+ token: "token",
2407
+ refreshToken: "refresh",
2408
+ tokenExpires: TOMORROW,
2409
+ portal: "https://gis.city.gov/sharing/rest"
2410
+ });
2411
+
2412
+ fetchMock.getOnce(
2413
+ "https://gis.city.gov/sharing/rest/portals/self?f=json&token=token",
2414
+ {
2415
+ authorizedCrossOriginDomains: ["one.city.gov", "https://two.city.gov"]
2416
+ }
2417
+ );
2418
+
2419
+ (session as any)
2420
+ .fetchAuthorizedDomains()
2421
+ .then(() => {
2422
+ expect((session as any).trustedDomains).toEqual([
2423
+ "https://one.city.gov",
2424
+ "https://two.city.gov"
2425
+ ]);
2426
+ done();
2427
+ })
2428
+ .catch((e: Error) => {
2429
+ fail(e);
2430
+ });
2431
+ });
2432
+
2433
+ it("should not use domain credentials if portal is null", (done) => {
2434
+ const session = new ArcGISIdentityManager({
2435
+ clientId: "id",
2436
+ token: "token",
2437
+ refreshToken: "refresh",
2438
+ tokenExpires: TOMORROW,
2439
+ portal: null,
2440
+ server: "https://fakeserver.com/arcgis"
2441
+ });
2442
+
2443
+ (session as any)
2444
+ .fetchAuthorizedDomains()
2445
+ .then(() => {
2446
+ done();
2447
+ })
2448
+ .catch((e: Error) => {
2449
+ fail(e);
2450
+ });
2451
+ });
2452
+
2453
+ it("should deprecate trustedServers", () => {
2454
+ const session = new ArcGISIdentityManager({
2455
+ clientId: "id",
2456
+ token: "token"
2457
+ });
2458
+
2459
+ expect((session as any).trustedServers).toBe(
2460
+ (session as any).federatedServers
2461
+ );
2462
+ });
2463
+
2464
+ describe(".destroy()", () => {
2465
+ it("should revoke a refresh token with ArcGISIdentityManager", () => {
2466
+ fetchMock.once("*", { success: true });
2467
+
2468
+ const session = new ArcGISIdentityManager({
2469
+ clientId: "clientId",
2470
+ token: "token",
2471
+ username: "c@sey",
2472
+ refreshToken: "refreshToken",
2473
+ refreshTokenExpires: TOMORROW
2474
+ });
2475
+ return ArcGISIdentityManager.destroy(session).then((response) => {
2476
+ const [url, options]: [string, RequestInit] = fetchMock.lastCall("*");
2477
+ expect(response).toEqual({ success: true });
2478
+ expect(url).toBe(
2479
+ "https://www.arcgis.com/sharing/rest/oauth2/revokeToken/"
2480
+ );
2481
+ expect(options.body).toContain("auth_token=refreshToken");
2482
+ expect(options.body).toContain("client_id=clientId");
2483
+ });
2484
+ });
2485
+
2486
+ it("should revoke a token with ArcGISIdentityManager", () => {
2487
+ fetchMock.once("*", { success: true });
2488
+
2489
+ const session = new ArcGISIdentityManager({
2490
+ clientId: "clientId",
2491
+ token: "token"
2492
+ });
2493
+ return ArcGISIdentityManager.destroy(session).then((response) => {
2494
+ const [url, options]: [string, RequestInit] = fetchMock.lastCall("*");
2495
+ expect(response).toEqual({ success: true });
2496
+ expect(url).toBe(
2497
+ "https://www.arcgis.com/sharing/rest/oauth2/revokeToken/"
2498
+ );
2499
+ expect(options.body).toContain("auth_token=token");
2500
+ expect(options.body).toContain("client_id=clientId");
2501
+ });
2502
+ });
2503
+
2504
+ it("should revoke a token with the signOut() instance method", () => {
2505
+ fetchMock.once("*", { success: true });
2506
+
2507
+ const session = new ArcGISIdentityManager({
2508
+ clientId: "clientId",
2509
+ token: "token"
2510
+ });
2511
+
2512
+ return session.signOut().then((response) => {
2513
+ const [url, options]: [string, RequestInit] = fetchMock.lastCall("*");
2514
+ expect(response).toEqual({ success: true });
2515
+ expect(url).toBe(
2516
+ "https://www.arcgis.com/sharing/rest/oauth2/revokeToken/"
2517
+ );
2518
+ expect(options.body).toContain("auth_token=token");
2519
+ expect(options.body).toContain("client_id=clientId");
2520
+ });
2521
+ });
2522
+ });
2523
+
2524
+ describe(".fromToken", () => {
2525
+ afterEach(fetchMock.restore);
2526
+
2527
+ it("should initialize a session from a token", () => {
2528
+ // we intentionally only mock one response
2529
+ fetchMock.once(
2530
+ "https://www.arcgis.com/sharing/rest/community/self?f=json&token=token",
2531
+ {
2532
+ username: "jsmith",
2533
+ fullName: "John Smith",
2534
+ role: "org_publisher"
2535
+ }
2536
+ );
2537
+
2538
+ return ArcGISIdentityManager.fromToken({
2539
+ token: "token",
2540
+ tokenExpires: TOMORROW
2541
+ }).then((session) => {
2542
+ expect(session.username).toEqual("jsmith");
2543
+ expect(session.token).toEqual("token");
2544
+ expect(session.tokenExpires).toEqual(TOMORROW);
2545
+ });
2546
+ });
2547
+ });
2548
+
2549
+ describe(".signIn", () => {
2550
+ afterEach(fetchMock.restore);
2551
+
2552
+ it("should initialize a session from a username and password", () => {
2553
+ // we intentionally only mock one response
2554
+ fetchMock.once(
2555
+ "https://www.arcgis.com/sharing/rest/community/self?f=json&token=token",
2556
+ {
2557
+ username: "jsmith",
2558
+ fullName: "John Smith",
2559
+ role: "org_publisher"
2560
+ }
2561
+ );
2562
+
2563
+ fetchMock.postOnce("https://www.arcgis.com/sharing/rest/generateToken", {
2564
+ token: "token",
2565
+ expires: TOMORROW.getTime(),
2566
+ username: " c@sey"
2567
+ });
2568
+
2569
+ return ArcGISIdentityManager.signIn({
2570
+ username: "c@sey",
2571
+ password: "123456"
2572
+ }).then((session) => {
2573
+ expect(session.username).toEqual("c@sey");
2574
+ expect(session.token).toEqual("token");
2575
+ expect(session.tokenExpires).toEqual(TOMORROW);
2576
+ });
2577
+ });
2578
+ });
2579
+ });