etincidunt 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (226) hide show
  1. package/.prettierignore +2 -0
  2. package/.travis.yml +29 -0
  3. package/.vscode/launch.json +24 -0
  4. package/.vscode/settings.json +3 -0
  5. package/demos/ago-node-cli/README.md +29 -0
  6. package/demos/ago-node-cli/ago.js +32 -0
  7. package/demos/ago-node-cli/index.js +11 -0
  8. package/demos/ago-node-cli/lib/item-export-command.js +48 -0
  9. package/demos/ago-node-cli/lib/item-search-command.js +35 -0
  10. package/demos/ago-node-cli/package-lock.json +172 -0
  11. package/demos/ago-node-cli/package.json +30 -0
  12. package/demos/attachments/README.md +5 -0
  13. package/demos/attachments/index.html +164 -0
  14. package/demos/attachments/package-lock.json +182 -0
  15. package/demos/attachments/package.json +18 -0
  16. package/demos/batch-geocoder-node/NYC_Restaurant_Inspection_Results.csv +100 -0
  17. package/demos/batch-geocoder-node/README.md +14 -0
  18. package/demos/batch-geocoder-node/batch-geocode.js +112 -0
  19. package/demos/batch-geocoder-node/config-template.js +18 -0
  20. package/demos/batch-geocoder-node/package-lock.json +109 -0
  21. package/demos/batch-geocoder-node/package.json +37 -0
  22. package/demos/express/README.md +10 -0
  23. package/demos/express/config.json.template +3 -0
  24. package/demos/express/package-lock.json +388 -0
  25. package/demos/express/package.json +18 -0
  26. package/demos/express/server.js +28 -0
  27. package/demos/feature-service-browser/README.md +6 -0
  28. package/demos/feature-service-browser/index.html +122 -0
  29. package/demos/feature-service-browser/package-lock.json +182 -0
  30. package/demos/feature-service-browser/package.json +18 -0
  31. package/demos/geocoder-browser/README.md +10 -0
  32. package/demos/geocoder-browser/config.js.template +1 -0
  33. package/demos/geocoder-browser/index.html +131 -0
  34. package/demos/geocoder-browser/package-lock.json +163 -0
  35. package/demos/geocoder-browser/package.json +19 -0
  36. package/demos/geocoder-browser/post-sign-in.html +25 -0
  37. package/demos/jsapi-integration/README.md +8 -0
  38. package/demos/jsapi-integration/config.js +6 -0
  39. package/demos/jsapi-integration/index.html +79 -0
  40. package/demos/jsapi-integration/package-lock.json +184 -0
  41. package/demos/jsapi-integration/package.json +19 -0
  42. package/demos/oauth2-browser/README.md +12 -0
  43. package/demos/oauth2-browser/authenticate.html +32 -0
  44. package/demos/oauth2-browser/config.js.template +6 -0
  45. package/demos/oauth2-browser/index.html +202 -0
  46. package/demos/oauth2-browser/logo.svg +4 -0
  47. package/demos/oauth2-browser/package-lock.json +163 -0
  48. package/demos/oauth2-browser/package.json +18 -0
  49. package/demos/oauth2-browser/style.css +36 -0
  50. package/demos/vue/.babelrc +6 -0
  51. package/demos/vue/.env.example +8 -0
  52. package/demos/vue/README.md +17 -0
  53. package/demos/vue/index.html +21 -0
  54. package/demos/vue/package-lock.json +7236 -0
  55. package/demos/vue/package.json +39 -0
  56. package/demos/vue/src/assets/logo.svg +29 -0
  57. package/demos/vue/src/components/App.vue +302 -0
  58. package/demos/vue/src/components/Authenticate.vue +68 -0
  59. package/demos/vue/src/components/Loader.vue +216 -0
  60. package/demos/vue/src/main.js +75 -0
  61. package/demos/vue/webpack.config.js +84 -0
  62. package/docs/FAQ.md +28 -0
  63. package/docs/HISTORY.md +62 -0
  64. package/docs/acetate.config.js +214 -0
  65. package/docs/build-typedoc.js +301 -0
  66. package/docs/src/_layout.html +82 -0
  67. package/docs/src/api/_declaration.html +496 -0
  68. package/docs/src/api/_layout.html +127 -0
  69. package/docs/src/api/_package.html +13 -0
  70. package/docs/src/api/index.html +23 -0
  71. package/docs/src/guides/_layout.html +24 -0
  72. package/docs/src/guides/amd-requirejs-dojo.md +40 -0
  73. package/docs/src/guides/babel-and-rollup.md +30 -0
  74. package/docs/src/guides/babel-and-webpack.md +30 -0
  75. package/docs/src/guides/browser-authentication.md +9 -0
  76. package/docs/src/guides/browserify.md +9 -0
  77. package/docs/src/guides/cli-authentication.md +9 -0
  78. package/docs/src/guides/client-server-authentication.md +9 -0
  79. package/docs/src/guides/from-a-cdn.md +36 -0
  80. package/docs/src/guides/index.md +52 -0
  81. package/docs/src/guides/node.md +30 -0
  82. package/docs/src/guides/package-overview.md +8 -0
  83. package/docs/src/guides/server-authentication.md +9 -0
  84. package/docs/src/guides/typescript-and-webpack.md +9 -0
  85. package/docs/src/img/icons.png +0 -0
  86. package/docs/src/img/icons@2x.png +0 -0
  87. package/docs/src/index.html +12 -0
  88. package/docs/src/js/api-search.js +113 -0
  89. package/docs/src/js/index.js +1 -0
  90. package/docs/src/js/nav-toggle.js +41 -0
  91. package/docs/src/sass/_highlight.scss +96 -0
  92. package/docs/src/sass/_icons.scss +157 -0
  93. package/docs/src/sass/style.scss +169 -0
  94. package/jasmine.json +7 -0
  95. package/karma.conf.js +100 -0
  96. package/lerna.json +8 -0
  97. package/notes/README.md +88 -0
  98. package/package.json +91 -0
  99. package/packages/arcgis-rest-auth/README.md +64 -0
  100. package/packages/arcgis-rest-auth/package-lock.json +11 -0
  101. package/packages/arcgis-rest-auth/package.json +51 -0
  102. package/packages/arcgis-rest-auth/src/ApplicationSession.ts +91 -0
  103. package/packages/arcgis-rest-auth/src/UserSession.ts +829 -0
  104. package/packages/arcgis-rest-auth/src/authenticated-request-options.ts +21 -0
  105. package/packages/arcgis-rest-auth/src/fetch-token.ts +55 -0
  106. package/packages/arcgis-rest-auth/src/generate-token.ts +36 -0
  107. package/packages/arcgis-rest-auth/src/index.ts +5 -0
  108. package/packages/arcgis-rest-auth/test/ApplicationSession.test.ts +121 -0
  109. package/packages/arcgis-rest-auth/test/UserSession.test.ts +883 -0
  110. package/packages/arcgis-rest-auth/test/fetchToken.test.ts +76 -0
  111. package/packages/arcgis-rest-auth/test/generateToken.test.ts +36 -0
  112. package/packages/arcgis-rest-auth/test/utils.ts +11 -0
  113. package/packages/arcgis-rest-auth/tsconfig.json +6 -0
  114. package/packages/arcgis-rest-common-types/README.md +61 -0
  115. package/packages/arcgis-rest-common-types/package.json +38 -0
  116. package/packages/arcgis-rest-common-types/src/group.ts +51 -0
  117. package/packages/arcgis-rest-common-types/src/index.ts +467 -0
  118. package/packages/arcgis-rest-common-types/src/item.ts +45 -0
  119. package/packages/arcgis-rest-common-types/src/webmap.ts +1225 -0
  120. package/packages/arcgis-rest-common-types/tsconfig.json +11 -0
  121. package/packages/arcgis-rest-feature-service/README.md +70 -0
  122. package/packages/arcgis-rest-feature-service/package-lock.json +11 -0
  123. package/packages/arcgis-rest-feature-service/package.json +50 -0
  124. package/packages/arcgis-rest-feature-service/src/add.ts +82 -0
  125. package/packages/arcgis-rest-feature-service/src/addAttachment.ts +65 -0
  126. package/packages/arcgis-rest-feature-service/src/delete.ts +85 -0
  127. package/packages/arcgis-rest-feature-service/src/deleteAttachments.ts +68 -0
  128. package/packages/arcgis-rest-feature-service/src/getAttachments.ts +64 -0
  129. package/packages/arcgis-rest-feature-service/src/helpers.ts +77 -0
  130. package/packages/arcgis-rest-feature-service/src/index.ts +8 -0
  131. package/packages/arcgis-rest-feature-service/src/query.ts +174 -0
  132. package/packages/arcgis-rest-feature-service/src/update.ts +81 -0
  133. package/packages/arcgis-rest-feature-service/src/updateAttachment.ts +74 -0
  134. package/packages/arcgis-rest-feature-service/test/attachments.test.ts +179 -0
  135. package/packages/arcgis-rest-feature-service/test/features.test.ts +172 -0
  136. package/packages/arcgis-rest-feature-service/test/mocks/feature.ts +220 -0
  137. package/packages/arcgis-rest-feature-service/test/mocks/foo.txt +1 -0
  138. package/packages/arcgis-rest-feature-service/tsconfig.json +6 -0
  139. package/packages/arcgis-rest-geocoder/README.md +73 -0
  140. package/packages/arcgis-rest-geocoder/package-lock.json +11 -0
  141. package/packages/arcgis-rest-geocoder/package.json +52 -0
  142. package/packages/arcgis-rest-geocoder/src/bulk.ts +102 -0
  143. package/packages/arcgis-rest-geocoder/src/geocode.ts +117 -0
  144. package/packages/arcgis-rest-geocoder/src/helpers.ts +81 -0
  145. package/packages/arcgis-rest-geocoder/src/index.ts +4 -0
  146. package/packages/arcgis-rest-geocoder/src/reverse.ts +84 -0
  147. package/packages/arcgis-rest-geocoder/src/suggest.ts +72 -0
  148. package/packages/arcgis-rest-geocoder/test/geocoder.test.ts +510 -0
  149. package/packages/arcgis-rest-geocoder/test/mocks/responses.ts +588 -0
  150. package/packages/arcgis-rest-geocoder/tsconfig.json +6 -0
  151. package/packages/arcgis-rest-groups/README.md +64 -0
  152. package/packages/arcgis-rest-groups/package-lock.json +11 -0
  153. package/packages/arcgis-rest-groups/package.json +52 -0
  154. package/packages/arcgis-rest-groups/src/groups.ts +272 -0
  155. package/packages/arcgis-rest-groups/src/index.ts +1 -0
  156. package/packages/arcgis-rest-groups/test/groups.test.ts +280 -0
  157. package/packages/arcgis-rest-groups/test/mocks/responses.ts +137 -0
  158. package/packages/arcgis-rest-groups/tsconfig.json +6 -0
  159. package/packages/arcgis-rest-items/README.md +66 -0
  160. package/packages/arcgis-rest-items/package-lock.json +11 -0
  161. package/packages/arcgis-rest-items/package.json +52 -0
  162. package/packages/arcgis-rest-items/src/index.ts +1 -0
  163. package/packages/arcgis-rest-items/src/items.ts +498 -0
  164. package/packages/arcgis-rest-items/test/items.test.ts +1153 -0
  165. package/packages/arcgis-rest-items/test/mocks/foo.zip +0 -0
  166. package/packages/arcgis-rest-items/test/mocks/item.ts +30 -0
  167. package/packages/arcgis-rest-items/test/mocks/resources.ts +28 -0
  168. package/packages/arcgis-rest-items/test/mocks/search.ts +60 -0
  169. package/packages/arcgis-rest-items/tsconfig.json +6 -0
  170. package/packages/arcgis-rest-request/README.md +65 -0
  171. package/packages/arcgis-rest-request/package-lock.json +11 -0
  172. package/packages/arcgis-rest-request/package.json +42 -0
  173. package/packages/arcgis-rest-request/src/index.ts +10 -0
  174. package/packages/arcgis-rest-request/src/request.ts +259 -0
  175. package/packages/arcgis-rest-request/src/utils/ArcGISAuthError.ts +67 -0
  176. package/packages/arcgis-rest-request/src/utils/ArcGISRequestError.ts +73 -0
  177. package/packages/arcgis-rest-request/src/utils/ErrorTypes.ts +29 -0
  178. package/packages/arcgis-rest-request/src/utils/check-for-errors.ts +65 -0
  179. package/packages/arcgis-rest-request/src/utils/encode-form-data.ts +29 -0
  180. package/packages/arcgis-rest-request/src/utils/encode-query-string.ts +23 -0
  181. package/packages/arcgis-rest-request/src/utils/get-portal-url.ts +25 -0
  182. package/packages/arcgis-rest-request/src/utils/get-portal.ts +45 -0
  183. package/packages/arcgis-rest-request/src/utils/process-params.ts +99 -0
  184. package/packages/arcgis-rest-request/test/mocks/errors.ts +59 -0
  185. package/packages/arcgis-rest-request/test/mocks/geojson-feature-collection.ts +10 -0
  186. package/packages/arcgis-rest-request/test/mocks/portal.ts +109 -0
  187. package/packages/arcgis-rest-request/test/mocks/sharing-rest-info.ts +38 -0
  188. package/packages/arcgis-rest-request/test/mocks/webmap.ts +38 -0
  189. package/packages/arcgis-rest-request/test/request.test.ts +296 -0
  190. package/packages/arcgis-rest-request/test/utils/ArcGISAuthError.test.ts +167 -0
  191. package/packages/arcgis-rest-request/test/utils/ArcGISRequestError.test.ts +40 -0
  192. package/packages/arcgis-rest-request/test/utils/check-for-errors.test.ts +101 -0
  193. package/packages/arcgis-rest-request/test/utils/encode-form-data.test.ts +112 -0
  194. package/packages/arcgis-rest-request/test/utils/get-portal-url.test.ts +34 -0
  195. package/packages/arcgis-rest-request/test/utils/portal.test.ts +94 -0
  196. package/packages/arcgis-rest-request/test/utils/process-params.test.ts +190 -0
  197. package/packages/arcgis-rest-request/tsconfig.json +6 -0
  198. package/packages/arcgis-rest-sharing/README.md +67 -0
  199. package/packages/arcgis-rest-sharing/package-lock.json +11 -0
  200. package/packages/arcgis-rest-sharing/package.json +55 -0
  201. package/packages/arcgis-rest-sharing/src/access.ts +91 -0
  202. package/packages/arcgis-rest-sharing/src/group-sharing.ts +212 -0
  203. package/packages/arcgis-rest-sharing/src/helpers.ts +92 -0
  204. package/packages/arcgis-rest-sharing/src/index.ts +2 -0
  205. package/packages/arcgis-rest-sharing/test/access.test.ts +153 -0
  206. package/packages/arcgis-rest-sharing/test/group-sharing.test.ts +436 -0
  207. package/packages/arcgis-rest-sharing/test/mocks/sharing.ts +15 -0
  208. package/packages/arcgis-rest-sharing/tsconfig.json +6 -0
  209. package/packages/arcgis-rest-users/README.md +71 -0
  210. package/packages/arcgis-rest-users/package-lock.json +11 -0
  211. package/packages/arcgis-rest-users/package.json +51 -0
  212. package/packages/arcgis-rest-users/src/index.ts +1 -0
  213. package/packages/arcgis-rest-users/src/users.ts +70 -0
  214. package/packages/arcgis-rest-users/test/mocks/responses.ts +170 -0
  215. package/packages/arcgis-rest-users/test/users.test.ts +97 -0
  216. package/packages/arcgis-rest-users/tsconfig.json +6 -0
  217. package/support/FormData.d.ts +1 -0
  218. package/support/changelog.js +388 -0
  219. package/support/commit-template.txt +19 -0
  220. package/support/deploy-doc-site.js +16 -0
  221. package/support/publish.sh +40 -0
  222. package/support/test-helpers.js +8 -0
  223. package/tsconfig.json +69 -0
  224. package/tslint.json +14 -0
  225. package/umd-base-profile.js +82 -0
  226. package/umd-production-profile.js +13 -0
@@ -0,0 +1,883 @@
1
+ import { UserSession } from "../src/index";
2
+ import { ICredential } from "../src/UserSession";
3
+
4
+ import {
5
+ ArcGISRequestError,
6
+ ArcGISAuthError,
7
+ ErrorTypes
8
+ } from "@esri/arcgis-rest-request";
9
+ import * as fetchMock from "fetch-mock";
10
+ import { YESTERDAY, TOMORROW } from "./utils";
11
+
12
+ describe("UserSession", () => {
13
+ afterEach(fetchMock.restore);
14
+
15
+ it("should serialize to and from JSON", () => {
16
+ const session = new UserSession({
17
+ clientId: "clientId",
18
+ redirectUri: "https://example-app.com/redirect-uri",
19
+ token: "token",
20
+ tokenExpires: TOMORROW,
21
+ refreshToken: "refreshToken",
22
+ refreshTokenExpires: TOMORROW,
23
+ refreshTokenTTL: 1440,
24
+ username: "c@sey",
25
+ password: "123456"
26
+ });
27
+
28
+ const session2 = UserSession.deserialize(session.serialize());
29
+
30
+ expect(session2.clientId).toEqual("clientId");
31
+ expect(session2.redirectUri).toEqual(
32
+ "https://example-app.com/redirect-uri"
33
+ );
34
+ expect(session2.token).toEqual("token");
35
+ expect(session2.tokenExpires).toEqual(TOMORROW);
36
+ expect(session2.refreshToken).toEqual("refreshToken");
37
+ expect(session2.refreshTokenExpires).toEqual(TOMORROW);
38
+ expect(session2.username).toEqual("c@sey");
39
+ expect(session2.password).toEqual("123456");
40
+ expect(session2.tokenDuration).toEqual(20160);
41
+ expect(session2.refreshTokenTTL).toEqual(1440);
42
+ });
43
+
44
+ describe(".getToken()", () => {
45
+ it("should return unexpired tokens for trusted arcgis.com domains", done => {
46
+ const session = new UserSession({
47
+ clientId: "id",
48
+ token: "token",
49
+ tokenExpires: TOMORROW
50
+ });
51
+
52
+ Promise.all([
53
+ session.getToken("https://www.arcgis.com/sharing/rest/portals/self"),
54
+ session.getToken(
55
+ "https://services1.arcgis.com/MOCK_ORG/arcgis/rest/services/Private_Service/FeatureServer"
56
+ )
57
+ ])
58
+ .then(([token1, token2]) => {
59
+ expect(token1).toBe("token");
60
+ expect(token2).toBe("token");
61
+ done();
62
+ })
63
+ .catch(e => {
64
+ fail(e);
65
+ });
66
+ });
67
+
68
+ it("should return unexpired tokens when an org url is passed", done => {
69
+ const session = new UserSession({
70
+ clientId: "id",
71
+ token: "token",
72
+ tokenExpires: TOMORROW,
73
+ portal: "https://custom.maps.arcgis.com/sharing/rest"
74
+ });
75
+
76
+ session
77
+ .getToken(
78
+ "https://services1.arcgis.com/MOCK_ORG/arcgis/rest/services/Private_Service/FeatureServer"
79
+ )
80
+ .then(token => {
81
+ expect(token).toBe("token");
82
+ done();
83
+ })
84
+ .catch(e => {
85
+ fail(e);
86
+ });
87
+ });
88
+
89
+ it("should return unexpired tokens for the configured portal domain", done => {
90
+ const session = new UserSession({
91
+ clientId: "id",
92
+ token: "token",
93
+ tokenExpires: TOMORROW,
94
+ portal: "https://gis.city.gov/sharing/rest"
95
+ });
96
+
97
+ session
98
+ .getToken("https://gis.city.gov/sharing/rest/portals/self")
99
+ .then(token => {
100
+ expect(token).toBe("token");
101
+ done();
102
+ });
103
+ });
104
+
105
+ it("should fetch new tokens when tokens for trusted arcgis.com domains are expired", done => {
106
+ const session = new UserSession({
107
+ clientId: "id",
108
+ token: "token",
109
+ refreshToken: "refresh",
110
+ tokenExpires: YESTERDAY
111
+ });
112
+
113
+ fetchMock.mock(
114
+ "https://www.arcgis.com/sharing/rest/oauth2/token",
115
+ {
116
+ access_token: "new",
117
+ expires_in: 1800,
118
+ username: "c@sey"
119
+ },
120
+ { times: 2, method: "POST" }
121
+ );
122
+
123
+ Promise.all([
124
+ session.getToken("https://www.arcgis.com/sharing/rest/portals/self"),
125
+ session.getToken(
126
+ "https://services1.arcgis.com/MOCK_ORG/arcgis/rest/services/Private_Service/FeatureServer"
127
+ )
128
+ ])
129
+ .then(([token1, token2]) => {
130
+ expect(token1).toBe("new");
131
+ expect(token2).toBe("new");
132
+ done();
133
+ })
134
+ .catch(e => {
135
+ fail(e);
136
+ });
137
+ });
138
+
139
+ it("should generate a token for an untrusted, federated server", done => {
140
+ const session = new UserSession({
141
+ clientId: "id",
142
+ token: "token",
143
+ refreshToken: "refresh",
144
+ tokenExpires: TOMORROW,
145
+ portal: "https://gis.city.gov/sharing/rest"
146
+ });
147
+
148
+ fetchMock.postOnce("https://gisservices.city.gov/public/rest/info", {
149
+ currentVersion: 10.51,
150
+ fullVersion: "10.5.1.120",
151
+ owningSystemUrl: "https://gis.city.gov",
152
+ authInfo: {
153
+ isTokenBasedSecurity: true,
154
+ tokenServicesUrl: "https://gis.city.gov/sharing/generateToken"
155
+ }
156
+ });
157
+
158
+ fetchMock.postOnce("https://gis.city.gov/sharing/rest/info", {
159
+ owningSystemUrl: "http://gis.city.gov",
160
+ authInfo: {
161
+ tokenServicesUrl: "https://gis.city.gov/sharing/generateToken",
162
+ isTokenBasedSecurity: true
163
+ }
164
+ });
165
+
166
+ fetchMock.postOnce("https://gis.city.gov/sharing/generateToken", {
167
+ token: "serverToken",
168
+ expires: TOMORROW
169
+ });
170
+
171
+ session
172
+ .getToken(
173
+ "https://gisservices.city.gov/public/rest/services/trees/FeatureServer/0/query"
174
+ )
175
+ .then(token => {
176
+ expect(token).toBe("serverToken");
177
+ return session.getToken(
178
+ "https://gisservices.city.gov/public/rest/services/trees/FeatureServer/0/query"
179
+ );
180
+ })
181
+ .then(token => {
182
+ expect(token).toBe("serverToken");
183
+ done();
184
+ });
185
+ });
186
+
187
+ it("should generate a token for an untrusted, federated server with user credentials", done => {
188
+ const session = new UserSession({
189
+ username: "c@sey",
190
+ password: "jones",
191
+ portal: "https://gis.city.gov/sharing/rest"
192
+ });
193
+
194
+ fetchMock.postOnce("https://gisservices.city.gov/public/rest/info", {
195
+ currentVersion: 10.51,
196
+ fullVersion: "10.5.1.120",
197
+ owningSystemUrl: "https://gis.city.gov",
198
+ authInfo: {
199
+ isTokenBasedSecurity: true,
200
+ tokenServicesUrl: "https://gis.city.gov/sharing/generateToken"
201
+ }
202
+ });
203
+
204
+ fetchMock.postOnce("https://gis.city.gov/sharing/rest/info", {
205
+ owningSystemUrl: "http://gis.city.gov",
206
+ authInfo: {
207
+ tokenServicesUrl: "https://gis.city.gov/sharing/generateToken",
208
+ isTokenBasedSecurity: true
209
+ }
210
+ });
211
+
212
+ fetchMock.postOnce("https://gis.city.gov/sharing/generateToken", {
213
+ token: "serverToken",
214
+ expires: TOMORROW
215
+ });
216
+
217
+ session
218
+ .getToken(
219
+ "https://gisservices.city.gov/public/rest/services/trees/FeatureServer/0/query"
220
+ )
221
+ .then(token => {
222
+ expect(token).toBe("serverToken");
223
+ done();
224
+ });
225
+ });
226
+
227
+ it("should only make 1 token request to an untrusted portal for similar URLs", done => {
228
+ const session = new UserSession({
229
+ clientId: "id",
230
+ token: "token",
231
+ refreshToken: "refresh",
232
+ tokenExpires: TOMORROW,
233
+ portal: "https://gis.city.gov/sharing/rest"
234
+ });
235
+
236
+ fetchMock.mock(
237
+ "https://gisservices.city.gov/public/rest/info",
238
+ {
239
+ currentVersion: 10.51,
240
+ fullVersion: "10.5.1.120",
241
+ owningSystemUrl: "https://gis.city.gov",
242
+ authInfo: {
243
+ isTokenBasedSecurity: true,
244
+ tokenServicesUrl: "https://gis.city.gov/sharing/generateToken"
245
+ }
246
+ },
247
+ { times: 1, method: "POST" }
248
+ );
249
+
250
+ fetchMock.mock(
251
+ "https://gis.city.gov/sharing/rest/info",
252
+ {
253
+ owningSystemUrl: "http://gis.city.gov",
254
+ authInfo: {
255
+ tokenServicesUrl: "https://gis.city.gov/sharing/generateToken",
256
+ isTokenBasedSecurity: true
257
+ }
258
+ },
259
+ { times: 1, method: "POST" }
260
+ );
261
+
262
+ fetchMock.mock(
263
+ "https://gis.city.gov/sharing/generateToken",
264
+ {
265
+ token: "serverToken",
266
+ expires: TOMORROW
267
+ },
268
+ { times: 1, method: "POST" }
269
+ );
270
+
271
+ Promise.all([
272
+ session.getToken(
273
+ "https://gisservices.city.gov/public/rest/services/trees/FeatureServer/0/query"
274
+ ),
275
+ session.getToken(
276
+ "https://gisservices.city.gov/public/rest/services/trees/FeatureServer/0/query"
277
+ )
278
+ ]).then(([token1, token2]) => {
279
+ expect(token1).toBe("serverToken");
280
+ expect(token2).toBe("serverToken");
281
+ expect(
282
+ fetchMock.calls("https://gis.city.gov/sharing/generateToken").length
283
+ ).toBe(1);
284
+ done();
285
+ });
286
+ });
287
+
288
+ it("should throw an ArcGISAuthError when the owning system doesn't match", done => {
289
+ const session = new UserSession({
290
+ clientId: "id",
291
+ token: "token",
292
+ refreshToken: "refresh",
293
+ tokenExpires: YESTERDAY
294
+ });
295
+
296
+ fetchMock.post("https://gisservices.city.gov/public/rest/info", {
297
+ currentVersion: 10.51,
298
+ fullVersion: "10.5.1.120",
299
+ owningSystemUrl: "https://gis.city.gov",
300
+ authInfo: {
301
+ isTokenBasedSecurity: true,
302
+ tokenServicesUrl: "https://gis.city.gov/sharing/generateToken"
303
+ }
304
+ });
305
+
306
+ fetchMock.post("https://gis.city.gov/sharing/generateToken", {
307
+ error: {
308
+ code: 400,
309
+ message: "Unable to generate token",
310
+ details: ["Unable to generate token for this server"]
311
+ }
312
+ });
313
+
314
+ session
315
+ .getToken(
316
+ "https://gisservices.city.gov/public/rest/services/trees/FeatureServer/0/query"
317
+ )
318
+ .catch(e => {
319
+ expect(e.name).toEqual(ErrorTypes.ArcGISAuthError);
320
+ expect(e.code).toEqual("NOT_FEDERATED");
321
+ expect(e.message).toEqual(
322
+ "NOT_FEDERATED: https://gisservices.city.gov/public/rest/services/trees/FeatureServer/0/query is not federated with https://www.arcgis.com/sharing/rest."
323
+ );
324
+ done();
325
+ });
326
+ });
327
+
328
+ it("should throw an ArcGISAuthError when no owning system is advertised", done => {
329
+ const session = new UserSession({
330
+ clientId: "id",
331
+ token: "token",
332
+ refreshToken: "refresh",
333
+ tokenExpires: YESTERDAY
334
+ });
335
+
336
+ fetchMock.post("https://gisservices.city.gov/public/rest/info", {
337
+ currentVersion: 10.51,
338
+ fullVersion: "10.5.1.120",
339
+ authInfo: {
340
+ isTokenBasedSecurity: true,
341
+ tokenServicesUrl: "https://gis.city.gov/sharing/generateToken"
342
+ }
343
+ });
344
+
345
+ session
346
+ .getToken(
347
+ "https://gisservices.city.gov/public/rest/services/trees/FeatureServer/0/query"
348
+ )
349
+ .catch(e => {
350
+ expect(e.name).toEqual(ErrorTypes.ArcGISAuthError);
351
+ expect(e.code).toEqual("NOT_FEDERATED");
352
+ expect(e.message).toEqual(
353
+ "NOT_FEDERATED: https://gisservices.city.gov/public/rest/services/trees/FeatureServer/0/query is not federated with https://www.arcgis.com/sharing/rest."
354
+ );
355
+ done();
356
+ });
357
+ });
358
+ });
359
+
360
+ describe(".refreshSession()", () => {
361
+ it("should refresh with a username and password if expired", done => {
362
+ const session = new UserSession({
363
+ username: "c@sey",
364
+ password: "123456"
365
+ });
366
+
367
+ fetchMock.postOnce("https://www.arcgis.com/sharing/rest/generateToken", {
368
+ token: "token",
369
+ expires: TOMORROW.getTime(),
370
+ username: " c@sey"
371
+ });
372
+
373
+ session
374
+ .refreshSession()
375
+ .then(s => {
376
+ expect(s.token).toBe("token");
377
+ expect(s.tokenExpires).toEqual(TOMORROW);
378
+ done();
379
+ })
380
+ .catch(e => {
381
+ fail(e);
382
+ });
383
+ });
384
+
385
+ it("should refresh with an unexpired refresh token", done => {
386
+ const session = new UserSession({
387
+ clientId: "clientId",
388
+ token: "token",
389
+ username: "c@sey",
390
+ refreshToken: "refreshToken",
391
+ refreshTokenExpires: TOMORROW
392
+ });
393
+
394
+ fetchMock.postOnce("https://www.arcgis.com/sharing/rest/oauth2/token", {
395
+ access_token: "newToken",
396
+ expires_in: 60,
397
+ username: " c@sey"
398
+ });
399
+
400
+ session
401
+ .refreshSession()
402
+ .then(s => {
403
+ expect(s.token).toBe("newToken");
404
+ expect(s.tokenExpires.getTime()).toBeGreaterThan(Date.now());
405
+ done();
406
+ })
407
+ .catch(e => {
408
+ fail(e);
409
+ });
410
+ });
411
+
412
+ it("should refresh with an expired refresh token", done => {
413
+ const session = new UserSession({
414
+ clientId: "clientId",
415
+ token: "token",
416
+ username: "c@sey",
417
+ refreshToken: "refreshToken",
418
+ refreshTokenExpires: YESTERDAY,
419
+ redirectUri: "https://example-app.com/redirect-uri"
420
+ });
421
+
422
+ fetchMock.postOnce("https://www.arcgis.com/sharing/rest/oauth2/token", {
423
+ access_token: "newToken",
424
+ expires_in: 60,
425
+ username: " c@sey",
426
+ refresh_token: "newRefreshToken"
427
+ });
428
+
429
+ session
430
+ .refreshSession()
431
+ .then(s => {
432
+ expect(s.token).toBe("newToken");
433
+ expect(s.tokenExpires.getTime()).toBeGreaterThan(Date.now());
434
+ expect(s.refreshToken).toBe("newRefreshToken");
435
+ expect(s.refreshTokenExpires.getTime()).toBeGreaterThan(Date.now());
436
+ done();
437
+ })
438
+ .catch(e => {
439
+ fail(e);
440
+ });
441
+ });
442
+
443
+ it("should reject if we cannot refresh the token", done => {
444
+ const session = new UserSession({
445
+ clientId: "clientId",
446
+ token: "token",
447
+ username: "c@sey"
448
+ });
449
+
450
+ session.refreshSession().catch(e => {
451
+ expect(e instanceof ArcGISAuthError).toBeTruthy();
452
+ expect(e.name).toBe("ArcGISAuthError");
453
+ expect(e.message).toBe("Unable to refresh token.");
454
+ done();
455
+ });
456
+ });
457
+
458
+ it("should only make 1 token request to the portal for similar URLs", done => {
459
+ const session = new UserSession({
460
+ clientId: "id",
461
+ token: "token",
462
+ refreshToken: "refresh",
463
+ tokenExpires: YESTERDAY
464
+ });
465
+
466
+ fetchMock.mock(
467
+ "https://www.arcgis.com/sharing/rest/oauth2/token",
468
+ {
469
+ access_token: "new",
470
+ expires_in: 1800,
471
+ username: "c@sey"
472
+ },
473
+ { times: 1, method: "POST" }
474
+ );
475
+
476
+ Promise.all([
477
+ session.getToken("https://www.arcgis.com/sharing/rest/portals/self"),
478
+ session.getToken("https://www.arcgis.com/sharing/rest/portals/self")
479
+ ])
480
+ .then(([token1, token2]) => {
481
+ expect(token1).toBe("new");
482
+ expect(token2).toBe("new");
483
+ expect(
484
+ fetchMock.calls("https://www.arcgis.com/sharing/rest/oauth2/token")
485
+ .length
486
+ ).toBe(1);
487
+ done();
488
+ })
489
+ .catch(e => {
490
+ fail(e);
491
+ });
492
+ });
493
+ });
494
+
495
+ describe(".beginOAuth2()", () => {
496
+ it("should authorize via a popup", done => {
497
+ const MockWindow: any = {
498
+ open: jasmine.createSpy("spy")
499
+ };
500
+
501
+ UserSession.beginOAuth2(
502
+ {
503
+ clientId: "clientId123",
504
+ redirectUri: "http://example-app.com/redirect",
505
+ state: "abc123"
506
+ },
507
+ MockWindow
508
+ )
509
+ .then(session => {
510
+ expect(session.token).toBe("token");
511
+ expect(session.username).toBe("c@sey");
512
+ expect(session.tokenExpires).toEqual(TOMORROW);
513
+ done();
514
+ })
515
+ .catch(e => {
516
+ fail(e);
517
+ });
518
+
519
+ expect(MockWindow.open).toHaveBeenCalledWith(
520
+ "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=",
521
+ "oauth-window",
522
+ "height=400,width=600,menubar=no,location=yes,resizable=yes,scrollbars=yes,status=yes"
523
+ );
524
+
525
+ MockWindow.__ESRI_REST_AUTH_HANDLER_clientId123(
526
+ JSON.stringify(undefined),
527
+ JSON.stringify({
528
+ token: "token",
529
+ expires: TOMORROW,
530
+ username: "c@sey"
531
+ })
532
+ );
533
+ });
534
+
535
+ it("should reject the promise if there is an error", done => {
536
+ const MockWindow: any = {
537
+ open: jasmine.createSpy("spy")
538
+ };
539
+
540
+ UserSession.beginOAuth2(
541
+ {
542
+ clientId: "clientId123",
543
+ redirectUri: "http://example-app.com/redirect",
544
+ locale: "fr"
545
+ },
546
+ MockWindow
547
+ ).catch(e => {
548
+ done();
549
+ });
550
+
551
+ expect(MockWindow.open).toHaveBeenCalledWith(
552
+ "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",
553
+ "oauth-window",
554
+ "height=400,width=600,menubar=no,location=yes,resizable=yes,scrollbars=yes,status=yes"
555
+ );
556
+
557
+ MockWindow.__ESRI_REST_AUTH_HANDLER_clientId123(
558
+ JSON.stringify({
559
+ errorMessage: "unable to sign in",
560
+ error: "SIGN_IN_FAILED"
561
+ })
562
+ );
563
+ });
564
+
565
+ it("should authorize in the same window/tab", () => {
566
+ const MockWindow: any = {
567
+ location: {
568
+ href: ""
569
+ }
570
+ };
571
+
572
+ // https://github.com/palantir/tslint/issues/3056
573
+ void UserSession.beginOAuth2(
574
+ {
575
+ clientId: "clientId123",
576
+ redirectUri: "http://example-app.com/redirect",
577
+ popup: false
578
+ },
579
+ MockWindow
580
+ );
581
+
582
+ expect(MockWindow.location.href).toBe(
583
+ "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="
584
+ );
585
+ });
586
+
587
+ it("should authorize using a social media provider", () => {
588
+ const MockWindow: any = {
589
+ location: {
590
+ href: ""
591
+ }
592
+ };
593
+
594
+ // https://github.com/palantir/tslint/issues/3056
595
+ void UserSession.beginOAuth2(
596
+ {
597
+ clientId: "clientId123",
598
+ redirectUri: "http://example-app.com/redirect",
599
+ popup: false,
600
+ provider: "facebook"
601
+ },
602
+ MockWindow
603
+ );
604
+
605
+ expect(MockWindow.location.href).toBe(
606
+ "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="
607
+ );
608
+ });
609
+
610
+ it("should authorize using the other social media provider", () => {
611
+ const MockWindow: any = {
612
+ location: {
613
+ href: ""
614
+ }
615
+ };
616
+
617
+ // https://github.com/palantir/tslint/issues/3056
618
+ void UserSession.beginOAuth2(
619
+ {
620
+ clientId: "clientId123",
621
+ redirectUri: "http://example-app.com/redirect",
622
+ popup: false,
623
+ provider: "google"
624
+ },
625
+ MockWindow
626
+ );
627
+
628
+ expect(MockWindow.location.href).toBe(
629
+ "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="
630
+ );
631
+ });
632
+ });
633
+
634
+ describe(".completeOAuth2()", () => {
635
+ it("should return a new user session if it cannot find a valid parent", () => {
636
+ const MockWindow = {
637
+ location: {
638
+ href:
639
+ "https://example-app.com/redirect-uri#access_token=token&expires_in=1209600&username=c%40sey&persist=true"
640
+ },
641
+ get parent() {
642
+ return this;
643
+ }
644
+ };
645
+
646
+ const session = UserSession.completeOAuth2(
647
+ {
648
+ clientId: "clientId",
649
+ redirectUri: "https://example-app.com/redirect-uri"
650
+ },
651
+ MockWindow
652
+ );
653
+
654
+ expect(session.token).toBe("token");
655
+ expect(session.tokenExpires.getTime()).toBeGreaterThan(Date.now());
656
+ expect(session.username).toBe("c@sey");
657
+ });
658
+
659
+ it("should callback to create a new user session if finds a valid opener", done => {
660
+ const MockWindow = {
661
+ opener: {
662
+ parent: {
663
+ __ESRI_REST_AUTH_HANDLER_clientId(
664
+ errorString: string,
665
+ oauthInfoString: string
666
+ ) {
667
+ const oauthInfo = JSON.parse(oauthInfoString);
668
+ expect(oauthInfo.token).toBe("token");
669
+ expect(oauthInfo.username).toBe("c@sey");
670
+ expect(new Date(oauthInfo.expires).getTime()).toBeGreaterThan(
671
+ Date.now()
672
+ );
673
+ }
674
+ }
675
+ },
676
+ close() {
677
+ done();
678
+ },
679
+ location: {
680
+ href:
681
+ "https://example-app.com/redirect-uri#access_token=token&expires_in=1209600&username=c%40sey"
682
+ }
683
+ };
684
+
685
+ UserSession.completeOAuth2(
686
+ {
687
+ clientId: "clientId",
688
+ redirectUri: "https://example-app.com/redirect-uri"
689
+ },
690
+ MockWindow
691
+ );
692
+ });
693
+
694
+ it("should callback to create a new user session if finds a valid parent", done => {
695
+ const MockWindow = {
696
+ parent: {
697
+ __ESRI_REST_AUTH_HANDLER_clientId(
698
+ errorString: string,
699
+ oauthInfoString: string
700
+ ) {
701
+ const oauthInfo = JSON.parse(oauthInfoString);
702
+ expect(oauthInfo.token).toBe("token");
703
+ expect(oauthInfo.username).toBe("c@sey");
704
+ expect(new Date(oauthInfo.expires).getTime()).toBeGreaterThan(
705
+ Date.now()
706
+ );
707
+ }
708
+ },
709
+ close() {
710
+ done();
711
+ },
712
+ location: {
713
+ href:
714
+ "https://example-app.com/redirect-uri#access_token=token&expires_in=1209600&username=c%40sey"
715
+ }
716
+ };
717
+
718
+ UserSession.completeOAuth2(
719
+ {
720
+ clientId: "clientId",
721
+ redirectUri: "https://example-app.com/redirect-uri"
722
+ },
723
+ MockWindow
724
+ );
725
+ });
726
+
727
+ it("should throw an error from the authorization window", () => {
728
+ const MockWindow = {
729
+ location: {
730
+ href:
731
+ "https://example-app.com/redirect-uri#error=Invalid_Signin&error_description=Invalid_Signin"
732
+ },
733
+ get parent() {
734
+ return this;
735
+ }
736
+ };
737
+
738
+ expect(function() {
739
+ UserSession.completeOAuth2(
740
+ {
741
+ clientId: "clientId",
742
+ redirectUri: "https://example-app.com/redirect-uri"
743
+ },
744
+ MockWindow
745
+ );
746
+ }).toThrowError(ArcGISRequestError, "Invalid_Signin: Invalid_Signin");
747
+ });
748
+ });
749
+
750
+ describe(".authorize()", () => {
751
+ it("should redirect the request to the authorization page", done => {
752
+ const spy = jasmine.createSpy("spy");
753
+ const MockResponse: any = {
754
+ writeHead: spy,
755
+ end() {
756
+ expect(spy.calls.mostRecent().args[0]).toBe(301);
757
+ expect(spy.calls.mostRecent().args[1].Location).toBe(
758
+ "https://arcgis.com/sharing/rest/oauth2/authorize?client_id=clientId&duration=20160&response_type=code&redirect_uri=https%3A%2F%2Fexample-app.com%2Fredirect-uri"
759
+ );
760
+ done();
761
+ }
762
+ };
763
+
764
+ UserSession.authorize(
765
+ {
766
+ clientId: "clientId",
767
+ redirectUri: "https://example-app.com/redirect-uri"
768
+ },
769
+ MockResponse
770
+ );
771
+ });
772
+ });
773
+
774
+ describe(".exchangeAuthorizationCode()", () => {
775
+ let paramsSpy: jasmine.Spy;
776
+
777
+ beforeEach(() => {
778
+ paramsSpy = spyOn(FormData.prototype, "append").and.callThrough();
779
+ });
780
+
781
+ afterAll(() => {
782
+ paramsSpy.calls.reset();
783
+ });
784
+
785
+ it("should exchange an authorization code for a new UserSession", done => {
786
+ fetchMock.postOnce("https://www.arcgis.com/sharing/rest/oauth2/token", {
787
+ access_token: "token",
788
+ expires_in: 1800,
789
+ refresh_token: "refreshToken",
790
+ username: "Casey"
791
+ });
792
+
793
+ UserSession.exchangeAuthorizationCode(
794
+ {
795
+ clientId: "clientId",
796
+ redirectUri: "https://example-app.com/redirect-uri"
797
+ },
798
+ "code"
799
+ )
800
+ .then(session => {
801
+ done();
802
+ })
803
+ .catch(e => {
804
+ fail(e);
805
+ });
806
+ });
807
+ });
808
+
809
+ describe(".getUser()", () => {
810
+ afterEach(fetchMock.restore);
811
+
812
+ it("should cache metadata about the user", done => {
813
+ // we intentionally only mock one response
814
+ fetchMock.once(
815
+ "https://www.arcgis.com/sharing/rest/community/users/jsmith?f=json&token=token",
816
+ {
817
+ username: "jsmith",
818
+ fullName: "John Smith",
819
+ role: "org_publisher"
820
+ }
821
+ );
822
+
823
+ const session = new UserSession({
824
+ clientId: "clientId",
825
+ redirectUri: "https://example-app.com/redirect-uri",
826
+ token: "token",
827
+ tokenExpires: TOMORROW,
828
+ refreshToken: "refreshToken",
829
+ refreshTokenExpires: TOMORROW,
830
+ refreshTokenTTL: 1440,
831
+ username: "jsmith",
832
+ password: "123456"
833
+ });
834
+
835
+ session.getUser().then(response => {
836
+ expect(response.role).toEqual("org_publisher");
837
+ session.getUser().then(cachedResponse => {
838
+ expect(cachedResponse.fullName).toEqual("John Smith");
839
+ done();
840
+ });
841
+ });
842
+ });
843
+ });
844
+
845
+ describe("to/fromCredential()", () => {
846
+ const MOCK_CREDENTIAL: ICredential = {
847
+ expires: TOMORROW.getTime(),
848
+ server: "https://www.arcgis.com",
849
+ ssl: true,
850
+ token: "token",
851
+ userId: "jsmith"
852
+ };
853
+
854
+ it("should create a credential object from a session", () => {
855
+ const session = new UserSession({
856
+ clientId: "clientId",
857
+ redirectUri: "https://example-app.com/redirect-uri",
858
+ token: "token",
859
+ tokenExpires: TOMORROW,
860
+ refreshToken: "refreshToken",
861
+ refreshTokenExpires: TOMORROW,
862
+ refreshTokenTTL: 1440,
863
+ username: "jsmith",
864
+ password: "123456"
865
+ });
866
+
867
+ const creds = session.toCredential();
868
+ expect(creds.userId).toEqual("jsmith");
869
+ expect(creds.server).toEqual("https://www.arcgis.com/sharing/rest");
870
+ expect(creds.ssl).toEqual(true);
871
+ expect(creds.token).toEqual("token");
872
+ expect(creds.expires).toEqual(TOMORROW.getTime());
873
+ });
874
+
875
+ it("should create a UserSession from a credential", () => {
876
+ const session = UserSession.fromCredential(MOCK_CREDENTIAL);
877
+ expect(session.username).toEqual("jsmith");
878
+ expect(session.portal).toEqual("https://www.arcgis.com/sharing/rest");
879
+ expect(session.token).toEqual("token");
880
+ expect(session.tokenExpires).toEqual(new Date(TOMORROW));
881
+ });
882
+ });
883
+ });