@webex/webex-core 3.0.0-beta.4 → 3.0.0-beta.400
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.js +1 -11
- package/dist/config.js.map +1 -1
- package/dist/credentials-config.js +44 -64
- package/dist/credentials-config.js.map +1 -1
- package/dist/index.js +0 -76
- package/dist/index.js.map +1 -1
- package/dist/interceptors/auth.js +22 -55
- package/dist/interceptors/auth.js.map +1 -1
- package/dist/interceptors/default-options.js +0 -20
- package/dist/interceptors/default-options.js.map +1 -1
- package/dist/interceptors/embargo.js +0 -21
- package/dist/interceptors/embargo.js.map +1 -1
- package/dist/interceptors/network-timing.js +2 -21
- package/dist/interceptors/network-timing.js.map +1 -1
- package/dist/interceptors/payload-transformer.js +2 -22
- package/dist/interceptors/payload-transformer.js.map +1 -1
- package/dist/interceptors/rate-limit.js +25 -57
- package/dist/interceptors/rate-limit.js.map +1 -1
- package/dist/interceptors/redirect.js +4 -33
- package/dist/interceptors/redirect.js.map +1 -1
- package/dist/interceptors/request-event.js +3 -30
- package/dist/interceptors/request-event.js.map +1 -1
- package/dist/interceptors/request-logger.js +1 -30
- package/dist/interceptors/request-logger.js.map +1 -1
- package/dist/interceptors/request-timing.js +3 -22
- package/dist/interceptors/request-timing.js.map +1 -1
- package/dist/interceptors/response-logger.js +2 -31
- package/dist/interceptors/response-logger.js.map +1 -1
- package/dist/interceptors/user-agent.js +2 -29
- package/dist/interceptors/user-agent.js.map +1 -1
- package/dist/interceptors/webex-tracking-id.js +5 -28
- package/dist/interceptors/webex-tracking-id.js.map +1 -1
- package/dist/interceptors/webex-user-agent.js +5 -38
- package/dist/interceptors/webex-user-agent.js.map +1 -1
- package/dist/lib/batcher.js +3 -51
- package/dist/lib/batcher.js.map +1 -1
- package/dist/lib/constants.js +14 -0
- package/dist/lib/constants.js.map +1 -0
- package/dist/lib/credentials/credentials.js +98 -139
- package/dist/lib/credentials/credentials.js.map +1 -1
- package/dist/lib/credentials/grant-errors.js +0 -49
- package/dist/lib/credentials/grant-errors.js.map +1 -1
- package/dist/lib/credentials/index.js +1 -13
- package/dist/lib/credentials/index.js.map +1 -1
- package/dist/lib/credentials/scope.js +25 -14
- package/dist/lib/credentials/scope.js.map +1 -1
- package/dist/lib/credentials/token-collection.js +1 -7
- package/dist/lib/credentials/token-collection.js.map +1 -1
- package/dist/lib/credentials/token.js +42 -118
- package/dist/lib/credentials/token.js.map +1 -1
- package/dist/lib/page.js +13 -26
- package/dist/lib/page.js.map +1 -1
- package/dist/lib/services/constants.js +0 -2
- package/dist/lib/services/constants.js.map +1 -1
- package/dist/lib/services/index.js +1 -28
- package/dist/lib/services/index.js.map +1 -1
- package/dist/lib/services/interceptors/server-error.js +2 -23
- package/dist/lib/services/interceptors/server-error.js.map +1 -1
- package/dist/lib/services/interceptors/service.js +15 -35
- package/dist/lib/services/interceptors/service.js.map +1 -1
- package/dist/lib/services/metrics.js +0 -2
- package/dist/lib/services/metrics.js.map +1 -1
- package/dist/lib/services/service-catalog.js +12 -91
- package/dist/lib/services/service-catalog.js.map +1 -1
- package/dist/lib/services/service-fed-ramp.js +0 -2
- package/dist/lib/services/service-fed-ramp.js.map +1 -1
- package/dist/lib/services/service-host.js +47 -62
- package/dist/lib/services/service-host.js.map +1 -1
- package/dist/lib/services/service-registry.js +78 -90
- package/dist/lib/services/service-registry.js.map +1 -1
- package/dist/lib/services/service-state.js +3 -15
- package/dist/lib/services/service-state.js.map +1 -1
- package/dist/lib/services/service-url.js +4 -25
- package/dist/lib/services/service-url.js.map +1 -1
- package/dist/lib/services/services.js +135 -239
- package/dist/lib/services/services.js.map +1 -1
- package/dist/lib/stateless-webex-plugin.js +5 -28
- package/dist/lib/stateless-webex-plugin.js.map +1 -1
- package/dist/lib/storage/decorators.js +19 -62
- package/dist/lib/storage/decorators.js.map +1 -1
- package/dist/lib/storage/errors.js +0 -23
- package/dist/lib/storage/errors.js.map +1 -1
- package/dist/lib/storage/index.js +2 -16
- package/dist/lib/storage/index.js.map +1 -1
- package/dist/lib/storage/make-webex-plugin-store.js +11 -41
- package/dist/lib/storage/make-webex-plugin-store.js.map +1 -1
- package/dist/lib/storage/make-webex-store.js +8 -30
- package/dist/lib/storage/make-webex-store.js.map +1 -1
- package/dist/lib/storage/memory-store-adapter.js +1 -19
- package/dist/lib/storage/memory-store-adapter.js.map +1 -1
- package/dist/lib/webex-core-plugin-mixin.js +9 -29
- package/dist/lib/webex-core-plugin-mixin.js.map +1 -1
- package/dist/lib/webex-http-error.js +1 -31
- package/dist/lib/webex-http-error.js.map +1 -1
- package/dist/lib/webex-internal-core-plugin-mixin.js +9 -29
- package/dist/lib/webex-internal-core-plugin-mixin.js.map +1 -1
- package/dist/lib/webex-plugin.js +6 -40
- package/dist/lib/webex-plugin.js.map +1 -1
- package/dist/plugins/logger.js +3 -17
- package/dist/plugins/logger.js.map +1 -1
- package/dist/webex-core.js +84 -203
- package/dist/webex-core.js.map +1 -1
- package/dist/webex-internal-core.js +0 -10
- package/dist/webex-internal-core.js.map +1 -1
- package/package.json +14 -14
- package/src/config.js +9 -11
- package/src/credentials-config.js +110 -72
- package/src/index.js +4 -14
- package/src/interceptors/auth.js +36 -37
- package/src/interceptors/default-options.js +0 -1
- package/src/interceptors/embargo.js +1 -1
- package/src/interceptors/payload-transformer.js +1 -2
- package/src/interceptors/rate-limit.js +8 -5
- package/src/interceptors/redirect.js +14 -8
- package/src/interceptors/request-event.js +4 -8
- package/src/interceptors/request-logger.js +8 -5
- package/src/interceptors/response-logger.js +11 -8
- package/src/interceptors/user-agent.js +1 -2
- package/src/interceptors/webex-user-agent.js +3 -9
- package/src/lib/batcher.js +70 -69
- package/src/lib/constants.js +6 -0
- package/src/lib/credentials/credentials.js +173 -141
- package/src/lib/credentials/grant-errors.js +6 -7
- package/src/lib/credentials/index.js +1 -4
- package/src/lib/credentials/scope.js +24 -8
- package/src/lib/credentials/token-collection.js +1 -1
- package/src/lib/credentials/token.js +95 -81
- package/src/lib/page.js +10 -11
- package/src/lib/services/constants.js +3 -13
- package/src/lib/services/index.js +2 -2
- package/src/lib/services/interceptors/server-error.js +12 -7
- package/src/lib/services/interceptors/service.js +7 -6
- package/src/lib/services/metrics.js +1 -1
- package/src/lib/services/service-catalog.js +112 -100
- package/src/lib/services/service-fed-ramp.js +1 -2
- package/src/lib/services/service-host.js +10 -17
- package/src/lib/services/service-registry.js +69 -96
- package/src/lib/services/service-state.js +4 -6
- package/src/lib/services/service-url.js +24 -23
- package/src/lib/services/services.js +272 -249
- package/src/lib/stateless-webex-plugin.js +4 -2
- package/src/lib/storage/decorators.js +68 -66
- package/src/lib/storage/index.js +4 -6
- package/src/lib/storage/make-webex-plugin-store.js +34 -21
- package/src/lib/storage/make-webex-store.js +6 -7
- package/src/lib/storage/memory-store-adapter.js +3 -3
- package/src/lib/webex-core-plugin-mixin.js +10 -7
- package/src/lib/webex-http-error.js +7 -8
- package/src/lib/webex-internal-core-plugin-mixin.js +9 -6
- package/src/lib/webex-plugin.js +41 -34
- package/src/plugins/logger.js +8 -3
- package/src/webex-core.js +198 -117
- package/src/webex-internal-core.js +15 -9
- package/test/integration/spec/credentials/credentials.js +26 -30
- package/test/integration/spec/credentials/token.js +36 -33
- package/test/integration/spec/services/service-catalog.js +177 -156
- package/test/integration/spec/services/services.js +313 -304
- package/test/integration/spec/webex-core.js +98 -86
- package/test/unit/spec/_setup.js +26 -18
- package/test/unit/spec/credentials/credentials.js +352 -162
- package/test/unit/spec/credentials/scope.js +80 -0
- package/test/unit/spec/credentials/token.js +105 -77
- package/test/unit/spec/interceptors/auth.js +294 -243
- package/test/unit/spec/interceptors/default-options.js +36 -24
- package/test/unit/spec/interceptors/embargo.js +32 -27
- package/test/unit/spec/interceptors/network-timing.js +2 -2
- package/test/unit/spec/interceptors/payload-transformer.js +61 -52
- package/test/unit/spec/interceptors/rate-limit.js +104 -75
- package/test/unit/spec/interceptors/redirect.js +22 -20
- package/test/unit/spec/interceptors/request-timing.js +18 -22
- package/test/unit/spec/interceptors/user-agent.js +28 -16
- package/test/unit/spec/interceptors/webex-tracking-id.js +14 -8
- package/test/unit/spec/interceptors/webex-user-agent.js +83 -37
- package/test/unit/spec/lib/batcher.js +36 -32
- package/test/unit/spec/lib/page.js +36 -32
- package/test/unit/spec/lib/webex-plugin.js +1 -1
- package/test/unit/spec/services/interceptors/server-error.js +67 -90
- package/test/unit/spec/services/interceptors/service.js +23 -28
- package/test/unit/spec/services/service-catalog.js +19 -27
- package/test/unit/spec/services/service-host.js +29 -26
- package/test/unit/spec/services/service-registry.js +128 -170
- package/test/unit/spec/services/service-state.js +13 -22
- package/test/unit/spec/services/service-url.js +24 -43
- package/test/unit/spec/services/services.js +147 -41
- package/test/unit/spec/storage/persist.js +6 -9
- package/test/unit/spec/storage/wait-for-value.js +22 -21
- package/test/unit/spec/webex-core.js +90 -57
- package/test/unit/spec/webex-internal-core.js +56 -31
|
@@ -11,6 +11,7 @@ import {inBrowser} from '@webex/common';
|
|
|
11
11
|
import FakeTimers from '@sinonjs/fake-timers';
|
|
12
12
|
import {skipInBrowser} from '@webex/test-helper-mocha';
|
|
13
13
|
import Logger from '@webex/plugin-logger';
|
|
14
|
+
import Metrics, {config} from '@webex/internal-plugin-metrics';
|
|
14
15
|
|
|
15
16
|
/* eslint camelcase: [0] */
|
|
16
17
|
|
|
@@ -28,7 +29,8 @@ function promiseTick(count) {
|
|
|
28
29
|
return promise;
|
|
29
30
|
}
|
|
30
31
|
|
|
31
|
-
const AUTHORIZATION_STRING =
|
|
32
|
+
const AUTHORIZATION_STRING =
|
|
33
|
+
'https://api.ciscospark.com/v1/authorize?client_id=MOCK_CLIENT_ID&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%3A8000&scope=spark%3Arooms_read%20spark%3Ateams_read&state=set_state_here';
|
|
32
34
|
|
|
33
35
|
describe('webex-core', () => {
|
|
34
36
|
describe('Credentials', () => {
|
|
@@ -58,6 +60,34 @@ describe('webex-core', () => {
|
|
|
58
60
|
});
|
|
59
61
|
});
|
|
60
62
|
|
|
63
|
+
describe('#isUnverifiedGuest', () => {
|
|
64
|
+
let credentials;
|
|
65
|
+
let webex;
|
|
66
|
+
beforeEach('generate the webex instance', () => {
|
|
67
|
+
webex = new MockWebex();
|
|
68
|
+
credentials = new Credentials(undefined, {parent: webex});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should have #isUnverifiedGuest', () => {
|
|
72
|
+
assert.exists(credentials.isUnverifiedGuest);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should get the user status and return as a boolean', () => {
|
|
76
|
+
credentials.set('supertoken', 'AT');
|
|
77
|
+
assert.isFalse(credentials.isUnverifiedGuest);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should get guest user ', () => {
|
|
81
|
+
credentials.set('supertoken', 'eyJhbGciOiJSUzI1NiJ9.eyJ1c2VyX3R5cGUiOiJndWVzdCJ9');
|
|
82
|
+
assert.isTrue(credentials.isUnverifiedGuest);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('should get login user ', () => {
|
|
86
|
+
credentials.set('supertoken', 'dGhpc2lzbm90YXJlYWx1c2VydG9rZW4=');
|
|
87
|
+
assert.isFalse(credentials.isUnverifiedGuest);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
61
91
|
describe('#canAuthorize', () => {
|
|
62
92
|
it('indicates if the current state has enough information to populate an auth header, even if a token refresh or token downscope is required', () => {
|
|
63
93
|
const webex = new MockWebex();
|
|
@@ -69,7 +99,7 @@ describe('webex-core', () => {
|
|
|
69
99
|
assert.isFalse(credentials.canAuthorize);
|
|
70
100
|
|
|
71
101
|
credentials.supertoken = makeToken(webex, {
|
|
72
|
-
access_token: 'AT'
|
|
102
|
+
access_token: 'AT',
|
|
73
103
|
});
|
|
74
104
|
assert.isTrue(credentials.canAuthorize);
|
|
75
105
|
|
|
@@ -77,13 +107,13 @@ describe('webex-core', () => {
|
|
|
77
107
|
assert.isFalse(credentials.canAuthorize);
|
|
78
108
|
|
|
79
109
|
credentials.supertoken = makeToken(webex, {
|
|
80
|
-
access_token: 'AT'
|
|
110
|
+
access_token: 'AT',
|
|
81
111
|
});
|
|
82
112
|
assert.isTrue(credentials.canAuthorize);
|
|
83
113
|
|
|
84
114
|
credentials.supertoken = makeToken(webex, {
|
|
85
115
|
access_token: 'AT',
|
|
86
|
-
expires: Date.now() - 10000
|
|
116
|
+
expires: Date.now() - 10000,
|
|
87
117
|
});
|
|
88
118
|
assert.isFalse(credentials.supertoken.canAuthorize);
|
|
89
119
|
assert.isFalse(credentials.canRefresh);
|
|
@@ -95,7 +125,7 @@ describe('webex-core', () => {
|
|
|
95
125
|
assert.isFalse(credentials.canAuthorize);
|
|
96
126
|
credentials.supertoken = makeToken(webex, {
|
|
97
127
|
access_token: 'AT',
|
|
98
|
-
refresh_token: 'RT'
|
|
128
|
+
refresh_token: 'RT',
|
|
99
129
|
});
|
|
100
130
|
credentials.supertoken.unset('access_token');
|
|
101
131
|
assert.isTrue(credentials.canAuthorize);
|
|
@@ -109,9 +139,13 @@ describe('webex-core', () => {
|
|
|
109
139
|
|
|
110
140
|
webex.trigger('change:config');
|
|
111
141
|
assert.isFalse(credentials.canRefresh);
|
|
112
|
-
credentials.supertoken = makeToken(
|
|
113
|
-
|
|
114
|
-
|
|
142
|
+
credentials.supertoken = makeToken(
|
|
143
|
+
webex,
|
|
144
|
+
{
|
|
145
|
+
access_token: 'AT',
|
|
146
|
+
},
|
|
147
|
+
{parent: true}
|
|
148
|
+
);
|
|
115
149
|
assert.isFalse(credentials.canRefresh);
|
|
116
150
|
|
|
117
151
|
webex.config.credentials.refreshCallback = inBrowser && noop;
|
|
@@ -120,7 +154,7 @@ describe('webex-core', () => {
|
|
|
120
154
|
assert.isFalse(credentials.canRefresh);
|
|
121
155
|
credentials.supertoken = makeToken(webex, {
|
|
122
156
|
access_token: 'AT',
|
|
123
|
-
refresh_token: 'RT'
|
|
157
|
+
refresh_token: 'RT',
|
|
124
158
|
});
|
|
125
159
|
assert.isTrue(credentials.supertoken.canRefresh);
|
|
126
160
|
assert.isTrue(credentials.canRefresh);
|
|
@@ -131,8 +165,8 @@ describe('webex-core', () => {
|
|
|
131
165
|
it('requires `state` to be an object', () => {
|
|
132
166
|
const webex = new MockWebex({
|
|
133
167
|
children: {
|
|
134
|
-
credentials: Credentials
|
|
135
|
-
}
|
|
168
|
+
credentials: Credentials,
|
|
169
|
+
},
|
|
136
170
|
});
|
|
137
171
|
|
|
138
172
|
webex.trigger('change:config)');
|
|
@@ -161,7 +195,7 @@ describe('webex-core', () => {
|
|
|
161
195
|
|
|
162
196
|
assert.match(credentials.buildLoginUrl({state: {}}), /idbroker/);
|
|
163
197
|
webex.config.credentials = {
|
|
164
|
-
authorizationString: AUTHORIZATION_STRING
|
|
198
|
+
authorizationString: AUTHORIZATION_STRING,
|
|
165
199
|
};
|
|
166
200
|
credentials = new Credentials({}, {parent: webex});
|
|
167
201
|
webex.trigger('change:config');
|
|
@@ -174,7 +208,12 @@ describe('webex-core', () => {
|
|
|
174
208
|
|
|
175
209
|
webex.trigger('change:config');
|
|
176
210
|
|
|
177
|
-
assert.equal(
|
|
211
|
+
assert.equal(
|
|
212
|
+
credentials.buildLoginUrl({state: {page: 'front'}}),
|
|
213
|
+
`${
|
|
214
|
+
process.env.IDBROKER_BASE_URL || 'https://idbroker.webex.com'
|
|
215
|
+
}/idb/oauth2/v1/authorize?state=eyJwYWdlIjoiZnJvbnQifQ&client_id=fake&redirect_uri=http%3A%2F%2Fexample.com&scope=scope%3Aone&response_type=code`
|
|
216
|
+
);
|
|
178
217
|
});
|
|
179
218
|
|
|
180
219
|
skipInBrowser(it)('generates the login url with empty state param', () => {
|
|
@@ -183,11 +222,15 @@ describe('webex-core', () => {
|
|
|
183
222
|
|
|
184
223
|
webex.trigger('change:config');
|
|
185
224
|
|
|
186
|
-
assert.equal(
|
|
225
|
+
assert.equal(
|
|
226
|
+
credentials.buildLoginUrl({state: {}}),
|
|
227
|
+
`${
|
|
228
|
+
process.env.IDBROKER_BASE_URL || 'https://idbroker.webex.com'
|
|
229
|
+
}/idb/oauth2/v1/authorize?client_id=fake&redirect_uri=http%3A%2F%2Fexample.com&scope=scope%3Aone&response_type=code`
|
|
230
|
+
);
|
|
187
231
|
});
|
|
188
232
|
});
|
|
189
233
|
|
|
190
|
-
|
|
191
234
|
describe('#buildLogoutUrl()', () => {
|
|
192
235
|
skipInBrowser(it)('generates the logout url', () => {
|
|
193
236
|
const webex = new MockWebex();
|
|
@@ -196,7 +239,12 @@ describe('webex-core', () => {
|
|
|
196
239
|
const credentials = new Credentials(undefined, {parent: webex});
|
|
197
240
|
|
|
198
241
|
webex.trigger('change:config');
|
|
199
|
-
assert.equal(
|
|
242
|
+
assert.equal(
|
|
243
|
+
credentials.buildLogoutUrl(),
|
|
244
|
+
`${
|
|
245
|
+
process.env.IDBROKER_BASE_URL || 'https://idbroker.webex.com'
|
|
246
|
+
}/idb/oauth2/v1/logout?cisService=webex&goto=ru`
|
|
247
|
+
);
|
|
200
248
|
});
|
|
201
249
|
|
|
202
250
|
skipInBrowser(it)('includes a token param if passed', () => {
|
|
@@ -206,7 +254,12 @@ describe('webex-core', () => {
|
|
|
206
254
|
const credentials = new Credentials(undefined, {parent: webex});
|
|
207
255
|
|
|
208
256
|
webex.trigger('change:config');
|
|
209
|
-
assert.equal(
|
|
257
|
+
assert.equal(
|
|
258
|
+
credentials.buildLogoutUrl({token: 't'}),
|
|
259
|
+
`${
|
|
260
|
+
process.env.IDBROKER_BASE_URL || 'https://idbroker.webex.com'
|
|
261
|
+
}/idb/oauth2/v1/logout?cisService=webex&goto=ru&token=t`
|
|
262
|
+
);
|
|
210
263
|
});
|
|
211
264
|
|
|
212
265
|
it('always fallsback to idbroker', () => {
|
|
@@ -224,7 +277,10 @@ describe('webex-core', () => {
|
|
|
224
277
|
const credentials = new Credentials(undefined, {parent: webex});
|
|
225
278
|
|
|
226
279
|
webex.trigger('change:config');
|
|
227
|
-
assert.match(
|
|
280
|
+
assert.match(
|
|
281
|
+
credentials.buildLogoutUrl({goto: 'http://example.com/'}),
|
|
282
|
+
/goto=http%3A%2F%2Fexample.com%2F/
|
|
283
|
+
);
|
|
228
284
|
});
|
|
229
285
|
});
|
|
230
286
|
|
|
@@ -240,7 +296,8 @@ describe('webex-core', () => {
|
|
|
240
296
|
|
|
241
297
|
it('should return the OrgId of JWT-authenticated user', () => {
|
|
242
298
|
credentials.supertoken = {
|
|
243
|
-
access_token:
|
|
299
|
+
access_token:
|
|
300
|
+
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJyZWFsbSI6Im15LXJlYWxtIn0.U16gzUsaRW1VVikJA2VeXRHPX716tG1_B42oxzy1UMk',
|
|
244
301
|
};
|
|
245
302
|
|
|
246
303
|
orgId = 'my-realm';
|
|
@@ -252,17 +309,14 @@ describe('webex-core', () => {
|
|
|
252
309
|
orgId = 'this-is-an-org-id';
|
|
253
310
|
|
|
254
311
|
credentials.supertoken = {
|
|
255
|
-
access_token: `000_000_${orgId}
|
|
312
|
+
access_token: `000_000_${orgId}`,
|
|
256
313
|
};
|
|
257
314
|
|
|
258
315
|
assert.equal(credentials.getOrgId(), orgId);
|
|
259
316
|
});
|
|
260
317
|
|
|
261
318
|
it('should throw if the OrgId was not determined', () =>
|
|
262
|
-
assert.throws(
|
|
263
|
-
() => credentials.getOrgId(),
|
|
264
|
-
'the provided token is not a valid format'
|
|
265
|
-
));
|
|
319
|
+
assert.throws(() => credentials.getOrgId(), 'the provided token is not a valid format'));
|
|
266
320
|
});
|
|
267
321
|
|
|
268
322
|
describe('#extractOrgIdFromJWT()', () => {
|
|
@@ -275,7 +329,8 @@ describe('webex-core', () => {
|
|
|
275
329
|
});
|
|
276
330
|
|
|
277
331
|
it('should return the OrgId of a provided JWT', () => {
|
|
278
|
-
const jwt =
|
|
332
|
+
const jwt =
|
|
333
|
+
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJyZWFsbSI6Im15LXJlYWxtIn0.U16gzUsaRW1VVikJA2VeXRHPX716tG1_B42oxzy1UMk';
|
|
279
334
|
const realm = 'my-realm';
|
|
280
335
|
|
|
281
336
|
assert.equal(credentials.extractOrgIdFromJWT(jwt), realm);
|
|
@@ -285,7 +340,8 @@ describe('webex-core', () => {
|
|
|
285
340
|
assert.throws(() => credentials.extractOrgIdFromJWT('not-valid')));
|
|
286
341
|
|
|
287
342
|
it('should throw if the provided JWT does not contain an OrgId', () => {
|
|
288
|
-
const jwtNoOrg =
|
|
343
|
+
const jwtNoOrg =
|
|
344
|
+
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c';
|
|
289
345
|
|
|
290
346
|
assert.throws(() => credentials.extractOrgIdFromJWT(jwtNoOrg));
|
|
291
347
|
});
|
|
@@ -324,7 +380,7 @@ describe('webex-core', () => {
|
|
|
324
380
|
webex.config.credentials = {
|
|
325
381
|
client_id: 'ci',
|
|
326
382
|
redirect_uri: 'ru',
|
|
327
|
-
scope: 's'
|
|
383
|
+
scope: 's',
|
|
328
384
|
};
|
|
329
385
|
|
|
330
386
|
let credentials = new Credentials(undefined, {parent: webex});
|
|
@@ -358,16 +414,26 @@ describe('webex-core', () => {
|
|
|
358
414
|
'data.supertoken.access_token',
|
|
359
415
|
'data.authorization',
|
|
360
416
|
'data.authorization.supertoken',
|
|
361
|
-
'data.authorization.supertoken.access_token'
|
|
362
|
-
]
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
417
|
+
'data.authorization.supertoken.access_token',
|
|
418
|
+
]
|
|
419
|
+
.reduce(
|
|
420
|
+
(acc, path) =>
|
|
421
|
+
acc.concat(
|
|
422
|
+
['ST', 'Bearer ST'].map((str) => {
|
|
423
|
+
const obj = {
|
|
424
|
+
msg: `accepts token string "${str}" at path "${path
|
|
425
|
+
.split('.')
|
|
426
|
+
.slice(1)
|
|
427
|
+
.join('.')}"`,
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
set(obj, path, str);
|
|
431
|
+
|
|
432
|
+
return obj;
|
|
433
|
+
})
|
|
434
|
+
),
|
|
435
|
+
[]
|
|
436
|
+
)
|
|
371
437
|
.forEach(({msg, data}) => {
|
|
372
438
|
it(msg, () => {
|
|
373
439
|
const webex = new MockWebex();
|
|
@@ -380,19 +446,24 @@ describe('webex-core', () => {
|
|
|
380
446
|
});
|
|
381
447
|
|
|
382
448
|
it('schedules a refreshTimer', () => {
|
|
383
|
-
const webex = new MockWebex(
|
|
449
|
+
const webex = new MockWebex({
|
|
450
|
+
children: {
|
|
451
|
+
metrics: Metrics,
|
|
452
|
+
},
|
|
453
|
+
});
|
|
384
454
|
const supertoken = makeToken(webex, {
|
|
385
455
|
access_token: 'ST',
|
|
386
456
|
refresh_token: 'RT',
|
|
387
|
-
expires: Date.now() + 10000
|
|
457
|
+
expires: Date.now() + 10000,
|
|
388
458
|
});
|
|
389
459
|
const supertoken2 = makeToken(webex, {
|
|
390
460
|
access_token: 'ST2',
|
|
391
461
|
refresh_token: 'RT2',
|
|
392
|
-
expires: Date.now() + 20000
|
|
462
|
+
expires: Date.now() + 20000,
|
|
393
463
|
});
|
|
394
464
|
|
|
395
465
|
sinon.stub(supertoken, 'refresh').returns(Promise.resolve(supertoken2));
|
|
466
|
+
sinon.stub(webex.internal.metrics, 'submitClientMetrics').callsFake(() => {});
|
|
396
467
|
const credentials = new Credentials(supertoken, {parent: webex});
|
|
397
468
|
|
|
398
469
|
webex.trigger('change:config');
|
|
@@ -414,7 +485,7 @@ describe('webex-core', () => {
|
|
|
414
485
|
const supertoken = makeToken(webex, {
|
|
415
486
|
access_token: 'ST',
|
|
416
487
|
refresh_token: 'RT',
|
|
417
|
-
expires: Date.now() - 10000
|
|
488
|
+
expires: Date.now() - 10000,
|
|
418
489
|
});
|
|
419
490
|
|
|
420
491
|
sinon.stub(supertoken, 'refresh').returns(Promise.reject());
|
|
@@ -427,7 +498,19 @@ describe('webex-core', () => {
|
|
|
427
498
|
});
|
|
428
499
|
|
|
429
500
|
describe('#getUserToken()', () => {
|
|
430
|
-
it('resolves with the supertoken if the supertoken matches the requested scopes')
|
|
501
|
+
it('resolves with the supertoken if the supertoken matches the requested scopes', () => {
|
|
502
|
+
const webex = new MockWebex();
|
|
503
|
+
const credentials = new Credentials(undefined, {parent: webex});
|
|
504
|
+
|
|
505
|
+
webex.trigger('change:config');
|
|
506
|
+
const st = makeToken(webex, {access_token: 'ST', scope: 'scope1'});
|
|
507
|
+
|
|
508
|
+
credentials.set({
|
|
509
|
+
supertoken: st,
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
return credentials.getUserToken('scope1').then((result) => assert.deepEqual(result, st));
|
|
513
|
+
});
|
|
431
514
|
|
|
432
515
|
it('resolves with the token identified by the specified scopes', () => {
|
|
433
516
|
const webex = new MockWebex();
|
|
@@ -437,28 +520,43 @@ describe('webex-core', () => {
|
|
|
437
520
|
const st = makeToken(webex, {access_token: 'ST'});
|
|
438
521
|
const t1 = makeToken(webex, {
|
|
439
522
|
access_token: 'AT1',
|
|
440
|
-
scope: 'scope1'
|
|
523
|
+
scope: 'scope1',
|
|
441
524
|
});
|
|
442
525
|
const t2 = makeToken(webex, {
|
|
443
526
|
access_token: 'AT2',
|
|
444
|
-
scope: 'scope2'
|
|
527
|
+
scope: 'scope2',
|
|
445
528
|
});
|
|
446
529
|
|
|
447
530
|
credentials.set({
|
|
448
531
|
supertoken: st,
|
|
449
|
-
userTokens: [
|
|
450
|
-
t1, t2
|
|
451
|
-
]
|
|
532
|
+
userTokens: [t1, t2],
|
|
452
533
|
});
|
|
453
534
|
|
|
454
535
|
return Promise.all([
|
|
455
|
-
credentials.getUserToken('scope1')
|
|
456
|
-
|
|
457
|
-
credentials.getUserToken('scope2')
|
|
458
|
-
.then((result) => assert.deepEqual(result, t2))
|
|
536
|
+
credentials.getUserToken('scope1').then((result) => assert.deepEqual(result, t1)),
|
|
537
|
+
credentials.getUserToken('scope2').then((result) => assert.deepEqual(result, t2)),
|
|
459
538
|
]);
|
|
460
539
|
});
|
|
461
540
|
|
|
541
|
+
it('uses the supertoken.scope instead of the config.scope for downscope', () => {
|
|
542
|
+
const webex = new MockWebex();
|
|
543
|
+
const credentials = new Credentials(undefined, {parent: webex});
|
|
544
|
+
|
|
545
|
+
webex.trigger('change:config');
|
|
546
|
+
const st = makeToken(webex, {access_token: 'ST', scope: 'scope1 spark:kms'});
|
|
547
|
+
|
|
548
|
+
credentials.set({
|
|
549
|
+
supertoken: st,
|
|
550
|
+
scope: 'invalidScope scope1',
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
sinon.stub(credentials, 'downscope').returns(Promise.resolve());
|
|
554
|
+
|
|
555
|
+
return credentials.getUserToken().then(() => {
|
|
556
|
+
assert.calledWith(credentials.downscope, 'scope1');
|
|
557
|
+
});
|
|
558
|
+
});
|
|
559
|
+
|
|
462
560
|
describe('when no matching token is found', () => {
|
|
463
561
|
it('downscopes the supertoken', () => {
|
|
464
562
|
const webex = new MockWebex();
|
|
@@ -467,42 +565,42 @@ describe('webex-core', () => {
|
|
|
467
565
|
webex.trigger('change:config');
|
|
468
566
|
|
|
469
567
|
credentials.supertoken = makeToken(webex, {
|
|
470
|
-
access_token: 'ST'
|
|
568
|
+
access_token: 'ST',
|
|
471
569
|
});
|
|
472
570
|
|
|
473
571
|
const t2 = makeToken(webex, {
|
|
474
|
-
access_token: 'AT2'
|
|
572
|
+
access_token: 'AT2',
|
|
475
573
|
});
|
|
476
574
|
|
|
477
575
|
sinon.stub(credentials.supertoken, 'downscope').returns(Promise.resolve(t2));
|
|
478
576
|
|
|
479
577
|
const t1 = makeToken(webex, {
|
|
480
578
|
access_token: 'AT1',
|
|
481
|
-
scope: 'scope1'
|
|
579
|
+
scope: 'scope1',
|
|
482
580
|
});
|
|
483
581
|
|
|
484
582
|
credentials.set({
|
|
485
|
-
userTokens: [t1]
|
|
583
|
+
userTokens: [t1],
|
|
486
584
|
});
|
|
487
585
|
|
|
488
|
-
return credentials
|
|
586
|
+
return credentials
|
|
587
|
+
.getUserToken('scope2')
|
|
489
588
|
.then((result) => assert.deepEqual(result, t2))
|
|
490
589
|
.then(() => assert.calledWith(credentials.supertoken.downscope, 'scope2'));
|
|
491
590
|
});
|
|
492
591
|
});
|
|
493
592
|
|
|
494
|
-
|
|
495
593
|
describe('when no scope is specified', () => {
|
|
496
594
|
it('resolves with a token containing all but the kms scopes', () => {
|
|
497
595
|
const webex = new MockWebex();
|
|
498
596
|
|
|
499
|
-
webex.config.credentials.scope = 'scope1 spark:kms';
|
|
500
597
|
const credentials = new Credentials(undefined, {parent: webex});
|
|
501
598
|
|
|
502
599
|
webex.trigger('change:config');
|
|
503
600
|
|
|
504
601
|
credentials.supertoken = makeToken(webex, {
|
|
505
|
-
access_token: 'ST'
|
|
602
|
+
access_token: 'ST',
|
|
603
|
+
scope: 'scope1 spark:kms',
|
|
506
604
|
});
|
|
507
605
|
|
|
508
606
|
// const t2 = makeToken(webex, {
|
|
@@ -513,15 +611,14 @@ describe('webex-core', () => {
|
|
|
513
611
|
|
|
514
612
|
const t1 = makeToken(webex, {
|
|
515
613
|
access_token: 'AT1',
|
|
516
|
-
scope: 'scope1'
|
|
614
|
+
scope: 'scope1',
|
|
517
615
|
});
|
|
518
616
|
|
|
519
617
|
credentials.set({
|
|
520
|
-
userTokens: [t1]
|
|
618
|
+
userTokens: [t1],
|
|
521
619
|
});
|
|
522
620
|
|
|
523
|
-
return credentials.getUserToken()
|
|
524
|
-
.then((result) => assert.deepEqual(result, t1));
|
|
621
|
+
return credentials.getUserToken().then((result) => assert.deepEqual(result, t1));
|
|
525
622
|
});
|
|
526
623
|
});
|
|
527
624
|
|
|
@@ -529,37 +626,57 @@ describe('webex-core', () => {
|
|
|
529
626
|
it('falls back to the supertoken', () => {
|
|
530
627
|
const webex = new MockWebex({
|
|
531
628
|
children: {
|
|
532
|
-
logger: Logger
|
|
533
|
-
|
|
629
|
+
logger: Logger,
|
|
630
|
+
metrics: Metrics,
|
|
631
|
+
},
|
|
534
632
|
});
|
|
535
633
|
|
|
634
|
+
webex.config.metrics = config.metrics;
|
|
536
635
|
webex.config.credentials.scope = 'scope1 spark:kms';
|
|
537
636
|
const credentials = new Credentials(undefined, {parent: webex});
|
|
538
637
|
|
|
539
638
|
webex.trigger('change:config');
|
|
540
639
|
|
|
541
640
|
credentials.supertoken = makeToken(webex, {
|
|
542
|
-
access_token: 'ST'
|
|
641
|
+
access_token: 'ST',
|
|
543
642
|
});
|
|
544
643
|
|
|
545
|
-
|
|
644
|
+
const failReason = 'downscope failed';
|
|
645
|
+
sinon.stub(credentials.supertoken, 'downscope').returns(Promise.reject(failReason));
|
|
646
|
+
|
|
647
|
+
sinon.stub(credentials.logger, 'warn').callsFake(() => {});
|
|
648
|
+
sinon.stub(webex.internal.metrics, 'submitClientMetrics').callsFake(() => {});
|
|
546
649
|
|
|
547
650
|
const t1 = makeToken(webex, {
|
|
548
651
|
access_token: 'AT1',
|
|
549
|
-
scope: 'scope1'
|
|
652
|
+
scope: 'scope1',
|
|
550
653
|
});
|
|
551
654
|
|
|
552
655
|
credentials.set({
|
|
553
|
-
userTokens: [t1]
|
|
656
|
+
userTokens: [t1],
|
|
554
657
|
});
|
|
555
658
|
|
|
556
|
-
return credentials.getUserToken('scope2')
|
|
557
|
-
|
|
659
|
+
return credentials.getUserToken('scope2').then((t) => {
|
|
660
|
+
assert.equal(t.access_token, credentials.supertoken.access_token);
|
|
661
|
+
assert.calledWith(
|
|
662
|
+
credentials.logger.warn,
|
|
663
|
+
'credentials: failed to downscope supertoken to "scope2"'
|
|
664
|
+
);
|
|
665
|
+
assert.calledWith(
|
|
666
|
+
webex.internal.metrics.submitClientMetrics,
|
|
667
|
+
'JS_SDK_CREDENTIALS_DOWNSCOPE_FAILED',
|
|
668
|
+
{fields: {failReason, requestedScope: 'scope2'}}
|
|
669
|
+
);
|
|
670
|
+
});
|
|
558
671
|
});
|
|
559
672
|
});
|
|
560
673
|
|
|
561
674
|
it('is blocked while a token refresh is inflight', () => {
|
|
562
|
-
const webex = new MockWebex(
|
|
675
|
+
const webex = new MockWebex({
|
|
676
|
+
children: {
|
|
677
|
+
metrics: Metrics,
|
|
678
|
+
},
|
|
679
|
+
});
|
|
563
680
|
|
|
564
681
|
webex.config.credentials.scope = 'scope1 spark:kms';
|
|
565
682
|
const credentials = new Credentials(undefined, {parent: webex});
|
|
@@ -568,14 +685,16 @@ describe('webex-core', () => {
|
|
|
568
685
|
|
|
569
686
|
const supertoken1 = makeToken(webex, {
|
|
570
687
|
access_token: 'ST1',
|
|
571
|
-
refresh_token: 'RT1'
|
|
688
|
+
refresh_token: 'RT1',
|
|
572
689
|
});
|
|
573
690
|
|
|
574
691
|
credentials.set({supertoken: supertoken1});
|
|
575
692
|
|
|
576
|
-
sinon
|
|
693
|
+
sinon
|
|
694
|
+
.stub(supertoken1, 'downscope')
|
|
695
|
+
.returns(Promise.resolve(new Token({access_token: 'ST1ATD'})));
|
|
577
696
|
const supertoken2 = makeToken(webex, {
|
|
578
|
-
access_token: 'ST2'
|
|
697
|
+
access_token: 'ST2',
|
|
579
698
|
});
|
|
580
699
|
|
|
581
700
|
sinon.stub(supertoken1, 'refresh').returns(Promise.resolve(supertoken2));
|
|
@@ -583,11 +702,11 @@ describe('webex-core', () => {
|
|
|
583
702
|
const at2 = makeToken(webex, {access_token: 'ST2ATD'});
|
|
584
703
|
|
|
585
704
|
sinon.stub(supertoken2, 'downscope').returns(Promise.resolve(at2));
|
|
705
|
+
sinon.stub(webex.internal.metrics, 'submitClientMetrics').callsFake(() => {});
|
|
586
706
|
|
|
587
707
|
return Promise.all([
|
|
588
708
|
credentials.refresh(),
|
|
589
|
-
credentials.getUserToken('scope2')
|
|
590
|
-
.then((result) => assert.deepEqual(result, at2))
|
|
709
|
+
credentials.getUserToken('scope2').then((result) => assert.deepEqual(result, at2)),
|
|
591
710
|
]);
|
|
592
711
|
});
|
|
593
712
|
});
|
|
@@ -600,16 +719,16 @@ describe('webex-core', () => {
|
|
|
600
719
|
webex.trigger('change:config');
|
|
601
720
|
const st = makeToken(webex, {
|
|
602
721
|
access_token: 'ST',
|
|
603
|
-
refresh_token: 'RT'
|
|
722
|
+
refresh_token: 'RT',
|
|
604
723
|
});
|
|
605
724
|
|
|
606
725
|
const st2 = makeToken(webex, {
|
|
607
726
|
access_token: 'ST2',
|
|
608
|
-
refresh_token: 'RT2'
|
|
727
|
+
refresh_token: 'RT2',
|
|
609
728
|
});
|
|
610
729
|
|
|
611
730
|
credentials.set({
|
|
612
|
-
supertoken: st
|
|
731
|
+
supertoken: st,
|
|
613
732
|
});
|
|
614
733
|
|
|
615
734
|
sinon.stub(credentials, 'refresh').returns(Promise.resolve(st2));
|
|
@@ -618,12 +737,11 @@ describe('webex-core', () => {
|
|
|
618
737
|
assert.isDefined(credentials.refreshTimer);
|
|
619
738
|
assert.notCalled(credentials.refresh);
|
|
620
739
|
|
|
621
|
-
return credentials.invalidate()
|
|
622
|
-
.
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
});
|
|
740
|
+
return credentials.invalidate().then(() => {
|
|
741
|
+
clock.tick(10000);
|
|
742
|
+
assert.isUndefined(credentials.refreshTimer);
|
|
743
|
+
assert.notCalled(credentials.refresh);
|
|
744
|
+
});
|
|
627
745
|
});
|
|
628
746
|
|
|
629
747
|
it('clears the tokens from boundedStorage', () => {
|
|
@@ -632,25 +750,22 @@ describe('webex-core', () => {
|
|
|
632
750
|
|
|
633
751
|
webex.trigger('change:config');
|
|
634
752
|
const st = makeToken(webex, {
|
|
635
|
-
access_token: 'ST'
|
|
753
|
+
access_token: 'ST',
|
|
636
754
|
});
|
|
637
755
|
|
|
638
756
|
const t1 = makeToken(webex, {
|
|
639
757
|
access_token: 'AT1',
|
|
640
|
-
scope: 'scope1'
|
|
758
|
+
scope: 'scope1',
|
|
641
759
|
});
|
|
642
760
|
|
|
643
761
|
const t2 = makeToken(webex, {
|
|
644
762
|
access_token: 'AT2',
|
|
645
|
-
scope: 'scope2'
|
|
763
|
+
scope: 'scope2',
|
|
646
764
|
});
|
|
647
765
|
|
|
648
766
|
credentials.set({
|
|
649
767
|
supertoken: st,
|
|
650
|
-
userTokens: [
|
|
651
|
-
t1,
|
|
652
|
-
t2
|
|
653
|
-
]
|
|
768
|
+
userTokens: [t1, t2],
|
|
654
769
|
});
|
|
655
770
|
|
|
656
771
|
return new Promise((resolve) => {
|
|
@@ -665,19 +780,24 @@ describe('webex-core', () => {
|
|
|
665
780
|
return credentials.invalidate();
|
|
666
781
|
})
|
|
667
782
|
.then(() => promiseTick(500))
|
|
668
|
-
.then(
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
783
|
+
.then(
|
|
784
|
+
() =>
|
|
785
|
+
new Promise((resolve) => {
|
|
786
|
+
setTimeout(resolve, 1);
|
|
787
|
+
clock.tick(1000);
|
|
788
|
+
})
|
|
789
|
+
)
|
|
672
790
|
.then(() => promiseTick(500))
|
|
673
|
-
.then(
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
791
|
+
.then(
|
|
792
|
+
() =>
|
|
793
|
+
new Promise((resolve) => {
|
|
794
|
+
setTimeout(resolve, 1);
|
|
795
|
+
clock.tick(1000);
|
|
796
|
+
})
|
|
797
|
+
)
|
|
677
798
|
.then(() => assert.isRejected(webex.boundedStorage.get('Credentials', '@'), /NotFound/));
|
|
678
799
|
});
|
|
679
800
|
|
|
680
|
-
|
|
681
801
|
it('does not induce any token refreshes');
|
|
682
802
|
|
|
683
803
|
it('prevents #getUserToken() from being invoked', () => {
|
|
@@ -687,67 +807,77 @@ describe('webex-core', () => {
|
|
|
687
807
|
webex.trigger('change:config');
|
|
688
808
|
const st = makeToken(webex, {
|
|
689
809
|
access_token: 'ST',
|
|
690
|
-
refresh_token: 'RT'
|
|
810
|
+
refresh_token: 'RT',
|
|
691
811
|
});
|
|
692
812
|
|
|
693
813
|
const t1 = makeToken(webex, {
|
|
694
814
|
access_token: 'AT1',
|
|
695
|
-
scope: 'scope1'
|
|
815
|
+
scope: 'scope1',
|
|
696
816
|
});
|
|
697
817
|
|
|
698
818
|
credentials.set({
|
|
699
819
|
supertoken: st,
|
|
700
|
-
userTokens: [
|
|
701
|
-
t1
|
|
702
|
-
]
|
|
820
|
+
userTokens: [t1],
|
|
703
821
|
});
|
|
704
822
|
|
|
705
|
-
return credentials
|
|
706
|
-
.
|
|
823
|
+
return credentials
|
|
824
|
+
.invalidate()
|
|
825
|
+
.then(() =>
|
|
826
|
+
assert.isRejected(
|
|
827
|
+
credentials.getUserToken(),
|
|
828
|
+
/Current state cannot produce an access token/
|
|
829
|
+
)
|
|
830
|
+
);
|
|
707
831
|
});
|
|
708
832
|
});
|
|
709
833
|
|
|
710
834
|
describe('#refresh()', () => {
|
|
711
835
|
it('refreshes and downscopes the supertoken, and revokes previous tokens', () => {
|
|
712
|
-
const webex = new MockWebex(
|
|
836
|
+
const webex = new MockWebex({
|
|
837
|
+
children: {
|
|
838
|
+
metrics: Metrics,
|
|
839
|
+
},
|
|
840
|
+
});
|
|
713
841
|
const credentials = new Credentials(undefined, {parent: webex});
|
|
714
842
|
|
|
715
843
|
webex.trigger('change:config');
|
|
716
844
|
const st = makeToken(webex, {
|
|
717
845
|
access_token: 'ST',
|
|
718
|
-
refresh_token: 'RT'
|
|
846
|
+
refresh_token: 'RT',
|
|
847
|
+
scope: 'scope1 scope2',
|
|
719
848
|
});
|
|
720
849
|
|
|
721
850
|
const st2 = makeToken(webex, {
|
|
722
851
|
access_token: 'ST2',
|
|
723
|
-
refresh_token: 'RT2'
|
|
852
|
+
refresh_token: 'RT2',
|
|
853
|
+
scope: 'scope1 scope2',
|
|
724
854
|
});
|
|
725
855
|
|
|
726
856
|
const t1 = makeToken(webex, {
|
|
727
857
|
access_token: 'AT1',
|
|
728
|
-
scope: 'scope1'
|
|
858
|
+
scope: 'scope1',
|
|
729
859
|
});
|
|
730
860
|
|
|
731
861
|
const t2 = makeToken(webex, {
|
|
732
862
|
access_token: 'AT2',
|
|
733
|
-
scope: 'scope2'
|
|
863
|
+
scope: 'scope2',
|
|
734
864
|
});
|
|
735
865
|
|
|
736
866
|
sinon.stub(st2, 'downscope').returns(Promise.resolve(t2));
|
|
737
867
|
sinon.stub(st, 'refresh').returns(Promise.resolve(st2));
|
|
738
868
|
sinon.stub(t1, 'revoke').returns(Promise.resolve());
|
|
739
869
|
sinon.spy(credentials, 'scheduleRefresh');
|
|
870
|
+
sinon.stub(webex.internal.metrics, 'submitClientMetrics').callsFake(() => {});
|
|
740
871
|
|
|
741
872
|
credentials.set({
|
|
742
873
|
supertoken: st,
|
|
743
|
-
userTokens: [
|
|
744
|
-
t1
|
|
745
|
-
]
|
|
874
|
+
userTokens: [t1],
|
|
746
875
|
});
|
|
747
876
|
|
|
748
877
|
assert.equal(credentials.userTokens.get(t1.scope), t1);
|
|
749
878
|
|
|
750
|
-
return credentials
|
|
879
|
+
return credentials
|
|
880
|
+
.refresh()
|
|
751
881
|
.then(() => assert.called(st.refresh))
|
|
752
882
|
.then(() => assert.calledWith(st2.downscope, 'scope1'))
|
|
753
883
|
.then(() => assert.called(t1.revoke))
|
|
@@ -757,45 +887,50 @@ describe('webex-core', () => {
|
|
|
757
887
|
});
|
|
758
888
|
|
|
759
889
|
it('refreshes and downscopes the supertoken even if revocation of previous token fails', () => {
|
|
760
|
-
const webex = new MockWebex(
|
|
890
|
+
const webex = new MockWebex({
|
|
891
|
+
children: {
|
|
892
|
+
metrics: Metrics,
|
|
893
|
+
},
|
|
894
|
+
});
|
|
761
895
|
const credentials = new Credentials(undefined, {parent: webex});
|
|
762
896
|
|
|
763
897
|
webex.trigger('change:config');
|
|
764
898
|
const st = makeToken(webex, {
|
|
765
899
|
access_token: 'ST',
|
|
766
|
-
refresh_token: 'RT'
|
|
900
|
+
refresh_token: 'RT',
|
|
767
901
|
});
|
|
768
902
|
|
|
769
903
|
const st2 = makeToken(webex, {
|
|
770
904
|
access_token: 'ST2',
|
|
771
|
-
refresh_token: 'RT2'
|
|
905
|
+
refresh_token: 'RT2',
|
|
906
|
+
scope: 'scope1 scope2',
|
|
772
907
|
});
|
|
773
908
|
|
|
774
909
|
const t1 = makeToken(webex, {
|
|
775
910
|
access_token: 'AT1',
|
|
776
|
-
scope: 'scope1'
|
|
911
|
+
scope: 'scope1',
|
|
777
912
|
});
|
|
778
913
|
|
|
779
914
|
const t2 = makeToken(webex, {
|
|
780
915
|
access_token: 'AT2',
|
|
781
|
-
scope: 'scope2'
|
|
916
|
+
scope: 'scope2',
|
|
782
917
|
});
|
|
783
918
|
|
|
784
919
|
sinon.stub(st2, 'downscope').returns(Promise.resolve(t2));
|
|
785
920
|
sinon.stub(st, 'refresh').returns(Promise.resolve(st2));
|
|
786
921
|
sinon.stub(t1, 'revoke').returns(Promise.reject());
|
|
787
922
|
sinon.spy(credentials, 'scheduleRefresh');
|
|
923
|
+
sinon.stub(webex.internal.metrics, 'submitClientMetrics').callsFake(() => {});
|
|
788
924
|
|
|
789
925
|
credentials.set({
|
|
790
926
|
supertoken: st,
|
|
791
|
-
userTokens: [
|
|
792
|
-
t1
|
|
793
|
-
]
|
|
927
|
+
userTokens: [t1],
|
|
794
928
|
});
|
|
795
929
|
|
|
796
930
|
assert.equal(credentials.userTokens.get(t1.scope), t1);
|
|
797
931
|
|
|
798
|
-
return credentials
|
|
932
|
+
return credentials
|
|
933
|
+
.refresh()
|
|
799
934
|
.then(() => assert.called(st.refresh))
|
|
800
935
|
.then(() => assert.calledWith(st2.downscope, 'scope1'))
|
|
801
936
|
.then(() => assert.called(t1.revoke))
|
|
@@ -807,74 +942,72 @@ describe('webex-core', () => {
|
|
|
807
942
|
it('removes and revokes all child tokens', () => {
|
|
808
943
|
const webex = new MockWebex({
|
|
809
944
|
children: {
|
|
810
|
-
logger: Logger
|
|
811
|
-
|
|
945
|
+
logger: Logger,
|
|
946
|
+
metrics: Metrics,
|
|
947
|
+
},
|
|
812
948
|
});
|
|
813
949
|
const credentials = new Credentials(undefined, {parent: webex});
|
|
814
950
|
|
|
815
951
|
webex.trigger('change:config');
|
|
816
952
|
const st = makeToken(webex, {
|
|
817
953
|
access_token: 'ST',
|
|
818
|
-
refresh_token: 'RT'
|
|
954
|
+
refresh_token: 'RT',
|
|
955
|
+
scope: '',
|
|
819
956
|
});
|
|
820
957
|
|
|
821
958
|
sinon.stub(st, 'refresh').returns(Promise.resolve(makeToken(webex, {access_token: 'ST2'})));
|
|
959
|
+
sinon.stub(webex.internal.metrics, 'submitClientMetrics').callsFake(() => {});
|
|
822
960
|
|
|
823
961
|
const t1 = makeToken(webex, {
|
|
824
962
|
access_token: 'AT1',
|
|
825
|
-
scope: 'scope1'
|
|
963
|
+
scope: 'scope1',
|
|
826
964
|
});
|
|
827
965
|
|
|
828
966
|
credentials.set({
|
|
829
967
|
supertoken: st,
|
|
830
|
-
userTokens: [
|
|
831
|
-
t1
|
|
832
|
-
]
|
|
968
|
+
userTokens: [t1],
|
|
833
969
|
});
|
|
834
970
|
|
|
835
|
-
return credentials.refresh()
|
|
836
|
-
.then(() => assert.called(st.refresh));
|
|
971
|
+
return credentials.refresh().then(() => assert.called(st.refresh));
|
|
837
972
|
});
|
|
838
973
|
|
|
839
974
|
it('allows #getUserToken() to be revoked, but #getUserToken() promises will not resolve until the suport token has been refreshed', () => {
|
|
840
|
-
const webex = new MockWebex();
|
|
975
|
+
const webex = new MockWebex({children: {metrics: Metrics}});
|
|
841
976
|
const credentials = new Credentials(undefined, {parent: webex});
|
|
842
977
|
|
|
843
978
|
webex.trigger('change:config');
|
|
844
979
|
const st1 = makeToken(webex, {
|
|
845
980
|
access_token: 'ST1',
|
|
846
|
-
refresh_token: 'RT1'
|
|
981
|
+
refresh_token: 'RT1',
|
|
847
982
|
});
|
|
848
983
|
|
|
849
984
|
const st2 = makeToken(webex, {
|
|
850
985
|
access_token: 'ST2',
|
|
851
|
-
refresh_token: 'RT1'
|
|
986
|
+
refresh_token: 'RT1',
|
|
852
987
|
});
|
|
853
988
|
|
|
854
989
|
const t1 = makeToken(webex, {
|
|
855
990
|
access_token: 'AT1',
|
|
856
|
-
scope: 'scope1'
|
|
991
|
+
scope: 'scope1',
|
|
857
992
|
});
|
|
858
993
|
|
|
859
994
|
const t2 = makeToken(webex, {
|
|
860
995
|
access_token: 'AT2',
|
|
861
|
-
scope: 'scope1'
|
|
996
|
+
scope: 'scope1',
|
|
862
997
|
});
|
|
863
998
|
|
|
864
999
|
sinon.stub(st1, 'refresh').returns(Promise.resolve(st2));
|
|
865
1000
|
sinon.stub(st2, 'downscope').returns(Promise.resolve(t2));
|
|
1001
|
+
sinon.stub(webex.internal.metrics, 'submitClientMetrics').callsFake(() => {});
|
|
866
1002
|
|
|
867
1003
|
credentials.set({
|
|
868
1004
|
supertoken: st1,
|
|
869
|
-
userTokens: [
|
|
870
|
-
t1
|
|
871
|
-
]
|
|
1005
|
+
userTokens: [t1],
|
|
872
1006
|
});
|
|
873
1007
|
|
|
874
1008
|
credentials.refresh();
|
|
875
1009
|
|
|
876
|
-
return credentials.getUserToken('scope1')
|
|
877
|
-
.then((result) => assert.deepEqual(result, t2));
|
|
1010
|
+
return credentials.getUserToken('scope1').then((result) => assert.deepEqual(result, t2));
|
|
878
1011
|
});
|
|
879
1012
|
|
|
880
1013
|
it('emits InvalidRequestError when the refresh token and access token expire', () => {
|
|
@@ -884,42 +1017,99 @@ describe('webex-core', () => {
|
|
|
884
1017
|
webex.trigger('change:config');
|
|
885
1018
|
const st = makeToken(webex, {
|
|
886
1019
|
access_token: 'ST',
|
|
887
|
-
refresh_token: 'RT'
|
|
1020
|
+
refresh_token: 'RT',
|
|
888
1021
|
});
|
|
889
1022
|
|
|
890
1023
|
const t1 = makeToken(webex, {
|
|
891
1024
|
access_token: 'AT1',
|
|
892
|
-
scope: 'scope1'
|
|
1025
|
+
scope: 'scope1',
|
|
893
1026
|
});
|
|
894
1027
|
|
|
895
1028
|
const res = {
|
|
896
1029
|
body: {
|
|
897
1030
|
error: 'invalid_request',
|
|
898
|
-
error_description:
|
|
899
|
-
|
|
900
|
-
|
|
1031
|
+
error_description:
|
|
1032
|
+
'The refresh token provided is expired, revoked, malformed, or invalid.',
|
|
1033
|
+
trackingID: 'test123',
|
|
1034
|
+
},
|
|
901
1035
|
};
|
|
902
1036
|
|
|
903
1037
|
const ErrorConstructor = grantErrors.select(res.body.error);
|
|
904
1038
|
|
|
905
|
-
sinon
|
|
1039
|
+
sinon
|
|
1040
|
+
.stub(st, 'refresh')
|
|
1041
|
+
.returns(Promise.reject(new ErrorConstructor('InvalidRequestError')));
|
|
906
1042
|
sinon.stub(credentials, 'unset').returns(Promise.resolve());
|
|
907
1043
|
const triggerSpy = sinon.spy(webex, 'trigger');
|
|
908
1044
|
|
|
909
1045
|
credentials.set({
|
|
910
1046
|
supertoken: st,
|
|
911
|
-
userTokens: [
|
|
912
|
-
t1
|
|
913
|
-
]
|
|
1047
|
+
userTokens: [t1],
|
|
914
1048
|
});
|
|
915
1049
|
|
|
916
|
-
return credentials
|
|
1050
|
+
return credentials
|
|
1051
|
+
.refresh()
|
|
917
1052
|
.then(() => assert.called(st.refresh))
|
|
918
1053
|
.catch(() => {
|
|
919
1054
|
assert.called(credentials.unset);
|
|
920
1055
|
assert.calledWith(triggerSpy, sinon.match('client:InvalidRequestError'));
|
|
921
1056
|
});
|
|
922
1057
|
});
|
|
1058
|
+
|
|
1059
|
+
it('exclude invalid scopes from user token, log and call metrics when fetched supertoken scope mismatch with the configured scope', () => {
|
|
1060
|
+
const webex = new MockWebex({
|
|
1061
|
+
children: {
|
|
1062
|
+
logger: Logger,
|
|
1063
|
+
metrics: Metrics,
|
|
1064
|
+
},
|
|
1065
|
+
});
|
|
1066
|
+
const credentials = new Credentials(undefined, {parent: webex});
|
|
1067
|
+
|
|
1068
|
+
webex.trigger('change:config');
|
|
1069
|
+
const st = makeToken(webex, {
|
|
1070
|
+
access_token: 'ST',
|
|
1071
|
+
refresh_token: 'RT',
|
|
1072
|
+
});
|
|
1073
|
+
|
|
1074
|
+
const st2 = makeToken(webex, {
|
|
1075
|
+
access_token: 'ST2',
|
|
1076
|
+
refresh_token: 'RT2',
|
|
1077
|
+
scope: 'scope1',
|
|
1078
|
+
});
|
|
1079
|
+
|
|
1080
|
+
const userToken = makeToken(webex, {
|
|
1081
|
+
access_token: 'AT1',
|
|
1082
|
+
scope: 'scope1 invalidScope1',
|
|
1083
|
+
});
|
|
1084
|
+
|
|
1085
|
+
credentials.set({
|
|
1086
|
+
supertoken: st,
|
|
1087
|
+
userTokens: [userToken],
|
|
1088
|
+
});
|
|
1089
|
+
const invalidScopes = 'invalidScope1 invalidScope2';
|
|
1090
|
+
credentials.config.scope = `scope1 ${invalidScopes}`;
|
|
1091
|
+
|
|
1092
|
+
sinon.stub(st2, 'downscope').returns(Promise.resolve());
|
|
1093
|
+
sinon.stub(st, 'refresh').returns(Promise.resolve(st2));
|
|
1094
|
+
sinon.spy(credentials, 'downscope');
|
|
1095
|
+
sinon.spy(credentials, 'scheduleRefresh');
|
|
1096
|
+
|
|
1097
|
+
sinon.stub(credentials.logger, 'warn').callsFake(() => {});
|
|
1098
|
+
sinon.stub(webex.internal.metrics, 'submitClientMetrics').callsFake(() => {});
|
|
1099
|
+
|
|
1100
|
+
return credentials.refresh().then(() => {
|
|
1101
|
+
assert.calledWith(
|
|
1102
|
+
credentials.logger.warn,
|
|
1103
|
+
`credentials: "${invalidScopes}" scope(s) are invalid because not listed in the supertoken, they will be excluded from user token requests.`
|
|
1104
|
+
);
|
|
1105
|
+
assert.calledWith(
|
|
1106
|
+
webex.internal.metrics.submitClientMetrics,
|
|
1107
|
+
'JS_SDK_CREDENTIALS_TOKEN_REFRESH_SCOPE_MISMATCH',
|
|
1108
|
+
{fields: {invalidScopes}}
|
|
1109
|
+
);
|
|
1110
|
+
assert.calledWith(credentials.downscope, 'scope1');
|
|
1111
|
+
});
|
|
1112
|
+
});
|
|
923
1113
|
});
|
|
924
1114
|
|
|
925
1115
|
describe('#scheduleRefresh()', () => {
|
|
@@ -930,16 +1120,16 @@ describe('webex-core', () => {
|
|
|
930
1120
|
webex.trigger('change:config');
|
|
931
1121
|
const st = makeToken(webex, {
|
|
932
1122
|
access_token: 'ST',
|
|
933
|
-
refresh_token: 'RT'
|
|
1123
|
+
refresh_token: 'RT',
|
|
934
1124
|
});
|
|
935
1125
|
|
|
936
1126
|
const st2 = makeToken(webex, {
|
|
937
1127
|
access_token: 'ST2',
|
|
938
|
-
refresh_token: 'RT2'
|
|
1128
|
+
refresh_token: 'RT2',
|
|
939
1129
|
});
|
|
940
1130
|
|
|
941
1131
|
credentials.set({
|
|
942
|
-
supertoken: st
|
|
1132
|
+
supertoken: st,
|
|
943
1133
|
});
|
|
944
1134
|
|
|
945
1135
|
sinon.stub(credentials, 'refresh').returns(Promise.resolve(st2));
|
|
@@ -956,16 +1146,16 @@ describe('webex-core', () => {
|
|
|
956
1146
|
webex.trigger('change:config');
|
|
957
1147
|
const st = makeToken(webex, {
|
|
958
1148
|
access_token: 'ST',
|
|
959
|
-
refresh_token: 'RT'
|
|
1149
|
+
refresh_token: 'RT',
|
|
960
1150
|
});
|
|
961
1151
|
|
|
962
1152
|
const st2 = makeToken(webex, {
|
|
963
1153
|
access_token: 'ST2',
|
|
964
|
-
refresh_token: 'RT2'
|
|
1154
|
+
refresh_token: 'RT2',
|
|
965
1155
|
});
|
|
966
1156
|
|
|
967
1157
|
credentials.set({
|
|
968
|
-
supertoken: st
|
|
1158
|
+
supertoken: st,
|
|
969
1159
|
});
|
|
970
1160
|
|
|
971
1161
|
sinon.stub(credentials, 'refresh').returns(Promise.resolve(st2));
|