@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
|
@@ -6,52 +6,40 @@ import querystring from 'querystring';
|
|
|
6
6
|
import url from 'url';
|
|
7
7
|
|
|
8
8
|
import jwt from 'jsonwebtoken';
|
|
9
|
-
import {
|
|
10
|
-
base64,
|
|
11
|
-
makeStateDataType,
|
|
12
|
-
oneFlight,
|
|
13
|
-
tap,
|
|
14
|
-
whileInFlight
|
|
15
|
-
} from '@webex/common';
|
|
9
|
+
import {base64, makeStateDataType, oneFlight, tap, whileInFlight} from '@webex/common';
|
|
16
10
|
import {safeSetTimeout} from '@webex/common-timers';
|
|
17
11
|
import {clone, cloneDeep, isObject, isEmpty} from 'lodash';
|
|
18
12
|
|
|
19
13
|
import WebexPlugin from '../webex-plugin';
|
|
20
14
|
import {persist, waitForValue} from '../storage/decorators';
|
|
21
15
|
|
|
22
|
-
import grantErrors from './grant-errors';
|
|
23
|
-
import {filterScope, sortScope} from './scope';
|
|
16
|
+
import grantErrors, {OAuthError} from './grant-errors';
|
|
17
|
+
import {filterScope, diffScopes, sortScope} from './scope';
|
|
24
18
|
import Token from './token';
|
|
25
19
|
import TokenCollection from './token-collection';
|
|
20
|
+
import {METRICS} from '../constants';
|
|
26
21
|
|
|
27
22
|
/**
|
|
28
23
|
* @class
|
|
29
24
|
*/
|
|
30
25
|
const Credentials = WebexPlugin.extend({
|
|
31
26
|
collections: {
|
|
32
|
-
userTokens: TokenCollection
|
|
27
|
+
userTokens: TokenCollection,
|
|
33
28
|
},
|
|
34
29
|
|
|
35
30
|
dataTypes: {
|
|
36
|
-
token: makeStateDataType(Token, 'token').dataType
|
|
31
|
+
token: makeStateDataType(Token, 'token').dataType,
|
|
37
32
|
},
|
|
38
33
|
|
|
39
34
|
derived: {
|
|
40
35
|
canAuthorize: {
|
|
41
|
-
deps: [
|
|
42
|
-
'supertoken',
|
|
43
|
-
'supertoken.canAuthorize',
|
|
44
|
-
'canRefresh'
|
|
45
|
-
],
|
|
36
|
+
deps: ['supertoken', 'supertoken.canAuthorize', 'canRefresh'],
|
|
46
37
|
fn() {
|
|
47
|
-
return Boolean(this.supertoken && this.supertoken.canAuthorize || this.canRefresh);
|
|
48
|
-
}
|
|
38
|
+
return Boolean((this.supertoken && this.supertoken.canAuthorize) || this.canRefresh);
|
|
39
|
+
},
|
|
49
40
|
},
|
|
50
41
|
canRefresh: {
|
|
51
|
-
deps: [
|
|
52
|
-
'supertoken',
|
|
53
|
-
'supertoken.canRefresh'
|
|
54
|
-
],
|
|
42
|
+
deps: ['supertoken', 'supertoken.canRefresh'],
|
|
55
43
|
fn() {
|
|
56
44
|
// If we're operating in JWT mode, we have to delegate to the consumer
|
|
57
45
|
if (this.config.jwtRefreshCallback) {
|
|
@@ -59,12 +47,31 @@ const Credentials = WebexPlugin.extend({
|
|
|
59
47
|
}
|
|
60
48
|
|
|
61
49
|
return Boolean(this.supertoken && this.supertoken.canRefresh);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
isUnverifiedGuest: {
|
|
53
|
+
deps: ['supertoken'],
|
|
54
|
+
/**
|
|
55
|
+
* Returns true if the user is an unverified guest
|
|
56
|
+
* @returns {boolean}
|
|
57
|
+
*/
|
|
58
|
+
fn() {
|
|
59
|
+
let isGuest = false;
|
|
60
|
+
try {
|
|
61
|
+
isGuest =
|
|
62
|
+
JSON.parse(base64.decode(this.supertoken.access_token.split('.')[1])).user_type ===
|
|
63
|
+
'guest';
|
|
64
|
+
} catch {
|
|
65
|
+
/* the non-guest token is formatted differently so catch is expected */
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return isGuest;
|
|
69
|
+
},
|
|
70
|
+
},
|
|
64
71
|
},
|
|
65
72
|
|
|
66
73
|
props: {
|
|
67
|
-
supertoken: makeStateDataType(Token, 'token').prop
|
|
74
|
+
supertoken: makeStateDataType(Token, 'token').prop,
|
|
68
75
|
},
|
|
69
76
|
|
|
70
77
|
namespace: 'Credentials',
|
|
@@ -72,7 +79,7 @@ const Credentials = WebexPlugin.extend({
|
|
|
72
79
|
session: {
|
|
73
80
|
isRefreshing: {
|
|
74
81
|
default: false,
|
|
75
|
-
type: 'boolean'
|
|
82
|
+
type: 'boolean',
|
|
76
83
|
},
|
|
77
84
|
/**
|
|
78
85
|
* Becomes `true` once the {@link loaded} event fires.
|
|
@@ -83,12 +90,12 @@ const Credentials = WebexPlugin.extend({
|
|
|
83
90
|
*/
|
|
84
91
|
ready: {
|
|
85
92
|
default: false,
|
|
86
|
-
type: 'boolean'
|
|
93
|
+
type: 'boolean',
|
|
87
94
|
},
|
|
88
95
|
refreshTimer: {
|
|
89
96
|
default: undefined,
|
|
90
|
-
type: 'any'
|
|
91
|
-
}
|
|
97
|
+
type: 'any',
|
|
98
|
+
},
|
|
92
99
|
},
|
|
93
100
|
|
|
94
101
|
/**
|
|
@@ -120,8 +127,7 @@ const Credentials = WebexPlugin.extend({
|
|
|
120
127
|
if (options.state) {
|
|
121
128
|
if (!isEmpty(options.state)) {
|
|
122
129
|
options.state = base64.toBase64Url(JSON.stringify(options.state));
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
130
|
+
} else {
|
|
125
131
|
delete options.state;
|
|
126
132
|
}
|
|
127
133
|
}
|
|
@@ -137,27 +143,21 @@ const Credentials = WebexPlugin.extend({
|
|
|
137
143
|
* @returns {string} - The OrgId.
|
|
138
144
|
*/
|
|
139
145
|
getOrgId() {
|
|
140
|
-
this.logger.info(
|
|
141
|
-
'credentials: attempting to retrieve the OrgId from token'
|
|
142
|
-
);
|
|
146
|
+
this.logger.info('credentials: attempting to retrieve the OrgId from token');
|
|
143
147
|
|
|
144
148
|
try {
|
|
145
149
|
// Attempt to extract a client-authenticated token's OrgId.
|
|
146
150
|
this.logger.info('credentials: trying to extract OrgId from JWT');
|
|
147
151
|
|
|
148
152
|
return this.extractOrgIdFromJWT(this.supertoken.access_token);
|
|
149
|
-
}
|
|
150
|
-
catch (e) {
|
|
153
|
+
} catch (e) {
|
|
151
154
|
// Attempt to extract a user token's OrgId.
|
|
152
155
|
this.logger.info('credentials: could not extract OrgId from JWT');
|
|
153
|
-
this.logger.info(
|
|
154
|
-
'credentials: attempting to extract OrgId from user token'
|
|
155
|
-
);
|
|
156
|
+
this.logger.info('credentials: attempting to extract OrgId from user token');
|
|
156
157
|
|
|
157
158
|
try {
|
|
158
159
|
return this.extractOrgIdFromUserToken(this.supertoken?.access_token);
|
|
159
|
-
}
|
|
160
|
-
catch (f) {
|
|
160
|
+
} catch (f) {
|
|
161
161
|
this.logger.info('credentials: could not extract OrgId from user token');
|
|
162
162
|
throw f;
|
|
163
163
|
}
|
|
@@ -218,10 +218,11 @@ const Credentials = WebexPlugin.extend({
|
|
|
218
218
|
* @returns {[type]}
|
|
219
219
|
*/
|
|
220
220
|
buildLogoutUrl(options = {}) {
|
|
221
|
-
return `${this.config.logoutUrl}?${querystring.stringify(
|
|
221
|
+
return `${this.config.logoutUrl}?${querystring.stringify({
|
|
222
222
|
cisService: this.config.service,
|
|
223
|
-
goto: this.config.redirect_uri
|
|
224
|
-
|
|
223
|
+
goto: this.config.redirect_uri,
|
|
224
|
+
...options,
|
|
225
|
+
})}`;
|
|
225
226
|
},
|
|
226
227
|
|
|
227
228
|
/**
|
|
@@ -233,7 +234,7 @@ const Credentials = WebexPlugin.extend({
|
|
|
233
234
|
* @returns {number}
|
|
234
235
|
*/
|
|
235
236
|
calcRefreshTimeout(expiration) {
|
|
236
|
-
return Math.floor((Math.floor(Math.random() * 4) + 6) / 10 * expiration);
|
|
237
|
+
return Math.floor(((Math.floor(Math.random() * 4) + 6) / 10) * expiration);
|
|
237
238
|
},
|
|
238
239
|
|
|
239
240
|
constructor(...args) {
|
|
@@ -258,13 +259,21 @@ const Credentials = WebexPlugin.extend({
|
|
|
258
259
|
* @returns {Promise<Token>}
|
|
259
260
|
*/
|
|
260
261
|
downscope(scope) {
|
|
261
|
-
return this.supertoken.downscope(scope)
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
262
|
+
return this.supertoken.downscope(scope).catch((reason) => {
|
|
263
|
+
const failReason = reason?.body ?? reason;
|
|
264
|
+
this.logger.warn(`credentials: failed to downscope supertoken to "${scope}"`, failReason);
|
|
265
|
+
this.logger.trace(`credentials: falling back to supertoken for ${scope}`);
|
|
266
|
+
this.webex.internal.metrics.submitClientMetrics(METRICS.JS_SDK_CREDENTIALS_DOWNSCOPE_FAILED, {
|
|
267
|
+
fields: {
|
|
268
|
+
requestedScope: scope,
|
|
269
|
+
failReason,
|
|
270
|
+
},
|
|
271
|
+
});
|
|
265
272
|
|
|
266
|
-
|
|
273
|
+
return Promise.resolve(new Token({scope, ...this.supertoken.serialize()}), {
|
|
274
|
+
parent: this,
|
|
267
275
|
});
|
|
276
|
+
});
|
|
268
277
|
},
|
|
269
278
|
|
|
270
279
|
/**
|
|
@@ -279,23 +288,24 @@ const Credentials = WebexPlugin.extend({
|
|
|
279
288
|
getClientToken(options = {}) {
|
|
280
289
|
this.logger.info('credentials: requesting client credentials grant');
|
|
281
290
|
|
|
282
|
-
return this.webex
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
291
|
+
return this.webex
|
|
292
|
+
.request({
|
|
293
|
+
/* eslint-disable camelcase */
|
|
294
|
+
method: 'POST',
|
|
295
|
+
uri: options.uri || this.config.tokenUrl,
|
|
296
|
+
form: {
|
|
297
|
+
grant_type: 'client_credentials',
|
|
298
|
+
scope: options.scope || 'webexsquare:admin',
|
|
299
|
+
self_contained_token: true,
|
|
300
|
+
},
|
|
301
|
+
auth: {
|
|
302
|
+
user: this.config.client_id,
|
|
303
|
+
pass: this.config.client_secret,
|
|
304
|
+
sendImmediately: true,
|
|
305
|
+
},
|
|
306
|
+
shouldRefreshAccessToken: false,
|
|
307
|
+
/* eslint-enable camelcase */
|
|
308
|
+
})
|
|
299
309
|
.then((res) => new Token(res.body, {parent: this}))
|
|
300
310
|
.catch((res) => {
|
|
301
311
|
if (res.statusCode !== 400) {
|
|
@@ -320,41 +330,44 @@ const Credentials = WebexPlugin.extend({
|
|
|
320
330
|
* @returns {Promise<Token>}
|
|
321
331
|
*/
|
|
322
332
|
getUserToken(scope) {
|
|
323
|
-
return Promise.resolve(
|
|
324
|
-
this.
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
333
|
+
return Promise.resolve(
|
|
334
|
+
!this.isRefreshing ||
|
|
335
|
+
new Promise((resolve) => {
|
|
336
|
+
this.logger.info(
|
|
337
|
+
'credentials: token refresh inflight; delaying getUserToken until refresh completes'
|
|
338
|
+
);
|
|
339
|
+
this.once('change:isRefreshing', () => {
|
|
340
|
+
this.logger.info('credentials: token refresh complete; reinvoking getUserToken');
|
|
341
|
+
resolve();
|
|
342
|
+
});
|
|
343
|
+
})
|
|
344
|
+
).then(() => {
|
|
345
|
+
if (!this.canAuthorize) {
|
|
346
|
+
this.logger.info('credentials: cannot produce an access token from current state');
|
|
347
|
+
|
|
348
|
+
return Promise.reject(new Error('Current state cannot produce an access token'));
|
|
349
|
+
}
|
|
336
350
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
351
|
+
if (!scope) {
|
|
352
|
+
scope = filterScope('spark:kms', this.supertoken.scope);
|
|
353
|
+
}
|
|
340
354
|
|
|
341
|
-
|
|
355
|
+
scope = sortScope(scope);
|
|
342
356
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
357
|
+
if (scope === sortScope(this.supertoken.scope)) {
|
|
358
|
+
return Promise.resolve(this.supertoken);
|
|
359
|
+
}
|
|
346
360
|
|
|
347
|
-
|
|
361
|
+
const token = this.userTokens.get(scope);
|
|
348
362
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
}
|
|
363
|
+
// we should also check for the token.access_token since token object does
|
|
364
|
+
// not get cleared on unsetting while logging out.
|
|
365
|
+
if (!token || !token.access_token) {
|
|
366
|
+
return this.downscope(scope).then(tap((t) => this.userTokens.add(t)));
|
|
367
|
+
}
|
|
355
368
|
|
|
356
|
-
|
|
357
|
-
|
|
369
|
+
return Promise.resolve(token);
|
|
370
|
+
});
|
|
358
371
|
},
|
|
359
372
|
|
|
360
373
|
@persist('@')
|
|
@@ -380,8 +393,7 @@ const Credentials = WebexPlugin.extend({
|
|
|
380
393
|
if (attrs.authorization) {
|
|
381
394
|
if (attrs.authorization.supertoken) {
|
|
382
395
|
this.supertoken = attrs.authorization.supertoken;
|
|
383
|
-
}
|
|
384
|
-
else {
|
|
396
|
+
} else {
|
|
385
397
|
this.supertoken = attrs.authorization;
|
|
386
398
|
}
|
|
387
399
|
}
|
|
@@ -434,16 +446,14 @@ const Credentials = WebexPlugin.extend({
|
|
|
434
446
|
|
|
435
447
|
try {
|
|
436
448
|
this.unset('supertoken');
|
|
437
|
-
}
|
|
438
|
-
catch (err) {
|
|
449
|
+
} catch (err) {
|
|
439
450
|
this.logger.warn('credentials: failed to clear supertoken', err);
|
|
440
451
|
}
|
|
441
452
|
|
|
442
453
|
while (this.userTokens.models.length) {
|
|
443
454
|
try {
|
|
444
455
|
this.userTokens.remove(this.userTokens.models[0]);
|
|
445
|
-
}
|
|
446
|
-
catch (err) {
|
|
456
|
+
} catch (err) {
|
|
447
457
|
this.logger.warn('credentials: failed to remove user token', err);
|
|
448
458
|
}
|
|
449
459
|
}
|
|
@@ -480,52 +490,29 @@ const Credentials = WebexPlugin.extend({
|
|
|
480
490
|
// while I like #2 from a code simplicity standpoint, the third-party DX
|
|
481
491
|
// isn't great
|
|
482
492
|
if (this.config.jwtRefreshCallback) {
|
|
483
|
-
return
|
|
484
|
-
|
|
493
|
+
return (
|
|
494
|
+
this.config
|
|
495
|
+
.jwtRefreshCallback(this.webex)
|
|
496
|
+
// eslint-disable-next-line no-shadow
|
|
497
|
+
.then((jwt) => this.webex.authorization.requestAccessTokenFromJwt({jwt}))
|
|
498
|
+
);
|
|
485
499
|
}
|
|
486
500
|
|
|
487
501
|
if (this.webex.internal.services) {
|
|
488
502
|
this.webex.internal.services.updateCredentialsConfig();
|
|
489
503
|
}
|
|
490
504
|
|
|
491
|
-
return supertoken
|
|
492
|
-
.
|
|
493
|
-
// clear refresh timer
|
|
494
|
-
if (this.refreshTimer) {
|
|
495
|
-
clearTimeout(this.refreshTimer);
|
|
496
|
-
this.unset('refreshTimer');
|
|
497
|
-
}
|
|
498
|
-
this.supertoken = st;
|
|
499
|
-
|
|
500
|
-
return Promise.all(tokens.map((token) => this.downscope(token.scope)
|
|
501
|
-
// eslint-disable-next-line max-nested-callbacks
|
|
502
|
-
.then((t) => {
|
|
503
|
-
this.logger.info(`credentials: revoking token for ${token.scope}`);
|
|
504
|
-
|
|
505
|
-
return token.revoke()
|
|
506
|
-
.catch((err) => {
|
|
507
|
-
this.logger.warn('credentials: failed to revoke user token', err);
|
|
508
|
-
})
|
|
509
|
-
.then(() => {
|
|
510
|
-
this.userTokens.remove(token.scope);
|
|
511
|
-
this.userTokens.add(t);
|
|
512
|
-
});
|
|
513
|
-
})));
|
|
514
|
-
})
|
|
515
|
-
.then(() => {
|
|
516
|
-
this.scheduleRefresh(this.supertoken.expires);
|
|
517
|
-
})
|
|
505
|
+
return supertoken
|
|
506
|
+
.refresh()
|
|
518
507
|
.catch((error) => {
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
// Error: The refresh token provided is expired, revoked, malformed, or invalid. Hence emit an event to the client, an opportunity to logout.
|
|
508
|
+
if (error instanceof OAuthError) {
|
|
509
|
+
// Error: super token refresh failed with 400 status code.
|
|
510
|
+
// Hence emit an event to the client, an opportunity to logout.
|
|
523
511
|
this.unset('supertoken');
|
|
524
512
|
while (this.userTokens.models.length) {
|
|
525
513
|
try {
|
|
526
514
|
this.userTokens.remove(this.userTokens.models[0]);
|
|
527
|
-
}
|
|
528
|
-
catch (err) {
|
|
515
|
+
} catch (err) {
|
|
529
516
|
this.logger.warn('credentials: failed to remove user token', err);
|
|
530
517
|
}
|
|
531
518
|
}
|
|
@@ -533,6 +520,53 @@ const Credentials = WebexPlugin.extend({
|
|
|
533
520
|
}
|
|
534
521
|
|
|
535
522
|
return Promise.reject(error);
|
|
523
|
+
})
|
|
524
|
+
.then((st) => {
|
|
525
|
+
// clear refresh timer
|
|
526
|
+
if (this.refreshTimer) {
|
|
527
|
+
clearTimeout(this.refreshTimer);
|
|
528
|
+
this.unset('refreshTimer');
|
|
529
|
+
}
|
|
530
|
+
this.supertoken = st;
|
|
531
|
+
|
|
532
|
+
const invalidScopes = diffScopes(this.config.scope, st.scope);
|
|
533
|
+
|
|
534
|
+
if (invalidScopes !== '') {
|
|
535
|
+
this.logger.warn(
|
|
536
|
+
`credentials: "${invalidScopes}" scope(s) are invalid because not listed in the supertoken, they will be excluded from user token requests.`
|
|
537
|
+
);
|
|
538
|
+
this.webex.internal.metrics.submitClientMetrics(
|
|
539
|
+
METRICS.JS_SDK_CREDENTIALS_TOKEN_REFRESH_SCOPE_MISMATCH,
|
|
540
|
+
{fields: {invalidScopes}}
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
return Promise.all(
|
|
545
|
+
tokens.map((token) => {
|
|
546
|
+
const tokenScope = filterScope(diffScopes(token.scope, st.scope), token.scope);
|
|
547
|
+
|
|
548
|
+
return (
|
|
549
|
+
this.downscope(tokenScope)
|
|
550
|
+
// eslint-disable-next-line max-nested-callbacks
|
|
551
|
+
.then((t) => {
|
|
552
|
+
this.logger.info(`credentials: revoking token for ${token.scope}`);
|
|
553
|
+
|
|
554
|
+
return token
|
|
555
|
+
.revoke()
|
|
556
|
+
.catch((err) => {
|
|
557
|
+
this.logger.warn('credentials: failed to revoke user token', err);
|
|
558
|
+
})
|
|
559
|
+
.then(() => {
|
|
560
|
+
this.userTokens.remove(token.scope);
|
|
561
|
+
this.userTokens.add(t);
|
|
562
|
+
});
|
|
563
|
+
})
|
|
564
|
+
);
|
|
565
|
+
})
|
|
566
|
+
);
|
|
567
|
+
})
|
|
568
|
+
.then(() => {
|
|
569
|
+
this.scheduleRefresh(this.supertoken.expires);
|
|
536
570
|
});
|
|
537
571
|
},
|
|
538
572
|
|
|
@@ -551,12 +585,10 @@ const Credentials = WebexPlugin.extend({
|
|
|
551
585
|
const timeoutLength = this.calcRefreshTimeout(expiresIn);
|
|
552
586
|
|
|
553
587
|
this.refreshTimer = safeSetTimeout(() => this.refresh(), timeoutLength);
|
|
554
|
-
}
|
|
555
|
-
else {
|
|
588
|
+
} else {
|
|
556
589
|
this.refresh();
|
|
557
590
|
}
|
|
558
|
-
}
|
|
559
|
-
|
|
591
|
+
},
|
|
560
592
|
});
|
|
561
593
|
|
|
562
594
|
export default Credentials;
|
|
@@ -21,20 +21,20 @@ export class OAuthError extends Exception {
|
|
|
21
21
|
Object.defineProperties(this, {
|
|
22
22
|
error: {
|
|
23
23
|
enumerable: true,
|
|
24
|
-
value: body.error
|
|
24
|
+
value: body.error,
|
|
25
25
|
},
|
|
26
26
|
errorDescription: {
|
|
27
27
|
enumerable: true,
|
|
28
|
-
value: body.error_description
|
|
28
|
+
value: body.error_description,
|
|
29
29
|
},
|
|
30
30
|
errorUri: {
|
|
31
31
|
enumerable: true,
|
|
32
|
-
value: body.error_uri
|
|
32
|
+
value: body.error_uri,
|
|
33
33
|
},
|
|
34
34
|
res: {
|
|
35
35
|
enumerable: false,
|
|
36
|
-
value: res
|
|
37
|
-
}
|
|
36
|
+
value: res,
|
|
37
|
+
},
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
return this.errorDescription;
|
|
@@ -70,7 +70,6 @@ class UnsupportGrantTypeError extends OAuthError {}
|
|
|
70
70
|
*/
|
|
71
71
|
class InvalidScopeError extends OAuthError {}
|
|
72
72
|
|
|
73
|
-
|
|
74
73
|
const errors = {
|
|
75
74
|
OAuthError,
|
|
76
75
|
InvalidRequestError,
|
|
@@ -87,7 +86,7 @@ const errors = {
|
|
|
87
86
|
invalid_scope: InvalidScopeError,
|
|
88
87
|
select(errorString) {
|
|
89
88
|
return errors[errorString] || OAuthError;
|
|
90
|
-
}
|
|
89
|
+
},
|
|
91
90
|
};
|
|
92
91
|
|
|
93
92
|
export default errors;
|
|
@@ -7,10 +7,7 @@ import {registerPlugin} from '../../webex-core';
|
|
|
7
7
|
import Credentials from './credentials';
|
|
8
8
|
|
|
9
9
|
registerPlugin('credentials', Credentials, {
|
|
10
|
-
proxies: [
|
|
11
|
-
'canAuthorize',
|
|
12
|
-
'canRefresh'
|
|
13
|
-
]
|
|
10
|
+
proxies: ['canAuthorize', 'canRefresh'],
|
|
14
11
|
});
|
|
15
12
|
|
|
16
13
|
export {default as Credentials} from './credentials';
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
* Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
import {difference} from 'lodash';
|
|
6
|
+
|
|
7
|
+
const SCOPE_SEPARATOR = ' ';
|
|
8
|
+
|
|
5
9
|
/**
|
|
6
10
|
* sorts a list of scopes
|
|
7
11
|
* @param {string} scope
|
|
@@ -12,15 +16,12 @@ export function sortScope(scope) {
|
|
|
12
16
|
return '';
|
|
13
17
|
}
|
|
14
18
|
|
|
15
|
-
return scope
|
|
16
|
-
.split(' ')
|
|
17
|
-
.sort()
|
|
18
|
-
.join(' ');
|
|
19
|
+
return scope.split(SCOPE_SEPARATOR).sort().join(SCOPE_SEPARATOR);
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
23
|
* sorts a list of scopes and filters the specified scope
|
|
23
|
-
* @param {string} toFilter
|
|
24
|
+
* @param {string|string[]} toFilter
|
|
24
25
|
* @param {string} scope
|
|
25
26
|
* @returns {string}
|
|
26
27
|
*/
|
|
@@ -28,10 +29,25 @@ export function filterScope(toFilter, scope) {
|
|
|
28
29
|
if (!scope) {
|
|
29
30
|
return '';
|
|
30
31
|
}
|
|
32
|
+
const toFilterArr = Array.isArray(toFilter) ? toFilter : [toFilter];
|
|
31
33
|
|
|
32
34
|
return scope
|
|
33
|
-
.split(
|
|
34
|
-
.filter((item) => item
|
|
35
|
+
.split(SCOPE_SEPARATOR)
|
|
36
|
+
.filter((item) => !toFilterArr.includes(item))
|
|
35
37
|
.sort()
|
|
36
|
-
.join(
|
|
38
|
+
.join(SCOPE_SEPARATOR);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Returns a string containing all items in scopeA that are not in scopeB, or an empty string if there are none.
|
|
43
|
+
*
|
|
44
|
+
* @param {string} scopeA
|
|
45
|
+
* @param {string} scopeB
|
|
46
|
+
* @returns {string}
|
|
47
|
+
*/
|
|
48
|
+
export function diffScopes(scopeA, scopeB) {
|
|
49
|
+
const a = scopeA?.split(SCOPE_SEPARATOR) ?? [];
|
|
50
|
+
const b = scopeB?.split(SCOPE_SEPARATOR) ?? [];
|
|
51
|
+
|
|
52
|
+
return difference(a, b).sort().join(SCOPE_SEPARATOR);
|
|
37
53
|
}
|