@azure/msal-angular 3.0.0 → 3.0.1

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 (86) hide show
  1. package/IMsalService.d.ts +15 -0
  2. package/README.md +166 -158
  3. package/constants.d.ts +5 -0
  4. package/esm2020/IMsalService.mjs +6 -0
  5. package/esm2020/azure-msal-angular.mjs +5 -0
  6. package/esm2020/constants.mjs +10 -0
  7. package/esm2020/msal.broadcast.config.mjs +6 -0
  8. package/esm2020/msal.broadcast.service.mjs +57 -0
  9. package/esm2020/msal.guard.config.mjs +6 -0
  10. package/esm2020/msal.guard.mjs +218 -0
  11. package/esm2020/msal.interceptor.config.mjs +6 -0
  12. package/esm2020/msal.interceptor.mjs +270 -0
  13. package/esm2020/msal.module.mjs +46 -0
  14. package/esm2020/msal.navigation.client.mjs +51 -0
  15. package/esm2020/msal.redirect.component.mjs +31 -0
  16. package/esm2020/msal.service.mjs +81 -0
  17. package/esm2020/packageMetadata.mjs +4 -0
  18. package/esm2020/public-api.mjs +18 -0
  19. package/fesm2015/azure-msal-angular.mjs +745 -0
  20. package/fesm2015/azure-msal-angular.mjs.map +1 -0
  21. package/fesm2020/azure-msal-angular.mjs +741 -0
  22. package/fesm2020/azure-msal-angular.mjs.map +1 -0
  23. package/index.d.ts +5 -0
  24. package/msal.broadcast.config.d.ts +3 -0
  25. package/msal.broadcast.service.d.ts +17 -0
  26. package/msal.guard.config.d.ts +9 -0
  27. package/msal.guard.d.ts +43 -0
  28. package/msal.interceptor.config.d.ts +17 -0
  29. package/msal.interceptor.d.ts +63 -0
  30. package/msal.module.d.ts +13 -0
  31. package/msal.navigation.client.d.ts +19 -0
  32. package/msal.redirect.component.d.ts +15 -0
  33. package/msal.service.d.ts +31 -0
  34. package/package.json +28 -55
  35. package/packageMetadata.d.ts +2 -0
  36. package/{src/public-api.ts → public-api.d.ts} +3 -21
  37. package/.beachballrc +0 -3
  38. package/.editorconfig +0 -16
  39. package/CHANGELOG.json +0 -1418
  40. package/CHANGELOG.md +0 -520
  41. package/FAQ.md +0 -175
  42. package/angular.json +0 -51
  43. package/docs/angular-universal.md +0 -60
  44. package/docs/configuration.md +0 -525
  45. package/docs/errors.md +0 -89
  46. package/docs/events.md +0 -262
  47. package/docs/initialization.md +0 -206
  48. package/docs/known-issues.md +0 -19
  49. package/docs/logging.md +0 -47
  50. package/docs/msal-guard.md +0 -208
  51. package/docs/msal-interceptor.md +0 -160
  52. package/docs/multi-tenant.md +0 -90
  53. package/docs/performance.md +0 -50
  54. package/docs/public-apis.md +0 -29
  55. package/docs/redirects.md +0 -214
  56. package/docs/security.md +0 -4
  57. package/docs/ssosilent.md +0 -84
  58. package/docs/v0-v1-upgrade-guide.md +0 -61
  59. package/docs/v1-v2-upgrade-guide.md +0 -70
  60. package/docs/v2-v3-upgrade-guide.md +0 -41
  61. package/karma.conf.js +0 -43
  62. package/ng-package.json +0 -7
  63. package/src/IMsalService.ts +0 -31
  64. package/src/constants.ts +0 -20
  65. package/src/msal.broadcast.config.ts +0 -8
  66. package/src/msal.broadcast.service.spec.ts +0 -456
  67. package/src/msal.broadcast.service.ts +0 -74
  68. package/src/msal.guard.config.ts +0 -27
  69. package/src/msal.guard.spec.ts +0 -525
  70. package/src/msal.guard.ts +0 -290
  71. package/src/msal.interceptor.config.ts +0 -44
  72. package/src/msal.interceptor.spec.ts +0 -1125
  73. package/src/msal.interceptor.ts +0 -390
  74. package/src/msal.module.ts +0 -51
  75. package/src/msal.navigation.client.spec.ts +0 -95
  76. package/src/msal.navigation.client.ts +0 -57
  77. package/src/msal.redirect.component.spec.ts +0 -63
  78. package/src/msal.redirect.component.ts +0 -26
  79. package/src/msal.service.spec.ts +0 -452
  80. package/src/msal.service.ts +0 -97
  81. package/src/packageMetadata.ts +0 -3
  82. package/src/test.ts +0 -19
  83. package/tsconfig.json +0 -25
  84. package/tsconfig.lib.json +0 -27
  85. package/tsconfig.lib.prod.json +0 -10
  86. package/tsconfig.spec.json +0 -17
@@ -1,390 +0,0 @@
1
- /*
2
- * Copyright (c) Microsoft Corporation. All rights reserved.
3
- * Licensed under the MIT License.
4
- */
5
-
6
- import { Injectable, Inject } from '@angular/core';
7
- import { Location, DOCUMENT } from '@angular/common';
8
- import {
9
- HttpRequest,
10
- HttpHandler,
11
- HttpEvent,
12
- HttpInterceptor,
13
- } from '@angular/common/http'; // eslint-disable-line import/no-unresolved
14
- import {
15
- AccountInfo,
16
- AuthenticationResult,
17
- BrowserConfigurationAuthError,
18
- InteractionStatus,
19
- InteractionType,
20
- StringUtils,
21
- UrlString,
22
- } from '@azure/msal-browser';
23
- import { Observable, EMPTY, of } from 'rxjs';
24
- import { switchMap, catchError, take, filter } from 'rxjs/operators';
25
- import { MsalService } from './msal.service';
26
- import {
27
- MsalInterceptorAuthRequest,
28
- MsalInterceptorConfiguration,
29
- ProtectedResourceScopes,
30
- MatchingResources,
31
- } from './msal.interceptor.config';
32
- import { MsalBroadcastService } from './msal.broadcast.service';
33
- import { MSAL_INTERCEPTOR_CONFIG } from './constants';
34
-
35
- @Injectable()
36
- export class MsalInterceptor implements HttpInterceptor {
37
- private _document?: Document;
38
-
39
- constructor(
40
- @Inject(MSAL_INTERCEPTOR_CONFIG)
41
- private msalInterceptorConfig: MsalInterceptorConfiguration,
42
- private authService: MsalService,
43
- private location: Location,
44
- private msalBroadcastService: MsalBroadcastService,
45
- // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/explicit-module-boundary-types
46
- @Inject(DOCUMENT) document?: any
47
- ) {
48
- this._document = document as Document;
49
- }
50
-
51
- intercept(
52
- req: HttpRequest<any>, // eslint-disable-line @typescript-eslint/no-explicit-any
53
- next: HttpHandler
54
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
55
- ): Observable<HttpEvent<any>> {
56
- if (
57
- this.msalInterceptorConfig.interactionType !== InteractionType.Popup &&
58
- this.msalInterceptorConfig.interactionType !== InteractionType.Redirect
59
- ) {
60
- throw new BrowserConfigurationAuthError(
61
- 'invalid_interaction_type',
62
- 'Invalid interaction type provided to MSAL Interceptor. InteractionType.Popup, InteractionType.Redirect must be provided in the msalInterceptorConfiguration'
63
- );
64
- }
65
-
66
- this.authService.getLogger().verbose('MSAL Interceptor activated');
67
- const scopes = this.getScopesForEndpoint(req.url, req.method);
68
-
69
- // If no scopes for endpoint, does not acquire token
70
- if (!scopes || scopes.length === 0) {
71
- this.authService
72
- .getLogger()
73
- .verbose('Interceptor - no scopes for endpoint');
74
- return next.handle(req);
75
- }
76
-
77
- // Sets account as active account or first account
78
- let account: AccountInfo;
79
- if (!!this.authService.instance.getActiveAccount()) {
80
- this.authService
81
- .getLogger()
82
- .verbose('Interceptor - active account selected');
83
- account = this.authService.instance.getActiveAccount();
84
- } else {
85
- this.authService
86
- .getLogger()
87
- .verbose('Interceptor - no active account, fallback to first account');
88
- account = this.authService.instance.getAllAccounts()[0];
89
- }
90
-
91
- const authRequest =
92
- typeof this.msalInterceptorConfig.authRequest === 'function'
93
- ? this.msalInterceptorConfig.authRequest(this.authService, req, {
94
- account: account,
95
- })
96
- : { ...this.msalInterceptorConfig.authRequest, account };
97
-
98
- this.authService
99
- .getLogger()
100
- .info(`Interceptor - ${scopes.length} scopes found for endpoint`);
101
- this.authService
102
- .getLogger()
103
- .infoPii(`Interceptor - [${scopes}] scopes found for ${req.url}`);
104
-
105
- return this.acquireToken(authRequest, scopes, account).pipe(
106
- switchMap((result: AuthenticationResult) => {
107
- this.authService
108
- .getLogger()
109
- .verbose('Interceptor - setting authorization headers');
110
- const headers = req.headers.set(
111
- 'Authorization',
112
- `Bearer ${result.accessToken}`
113
- );
114
-
115
- const requestClone = req.clone({ headers });
116
- return next.handle(requestClone);
117
- })
118
- );
119
- }
120
-
121
- /**
122
- * Try to acquire token silently. Invoke interaction if acquireTokenSilent rejected with error or resolved with null access token
123
- * @param authRequest Request
124
- * @param scopes Array of scopes for the request
125
- * @param account Account
126
- * @returns Authentication result
127
- */
128
- private acquireToken(
129
- authRequest: MsalInterceptorAuthRequest,
130
- scopes: string[],
131
- account: AccountInfo
132
- ): Observable<AuthenticationResult> {
133
- // Note: For MSA accounts, include openid scope when calling acquireTokenSilent to return idToken
134
- return this.authService
135
- .acquireTokenSilent({ ...authRequest, scopes, account })
136
- .pipe(
137
- catchError(() => {
138
- this.authService
139
- .getLogger()
140
- .error(
141
- 'Interceptor - acquireTokenSilent rejected with error. Invoking interaction to resolve.'
142
- );
143
- return this.msalBroadcastService.inProgress$.pipe(
144
- take(1),
145
- switchMap((status: InteractionStatus) => {
146
- if (status === InteractionStatus.None) {
147
- return this.acquireTokenInteractively(authRequest, scopes);
148
- }
149
-
150
- return this.msalBroadcastService.inProgress$.pipe(
151
- filter(
152
- (status: InteractionStatus) =>
153
- status === InteractionStatus.None
154
- ),
155
- take(1),
156
- switchMap(() => this.acquireToken(authRequest, scopes, account))
157
- );
158
- })
159
- );
160
- }),
161
- switchMap((result: AuthenticationResult) => {
162
- if (!result.accessToken) {
163
- this.authService
164
- .getLogger()
165
- .error(
166
- 'Interceptor - acquireTokenSilent resolved with null access token. Known issue with B2C tenants, invoking interaction to resolve.'
167
- );
168
- return this.msalBroadcastService.inProgress$.pipe(
169
- filter(
170
- (status: InteractionStatus) => status === InteractionStatus.None
171
- ),
172
- take(1),
173
- switchMap(() =>
174
- this.acquireTokenInteractively(authRequest, scopes)
175
- )
176
- );
177
- }
178
- return of(result);
179
- })
180
- );
181
- }
182
-
183
- /**
184
- * Invoke interaction for the given set of scopes
185
- * @param authRequest Request
186
- * @param scopes Array of scopes for the request
187
- * @returns Result from the interactive request
188
- */
189
- private acquireTokenInteractively(
190
- authRequest: MsalInterceptorAuthRequest,
191
- scopes: string[]
192
- ): Observable<AuthenticationResult> {
193
- if (this.msalInterceptorConfig.interactionType === InteractionType.Popup) {
194
- this.authService
195
- .getLogger()
196
- .verbose(
197
- 'Interceptor - error acquiring token silently, acquiring by popup'
198
- );
199
- return this.authService.acquireTokenPopup({ ...authRequest, scopes });
200
- }
201
- this.authService
202
- .getLogger()
203
- .verbose(
204
- 'Interceptor - error acquiring token silently, acquiring by redirect'
205
- );
206
- const redirectStartPage = window.location.href;
207
- this.authService.acquireTokenRedirect({
208
- ...authRequest,
209
- scopes,
210
- redirectStartPage,
211
- });
212
- return EMPTY;
213
- }
214
-
215
- /**
216
- * Looks up the scopes for the given endpoint from the protectedResourceMap
217
- * @param endpoint Url of the request
218
- * @param httpMethod Http method of the request
219
- * @returns Array of scopes, or null if not found
220
- *
221
- */
222
- private getScopesForEndpoint(
223
- endpoint: string,
224
- httpMethod: string
225
- ): Array<string> | null {
226
- this.authService
227
- .getLogger()
228
- .verbose('Interceptor - getting scopes for endpoint');
229
-
230
- // Ensures endpoints and protected resources compared are normalized
231
- const normalizedEndpoint = this.location.normalize(endpoint);
232
-
233
- const protectedResourcesArray = Array.from(
234
- this.msalInterceptorConfig.protectedResourceMap.keys()
235
- );
236
-
237
- const matchingProtectedResources = this.matchResourcesToEndpoint(
238
- protectedResourcesArray,
239
- normalizedEndpoint
240
- );
241
-
242
- // Check absolute urls of resources first before checking relative to prevent incorrect matching where multiple resources have similar relative urls
243
- if (matchingProtectedResources.absoluteResources.length > 0) {
244
- return this.matchScopesToEndpoint(
245
- this.msalInterceptorConfig.protectedResourceMap,
246
- matchingProtectedResources.absoluteResources,
247
- httpMethod
248
- );
249
- } else if (matchingProtectedResources.relativeResources.length > 0) {
250
- return this.matchScopesToEndpoint(
251
- this.msalInterceptorConfig.protectedResourceMap,
252
- matchingProtectedResources.relativeResources,
253
- httpMethod
254
- );
255
- }
256
-
257
- return null;
258
- }
259
-
260
- /**
261
- * Finds resource endpoints that match request endpoint
262
- * @param protectedResourcesEndpoints
263
- * @param endpoint
264
- * @returns
265
- */
266
- private matchResourcesToEndpoint(
267
- protectedResourcesEndpoints: string[],
268
- endpoint: string
269
- ): MatchingResources {
270
- const matchingResources: MatchingResources = {
271
- absoluteResources: [],
272
- relativeResources: [],
273
- };
274
-
275
- protectedResourcesEndpoints.forEach((key) => {
276
- // Normalizes and adds resource to matchingResources.absoluteResources if key matches endpoint. StringUtils.matchPattern accounts for wildcards
277
- const normalizedKey = this.location.normalize(key);
278
- if (StringUtils.matchPattern(normalizedKey, endpoint)) {
279
- matchingResources.absoluteResources.push(key);
280
- }
281
-
282
- // Get url components for relative urls
283
- const absoluteKey = this.getAbsoluteUrl(key);
284
- const keyComponents = new UrlString(absoluteKey).getUrlComponents();
285
- const absoluteEndpoint = this.getAbsoluteUrl(endpoint);
286
- const endpointComponents = new UrlString(
287
- absoluteEndpoint
288
- ).getUrlComponents();
289
-
290
- // Normalized key should include query strings if applicable
291
- const relativeNormalizedKey = keyComponents.QueryString
292
- ? `${keyComponents.AbsolutePath}?${keyComponents.QueryString}`
293
- : this.location.normalize(keyComponents.AbsolutePath);
294
-
295
- // Add resource to matchingResources.relativeResources if same origin, relativeKey matches endpoint, and is not empty
296
- if (
297
- keyComponents.HostNameAndPort === endpointComponents.HostNameAndPort &&
298
- StringUtils.matchPattern(relativeNormalizedKey, absoluteEndpoint) &&
299
- relativeNormalizedKey !== '' &&
300
- relativeNormalizedKey !== '/*'
301
- ) {
302
- matchingResources.relativeResources.push(key);
303
- }
304
- });
305
-
306
- return matchingResources;
307
- }
308
-
309
- /**
310
- * Transforms relative urls to absolute urls
311
- * @param url
312
- * @returns
313
- */
314
- private getAbsoluteUrl(url: string): string {
315
- const link = this._document.createElement('a');
316
- link.href = url;
317
- return link.href;
318
- }
319
-
320
- /**
321
- * Finds scopes from first matching endpoint with HTTP method that matches request
322
- * @param protectedResourceMap Protected resource map
323
- * @param endpointArray Array of resources that match request endpoint
324
- * @param httpMethod Http method of the request
325
- * @returns
326
- */
327
- private matchScopesToEndpoint(
328
- protectedResourceMap: Map<
329
- string,
330
- Array<string | ProtectedResourceScopes> | null
331
- >,
332
- endpointArray: string[],
333
- httpMethod: string
334
- ): Array<string> | null {
335
- const allMatchedScopes = [];
336
-
337
- // Check each matched endpoint for matching HttpMethod and scopes
338
- endpointArray.forEach((matchedEndpoint) => {
339
- const scopesForEndpoint = [];
340
- const methodAndScopesArray = protectedResourceMap.get(matchedEndpoint);
341
-
342
- // Return if resource is unprotected
343
- if (methodAndScopesArray === null) {
344
- allMatchedScopes.push(null);
345
- return;
346
- }
347
-
348
- methodAndScopesArray.forEach((entry) => {
349
- // Entry is either array of scopes or ProtectedResourceScopes object
350
- if (typeof entry === 'string') {
351
- scopesForEndpoint.push(entry);
352
- } else {
353
- // Ensure methods being compared are normalized
354
- const normalizedRequestMethod = httpMethod.toLowerCase();
355
- const normalizedResourceMethod = entry.httpMethod.toLowerCase();
356
- // Method in protectedResourceMap matches request http method
357
- if (normalizedResourceMethod === normalizedRequestMethod) {
358
- // Validate if scopes comes null to unprotect the resource in a certain http method
359
- if (entry.scopes === null) {
360
- allMatchedScopes.push(null);
361
- } else {
362
- entry.scopes.forEach((scope) => {
363
- scopesForEndpoint.push(scope);
364
- });
365
- }
366
- }
367
- }
368
- });
369
-
370
- // Only add to all scopes if scopes for endpoint and method is found
371
- if (scopesForEndpoint.length > 0) {
372
- allMatchedScopes.push(scopesForEndpoint);
373
- }
374
- });
375
-
376
- if (allMatchedScopes.length > 0) {
377
- if (allMatchedScopes.length > 1) {
378
- this.authService
379
- .getLogger()
380
- .warning(
381
- 'Interceptor - More than 1 matching scopes for endpoint found.'
382
- );
383
- }
384
- // Returns scopes for first matching endpoint
385
- return allMatchedScopes[0];
386
- }
387
-
388
- return null;
389
- }
390
- }
@@ -1,51 +0,0 @@
1
- /*
2
- * Copyright (c) Microsoft Corporation. All rights reserved.
3
- * Licensed under the MIT License.
4
- */
5
-
6
- import { ModuleWithProviders, NgModule } from '@angular/core';
7
- import { CommonModule } from '@angular/common';
8
- import { IPublicClientApplication } from '@azure/msal-browser';
9
- import { MsalBroadcastService } from './msal.broadcast.service';
10
- import { MsalGuard } from './msal.guard';
11
- import { MsalGuardConfiguration } from './msal.guard.config';
12
- import { MsalInterceptorConfiguration } from './msal.interceptor.config';
13
- import { MsalRedirectComponent } from './msal.redirect.component';
14
- import { MsalService } from './msal.service';
15
- import {
16
- MSAL_INSTANCE,
17
- MSAL_GUARD_CONFIG,
18
- MSAL_INTERCEPTOR_CONFIG,
19
- } from './constants';
20
-
21
- @NgModule({
22
- declarations: [MsalRedirectComponent],
23
- imports: [CommonModule],
24
- providers: [MsalGuard, MsalBroadcastService],
25
- })
26
- export class MsalModule {
27
- static forRoot(
28
- msalInstance: IPublicClientApplication,
29
- guardConfig: MsalGuardConfiguration,
30
- interceptorConfig: MsalInterceptorConfiguration
31
- ): ModuleWithProviders<MsalModule> {
32
- return {
33
- ngModule: MsalModule,
34
- providers: [
35
- {
36
- provide: MSAL_INSTANCE,
37
- useValue: msalInstance,
38
- },
39
- {
40
- provide: MSAL_GUARD_CONFIG,
41
- useValue: guardConfig,
42
- },
43
- {
44
- provide: MSAL_INTERCEPTOR_CONFIG,
45
- useValue: interceptorConfig,
46
- },
47
- MsalService,
48
- ],
49
- };
50
- }
51
- }
@@ -1,95 +0,0 @@
1
- import { TestBed } from '@angular/core/testing';
2
- import { Location } from '@angular/common';
3
- import { Router } from '@angular/router';
4
- import {
5
- InteractionType,
6
- NavigationClient,
7
- NavigationOptions,
8
- PublicClientApplication,
9
- } from '@azure/msal-browser';
10
- import { MsalBroadcastService } from './msal.broadcast.service';
11
- import { MsalGuard } from './msal.guard';
12
- import { MsalCustomNavigationClient } from './msal.navigation.client';
13
- import { MsalModule, MsalService } from './public-api';
14
-
15
- let authService: MsalService;
16
- let navigationClient: MsalCustomNavigationClient;
17
- let routerMock = {
18
- navigateByUrl: jasmine.createSpy('navigateByUrl').and.resolveTo(),
19
- };
20
-
21
- const msalInstance = new PublicClientApplication({
22
- auth: {
23
- clientId: '6226576d-37e9-49eb-b201-ec1eeb0029b6',
24
- redirectUri: 'http://localhost:4200',
25
- },
26
- });
27
-
28
- describe('MsalCustomNaviationClient', () => {
29
- beforeAll(() => {
30
- TestBed.resetTestingModule();
31
-
32
- TestBed.configureTestingModule({
33
- imports: [
34
- MsalModule.forRoot(msalInstance, null, {
35
- interactionType: InteractionType.Popup,
36
- protectedResourceMap: new Map(),
37
- }),
38
- ],
39
- providers: [
40
- MsalBroadcastService,
41
- { provide: Router, useValue: routerMock },
42
- Location,
43
- MsalCustomNavigationClient,
44
- MsalService,
45
- MsalGuard,
46
- ],
47
- });
48
- authService = TestBed.inject(MsalService);
49
- navigationClient = TestBed.inject(MsalCustomNavigationClient);
50
- });
51
-
52
- describe('NavigateInternal Unit tests', () => {
53
- it('is created', () => {
54
- expect(navigationClient).toBeTruthy();
55
- });
56
-
57
- it('NavigateInternal (noHistory false)', (done) => {
58
- const url = 'http://localhost:4200/profile';
59
- const normalizedAbsoluteUrl = '/profile';
60
-
61
- const options = {
62
- noHistory: false,
63
- } as NavigationOptions;
64
-
65
- const navigateByUrlOptions = {
66
- replaceUrl: false,
67
- };
68
-
69
- navigationClient.navigateInternal(url, options).then(() => {
70
- expect(routerMock.navigateByUrl).toHaveBeenCalledWith(
71
- normalizedAbsoluteUrl,
72
- navigateByUrlOptions
73
- );
74
- done();
75
- });
76
- });
77
-
78
- it('NavigateInternal (noHistory true)', (done) => {
79
- const url = 'http://localhost:4200/profile';
80
-
81
- const options = {
82
- noHistory: true,
83
- } as NavigationOptions;
84
-
85
- const windowLocationReplaceSpy = spyOn(
86
- NavigationClient.prototype,
87
- 'navigateInternal'
88
- );
89
- navigationClient.navigateInternal(url, options).then(() => {
90
- expect(windowLocationReplaceSpy).toHaveBeenCalledWith(url, options);
91
- done();
92
- });
93
- });
94
- });
95
- });
@@ -1,57 +0,0 @@
1
- /*
2
- * Copyright (c) Microsoft Corporation. All rights reserved.
3
- * Licensed under the MIT License.
4
- */
5
-
6
- import { Injectable } from '@angular/core';
7
- import { Location } from '@angular/common';
8
- import { Router } from '@angular/router';
9
- import {
10
- NavigationClient,
11
- NavigationOptions,
12
- UrlString,
13
- } from '@azure/msal-browser';
14
- import { MsalService } from './msal.service';
15
-
16
- /**
17
- * Custom navigation used for Angular client-side navigation.
18
- * See performance doc for details:
19
- * https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-angular/docs/performance.md
20
- */
21
- @Injectable()
22
- export class MsalCustomNavigationClient extends NavigationClient {
23
- constructor(
24
- private authService: MsalService,
25
- private router: Router,
26
- private location: Location
27
- ) {
28
- super();
29
- }
30
-
31
- async navigateInternal(
32
- url: string,
33
- options: NavigationOptions
34
- ): Promise<boolean> {
35
- this.authService.getLogger().trace('MsalCustomNavigationClient called');
36
-
37
- this.authService
38
- .getLogger()
39
- .verbose('MsalCustomNavigationClient - navigating');
40
- this.authService
41
- .getLogger()
42
- .verbosePii(`MsalCustomNavigationClient - navigating to url: ${url}`);
43
-
44
- // Prevent hash clearing from causing an issue with Client-side navigation after redirect is handled
45
- if (options.noHistory) {
46
- return super.navigateInternal(url, options);
47
- } else {
48
- // Normalizing newUrl if no query string
49
- const urlComponents = new UrlString(url).getUrlComponents();
50
- const newUrl = urlComponents.QueryString
51
- ? `${urlComponents.AbsolutePath}?${urlComponents.QueryString}`
52
- : this.location.normalize(urlComponents.AbsolutePath);
53
- this.router.navigateByUrl(newUrl, { replaceUrl: options.noHistory });
54
- }
55
- return Promise.resolve(options.noHistory);
56
- }
57
- }
@@ -1,63 +0,0 @@
1
- import { TestBed } from '@angular/core/testing';
2
- import {
3
- IPublicClientApplication,
4
- PublicClientApplication,
5
- } from '@azure/msal-browser';
6
- import { MsalBroadcastService } from './msal.broadcast.service';
7
- import { MsalModule } from './msal.module';
8
- import { MsalRedirectComponent } from './msal.redirect.component';
9
- import { MsalService } from './msal.service';
10
-
11
- let authService: MsalService;
12
- let broadcastService: MsalBroadcastService;
13
-
14
- function MSALInstanceFactory(): IPublicClientApplication {
15
- return new PublicClientApplication({
16
- auth: {
17
- clientId: '6226576d-37e9-49eb-b201-ec1eeb0029b6',
18
- redirectUri: 'http://localhost:4200',
19
- },
20
- });
21
- }
22
-
23
- function initializeMsal() {
24
- TestBed.resetTestingModule();
25
-
26
- TestBed.configureTestingModule({
27
- declarations: [MsalRedirectComponent],
28
- imports: [MsalModule.forRoot(MSALInstanceFactory(), null, null)],
29
- providers: [],
30
- });
31
-
32
- authService = TestBed.inject(MsalService);
33
- broadcastService = TestBed.inject(MsalBroadcastService);
34
- }
35
-
36
- describe('MsalRedirectComponent', () => {
37
- beforeAll(initializeMsal);
38
-
39
- it('calls handleRedirectObservable on ngInit', (done) => {
40
- const sampleAccessToken = {
41
- accessToken: '123abc',
42
- };
43
-
44
- spyOn(
45
- PublicClientApplication.prototype,
46
- 'handleRedirectPromise'
47
- ).and.callFake(() => {
48
- return new Promise((resolve) => {
49
- console.log('Spy called');
50
- //@ts-ignore
51
- resolve(sampleAccessToken);
52
-
53
- expect(
54
- PublicClientApplication.prototype.handleRedirectPromise
55
- ).toHaveBeenCalled();
56
- done();
57
- });
58
- });
59
-
60
- const component = new MsalRedirectComponent(authService);
61
- component.ngOnInit();
62
- });
63
- });
@@ -1,26 +0,0 @@
1
- /*
2
- * Copyright (c) Microsoft Corporation. All rights reserved.
3
- * Licensed under the MIT License.
4
- */
5
-
6
- /**
7
- * This is a dedicated redirect component to be added to Angular apps to
8
- * handle redirects when using @azure/msal-angular.
9
- * Import this component to use redirects in your app.
10
- */
11
-
12
- import { Component, OnInit } from '@angular/core';
13
- import { MsalService } from './msal.service';
14
-
15
- @Component({
16
- selector: 'app-redirect',
17
- template: '',
18
- })
19
- export class MsalRedirectComponent implements OnInit {
20
- constructor(private authService: MsalService) {}
21
-
22
- ngOnInit(): void {
23
- this.authService.getLogger().verbose('MsalRedirectComponent activated');
24
- this.authService.handleRedirectObservable().subscribe();
25
- }
26
- }