@webex/plugin-authorization-browser 3.8.0 → 3.8.1-next.2
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/BROWSER-OAUTH-FLOW-GUIDE.md +541 -0
- package/README.md +309 -22
- package/dist/authorization.js +61 -15
- package/dist/authorization.js.map +1 -1
- package/package.json +13 -13
- package/src/authorization.js +61 -16
- package/test/unit/spec/authorization.js +59 -0
package/package.json
CHANGED
|
@@ -24,23 +24,23 @@
|
|
|
24
24
|
"@webex/eslint-config-legacy": "0.0.0",
|
|
25
25
|
"@webex/jest-config-legacy": "0.0.0",
|
|
26
26
|
"@webex/legacy-tools": "0.0.0",
|
|
27
|
-
"@webex/test-helper-appid": "3.8.
|
|
28
|
-
"@webex/test-helper-automation": "3.8.
|
|
29
|
-
"@webex/test-helper-chai": "3.8.
|
|
30
|
-
"@webex/test-helper-mocha": "3.8.
|
|
31
|
-
"@webex/test-helper-mock-webex": "3.8.
|
|
32
|
-
"@webex/test-helper-test-users": "3.8.
|
|
27
|
+
"@webex/test-helper-appid": "3.8.1-next.2",
|
|
28
|
+
"@webex/test-helper-automation": "3.8.1-next.2",
|
|
29
|
+
"@webex/test-helper-chai": "3.8.1-next.2",
|
|
30
|
+
"@webex/test-helper-mocha": "3.8.1-next.2",
|
|
31
|
+
"@webex/test-helper-mock-webex": "3.8.1-next.2",
|
|
32
|
+
"@webex/test-helper-test-users": "3.8.1-next.2",
|
|
33
33
|
"eslint": "^8.24.0",
|
|
34
34
|
"prettier": "^2.7.1",
|
|
35
35
|
"sinon": "^9.2.4"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@webex/common": "3.8.
|
|
39
|
-
"@webex/internal-plugin-device": "3.8.
|
|
40
|
-
"@webex/plugin-authorization-node": "3.8.
|
|
41
|
-
"@webex/storage-adapter-local-storage": "3.8.
|
|
42
|
-
"@webex/storage-adapter-spec": "3.8.
|
|
43
|
-
"@webex/webex-core": "3.8.
|
|
38
|
+
"@webex/common": "3.8.1-next.2",
|
|
39
|
+
"@webex/internal-plugin-device": "3.8.1-next.2",
|
|
40
|
+
"@webex/plugin-authorization-node": "3.8.1-next.2",
|
|
41
|
+
"@webex/storage-adapter-local-storage": "3.8.1-next.2",
|
|
42
|
+
"@webex/storage-adapter-spec": "3.8.1-next.2",
|
|
43
|
+
"@webex/webex-core": "3.8.1-next.2",
|
|
44
44
|
"jsonwebtoken": "^9.0.2",
|
|
45
45
|
"lodash": "^4.17.21",
|
|
46
46
|
"uuid": "^3.3.2"
|
|
@@ -54,5 +54,5 @@
|
|
|
54
54
|
"test:style": "eslint ./src/**/*.*",
|
|
55
55
|
"test:unit": "webex-legacy-tools test --unit --runner jest"
|
|
56
56
|
},
|
|
57
|
-
"version": "3.8.
|
|
57
|
+
"version": "3.8.1-next.2"
|
|
58
58
|
}
|
package/src/authorization.js
CHANGED
|
@@ -113,13 +113,26 @@ const Authorization = WebexPlugin.extend({
|
|
|
113
113
|
return ret;
|
|
114
114
|
},
|
|
115
115
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
116
|
+
/**
|
|
117
|
+
* Initiates the OAuth flow for user authentication.
|
|
118
|
+
* This function determines the type of OAuth flow to use based on the client type configuration.
|
|
119
|
+
* If the client is configured as "confidential", it will initiate the Authorization Code Grant flow;
|
|
120
|
+
* otherwise, it will initiate the Implicit Grant flow.
|
|
121
|
+
*
|
|
122
|
+
* @instance
|
|
123
|
+
* @memberof AuthorizationBrowser
|
|
124
|
+
* @param {Object} options - The options to configure the OAuth flow.
|
|
125
|
+
* @param {Object} [options.state] - An optional state object that can be used to include additional
|
|
126
|
+
* information such as security tokens. A CSRF token will be automatically generated and added to
|
|
127
|
+
* this state object.
|
|
128
|
+
* @param {boolean|Object} [options.separateWindow] - Determines if the login should open in a separate window.
|
|
129
|
+
* This can be a boolean or an object specifying window features:
|
|
130
|
+
* - If `true`, a new window with default dimensions is opened.
|
|
131
|
+
* - If an object, custom window features can be specified (e.g., `{width: 800, height: 600}`).
|
|
132
|
+
* @returns {Promise<void>} - A promise that resolves when the appropriate OAuth flow has been initiated.
|
|
133
|
+
* The promise does not necessarily indicate the completion of the login process.
|
|
134
|
+
* @throws {Error} - Throws an error if there are issues initiating the OAuth flow.
|
|
135
|
+
*/
|
|
123
136
|
initiateLogin(options = {}) {
|
|
124
137
|
options.state = options.state || {};
|
|
125
138
|
options.state.csrf_token = this._generateSecurityToken();
|
|
@@ -134,20 +147,52 @@ const Authorization = WebexPlugin.extend({
|
|
|
134
147
|
},
|
|
135
148
|
|
|
136
149
|
@whileInFlight('isAuthorizing')
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
150
|
+
/**
|
|
151
|
+
* Initiates the Implicit Grant flow for authorization.
|
|
152
|
+
* This function constructs the login URL and either opens it in a new
|
|
153
|
+
* window or in the current window based on the provided options.
|
|
154
|
+
* Typically called via {@link AuthorizationBrowser#initiateLogin}.
|
|
155
|
+
*
|
|
156
|
+
* @instance
|
|
157
|
+
* @memberof AuthorizationBrowser
|
|
158
|
+
* @param {Object} options - The options to configure the login flow.
|
|
159
|
+
* @param {Object} [options.separateWindow] - Determines if the login should open in a separate window.
|
|
160
|
+
* This can be a boolean or an object specifying window features:
|
|
161
|
+
* - If `true`, a new window with default dimensions is opened.
|
|
162
|
+
* - If an object, custom window features can be specified (e.g., `{width: 800, height: 600}`).
|
|
163
|
+
* @returns {Promise<void>} - A promise that resolves immediately after initiating the login flow.
|
|
164
|
+
* This promise does not indicate the completion of the login process.
|
|
165
|
+
* @throws {Error} - Throws an error if the login URL cannot be constructed or if window opening fails.
|
|
166
|
+
*/
|
|
145
167
|
initiateImplicitGrant(options) {
|
|
168
|
+
|
|
146
169
|
this.logger.info('authorization: initiating implicit grant flow');
|
|
147
|
-
|
|
170
|
+
const loginUrl = this.webex.credentials.buildLoginUrl(
|
|
148
171
|
Object.assign({response_type: 'token'}, options)
|
|
149
172
|
);
|
|
150
173
|
|
|
174
|
+
if (options?.separateWindow) {
|
|
175
|
+
// Default window settings
|
|
176
|
+
const defaultWindowSettings = {
|
|
177
|
+
width: 600,
|
|
178
|
+
height: 800
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
// Merge user provided settings with defaults
|
|
182
|
+
const windowSettings = Object.assign(
|
|
183
|
+
defaultWindowSettings,
|
|
184
|
+
typeof options.separateWindow === 'object' ? options.separateWindow : {}
|
|
185
|
+
);
|
|
186
|
+
// Convert settings object to window.open features string
|
|
187
|
+
const windowFeatures = Object.entries(windowSettings)
|
|
188
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
189
|
+
.join(',');
|
|
190
|
+
this.webex.getWindow().open(loginUrl, '_blank', windowFeatures);
|
|
191
|
+
} else {
|
|
192
|
+
// Default behavior - open in same window
|
|
193
|
+
this.webex.getWindow().location = loginUrl;
|
|
194
|
+
}
|
|
195
|
+
|
|
151
196
|
return Promise.resolve();
|
|
152
197
|
},
|
|
153
198
|
|
|
@@ -408,6 +408,65 @@ describe('plugin-authorization-browser', () => {
|
|
|
408
408
|
assert.include(webex.getWindow().location, 'response_type=token');
|
|
409
409
|
});
|
|
410
410
|
});
|
|
411
|
+
|
|
412
|
+
it('redirects to the login page in the same window by default', () => {
|
|
413
|
+
const webex = makeWebexCore();
|
|
414
|
+
|
|
415
|
+
return webex.authorization.initiateImplicitGrant().then(() => {
|
|
416
|
+
assert.isDefined(webex.getWindow().location);
|
|
417
|
+
assert.isUndefined(webex.getWindow().open);
|
|
418
|
+
});
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
it('opens login page in a new window when separateWindow is true', () => {
|
|
422
|
+
const webex = makeWebexCore();
|
|
423
|
+
webex.getWindow().open = sinon.spy();
|
|
424
|
+
|
|
425
|
+
return webex.authorization.initiateImplicitGrant({ separateWindow: true }).then(() => {
|
|
426
|
+
assert.called(webex.getWindow().open);
|
|
427
|
+
const openCall = webex.getWindow().open.getCall(0);
|
|
428
|
+
assert.equal(openCall.args[1], '_blank');
|
|
429
|
+
assert.equal(openCall.args[2], 'width=600,height=800');
|
|
430
|
+
});
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
it('opens login page in a new window with custom dimensions', () => {
|
|
434
|
+
const webex = makeWebexCore();
|
|
435
|
+
webex.getWindow().open = sinon.spy();
|
|
436
|
+
|
|
437
|
+
const customWindow = {
|
|
438
|
+
width: 800,
|
|
439
|
+
height: 600,
|
|
440
|
+
menubar: 'no',
|
|
441
|
+
toolbar: 'no'
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
return webex.authorization.initiateImplicitGrant({
|
|
445
|
+
separateWindow: customWindow
|
|
446
|
+
}).then(() => {
|
|
447
|
+
assert.called(webex.getWindow().open);
|
|
448
|
+
const openCall = webex.getWindow().open.getCall(0);
|
|
449
|
+
assert.equal(openCall.args[1], '_blank');
|
|
450
|
+
assert.equal(
|
|
451
|
+
openCall.args[2],
|
|
452
|
+
'width=800,height=600,menubar=no,toolbar=no'
|
|
453
|
+
);
|
|
454
|
+
});
|
|
455
|
+
});
|
|
456
|
+
|
|
457
|
+
it('preserves other options when using separateWindow', () => {
|
|
458
|
+
const webex = makeWebexCore();
|
|
459
|
+
webex.getWindow().open = sinon.spy();
|
|
460
|
+
|
|
461
|
+
return webex.authorization.initiateImplicitGrant({
|
|
462
|
+
separateWindow: true,
|
|
463
|
+
state: {}
|
|
464
|
+
}).then(() => {
|
|
465
|
+
assert.called(webex.getWindow().open);
|
|
466
|
+
const url = webex.getWindow().open.getCall(0).args[0];
|
|
467
|
+
assert.include(url, "https://idbrokerbts.webex.com/idb/oauth2/v1/authorize?response_type=token&separateWindow=true&client_id=fake&redirect_uri=http%3A%2F%2Fexample.com&scope=scope%3Aone");
|
|
468
|
+
});
|
|
469
|
+
});
|
|
411
470
|
});
|
|
412
471
|
|
|
413
472
|
describe('#initiateAuthorizationCodeGrant()', () => {
|