@webex/plugin-authorization-browser-first-party 3.0.0-beta.8 → 3.0.0-bnr.0

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.
@@ -19,28 +19,28 @@ import Authorization from '@webex/plugin-authorization-browser-first-party';
19
19
  // Necessary to require lodash this way in order to stub the method
20
20
  const lodash = require('lodash');
21
21
 
22
-
23
22
  browserOnly(describe)('plugin-authorization-browser-first-party', () => {
24
23
  describe('Authorization', () => {
25
- function makeWebex(href = 'https://example.com', csrfToken = undefined, pkceVerifier = undefined, config = {}) {
24
+ function makeWebex(
25
+ href = 'https://example.com',
26
+ csrfToken = undefined,
27
+ pkceVerifier = undefined,
28
+ config = {}
29
+ ) {
26
30
  const mockWindow = {
27
31
  history: {
28
32
  replaceState(a, b, location) {
29
33
  mockWindow.location.href = location;
30
- }
34
+ },
31
35
  },
32
36
  location: {
33
- href
37
+ href,
34
38
  },
35
39
  sessionStorage: {
36
- getItem: sinon.stub()
37
- .onCall(0)
38
- .returns(pkceVerifier)
39
- .onCall(1)
40
- .returns(csrfToken),
40
+ getItem: sinon.stub().onCall(0).returns(pkceVerifier).onCall(1).returns(csrfToken),
41
41
  removeItem: sinon.spy(),
42
- setItem: sinon.spy()
43
- }
42
+ setItem: sinon.spy(),
43
+ },
44
44
  };
45
45
 
46
46
  sinon.spy(mockWindow.history, 'replaceState');
@@ -49,37 +49,52 @@ browserOnly(describe)('plugin-authorization-browser-first-party', () => {
49
49
  children: {
50
50
  authorization: Authorization,
51
51
  credentials: Credentials,
52
- services: Services
52
+ services: Services,
53
53
  },
54
- request: sinon.stub().returns(Promise.resolve({body: {access_token: 'AT', token_type: 'Fake', refresh_token: 'RT'}})),
55
- config: merge({
56
- credentials: {
57
- idbroker: {
58
- url: process.env.IDBROKER_BASE_URL,
59
- defaultUrl: process.env.IDBROKER_BASE_URL
60
- },
61
- identity: {
62
- url: process.env.IDENTITY_BASE_URL,
63
- defaultUrl: process.env.IDENTITY_BASE_URL
54
+ request: sinon
55
+ .stub()
56
+ .returns(
57
+ Promise.resolve({body: {access_token: 'AT', token_type: 'Fake', refresh_token: 'RT'}})
58
+ ),
59
+ config: merge(
60
+ {
61
+ credentials: {
62
+ idbroker: {
63
+ url: process.env.IDBROKER_BASE_URL,
64
+ defaultUrl: process.env.IDBROKER_BASE_URL,
65
+ },
66
+ identity: {
67
+ url: process.env.IDENTITY_BASE_URL,
68
+ defaultUrl: process.env.IDENTITY_BASE_URL,
69
+ },
70
+ activationUrl: `${
71
+ process.env.IDBROKER_BASE_URL || 'https://idbroker.webex.com'
72
+ }/idb/token/v1/actions/UserActivation/invoke`,
73
+ authorizeUrl: `${
74
+ process.env.IDBROKER_BASE_URL || 'https://idbroker.webex.com'
75
+ }/idb/oauth2/v1/authorize`,
76
+ setPasswordUrl: `${
77
+ process.env.IDBROKER_BASE_URL || 'https://identity.webex.com'
78
+ }/identity/scim/v1/Users`,
79
+ logoutUrl: `${
80
+ process.env.IDBROKER_BASE_URL || 'https://idbroker.webex.com'
81
+ }/idb/oauth2/v1/logout`,
82
+ // eslint-disable-next-line camelcase
83
+ client_id: 'fake',
84
+ // eslint-disable-next-line camelcase
85
+ client_secret: 'fake',
86
+ // eslint-disable-next-line camelcase
87
+ redirect_uri: 'http://example.com',
88
+ // eslint-disable-next-line camelcase
89
+ scope: 'scope:one',
90
+ refreshCallback: () => Promise.resolve(),
64
91
  },
65
- activationUrl: `${process.env.IDBROKER_BASE_URL || 'https://idbroker.webex.com'}/idb/token/v1/actions/UserActivation/invoke`,
66
- authorizeUrl: `${process.env.IDBROKER_BASE_URL || 'https://idbroker.webex.com'}/idb/oauth2/v1/authorize`,
67
- setPasswordUrl: `${process.env.IDBROKER_BASE_URL || 'https://identity.webex.com'}/identity/scim/v1/Users`,
68
- logoutUrl: `${process.env.IDBROKER_BASE_URL || 'https://idbroker.webex.com'}/idb/oauth2/v1/logout`,
69
- // eslint-disable-next-line camelcase
70
- client_id: 'fake',
71
- // eslint-disable-next-line camelcase
72
- client_secret: 'fake',
73
- // eslint-disable-next-line camelcase
74
- redirect_uri: 'http://example.com',
75
- // eslint-disable-next-line camelcase
76
- scope: 'scope:one',
77
- refreshCallback: () => Promise.resolve()
78
- }
79
- }, config),
92
+ },
93
+ config
94
+ ),
80
95
  getWindow() {
81
96
  return mockWindow;
82
- }
97
+ },
83
98
  });
84
99
 
85
100
  return webex;
@@ -93,15 +108,14 @@ browserOnly(describe)('plugin-authorization-browser-first-party', () => {
93
108
  assert.isFalse(webex.authorization.ready);
94
109
  assert.isFalse(webex.credentials.canAuthorize);
95
110
 
96
- return webex.authorization.when('change:ready')
97
- .then(() => {
98
- // Webex request gets called twice:
99
- // once for the pre-auth catalog
100
- // once for auth token exchange
101
- assert.calledTwice(webex.request);
102
- assert.isTrue(webex.authorization.ready);
103
- assert.isTrue(webex.credentials.canAuthorize);
104
- });
111
+ return webex.authorization.when('change:ready').then(() => {
112
+ // Webex request gets called twice:
113
+ // once for the pre-auth catalog
114
+ // once for auth token exchange
115
+ assert.calledTwice(webex.request);
116
+ assert.isTrue(webex.authorization.ready);
117
+ assert.isTrue(webex.credentials.canAuthorize);
118
+ });
105
119
  });
106
120
 
107
121
  it('validates the csrf token', () => {
@@ -109,12 +123,20 @@ browserOnly(describe)('plugin-authorization-browser-first-party', () => {
109
123
 
110
124
  assert.throws(() => {
111
125
  // eslint-disable-next-line no-unused-vars
112
- const webex = makeWebex(`http://example.com/?code=5&state=${base64.encode(JSON.stringify({csrf_token: 'someothertoken'}))}`, csrfToken);
126
+ const webex = makeWebex(
127
+ `http://example.com/?code=5&state=${base64.encode(
128
+ JSON.stringify({csrf_token: 'someothertoken'})
129
+ )}`,
130
+ csrfToken
131
+ );
113
132
  }, /CSRF token someothertoken does not match stored token abcd/);
114
133
 
115
134
  assert.throws(() => {
116
135
  // eslint-disable-next-line no-unused-vars
117
- const webex = makeWebex(`http://example.com/?code=5&state=${base64.encode(JSON.stringify({}))}`, csrfToken);
136
+ const webex = makeWebex(
137
+ `http://example.com/?code=5&state=${base64.encode(JSON.stringify({}))}`,
138
+ csrfToken
139
+ );
118
140
  }, /Expected CSRF token abcd, but not found in redirect query/);
119
141
 
120
142
  assert.throws(() => {
@@ -122,33 +144,46 @@ browserOnly(describe)('plugin-authorization-browser-first-party', () => {
122
144
  const webex = makeWebex('http://example.com/?code=5', csrfToken);
123
145
  }, /Expected CSRF token abcd, but not found in redirect query/);
124
146
 
125
- const webex = makeWebex(`http://example.com/?code=5&state=${base64.encode(JSON.stringify({csrf_token: csrfToken}))}`, csrfToken);
147
+ const webex = makeWebex(
148
+ `http://example.com/?code=5&state=${base64.encode(
149
+ JSON.stringify({csrf_token: csrfToken})
150
+ )}`,
151
+ csrfToken
152
+ );
126
153
 
127
- return webex.authorization.when('change:ready')
128
- .then(() => {
129
- assert.isTrue(webex.credentials.canAuthorize);
130
- assert.called(webex.getWindow().sessionStorage.removeItem);
131
- });
154
+ return webex.authorization.when('change:ready').then(() => {
155
+ assert.isTrue(webex.credentials.canAuthorize);
156
+ assert.called(webex.getWindow().sessionStorage.removeItem);
157
+ });
132
158
  });
133
159
 
134
160
  it('removes the oauth parameters from the url', () => {
135
161
  const csrfToken = 'abcd';
136
162
 
137
- const webex = makeWebex(`http://example.com/?code=5&state=${base64.encode(JSON.stringify({csrf_token: csrfToken, something: true}))}`, csrfToken);
163
+ const webex = makeWebex(
164
+ `http://example.com/?code=5&state=${base64.encode(
165
+ JSON.stringify({csrf_token: csrfToken, something: true})
166
+ )}`,
167
+ csrfToken
168
+ );
138
169
 
139
- return webex.authorization.when('change:ready')
140
- .then(() => {
141
- assert.isTrue(webex.credentials.canAuthorize);
142
- assert.called(webex.getWindow().sessionStorage.removeItem);
143
- assert.called(webex.getWindow().history.replaceState);
144
- assert.equal(webex.getWindow().location.href, `http://example.com/?state=${base64.encode(JSON.stringify({something: true}))}`);
145
- });
170
+ return webex.authorization.when('change:ready').then(() => {
171
+ assert.isTrue(webex.credentials.canAuthorize);
172
+ assert.called(webex.getWindow().sessionStorage.removeItem);
173
+ assert.called(webex.getWindow().history.replaceState);
174
+ assert.equal(
175
+ webex.getWindow().location.href,
176
+ `http://example.com/?state=${base64.encode(JSON.stringify({something: true}))}`
177
+ );
178
+ });
146
179
  });
147
180
  });
148
181
  describe('when the url contains an error', () => {
149
182
  it('throws a grant error', () => {
150
183
  assert.throws(() => {
151
- makeWebex('http://127.0.0.1:8000/?error=invalid_scope&error_description=The%20requested%20scope%20is%20invalid.');
184
+ makeWebex(
185
+ 'http://127.0.0.1:8000/?error=invalid_scope&error_description=The%20requested%20scope%20is%20invalid.'
186
+ );
152
187
  }, /The requested scope is invalid./);
153
188
  });
154
189
  });
@@ -166,28 +201,14 @@ browserOnly(describe)('plugin-authorization-browser-first-party', () => {
166
201
  it('passes codeVerifier to requestAuthorizationCodeGrant', () => {
167
202
  const expectedVerifier = 'test verifier';
168
203
 
169
- const webex = makeWebex(
170
- 'http://example.com?code=5',
171
- undefined,
172
- expectedVerifier
173
- );
204
+ const webex = makeWebex('http://example.com?code=5', undefined, expectedVerifier);
174
205
 
175
- return webex.authorization.when('change:ready')
176
- .then(() => {
177
- assert.calledTwice(webex.request);
178
- assert.calledWith(
179
- webex.getWindow().sessionStorage.getItem,
180
- 'oauth2-code-verifier'
181
- );
182
- assert.calledWith(
183
- webex.getWindow().sessionStorage.removeItem,
184
- 'oauth2-code-verifier'
185
- );
186
- assert.equal(
187
- webex.request.getCall(1).args[0].form.code_verifier,
188
- expectedVerifier
189
- );
190
- });
206
+ return webex.authorization.when('change:ready').then(() => {
207
+ assert.calledTwice(webex.request);
208
+ assert.calledWith(webex.getWindow().sessionStorage.getItem, 'oauth2-code-verifier');
209
+ assert.calledWith(webex.getWindow().sessionStorage.removeItem, 'oauth2-code-verifier');
210
+ assert.equal(webex.request.getCall(1).args[0].form.code_verifier, expectedVerifier);
211
+ });
191
212
  });
192
213
  });
193
214
  });
@@ -196,95 +217,97 @@ browserOnly(describe)('plugin-authorization-browser-first-party', () => {
196
217
  it('calls #initiateAuthorizationCodeGrant()', () => {
197
218
  const webex = makeWebex(undefined, undefined, {
198
219
  credentials: {
199
- clientType: 'confidential'
200
- }
220
+ clientType: 'confidential',
221
+ },
201
222
  });
202
223
 
203
224
  sinon.spy(webex.authorization, 'initiateAuthorizationCodeGrant');
204
225
 
205
- return webex.authorization.initiateLogin()
206
- .then(() => {
207
- assert.called(webex.authorization.initiateAuthorizationCodeGrant);
208
- assert.include(webex.getWindow().location, 'response_type=code');
209
- });
226
+ return webex.authorization.initiateLogin().then(() => {
227
+ assert.called(webex.authorization.initiateAuthorizationCodeGrant);
228
+ assert.include(webex.getWindow().location, 'response_type=code');
229
+ });
210
230
  });
211
231
 
212
232
  it('adds a csrf_token to the login url and sessionStorage', () => {
213
233
  const webex = makeWebex(undefined, undefined, {
214
234
  credentials: {
215
- clientType: 'confidential'
216
- }
235
+ clientType: 'confidential',
236
+ },
217
237
  });
218
238
 
219
239
  sinon.spy(webex.authorization, 'initiateAuthorizationCodeGrant');
220
240
 
221
- return webex.authorization.initiateLogin()
222
- .then(() => {
223
- assert.called(webex.authorization.initiateAuthorizationCodeGrant);
224
- assert.include(webex.getWindow().location, 'response_type=code');
225
- const {query} = url.parse(webex.getWindow().location, true);
226
- let {state} = query;
227
-
228
- state = JSON.parse(base64.decode(state));
229
- assert.property(state, 'csrf_token');
230
- assert.isDefined(state.csrf_token);
231
- assert.match(state.csrf_token, patterns.uuid);
232
- assert.called(webex.getWindow().sessionStorage.setItem);
233
- assert.calledWith(webex.getWindow().sessionStorage.setItem, 'oauth2-csrf-token', state.csrf_token);
234
- });
241
+ return webex.authorization.initiateLogin().then(() => {
242
+ assert.called(webex.authorization.initiateAuthorizationCodeGrant);
243
+ assert.include(webex.getWindow().location, 'response_type=code');
244
+ const {query} = url.parse(webex.getWindow().location, true);
245
+ let {state} = query;
246
+
247
+ state = JSON.parse(base64.decode(state));
248
+ assert.property(state, 'csrf_token');
249
+ assert.isDefined(state.csrf_token);
250
+ assert.match(state.csrf_token, patterns.uuid);
251
+ assert.called(webex.getWindow().sessionStorage.setItem);
252
+ assert.calledWith(
253
+ webex.getWindow().sessionStorage.setItem,
254
+ 'oauth2-csrf-token',
255
+ state.csrf_token
256
+ );
257
+ });
235
258
  });
236
259
 
237
260
  it('adds a pkce code challenge', () => {
238
261
  const webex = makeWebex(undefined, undefined, {
239
262
  credentials: {
240
- clientType: 'confidential'
241
- }
263
+ clientType: 'confidential',
264
+ },
242
265
  });
243
266
 
244
267
  const expectedCodeChallenge = 'test challenge';
245
268
 
246
269
  sinon.spy(webex.authorization, 'initiateAuthorizationCodeGrant');
247
- sinon.stub(webex.authorization, '_generateCodeChallenge')
248
- .returns(expectedCodeChallenge);
249
-
250
- return webex.authorization.initiateLogin()
251
- .then(() => {
252
- assert.called(webex.authorization.initiateAuthorizationCodeGrant);
253
- const grantOptions = webex.authorization.initiateAuthorizationCodeGrant.getCall(0).args[0];
254
-
255
- assert.equal(grantOptions.code_challenge, expectedCodeChallenge);
256
- assert.equal(grantOptions.code_challenge_method, 'S256');
257
- // eslint-disable-next-line no-underscore-dangle
258
- assert.calledWith(webex.authorization._generateCodeChallenge);
259
- });
270
+ sinon.stub(webex.authorization, '_generateCodeChallenge').returns(expectedCodeChallenge);
271
+
272
+ return webex.authorization.initiateLogin().then(() => {
273
+ assert.called(webex.authorization.initiateAuthorizationCodeGrant);
274
+ const grantOptions =
275
+ webex.authorization.initiateAuthorizationCodeGrant.getCall(0).args[0];
276
+
277
+ assert.equal(grantOptions.code_challenge, expectedCodeChallenge);
278
+ assert.equal(grantOptions.code_challenge_method, 'S256');
279
+ // eslint-disable-next-line no-underscore-dangle
280
+ assert.calledWith(webex.authorization._generateCodeChallenge);
281
+ });
260
282
  });
261
283
 
262
284
  it('adds emailHash', () => {
263
285
  const webex = makeWebex(undefined, undefined, {
264
286
  credentials: {
265
- clientType: 'confidential'
266
- }
287
+ clientType: 'confidential',
288
+ },
267
289
  });
268
290
 
269
- const expectedEmailHash = '73062d872926c2a556f17b36f50e328ddf9bff9d403939bd14b6c3b7f5a33fc2';
291
+ const expectedEmailHash =
292
+ '73062d872926c2a556f17b36f50e328ddf9bff9d403939bd14b6c3b7f5a33fc2';
270
293
 
271
294
  sinon.spy(webex.authorization, 'initiateAuthorizationCodeGrant');
272
295
 
273
- return webex.authorization.initiateLogin({email: 'test@email.com'})
274
- .then(() => {
275
- assert.called(webex.authorization.initiateAuthorizationCodeGrant);
276
- const grantOptions = webex.authorization.initiateAuthorizationCodeGrant.getCall(0).args[0];
296
+ return webex.authorization.initiateLogin({email: 'test@email.com'}).then(() => {
297
+ assert.called(webex.authorization.initiateAuthorizationCodeGrant);
298
+ const grantOptions =
299
+ webex.authorization.initiateAuthorizationCodeGrant.getCall(0).args[0];
277
300
 
278
- assert.equal(grantOptions.emailHash, expectedEmailHash);
279
- assert.isUndefined(grantOptions.email);
280
- });
301
+ assert.equal(grantOptions.emailHash, expectedEmailHash);
302
+ assert.isUndefined(grantOptions.email);
303
+ });
281
304
  });
282
305
 
283
306
  it('sets #isAuthorizing', () => {
284
307
  const webex = makeWebex(undefined, undefined, {
285
308
  credentials: {
286
- clientType: 'confidential'
287
- }
309
+ clientType: 'confidential',
310
+ },
288
311
  });
289
312
 
290
313
  assert.isFalse(webex.authorization.isAuthorizing);
@@ -298,8 +321,8 @@ browserOnly(describe)('plugin-authorization-browser-first-party', () => {
298
321
  it('sets #isAuthenticating', () => {
299
322
  const webex = makeWebex(undefined, undefined, {
300
323
  credentials: {
301
- clientType: 'confidential'
302
- }
324
+ clientType: 'confidential',
325
+ },
303
326
  });
304
327
 
305
328
  assert.isFalse(webex.authorization.isAuthenticating);
@@ -315,17 +338,16 @@ browserOnly(describe)('plugin-authorization-browser-first-party', () => {
315
338
  it('redirects to the login page with response_type=code', () => {
316
339
  const webex = makeWebex(undefined, undefined, {
317
340
  credentials: {
318
- clientType: 'confidential'
319
- }
341
+ clientType: 'confidential',
342
+ },
320
343
  });
321
344
 
322
345
  sinon.spy(webex.authorization, 'initiateAuthorizationCodeGrant');
323
346
 
324
- return webex.authorization.initiateLogin()
325
- .then(() => {
326
- assert.called(webex.authorization.initiateAuthorizationCodeGrant);
327
- assert.include(webex.getWindow().location, 'response_type=code');
328
- });
347
+ return webex.authorization.initiateLogin().then(() => {
348
+ assert.called(webex.authorization.initiateAuthorizationCodeGrant);
349
+ assert.include(webex.getWindow().location, 'response_type=code');
350
+ });
329
351
  });
330
352
  });
331
353
 
@@ -342,7 +364,7 @@ browserOnly(describe)('plugin-authorization-browser-first-party', () => {
342
364
  const toStringStub = sinon.stub().returns(expectedCodeChallenge);
343
365
  const randomStub = sinon.stub(lodash, 'random').returns(0);
344
366
  const sha256Stub = sinon.stub(CryptoJS, 'SHA256').returns({
345
- toString: toStringStub
367
+ toString: toStringStub,
346
368
  });
347
369
 
348
370
  // eslint-disable-next-line no-underscore-dangle
@@ -365,16 +387,16 @@ browserOnly(describe)('plugin-authorization-browser-first-party', () => {
365
387
  it('removes the state parameter when it has no keys', () => {
366
388
  const webex = makeWebex(undefined, undefined, {
367
389
  credentials: {
368
- clientType: 'confidential'
369
- }
390
+ clientType: 'confidential',
391
+ },
370
392
  });
371
393
  const location = {
372
394
  query: {
373
395
  code: 'code',
374
396
  state: {
375
- csrf_token: 'token'
376
- }
377
- }
397
+ csrf_token: 'token',
398
+ },
399
+ },
378
400
  };
379
401
 
380
402
  sinon.spy(webex.authorization, '_cleanUrl');
@@ -386,17 +408,17 @@ browserOnly(describe)('plugin-authorization-browser-first-party', () => {
386
408
  it('keeps the parameter when it has keys', () => {
387
409
  const webex = makeWebex(undefined, undefined, {
388
410
  credentials: {
389
- clientType: 'confidential'
390
- }
411
+ clientType: 'confidential',
412
+ },
391
413
  });
392
414
  const location = {
393
415
  query: {
394
416
  code: 'code',
395
417
  state: {
396
418
  csrf_token: 'token',
397
- key: 'value'
398
- }
399
- }
419
+ key: 'value',
420
+ },
421
+ },
400
422
  };
401
423
 
402
424
  sinon.spy(webex.authorization, '_cleanUrl');