@azure/msal-angular 3.0.0-beta.1 → 3.0.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.
Files changed (87) hide show
  1. package/.beachballrc +3 -0
  2. package/.editorconfig +16 -0
  3. package/CHANGELOG.json +1418 -0
  4. package/CHANGELOG.md +520 -0
  5. package/FAQ.md +175 -0
  6. package/LICENSE +20 -20
  7. package/README.md +157 -157
  8. package/angular.json +51 -0
  9. package/docs/angular-universal.md +60 -0
  10. package/docs/configuration.md +525 -0
  11. package/docs/errors.md +89 -0
  12. package/docs/events.md +262 -0
  13. package/docs/initialization.md +206 -0
  14. package/docs/known-issues.md +19 -0
  15. package/docs/logging.md +47 -0
  16. package/docs/msal-guard.md +208 -0
  17. package/docs/msal-interceptor.md +160 -0
  18. package/docs/multi-tenant.md +90 -0
  19. package/docs/performance.md +50 -0
  20. package/docs/public-apis.md +29 -0
  21. package/docs/redirects.md +214 -0
  22. package/docs/security.md +4 -0
  23. package/docs/ssosilent.md +84 -0
  24. package/docs/v0-v1-upgrade-guide.md +61 -0
  25. package/docs/v1-v2-upgrade-guide.md +70 -0
  26. package/docs/v2-v3-upgrade-guide.md +41 -0
  27. package/karma.conf.js +43 -0
  28. package/ng-package.json +7 -0
  29. package/package.json +55 -30
  30. package/src/IMsalService.ts +31 -0
  31. package/src/constants.ts +20 -0
  32. package/src/msal.broadcast.config.ts +8 -0
  33. package/src/msal.broadcast.service.spec.ts +456 -0
  34. package/src/msal.broadcast.service.ts +74 -0
  35. package/src/msal.guard.config.ts +27 -0
  36. package/src/msal.guard.spec.ts +525 -0
  37. package/src/msal.guard.ts +290 -0
  38. package/src/msal.interceptor.config.ts +44 -0
  39. package/src/msal.interceptor.spec.ts +1125 -0
  40. package/src/msal.interceptor.ts +390 -0
  41. package/src/msal.module.ts +51 -0
  42. package/src/msal.navigation.client.spec.ts +95 -0
  43. package/src/msal.navigation.client.ts +57 -0
  44. package/src/msal.redirect.component.spec.ts +63 -0
  45. package/src/msal.redirect.component.ts +26 -0
  46. package/src/msal.service.spec.ts +452 -0
  47. package/src/msal.service.ts +97 -0
  48. package/src/packageMetadata.ts +3 -0
  49. package/{public-api.d.ts → src/public-api.ts} +35 -17
  50. package/src/test.ts +19 -0
  51. package/tsconfig.json +25 -0
  52. package/tsconfig.lib.json +27 -0
  53. package/tsconfig.lib.prod.json +10 -0
  54. package/tsconfig.spec.json +17 -0
  55. package/IMsalService.d.ts +0 -15
  56. package/constants.d.ts +0 -5
  57. package/esm2020/IMsalService.mjs +0 -6
  58. package/esm2020/azure-msal-angular.mjs +0 -5
  59. package/esm2020/constants.mjs +0 -10
  60. package/esm2020/msal.broadcast.config.mjs +0 -6
  61. package/esm2020/msal.broadcast.service.mjs +0 -57
  62. package/esm2020/msal.guard.config.mjs +0 -6
  63. package/esm2020/msal.guard.mjs +0 -218
  64. package/esm2020/msal.interceptor.config.mjs +0 -6
  65. package/esm2020/msal.interceptor.mjs +0 -270
  66. package/esm2020/msal.module.mjs +0 -46
  67. package/esm2020/msal.navigation.client.mjs +0 -51
  68. package/esm2020/msal.redirect.component.mjs +0 -31
  69. package/esm2020/msal.service.mjs +0 -81
  70. package/esm2020/packageMetadata.mjs +0 -4
  71. package/esm2020/public-api.mjs +0 -18
  72. package/fesm2015/azure-msal-angular.mjs +0 -736
  73. package/fesm2015/azure-msal-angular.mjs.map +0 -1
  74. package/fesm2020/azure-msal-angular.mjs +0 -732
  75. package/fesm2020/azure-msal-angular.mjs.map +0 -1
  76. package/index.d.ts +0 -5
  77. package/msal.broadcast.config.d.ts +0 -3
  78. package/msal.broadcast.service.d.ts +0 -17
  79. package/msal.guard.config.d.ts +0 -9
  80. package/msal.guard.d.ts +0 -43
  81. package/msal.interceptor.config.d.ts +0 -17
  82. package/msal.interceptor.d.ts +0 -63
  83. package/msal.module.d.ts +0 -13
  84. package/msal.navigation.client.d.ts +0 -19
  85. package/msal.redirect.component.d.ts +0 -15
  86. package/msal.service.d.ts +0 -31
  87. package/packageMetadata.d.ts +0 -2
@@ -0,0 +1,525 @@
1
+ import { HttpClientTestingModule } from '@angular/common/http/testing';
2
+ import { TestBed } from '@angular/core/testing';
3
+ import { UrlTree } from '@angular/router';
4
+ import { RouterTestingModule } from '@angular/router/testing';
5
+ import { Location } from '@angular/common';
6
+ import {
7
+ BrowserSystemOptions,
8
+ BrowserUtils,
9
+ InteractionType,
10
+ IPublicClientApplication,
11
+ LogLevel,
12
+ PublicClientApplication,
13
+ UrlString,
14
+ } from '@azure/msal-browser';
15
+ import { of } from 'rxjs';
16
+ import {
17
+ MsalModule,
18
+ MsalGuard,
19
+ MsalService,
20
+ MsalBroadcastService,
21
+ } from './public-api';
22
+ import { MsalGuardConfiguration } from './msal.guard.config';
23
+
24
+ let guard: MsalGuard;
25
+ let authService: MsalService;
26
+ let routeMock: any = { snapshot: {} };
27
+ let routeStateMock: any = { snapshot: {}, url: '/' };
28
+ let testInteractionType: InteractionType;
29
+ let testLoginFailedRoute: string;
30
+ let testConfiguration: Partial<MsalGuardConfiguration>;
31
+ let browserSystemOptions: BrowserSystemOptions;
32
+
33
+ function MSALInstanceFactory(): IPublicClientApplication {
34
+ return new PublicClientApplication({
35
+ auth: {
36
+ clientId: '6226576d-37e9-49eb-b201-ec1eeb0029b6',
37
+ redirectUri: 'http://localhost:4200',
38
+ },
39
+ system: {
40
+ loggerOptions: {
41
+ loggerCallback: (level, message) => {
42
+ // console.log(message)
43
+ },
44
+ logLevel: LogLevel.Verbose,
45
+ piiLoggingEnabled: true,
46
+ },
47
+ },
48
+ });
49
+ }
50
+
51
+ function MSALGuardConfigFactory(): MsalGuardConfiguration {
52
+ return {
53
+ //@ts-ignore
54
+ interactionType: testInteractionType,
55
+ loginFailedRoute: testLoginFailedRoute,
56
+ authRequest: testConfiguration?.authRequest,
57
+ };
58
+ }
59
+
60
+ function initializeMsal(providers: any[] = []) {
61
+ TestBed.resetTestingModule();
62
+ TestBed.configureTestingModule({
63
+ imports: [
64
+ MsalModule.forRoot(MSALInstanceFactory(), MSALGuardConfigFactory(), {
65
+ interactionType: InteractionType.Popup,
66
+ protectedResourceMap: new Map(),
67
+ }),
68
+ HttpClientTestingModule,
69
+ RouterTestingModule.withRoutes([]),
70
+ ],
71
+ providers: [MsalGuard, MsalService, MsalBroadcastService, ...providers],
72
+ });
73
+
74
+ authService = TestBed.inject(MsalService);
75
+ guard = TestBed.inject(MsalGuard);
76
+ }
77
+
78
+ describe('MsalGuard', () => {
79
+ beforeEach(() => {
80
+ testInteractionType = InteractionType.Popup;
81
+ testLoginFailedRoute = undefined;
82
+ testConfiguration = {};
83
+ browserSystemOptions = {};
84
+ routeStateMock = { snapshot: {}, url: '/' };
85
+ initializeMsal();
86
+ });
87
+
88
+ it('is created', () => {
89
+ expect(guard).toBeTruthy();
90
+ });
91
+
92
+ it('throws error for silent interaction type', (done) => {
93
+ testInteractionType = InteractionType.Silent;
94
+ initializeMsal();
95
+ try {
96
+ guard.canActivate(routeMock, routeStateMock).subscribe((result) => {});
97
+ } catch (err) {
98
+ expect(err.errorCode).toBe('invalid_interaction_type');
99
+ done();
100
+ }
101
+ });
102
+
103
+ it('returns false if page with MSAL Guard is set as redirectUri', (done) => {
104
+ spyOn(UrlString, 'hashContainsKnownProperties').and.returnValue(true);
105
+ spyOn(BrowserUtils, 'isInIframe').and.returnValue(true);
106
+
107
+ guard.canActivate(routeMock, routeStateMock).subscribe((result) => {
108
+ expect(result).toBeFalse();
109
+ done();
110
+ });
111
+ });
112
+
113
+ it('returns false if page contains known successful response (path routing)', (done) => {
114
+ initializeMsal([
115
+ {
116
+ provide: Location,
117
+ useValue: {
118
+ path: jasmine
119
+ .createSpy('path')
120
+ .and.callFake((hash: boolean) =>
121
+ hash ? '/path?code=123#code=456' : '/path'
122
+ ),
123
+ prepareExternalUrl: jasmine
124
+ .createSpy('prepareExternalUrl')
125
+ .and.callFake((url: string) => '/path'),
126
+ },
127
+ },
128
+ ]);
129
+
130
+ routeStateMock = {
131
+ snapshot: {},
132
+ url: '/path?code=123#code=456',
133
+ root: {
134
+ fragment: 'code=456',
135
+ },
136
+ };
137
+
138
+ spyOn(MsalService.prototype, 'handleRedirectObservable').and.returnValue(
139
+ //@ts-ignore
140
+ of('test')
141
+ );
142
+
143
+ spyOn(PublicClientApplication.prototype, 'getAllAccounts').and.returnValue([
144
+ {
145
+ homeAccountId: 'test',
146
+ localAccountId: 'test',
147
+ environment: 'test',
148
+ tenantId: 'test',
149
+ username: 'test',
150
+ },
151
+ ]);
152
+
153
+ guard
154
+ .canActivate(routeMock, routeStateMock)
155
+ .subscribe((result: UrlTree) => {
156
+ expect(result.toString()).toEqual('/path');
157
+ done();
158
+ });
159
+ });
160
+
161
+ it('returns true if page contains code= in query parameters only', (done) => {
162
+ initializeMsal([
163
+ {
164
+ provide: Location,
165
+ useValue: {
166
+ path: jasmine
167
+ .createSpy('path')
168
+ .and.callFake((hash: boolean) =>
169
+ hash ? '/path?code=123' : '/path'
170
+ ),
171
+ prepareExternalUrl: jasmine
172
+ .createSpy('prepareExternalUrl')
173
+ .and.callFake((url: string) => '/path'),
174
+ },
175
+ },
176
+ ]);
177
+
178
+ routeStateMock = {
179
+ snapshot: {},
180
+ url: '/path?code=123',
181
+ root: {
182
+ fragment: null,
183
+ },
184
+ };
185
+
186
+ spyOn(MsalService.prototype, 'handleRedirectObservable').and.returnValue(
187
+ //@ts-ignore
188
+ of('test')
189
+ );
190
+
191
+ spyOn(PublicClientApplication.prototype, 'getAllAccounts').and.returnValue([
192
+ {
193
+ homeAccountId: 'test',
194
+ localAccountId: 'test',
195
+ environment: 'test',
196
+ tenantId: 'test',
197
+ username: 'test',
198
+ },
199
+ ]);
200
+
201
+ guard
202
+ .canActivate(routeMock, routeStateMock)
203
+ .subscribe((result: UrlTree) => {
204
+ expect(result).toBeTrue();
205
+ done();
206
+ });
207
+ });
208
+
209
+ it('returns true if page route doesnt end with /code', (done) => {
210
+ initializeMsal([
211
+ {
212
+ provide: Location,
213
+ useValue: {
214
+ path: jasmine
215
+ .createSpy('path')
216
+ .and.callFake((hash: boolean) => (hash ? '/codes' : '/')),
217
+ prepareExternalUrl: jasmine
218
+ .createSpy('prepareExternalUrl')
219
+ .and.callFake((url: string) => '#/codes'),
220
+ },
221
+ },
222
+ ]);
223
+
224
+ routeStateMock = {
225
+ snapshot: {},
226
+ url: '/codes',
227
+ root: {
228
+ fragment: null,
229
+ },
230
+ };
231
+
232
+ spyOn(MsalService.prototype, 'handleRedirectObservable').and.returnValue(
233
+ //@ts-ignore
234
+ of('test')
235
+ );
236
+
237
+ spyOn(PublicClientApplication.prototype, 'getAllAccounts').and.returnValue([
238
+ {
239
+ homeAccountId: 'test',
240
+ localAccountId: 'test',
241
+ environment: 'test',
242
+ tenantId: 'test',
243
+ username: 'test',
244
+ },
245
+ ]);
246
+
247
+ guard
248
+ .canActivate(routeMock, routeStateMock)
249
+ .subscribe((result: UrlTree) => {
250
+ expect(result).toBeTrue();
251
+ done();
252
+ });
253
+ });
254
+
255
+ it('returns true if page route doesnt end with /code (short path)', (done) => {
256
+ initializeMsal([
257
+ {
258
+ provide: Location,
259
+ useValue: {
260
+ path: jasmine
261
+ .createSpy('path')
262
+ .and.callFake((hash: boolean) => (hash ? '/cod' : '/')),
263
+ prepareExternalUrl: jasmine
264
+ .createSpy('prepareExternalUrl')
265
+ .and.callFake((url: string) => '#/cod'),
266
+ },
267
+ },
268
+ ]);
269
+
270
+ routeStateMock = {
271
+ snapshot: {},
272
+ url: '/cod',
273
+ root: {
274
+ fragment: null,
275
+ },
276
+ };
277
+
278
+ spyOn(MsalService.prototype, 'handleRedirectObservable').and.returnValue(
279
+ //@ts-ignore
280
+ of('test')
281
+ );
282
+
283
+ spyOn(PublicClientApplication.prototype, 'getAllAccounts').and.returnValue([
284
+ {
285
+ homeAccountId: 'test',
286
+ localAccountId: 'test',
287
+ environment: 'test',
288
+ tenantId: 'test',
289
+ username: 'test',
290
+ },
291
+ ]);
292
+
293
+ guard
294
+ .canActivate(routeMock, routeStateMock)
295
+ .subscribe((result: UrlTree) => {
296
+ expect(result).toBeTrue();
297
+ done();
298
+ });
299
+ });
300
+
301
+ it('returns false if page contains known successful response (hash routing)', (done) => {
302
+ initializeMsal([
303
+ {
304
+ provide: Location,
305
+ useValue: {
306
+ path: jasmine
307
+ .createSpy('path')
308
+ .and.callFake((hash: boolean) => (hash ? '/code=' : '/')),
309
+ prepareExternalUrl: jasmine
310
+ .createSpy('prepareExternalUrl')
311
+ .and.callFake((url: string) => '#/'),
312
+ },
313
+ },
314
+ ]);
315
+
316
+ routeStateMock = {
317
+ snapshot: {},
318
+ url: '/code',
319
+ root: {
320
+ fragment: null,
321
+ },
322
+ };
323
+
324
+ spyOn(MsalService.prototype, 'handleRedirectObservable').and.returnValue(
325
+ //@ts-ignore
326
+ of('test')
327
+ );
328
+
329
+ spyOn(PublicClientApplication.prototype, 'getAllAccounts').and.returnValue([
330
+ {
331
+ homeAccountId: 'test',
332
+ localAccountId: 'test',
333
+ environment: 'test',
334
+ tenantId: 'test',
335
+ username: 'test',
336
+ },
337
+ ]);
338
+
339
+ guard
340
+ .canActivate(routeMock, routeStateMock)
341
+ .subscribe((result: UrlTree) => {
342
+ expect(result.toString()).toEqual('/');
343
+ done();
344
+ });
345
+ });
346
+
347
+ it('returns true for a logged in user', (done) => {
348
+ spyOn(MsalService.prototype, 'handleRedirectObservable').and.returnValue(
349
+ //@ts-ignore
350
+ of('test')
351
+ );
352
+
353
+ spyOn(PublicClientApplication.prototype, 'getAllAccounts').and.returnValue([
354
+ {
355
+ homeAccountId: 'test',
356
+ localAccountId: 'test',
357
+ environment: 'test',
358
+ tenantId: 'test',
359
+ username: 'test',
360
+ },
361
+ ]);
362
+
363
+ guard.canActivate(routeMock, routeStateMock).subscribe((result) => {
364
+ expect(result).toBeTrue();
365
+ done();
366
+ });
367
+ });
368
+
369
+ it('should return true after logging in with popup', (done) => {
370
+ testConfiguration = {
371
+ authRequest: (authService, state) => {
372
+ expect(state).toBeDefined();
373
+ expect(authService).toBeDefined();
374
+ return {};
375
+ },
376
+ };
377
+ initializeMsal();
378
+ spyOn(MsalService.prototype, 'handleRedirectObservable').and.returnValue(
379
+ //@ts-ignore
380
+ of('test')
381
+ );
382
+
383
+ spyOn(PublicClientApplication.prototype, 'getAllAccounts').and.returnValue(
384
+ []
385
+ );
386
+
387
+ spyOn(MsalService.prototype, 'loginPopup').and.returnValue(
388
+ //@ts-ignore
389
+ of(true)
390
+ );
391
+
392
+ guard.canActivate(routeMock, routeStateMock).subscribe((result) => {
393
+ expect(result).toBeTrue();
394
+ done();
395
+ });
396
+ });
397
+
398
+ it('should return false after login with popup fails and no loginFailedRoute set', (done) => {
399
+ spyOn(MsalService.prototype, 'handleRedirectObservable').and.returnValue(
400
+ //@ts-ignore
401
+ of('test')
402
+ );
403
+
404
+ spyOn(PublicClientApplication.prototype, 'getAllAccounts').and.returnValue(
405
+ []
406
+ );
407
+
408
+ spyOn(MsalService.prototype, 'loginPopup').and.throwError('login error');
409
+
410
+ guard.canActivate(routeMock, routeStateMock).subscribe((result) => {
411
+ expect(result).toBeFalse();
412
+ done();
413
+ });
414
+ });
415
+
416
+ it('should return loginFailedRoute after login with popup fails and loginFailedRoute set', (done) => {
417
+ testLoginFailedRoute = 'failed';
418
+ initializeMsal();
419
+
420
+ spyOn(guard, 'parseUrl').and.returnValue(
421
+ testLoginFailedRoute as unknown as UrlTree
422
+ );
423
+
424
+ spyOn(MsalService.prototype, 'handleRedirectObservable').and.returnValue(
425
+ //@ts-ignore
426
+ of('test')
427
+ );
428
+
429
+ spyOn(PublicClientApplication.prototype, 'getAllAccounts').and.returnValue(
430
+ []
431
+ );
432
+
433
+ spyOn(MsalService.prototype, 'loginPopup').and.throwError('login error');
434
+
435
+ guard.canActivate(routeMock, routeStateMock).subscribe((result) => {
436
+ expect(result).toBe('failed' as unknown as UrlTree);
437
+ done();
438
+ });
439
+ });
440
+
441
+ it('should return false after logging in with redirect', (done) => {
442
+ testInteractionType = InteractionType.Redirect;
443
+ initializeMsal();
444
+
445
+ spyOn(MsalService.prototype, 'handleRedirectObservable').and.returnValue(
446
+ //@ts-ignore
447
+ of('test')
448
+ );
449
+
450
+ spyOn(PublicClientApplication.prototype, 'getAllAccounts').and.returnValue(
451
+ []
452
+ );
453
+
454
+ spyOn(PublicClientApplication.prototype, 'loginRedirect').and.returnValue(
455
+ new Promise((resolve) => {
456
+ resolve();
457
+ })
458
+ );
459
+
460
+ guard.canActivate(routeMock, routeStateMock).subscribe((result) => {
461
+ expect(result).toBeFalse();
462
+ done();
463
+ });
464
+ });
465
+
466
+ it('canActivateChild returns true with logged in user', (done) => {
467
+ spyOn(MsalService.prototype, 'handleRedirectObservable').and.returnValue(
468
+ //@ts-ignore
469
+ of('test')
470
+ );
471
+
472
+ spyOn(PublicClientApplication.prototype, 'getAllAccounts').and.returnValue([
473
+ {
474
+ homeAccountId: 'test',
475
+ localAccountId: 'test',
476
+ environment: 'test',
477
+ tenantId: 'test',
478
+ username: 'test',
479
+ },
480
+ ]);
481
+
482
+ guard.canActivateChild(routeMock, routeStateMock).subscribe((result) => {
483
+ expect(result).toBeTrue();
484
+ done();
485
+ });
486
+ });
487
+
488
+ it('canLoad returns true with logged in user', (done) => {
489
+ spyOn(MsalService.prototype, 'handleRedirectObservable').and.returnValue(
490
+ //@ts-ignore
491
+ of('test')
492
+ );
493
+
494
+ spyOn(PublicClientApplication.prototype, 'getAllAccounts').and.returnValue([
495
+ {
496
+ homeAccountId: 'test',
497
+ localAccountId: 'test',
498
+ environment: 'test',
499
+ tenantId: 'test',
500
+ username: 'test',
501
+ },
502
+ ]);
503
+
504
+ guard.canMatch().subscribe((result) => {
505
+ expect(result).toBeTrue();
506
+ done();
507
+ });
508
+ });
509
+
510
+ it('canLoad returns false with no users logged in', (done) => {
511
+ spyOn(MsalService.prototype, 'handleRedirectObservable').and.returnValue(
512
+ //@ts-ignore
513
+ of('test')
514
+ );
515
+
516
+ spyOn(PublicClientApplication.prototype, 'getAllAccounts').and.returnValue(
517
+ []
518
+ );
519
+
520
+ guard.canMatch().subscribe((result) => {
521
+ expect(result).toBeFalse();
522
+ done();
523
+ });
524
+ });
525
+ });