@webex/webex-core 2.59.3-next.1 → 2.59.4

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 (189) hide show
  1. package/.eslintrc.js +6 -6
  2. package/README.md +79 -79
  3. package/babel.config.js +3 -3
  4. package/dist/config.js +24 -24
  5. package/dist/config.js.map +1 -1
  6. package/dist/credentials-config.js +56 -56
  7. package/dist/credentials-config.js.map +1 -1
  8. package/dist/index.js.map +1 -1
  9. package/dist/interceptors/auth.js +28 -28
  10. package/dist/interceptors/auth.js.map +1 -1
  11. package/dist/interceptors/default-options.js +24 -24
  12. package/dist/interceptors/default-options.js.map +1 -1
  13. package/dist/interceptors/embargo.js +9 -9
  14. package/dist/interceptors/embargo.js.map +1 -1
  15. package/dist/interceptors/network-timing.js +19 -19
  16. package/dist/interceptors/network-timing.js.map +1 -1
  17. package/dist/interceptors/payload-transformer.js +19 -19
  18. package/dist/interceptors/payload-transformer.js.map +1 -1
  19. package/dist/interceptors/rate-limit.js +40 -40
  20. package/dist/interceptors/rate-limit.js.map +1 -1
  21. package/dist/interceptors/redirect.js +13 -13
  22. package/dist/interceptors/redirect.js.map +1 -1
  23. package/dist/interceptors/request-event.js +23 -23
  24. package/dist/interceptors/request-event.js.map +1 -1
  25. package/dist/interceptors/request-logger.js +13 -13
  26. package/dist/interceptors/request-logger.js.map +1 -1
  27. package/dist/interceptors/request-timing.js +23 -23
  28. package/dist/interceptors/request-timing.js.map +1 -1
  29. package/dist/interceptors/response-logger.js +19 -19
  30. package/dist/interceptors/response-logger.js.map +1 -1
  31. package/dist/interceptors/user-agent.js +29 -29
  32. package/dist/interceptors/user-agent.js.map +1 -1
  33. package/dist/interceptors/webex-tracking-id.js +15 -15
  34. package/dist/interceptors/webex-tracking-id.js.map +1 -1
  35. package/dist/interceptors/webex-user-agent.js +13 -13
  36. package/dist/interceptors/webex-user-agent.js.map +1 -1
  37. package/dist/lib/batcher.js +83 -83
  38. package/dist/lib/batcher.js.map +1 -1
  39. package/dist/lib/credentials/credentials.js +103 -103
  40. package/dist/lib/credentials/credentials.js.map +1 -1
  41. package/dist/lib/credentials/grant-errors.js +17 -17
  42. package/dist/lib/credentials/grant-errors.js.map +1 -1
  43. package/dist/lib/credentials/index.js +2 -2
  44. package/dist/lib/credentials/index.js.map +1 -1
  45. package/dist/lib/credentials/scope.js +11 -11
  46. package/dist/lib/credentials/scope.js.map +1 -1
  47. package/dist/lib/credentials/token-collection.js +2 -2
  48. package/dist/lib/credentials/token-collection.js.map +1 -1
  49. package/dist/lib/credentials/token.js +145 -145
  50. package/dist/lib/credentials/token.js.map +1 -1
  51. package/dist/lib/page.js +49 -49
  52. package/dist/lib/page.js.map +1 -1
  53. package/dist/lib/services/constants.js.map +1 -1
  54. package/dist/lib/services/index.js +2 -2
  55. package/dist/lib/services/index.js.map +1 -1
  56. package/dist/lib/services/interceptors/server-error.js +9 -9
  57. package/dist/lib/services/interceptors/server-error.js.map +1 -1
  58. package/dist/lib/services/interceptors/service.js +24 -24
  59. package/dist/lib/services/interceptors/service.js.map +1 -1
  60. package/dist/lib/services/metrics.js.map +1 -1
  61. package/dist/lib/services/service-catalog.js +104 -104
  62. package/dist/lib/services/service-catalog.js.map +1 -1
  63. package/dist/lib/services/service-fed-ramp.js.map +1 -1
  64. package/dist/lib/services/service-host.js +134 -134
  65. package/dist/lib/services/service-host.js.map +1 -1
  66. package/dist/lib/services/service-registry.js +175 -175
  67. package/dist/lib/services/service-registry.js.map +1 -1
  68. package/dist/lib/services/service-state.js +38 -38
  69. package/dist/lib/services/service-state.js.map +1 -1
  70. package/dist/lib/services/service-url.js +31 -31
  71. package/dist/lib/services/service-url.js.map +1 -1
  72. package/dist/lib/services/services.js +245 -245
  73. package/dist/lib/services/services.js.map +1 -1
  74. package/dist/lib/stateless-webex-plugin.js +28 -28
  75. package/dist/lib/stateless-webex-plugin.js.map +1 -1
  76. package/dist/lib/storage/decorators.js +27 -27
  77. package/dist/lib/storage/decorators.js.map +1 -1
  78. package/dist/lib/storage/errors.js +4 -4
  79. package/dist/lib/storage/errors.js.map +1 -1
  80. package/dist/lib/storage/index.js.map +1 -1
  81. package/dist/lib/storage/make-webex-plugin-store.js +44 -44
  82. package/dist/lib/storage/make-webex-plugin-store.js.map +1 -1
  83. package/dist/lib/storage/make-webex-store.js +40 -40
  84. package/dist/lib/storage/make-webex-store.js.map +1 -1
  85. package/dist/lib/storage/memory-store-adapter.js +9 -9
  86. package/dist/lib/storage/memory-store-adapter.js.map +1 -1
  87. package/dist/lib/webex-core-plugin-mixin.js +13 -13
  88. package/dist/lib/webex-core-plugin-mixin.js.map +1 -1
  89. package/dist/lib/webex-http-error.js +9 -9
  90. package/dist/lib/webex-http-error.js.map +1 -1
  91. package/dist/lib/webex-internal-core-plugin-mixin.js +13 -13
  92. package/dist/lib/webex-internal-core-plugin-mixin.js.map +1 -1
  93. package/dist/lib/webex-plugin.js +36 -36
  94. package/dist/lib/webex-plugin.js.map +1 -1
  95. package/dist/plugins/logger.js +9 -9
  96. package/dist/plugins/logger.js.map +1 -1
  97. package/dist/webex-core.js +104 -104
  98. package/dist/webex-core.js.map +1 -1
  99. package/dist/webex-internal-core.js +12 -12
  100. package/dist/webex-internal-core.js.map +1 -1
  101. package/jest.config.js +3 -3
  102. package/package.json +19 -20
  103. package/process +1 -1
  104. package/src/config.js +90 -90
  105. package/src/credentials-config.js +212 -212
  106. package/src/index.js +62 -62
  107. package/src/interceptors/auth.js +186 -186
  108. package/src/interceptors/default-options.js +55 -55
  109. package/src/interceptors/embargo.js +43 -43
  110. package/src/interceptors/network-timing.js +54 -54
  111. package/src/interceptors/payload-transformer.js +55 -55
  112. package/src/interceptors/rate-limit.js +169 -169
  113. package/src/interceptors/redirect.js +106 -106
  114. package/src/interceptors/request-event.js +93 -93
  115. package/src/interceptors/request-logger.js +78 -78
  116. package/src/interceptors/request-timing.js +65 -65
  117. package/src/interceptors/response-logger.js +98 -98
  118. package/src/interceptors/user-agent.js +77 -77
  119. package/src/interceptors/webex-tracking-id.js +73 -73
  120. package/src/interceptors/webex-user-agent.js +79 -79
  121. package/src/lib/batcher.js +307 -307
  122. package/src/lib/credentials/credentials.js +552 -552
  123. package/src/lib/credentials/grant-errors.js +92 -92
  124. package/src/lib/credentials/index.js +16 -16
  125. package/src/lib/credentials/scope.js +34 -34
  126. package/src/lib/credentials/token-collection.js +17 -17
  127. package/src/lib/credentials/token.js +559 -559
  128. package/src/lib/page.js +159 -159
  129. package/src/lib/services/constants.js +9 -9
  130. package/src/lib/services/index.js +26 -26
  131. package/src/lib/services/interceptors/server-error.js +48 -48
  132. package/src/lib/services/interceptors/service.js +101 -101
  133. package/src/lib/services/metrics.js +4 -4
  134. package/src/lib/services/service-catalog.js +435 -435
  135. package/src/lib/services/service-fed-ramp.js +4 -4
  136. package/src/lib/services/service-host.js +267 -267
  137. package/src/lib/services/service-registry.js +465 -465
  138. package/src/lib/services/service-state.js +78 -78
  139. package/src/lib/services/service-url.js +124 -124
  140. package/src/lib/services/services.js +1018 -1018
  141. package/src/lib/stateless-webex-plugin.js +98 -98
  142. package/src/lib/storage/decorators.js +220 -220
  143. package/src/lib/storage/errors.js +15 -15
  144. package/src/lib/storage/index.js +10 -10
  145. package/src/lib/storage/make-webex-plugin-store.js +211 -211
  146. package/src/lib/storage/make-webex-store.js +140 -140
  147. package/src/lib/storage/memory-store-adapter.js +79 -79
  148. package/src/lib/webex-core-plugin-mixin.js +114 -114
  149. package/src/lib/webex-http-error.js +61 -61
  150. package/src/lib/webex-internal-core-plugin-mixin.js +107 -107
  151. package/src/lib/webex-plugin.js +222 -222
  152. package/src/plugins/logger.js +60 -60
  153. package/src/webex-core.js +745 -745
  154. package/src/webex-internal-core.js +46 -46
  155. package/test/integration/spec/credentials/credentials.js +139 -139
  156. package/test/integration/spec/credentials/token.js +102 -102
  157. package/test/integration/spec/services/service-catalog.js +838 -838
  158. package/test/integration/spec/services/services.js +1221 -1221
  159. package/test/integration/spec/webex-core.js +178 -178
  160. package/test/unit/spec/_setup.js +44 -44
  161. package/test/unit/spec/credentials/credentials.js +1017 -1017
  162. package/test/unit/spec/credentials/token.js +441 -441
  163. package/test/unit/spec/interceptors/auth.js +521 -521
  164. package/test/unit/spec/interceptors/default-options.js +84 -84
  165. package/test/unit/spec/interceptors/embargo.js +144 -144
  166. package/test/unit/spec/interceptors/network-timing.js +49 -49
  167. package/test/unit/spec/interceptors/payload-transformer.js +155 -155
  168. package/test/unit/spec/interceptors/rate-limit.js +302 -302
  169. package/test/unit/spec/interceptors/redirect.js +102 -102
  170. package/test/unit/spec/interceptors/request-timing.js +92 -92
  171. package/test/unit/spec/interceptors/user-agent.js +76 -76
  172. package/test/unit/spec/interceptors/webex-tracking-id.js +76 -76
  173. package/test/unit/spec/interceptors/webex-user-agent.js +159 -159
  174. package/test/unit/spec/lib/batcher.js +330 -330
  175. package/test/unit/spec/lib/page.js +148 -148
  176. package/test/unit/spec/lib/webex-plugin.js +48 -48
  177. package/test/unit/spec/services/interceptors/server-error.js +204 -204
  178. package/test/unit/spec/services/interceptors/service.js +188 -188
  179. package/test/unit/spec/services/service-catalog.js +194 -194
  180. package/test/unit/spec/services/service-host.js +260 -260
  181. package/test/unit/spec/services/service-registry.js +747 -747
  182. package/test/unit/spec/services/service-state.js +60 -60
  183. package/test/unit/spec/services/service-url.js +258 -258
  184. package/test/unit/spec/services/services.js +348 -348
  185. package/test/unit/spec/storage/persist.js +50 -50
  186. package/test/unit/spec/storage/storage-adapter.js +12 -12
  187. package/test/unit/spec/storage/wait-for-value.js +81 -81
  188. package/test/unit/spec/webex-core.js +253 -253
  189. package/test/unit/spec/webex-internal-core.js +91 -91
@@ -1,441 +1,441 @@
1
- /*!
2
- * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
3
- */
4
-
5
- import {assert} from '@webex/test-helper-chai';
6
- import sinon from 'sinon';
7
- import {nodeOnly, browserOnly} from '@webex/test-helper-mocha';
8
- import FakeTimers from '@sinonjs/fake-timers';
9
- import MockWebex from '@webex/test-helper-mock-webex';
10
- import {Token} from '@webex/webex-core';
11
-
12
- /* eslint camelcase: [0] */
13
-
14
- // eslint-disable-next-line no-empty-function
15
- function noop() {}
16
-
17
- describe('webex-core', () => {
18
- describe('Credentials', () => {
19
- describe('Token', () => {
20
- let webex;
21
-
22
- beforeEach(() => {
23
- webex = new MockWebex();
24
- });
25
-
26
- function makeToken(options = {}) {
27
- return new Token(
28
- Object.assign(
29
- {
30
- access_token: 'AT',
31
- expires_in: 10000,
32
- token_type: 'Fake',
33
- refresh_token: 'RT',
34
- refresh_token_expires_in: 20000,
35
- },
36
- options
37
- ),
38
- {parent: webex}
39
- );
40
- }
41
-
42
- describe('#canAuthorize', () => {
43
- it('indicates if this token can be used to authorize a request', () => {
44
- let token = makeToken();
45
-
46
- assert.isTrue(token.canAuthorize);
47
-
48
- token = makeToken({expires: Date.now() + 10000});
49
- assert.isFalse(token.isExpired);
50
- assert.isTrue(token.canAuthorize);
51
-
52
- token = makeToken({expires: Date.now() - 10000});
53
- assert.isTrue(token.isExpired);
54
- assert.isFalse(token.canAuthorize);
55
-
56
- token.unset('expires');
57
- assert.isFalse(token.isExpired);
58
- assert.isTrue(token.canAuthorize);
59
-
60
- token.unset('access_token');
61
- assert.isFalse(token.canAuthorize);
62
- });
63
- });
64
-
65
- describe('#canDownscope', () => {
66
- it('indicates if this token can be used to get a token of lesser scope', () => {
67
- let token = makeToken();
68
-
69
- assert.isTrue(token.canDownscope);
70
-
71
- webex.config.credentials.client_id = undefined;
72
- token = makeToken();
73
- assert.isFalse(token.canDownscope);
74
-
75
- webex.config.credentials.client_id = 'blarg';
76
- token = makeToken();
77
- assert.isTrue(token.canDownscope);
78
-
79
- token = makeToken({expires: Date.now() - 10000});
80
- assert.isFalse(token.canAuthorize);
81
- assert.isFalse(token.canDownscope);
82
-
83
- token.unset('expires');
84
- assert.isTrue(token.canDownscope);
85
-
86
- token = makeToken({expires: Date.now() + 10000});
87
- assert.isTrue(token.canAuthorize);
88
- assert.isTrue(token.canDownscope);
89
-
90
- token.unset('access_token');
91
- assert.isFalse(token.canDownscope);
92
- });
93
- });
94
-
95
- describe('#canRefresh', () => {
96
- browserOnly(it)('indicates if this token can be refreshed', () => {
97
- let token = makeToken();
98
-
99
- assert.isFalse(token.canRefresh);
100
- token.unset('refresh_token');
101
- assert.isFalse(token.canRefresh);
102
-
103
- webex.config.credentials.refreshCallback = noop;
104
- token = makeToken();
105
- assert.isTrue(token.canRefresh);
106
- token.unset('refresh_token');
107
- assert.isFalse(token.canRefresh);
108
- });
109
-
110
- nodeOnly(it)('indicates if this token can be refreshed', () => {
111
- let token = makeToken();
112
-
113
- assert.isTrue(token.canRefresh);
114
- token.unset('refresh_token');
115
- assert.isFalse(token.canRefresh);
116
-
117
- webex.config.credentials.client_secret = undefined;
118
- token = makeToken();
119
- assert.isFalse(token.canRefresh);
120
- });
121
- });
122
-
123
- describe('#isExpired', () => {
124
- it('derives from `expires`', () => {
125
- let token = makeToken();
126
-
127
- token.unset('expires');
128
- token.unset('expires_in');
129
- assert.isFalse(token.isExpired);
130
-
131
- token = makeToken({expires: Date.now() - 10000});
132
- assert.isTrue(token.isExpired);
133
-
134
- token = makeToken({expires: Date.now() + 10000});
135
- assert.isFalse(token.isExpired);
136
- });
137
- });
138
-
139
- describe('#_string', () => {
140
- it('derives from `access_token` and `token_type`', () => {
141
- const token = makeToken();
142
-
143
- assert.equal(token._string, 'Fake AT');
144
-
145
- token.token_type = 'Fake';
146
- token.unset('access_token');
147
- assert.equal(token._string, '');
148
- });
149
- });
150
-
151
- describe('#downscope()', () => {
152
- it('requires an access token', () => {
153
- const token = makeToken();
154
-
155
- token.unset('access_token');
156
-
157
- return assert.isRejected(token.downscope('spark:kms'), /cannot downscope access token/);
158
- });
159
-
160
- it('requires an unexpired access token', () => {
161
- const token = makeToken({expires: Date.now() - 10000});
162
-
163
- return assert.isRejected(
164
- token.downscope('spark:kms'),
165
- /cannot downscope expired access token/
166
- );
167
- });
168
-
169
- it('alphabetizes the requested scope', () => {
170
- const token = makeToken();
171
-
172
- webex.request.returns(Promise.resolve({body: {access_token: 'AT2'}}));
173
-
174
- return token
175
- .downscope('b a')
176
- .then(() => assert.equal(webex.request.args[0][0].form.scope, 'a b'));
177
- });
178
- });
179
-
180
- describe('#initialize()', () => {
181
- let clock;
182
-
183
- beforeEach(() => {
184
- clock = FakeTimers.install();
185
- });
186
- afterEach(() => clock.uninstall());
187
-
188
- it('requires an access token', () => {
189
- assert.throws(() => {
190
- // eslint-disable-next-line no-unused-vars
191
- const x = new Token();
192
- }, /`access_token` is required/);
193
-
194
- assert.doesNotThrow(() => {
195
- // eslint-disable-next-line no-unused-vars
196
- const x = new Token({access_token: 'AT'});
197
- }, /`access_token` is required/);
198
- });
199
-
200
- it('infers token_type from an access token string', () => {
201
- const t = new Token({
202
- access_token: 'Fake AT',
203
- });
204
-
205
- assert.equal(t.access_token, 'AT');
206
- assert.equal(t.token_type, 'Fake');
207
- });
208
-
209
- it('computes expires_in and refresh_token_expires_in if not specified', () => {
210
- const now = Date.now();
211
-
212
- const t = makeToken({
213
- expires_in: 6000,
214
- refresh_token_expires_in: 12000,
215
- expires: null,
216
- refresh_token_expires: null,
217
- });
218
-
219
- assert.approximately(t.expires, 6000000 + now, 5);
220
- assert.approximately(t.refresh_token_expires, 12000000 + now, 5);
221
- });
222
-
223
- it("alphabetizes the token's scopes", () => {
224
- const t = new Token({
225
- access_token: 'AT',
226
- scope: 'b a',
227
- });
228
-
229
- assert.equal(t.scope, 'a b');
230
- });
231
-
232
- it('it sets a timer to set Token#_isExpired (and therefore Token#isExpired)', () => {
233
- const t = makeToken({
234
- // Reminder: expires_in is in seconds, ticks are in miliseconds
235
- expires_in: 1,
236
- });
237
-
238
- assert.isFalse(t.isExpired);
239
- clock.tick(900);
240
- assert.isFalse(t.isExpired);
241
- clock.tick(100);
242
- assert.isTrue(t.isExpired);
243
- });
244
-
245
- it('accepts a string as an access token', () => {
246
- const token = new Token('AT', {parent: webex});
247
-
248
- assert.isTrue(token.canAuthorize);
249
- assert.equal(token.toString(), 'Bearer AT');
250
- });
251
-
252
- it('accepts a string as an access token (with token type)', () => {
253
- const token = new Token('Fake AT', {parent: webex});
254
-
255
- assert.isTrue(token.canAuthorize);
256
- assert.equal(token.toString(), 'Fake AT');
257
- });
258
- });
259
-
260
- describe('#refresh()', () => {
261
- browserOnly(it)('refreshes the access_token', () => {
262
- const token = makeToken();
263
-
264
- webex.config.credentials.refreshCallback = sinon.stub().returns(
265
- Promise.resolve({
266
- access_token: 'AT2',
267
- expires_in: 10000,
268
- token_type: 'Fake',
269
- })
270
- );
271
-
272
- // FIXME this next line should be necessary. we need a better way to
273
- // do config
274
- token.trigger('change:config');
275
-
276
- return token.refresh().then((token2) => {
277
- assert.equal(token2.access_token, 'AT2');
278
- });
279
- });
280
-
281
- nodeOnly(it)('refreshes the access_token', () => {
282
- const token = makeToken();
283
-
284
- webex.request.onCall(0).returns(
285
- Promise.resolve({
286
- body: {
287
- access_token: 'AT2',
288
- expires_in: 10000,
289
- token_type: 'Fake',
290
- },
291
- })
292
- );
293
-
294
- return token.refresh().then((token2) => {
295
- assert.equal(token2.access_token, 'AT2');
296
- });
297
- });
298
-
299
- browserOnly(it)('revokes the previous token when set', () => {
300
- const token = makeToken();
301
-
302
- sinon.spy(token, 'revoke');
303
- webex.config.credentials.refreshCallback = sinon.stub();
304
-
305
- webex.config.credentials.refreshCallback.onCall(0).returns(
306
- Promise.resolve({
307
- access_token: 'AT2',
308
- expires_in: 10000,
309
- token_type: 'Fake',
310
- })
311
- );
312
-
313
- webex.config.credentials.refreshCallback.onCall(1).returns(
314
- Promise.resolve({
315
- access_token: 'AT3',
316
- expires_in: 10000,
317
- token_type: 'Fake',
318
- })
319
- );
320
-
321
- // FIXME this next line should be necessary. we need a better way to
322
- // do config
323
- token.trigger('change:config');
324
-
325
- return token
326
- .refresh()
327
- .then((token2) => {
328
- assert.isTrue(token.canRefresh);
329
- assert.notCalled(token.revoke);
330
-
331
- return token2.refresh();
332
- })
333
- .then((token3) => {
334
- assert.equal(token3.access_token, 'AT3');
335
- assert.called(token.revoke);
336
- });
337
- });
338
-
339
- nodeOnly(it)('revokes the previous token when set', () => {
340
- const token = makeToken();
341
-
342
- sinon.spy(token, 'revoke');
343
-
344
- webex.request.onCall(0).returns(
345
- Promise.resolve({
346
- body: {
347
- access_token: 'AT2',
348
- expires_in: 10000,
349
- token_type: 'Fake',
350
- },
351
- })
352
- );
353
-
354
- webex.request.onCall(1).returns(
355
- Promise.resolve({
356
- body: {
357
- access_token: 'AT3',
358
- expires_in: 10000,
359
- token_type: 'Fake',
360
- },
361
- })
362
- );
363
-
364
- return token
365
- .refresh()
366
- .then((token2) => {
367
- assert.isTrue(token.canRefresh);
368
- assert.notCalled(token.revoke);
369
-
370
- return token2.refresh();
371
- })
372
- .then((token3) => {
373
- assert.equal(token3.access_token, 'AT3');
374
- assert.called(token.revoke);
375
- });
376
- });
377
- });
378
-
379
- describe('#revoke()', () => {
380
- describe('when the token has expired', () => {
381
- it('is a noop', () => {
382
- const token = makeToken({expires: Date.now() - 10000});
383
-
384
- return token.revoke().then(() => assert.notCalled(webex.request));
385
- });
386
- });
387
-
388
- describe('when access_token has been unset', () => {
389
- it('is a noop', () => {
390
- const token = makeToken();
391
-
392
- token.unset('access_token');
393
-
394
- return token.revoke().then(() => assert.notCalled(webex.request));
395
- });
396
- });
397
-
398
- // FIXME this test is temporary; there's currently no practical way to
399
- // revoke a token without a client secret.
400
- describe('when the client_secret is unavailable', () => {
401
- it('is a noop', () => {
402
- webex.config.credentials.client_secret = undefined;
403
-
404
- const token = makeToken();
405
-
406
- token.unset('access_token');
407
-
408
- return token.revoke().then(() => assert.notCalled(webex.request));
409
- });
410
- });
411
-
412
- it('unsets the access_token and related values', () => {
413
- const token = makeToken();
414
-
415
- return token.revoke().then(() => {
416
- assert.isUndefined(token.access_token);
417
- assert.isUndefined(token.expires);
418
- assert.isUndefined(token.expires_in);
419
- assert.isDefined(token.refresh_token);
420
- assert.isDefined(token.refresh_token_expires);
421
- assert.isDefined(token.refresh_token_expires_in);
422
- });
423
- });
424
- });
425
-
426
- describe('#toString()', () => {
427
- it('produces a value for an auth header', () => {
428
- const token = makeToken();
429
-
430
- assert.equal(token.toString(), 'Fake AT');
431
-
432
- token.unset('access_token');
433
-
434
- assert.throws(() => {
435
- token.toString();
436
- }, /cannot stringify Token/);
437
- });
438
- });
439
- });
440
- });
441
- });
1
+ /*!
2
+ * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
3
+ */
4
+
5
+ import {assert} from '@webex/test-helper-chai';
6
+ import sinon from 'sinon';
7
+ import {nodeOnly, browserOnly} from '@webex/test-helper-mocha';
8
+ import FakeTimers from '@sinonjs/fake-timers';
9
+ import MockWebex from '@webex/test-helper-mock-webex';
10
+ import {Token} from '@webex/webex-core';
11
+
12
+ /* eslint camelcase: [0] */
13
+
14
+ // eslint-disable-next-line no-empty-function
15
+ function noop() {}
16
+
17
+ describe('webex-core', () => {
18
+ describe('Credentials', () => {
19
+ describe('Token', () => {
20
+ let webex;
21
+
22
+ beforeEach(() => {
23
+ webex = new MockWebex();
24
+ });
25
+
26
+ function makeToken(options = {}) {
27
+ return new Token(
28
+ Object.assign(
29
+ {
30
+ access_token: 'AT',
31
+ expires_in: 10000,
32
+ token_type: 'Fake',
33
+ refresh_token: 'RT',
34
+ refresh_token_expires_in: 20000,
35
+ },
36
+ options
37
+ ),
38
+ {parent: webex}
39
+ );
40
+ }
41
+
42
+ describe('#canAuthorize', () => {
43
+ it('indicates if this token can be used to authorize a request', () => {
44
+ let token = makeToken();
45
+
46
+ assert.isTrue(token.canAuthorize);
47
+
48
+ token = makeToken({expires: Date.now() + 10000});
49
+ assert.isFalse(token.isExpired);
50
+ assert.isTrue(token.canAuthorize);
51
+
52
+ token = makeToken({expires: Date.now() - 10000});
53
+ assert.isTrue(token.isExpired);
54
+ assert.isFalse(token.canAuthorize);
55
+
56
+ token.unset('expires');
57
+ assert.isFalse(token.isExpired);
58
+ assert.isTrue(token.canAuthorize);
59
+
60
+ token.unset('access_token');
61
+ assert.isFalse(token.canAuthorize);
62
+ });
63
+ });
64
+
65
+ describe('#canDownscope', () => {
66
+ it('indicates if this token can be used to get a token of lesser scope', () => {
67
+ let token = makeToken();
68
+
69
+ assert.isTrue(token.canDownscope);
70
+
71
+ webex.config.credentials.client_id = undefined;
72
+ token = makeToken();
73
+ assert.isFalse(token.canDownscope);
74
+
75
+ webex.config.credentials.client_id = 'blarg';
76
+ token = makeToken();
77
+ assert.isTrue(token.canDownscope);
78
+
79
+ token = makeToken({expires: Date.now() - 10000});
80
+ assert.isFalse(token.canAuthorize);
81
+ assert.isFalse(token.canDownscope);
82
+
83
+ token.unset('expires');
84
+ assert.isTrue(token.canDownscope);
85
+
86
+ token = makeToken({expires: Date.now() + 10000});
87
+ assert.isTrue(token.canAuthorize);
88
+ assert.isTrue(token.canDownscope);
89
+
90
+ token.unset('access_token');
91
+ assert.isFalse(token.canDownscope);
92
+ });
93
+ });
94
+
95
+ describe('#canRefresh', () => {
96
+ browserOnly(it)('indicates if this token can be refreshed', () => {
97
+ let token = makeToken();
98
+
99
+ assert.isFalse(token.canRefresh);
100
+ token.unset('refresh_token');
101
+ assert.isFalse(token.canRefresh);
102
+
103
+ webex.config.credentials.refreshCallback = noop;
104
+ token = makeToken();
105
+ assert.isTrue(token.canRefresh);
106
+ token.unset('refresh_token');
107
+ assert.isFalse(token.canRefresh);
108
+ });
109
+
110
+ nodeOnly(it)('indicates if this token can be refreshed', () => {
111
+ let token = makeToken();
112
+
113
+ assert.isTrue(token.canRefresh);
114
+ token.unset('refresh_token');
115
+ assert.isFalse(token.canRefresh);
116
+
117
+ webex.config.credentials.client_secret = undefined;
118
+ token = makeToken();
119
+ assert.isFalse(token.canRefresh);
120
+ });
121
+ });
122
+
123
+ describe('#isExpired', () => {
124
+ it('derives from `expires`', () => {
125
+ let token = makeToken();
126
+
127
+ token.unset('expires');
128
+ token.unset('expires_in');
129
+ assert.isFalse(token.isExpired);
130
+
131
+ token = makeToken({expires: Date.now() - 10000});
132
+ assert.isTrue(token.isExpired);
133
+
134
+ token = makeToken({expires: Date.now() + 10000});
135
+ assert.isFalse(token.isExpired);
136
+ });
137
+ });
138
+
139
+ describe('#_string', () => {
140
+ it('derives from `access_token` and `token_type`', () => {
141
+ const token = makeToken();
142
+
143
+ assert.equal(token._string, 'Fake AT');
144
+
145
+ token.token_type = 'Fake';
146
+ token.unset('access_token');
147
+ assert.equal(token._string, '');
148
+ });
149
+ });
150
+
151
+ describe('#downscope()', () => {
152
+ it('requires an access token', () => {
153
+ const token = makeToken();
154
+
155
+ token.unset('access_token');
156
+
157
+ return assert.isRejected(token.downscope('spark:kms'), /cannot downscope access token/);
158
+ });
159
+
160
+ it('requires an unexpired access token', () => {
161
+ const token = makeToken({expires: Date.now() - 10000});
162
+
163
+ return assert.isRejected(
164
+ token.downscope('spark:kms'),
165
+ /cannot downscope expired access token/
166
+ );
167
+ });
168
+
169
+ it('alphabetizes the requested scope', () => {
170
+ const token = makeToken();
171
+
172
+ webex.request.returns(Promise.resolve({body: {access_token: 'AT2'}}));
173
+
174
+ return token
175
+ .downscope('b a')
176
+ .then(() => assert.equal(webex.request.args[0][0].form.scope, 'a b'));
177
+ });
178
+ });
179
+
180
+ describe('#initialize()', () => {
181
+ let clock;
182
+
183
+ beforeEach(() => {
184
+ clock = FakeTimers.install();
185
+ });
186
+ afterEach(() => clock.uninstall());
187
+
188
+ it('requires an access token', () => {
189
+ assert.throws(() => {
190
+ // eslint-disable-next-line no-unused-vars
191
+ const x = new Token();
192
+ }, /`access_token` is required/);
193
+
194
+ assert.doesNotThrow(() => {
195
+ // eslint-disable-next-line no-unused-vars
196
+ const x = new Token({access_token: 'AT'});
197
+ }, /`access_token` is required/);
198
+ });
199
+
200
+ it('infers token_type from an access token string', () => {
201
+ const t = new Token({
202
+ access_token: 'Fake AT',
203
+ });
204
+
205
+ assert.equal(t.access_token, 'AT');
206
+ assert.equal(t.token_type, 'Fake');
207
+ });
208
+
209
+ it('computes expires_in and refresh_token_expires_in if not specified', () => {
210
+ const now = Date.now();
211
+
212
+ const t = makeToken({
213
+ expires_in: 6000,
214
+ refresh_token_expires_in: 12000,
215
+ expires: null,
216
+ refresh_token_expires: null,
217
+ });
218
+
219
+ assert.approximately(t.expires, 6000000 + now, 5);
220
+ assert.approximately(t.refresh_token_expires, 12000000 + now, 5);
221
+ });
222
+
223
+ it("alphabetizes the token's scopes", () => {
224
+ const t = new Token({
225
+ access_token: 'AT',
226
+ scope: 'b a',
227
+ });
228
+
229
+ assert.equal(t.scope, 'a b');
230
+ });
231
+
232
+ it('it sets a timer to set Token#_isExpired (and therefore Token#isExpired)', () => {
233
+ const t = makeToken({
234
+ // Reminder: expires_in is in seconds, ticks are in miliseconds
235
+ expires_in: 1,
236
+ });
237
+
238
+ assert.isFalse(t.isExpired);
239
+ clock.tick(900);
240
+ assert.isFalse(t.isExpired);
241
+ clock.tick(100);
242
+ assert.isTrue(t.isExpired);
243
+ });
244
+
245
+ it('accepts a string as an access token', () => {
246
+ const token = new Token('AT', {parent: webex});
247
+
248
+ assert.isTrue(token.canAuthorize);
249
+ assert.equal(token.toString(), 'Bearer AT');
250
+ });
251
+
252
+ it('accepts a string as an access token (with token type)', () => {
253
+ const token = new Token('Fake AT', {parent: webex});
254
+
255
+ assert.isTrue(token.canAuthorize);
256
+ assert.equal(token.toString(), 'Fake AT');
257
+ });
258
+ });
259
+
260
+ describe('#refresh()', () => {
261
+ browserOnly(it)('refreshes the access_token', () => {
262
+ const token = makeToken();
263
+
264
+ webex.config.credentials.refreshCallback = sinon.stub().returns(
265
+ Promise.resolve({
266
+ access_token: 'AT2',
267
+ expires_in: 10000,
268
+ token_type: 'Fake',
269
+ })
270
+ );
271
+
272
+ // FIXME this next line should be necessary. we need a better way to
273
+ // do config
274
+ token.trigger('change:config');
275
+
276
+ return token.refresh().then((token2) => {
277
+ assert.equal(token2.access_token, 'AT2');
278
+ });
279
+ });
280
+
281
+ nodeOnly(it)('refreshes the access_token', () => {
282
+ const token = makeToken();
283
+
284
+ webex.request.onCall(0).returns(
285
+ Promise.resolve({
286
+ body: {
287
+ access_token: 'AT2',
288
+ expires_in: 10000,
289
+ token_type: 'Fake',
290
+ },
291
+ })
292
+ );
293
+
294
+ return token.refresh().then((token2) => {
295
+ assert.equal(token2.access_token, 'AT2');
296
+ });
297
+ });
298
+
299
+ browserOnly(it)('revokes the previous token when set', () => {
300
+ const token = makeToken();
301
+
302
+ sinon.spy(token, 'revoke');
303
+ webex.config.credentials.refreshCallback = sinon.stub();
304
+
305
+ webex.config.credentials.refreshCallback.onCall(0).returns(
306
+ Promise.resolve({
307
+ access_token: 'AT2',
308
+ expires_in: 10000,
309
+ token_type: 'Fake',
310
+ })
311
+ );
312
+
313
+ webex.config.credentials.refreshCallback.onCall(1).returns(
314
+ Promise.resolve({
315
+ access_token: 'AT3',
316
+ expires_in: 10000,
317
+ token_type: 'Fake',
318
+ })
319
+ );
320
+
321
+ // FIXME this next line should be necessary. we need a better way to
322
+ // do config
323
+ token.trigger('change:config');
324
+
325
+ return token
326
+ .refresh()
327
+ .then((token2) => {
328
+ assert.isTrue(token.canRefresh);
329
+ assert.notCalled(token.revoke);
330
+
331
+ return token2.refresh();
332
+ })
333
+ .then((token3) => {
334
+ assert.equal(token3.access_token, 'AT3');
335
+ assert.called(token.revoke);
336
+ });
337
+ });
338
+
339
+ nodeOnly(it)('revokes the previous token when set', () => {
340
+ const token = makeToken();
341
+
342
+ sinon.spy(token, 'revoke');
343
+
344
+ webex.request.onCall(0).returns(
345
+ Promise.resolve({
346
+ body: {
347
+ access_token: 'AT2',
348
+ expires_in: 10000,
349
+ token_type: 'Fake',
350
+ },
351
+ })
352
+ );
353
+
354
+ webex.request.onCall(1).returns(
355
+ Promise.resolve({
356
+ body: {
357
+ access_token: 'AT3',
358
+ expires_in: 10000,
359
+ token_type: 'Fake',
360
+ },
361
+ })
362
+ );
363
+
364
+ return token
365
+ .refresh()
366
+ .then((token2) => {
367
+ assert.isTrue(token.canRefresh);
368
+ assert.notCalled(token.revoke);
369
+
370
+ return token2.refresh();
371
+ })
372
+ .then((token3) => {
373
+ assert.equal(token3.access_token, 'AT3');
374
+ assert.called(token.revoke);
375
+ });
376
+ });
377
+ });
378
+
379
+ describe('#revoke()', () => {
380
+ describe('when the token has expired', () => {
381
+ it('is a noop', () => {
382
+ const token = makeToken({expires: Date.now() - 10000});
383
+
384
+ return token.revoke().then(() => assert.notCalled(webex.request));
385
+ });
386
+ });
387
+
388
+ describe('when access_token has been unset', () => {
389
+ it('is a noop', () => {
390
+ const token = makeToken();
391
+
392
+ token.unset('access_token');
393
+
394
+ return token.revoke().then(() => assert.notCalled(webex.request));
395
+ });
396
+ });
397
+
398
+ // FIXME this test is temporary; there's currently no practical way to
399
+ // revoke a token without a client secret.
400
+ describe('when the client_secret is unavailable', () => {
401
+ it('is a noop', () => {
402
+ webex.config.credentials.client_secret = undefined;
403
+
404
+ const token = makeToken();
405
+
406
+ token.unset('access_token');
407
+
408
+ return token.revoke().then(() => assert.notCalled(webex.request));
409
+ });
410
+ });
411
+
412
+ it('unsets the access_token and related values', () => {
413
+ const token = makeToken();
414
+
415
+ return token.revoke().then(() => {
416
+ assert.isUndefined(token.access_token);
417
+ assert.isUndefined(token.expires);
418
+ assert.isUndefined(token.expires_in);
419
+ assert.isDefined(token.refresh_token);
420
+ assert.isDefined(token.refresh_token_expires);
421
+ assert.isDefined(token.refresh_token_expires_in);
422
+ });
423
+ });
424
+ });
425
+
426
+ describe('#toString()', () => {
427
+ it('produces a value for an auth header', () => {
428
+ const token = makeToken();
429
+
430
+ assert.equal(token.toString(), 'Fake AT');
431
+
432
+ token.unset('access_token');
433
+
434
+ assert.throws(() => {
435
+ token.toString();
436
+ }, /cannot stringify Token/);
437
+ });
438
+ });
439
+ });
440
+ });
441
+ });