@azure/msal-angular 3.0.0-beta.0 → 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 -31
  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,60 @@
1
+ # Angular Universal SSR with MSAL Angular
2
+
3
+ Angular Universal is minimally supported in `@azure/msal-angular`. As `@azure/msal-angular` is a wrapper library for `@azure/msal-browser`, which uses browser-only global objects such as `window` and `location` objects, not all of `@azure/msal-angular`'s features are available when using Angular Universal. While login and token acquisition is not supported server-side, Angular Universal can be used with `@azure/msal-angular` without breaking your app.
4
+
5
+ Please see instructions from the [Angular docs](https://angular.io/guide/universal) on how to install Angular Universal with an existing application, and for more information on [browser-only global objects](https://angular.io/guide/universal#working-around-the-browser-apis).
6
+
7
+ To use `@azure/msal-angular` with Angular Universal, make the following adjustments:
8
+
9
+ 1. Remove references to browser-only objects. Our [Angular 15 sample app](https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/samples/msal-angular-v3-samples/angular15-sample-app) has comments next to relevant lines that should be removed to render server-side. Removing these lines will not affect the sample app if using Angular Universal.
10
+
11
+ ```ts
12
+ this.isIframe = window !== window.parent && !window.opener; // Remove this line to use Angular Universal
13
+ ```
14
+
15
+ 1. Alternatively, checks could be added before any lines that use browser-only global objects.
16
+
17
+ ```ts
18
+ if (typeof window !== "undefined") {
19
+ this.isIframe = window !== window.parent && !window.opener;
20
+ }
21
+ ```
22
+
23
+ 1. The same check should be added to any HTTP calls made by your app, as the `MsalInterceptor` currently uses browser-only objects. This will be addressed in a future fix. See the example below in the *profile.component.ts* from our [older MSAL Angular v2 Angular 11 sample app](https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/msal-lts/samples/msal-angular-v2-samples/angular11-sample-app):
24
+
25
+ ```ts
26
+ export class ProfileComponent implements OnInit {
27
+ profile!: ProfileType;
28
+
29
+ constructor(
30
+ private http: HttpClient
31
+ ) { }
32
+
33
+ ngOnInit() {
34
+ // This check is added to ensure HTTP calls are made client-side
35
+ if (typeof window !== "undefined") {
36
+ this.getProfile();
37
+ }
38
+ }
39
+
40
+ getProfile() {
41
+ this.http.get(GRAPH_ENDPOINT)
42
+ .subscribe(profile => {
43
+ this.profile = profile;
44
+ });
45
+ }
46
+ }
47
+ ```
48
+
49
+ 1. Ensure that your app is not using hash routing. The default routing strategy for the [older MSAL Angular v2 Angular 11 sample app](https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/msal-lts/samples/msal-angular-v2-samples/angular11-sample-app) is hash routing, so ensure `useHash` is set to `false` in the *app-routing.module.ts*:
50
+
51
+ ```ts
52
+ @NgModule({
53
+ imports: [RouterModule.forRoot(routes, {
54
+ useHash: false,
55
+ initialNavigation: 'enabled'
56
+ })],
57
+ exports: [RouterModule]
58
+ })
59
+ export class AppRoutingModule { }
60
+ ```
@@ -0,0 +1,525 @@
1
+ # MSAL Angular Configuration
2
+
3
+ MSAL for Angular can be configured in multiple ways:
4
+ - `MsalModule.forRoot`
5
+ - Factory providers
6
+ - `platformBrowserDynamic`
7
+ - Dynamic configurations using Factory Providers and `APP_INITIALIZER`
8
+
9
+ This guide will detail how to leverage each method for your application.
10
+
11
+ ## Configuration Options
12
+
13
+ `@azure/msal-angular` accepts three configuration objects:
14
+
15
+ 1. [Configuration](https://azuread.github.io/microsoft-authentication-library-for-js/ref/modules/_azure_msal_browser.html#configuration): This is the same configuration object that is used for the core `@azure/msal-browser` library. All configuration options can be found [here](https://azuread.github.io/microsoft-authentication-library-for-js/ref/modules/_azure_msal_browser.html#configuration).
16
+ 2. [`MsalGuardConfiguration`](https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-angular/src/msal.guard.config.ts): A set of options specifically for the Angular guard.
17
+ 3. [`MsalInterceptorConfiguration`](https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-angular/src/msal.interceptor.config.ts): A set of options specifically for the Angular interceptor.
18
+
19
+ ### Angular-specific configurations
20
+
21
+ * An `interactionType` must be specified on `MsalGuardConfiguration` and `MsalInterceptorConfiguration`, and can be set to `Popup` or `Redirect`.
22
+ * The `protectedResourceMap` object on `MsalInterceptorConfiguration` is used to protect routes.
23
+ * An optional `authRequest` object can be specified on `MsalGuardConfiguration` and `MsalInterceptorConfiguration` to set additional options.
24
+ * An optional `loginFailedRoute` string can be set on `MsalGuardConfiguration`. Msal Guard will redirect to this route if login is required and fails.
25
+
26
+ Please see our [MsalInterceptor](https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-angular/docs/msal-interceptor.md) and [MsalGuard](https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-angular/docs/msal-guard.md) docs for more details on configurations, usage, and differences to MSAL Angular v1.
27
+
28
+ ### Configuration for redirects
29
+
30
+ We recommend importing `MsalRedirectComponent` and bootstrapping with the `AppComponent` if you intend to use redirects. Please see the [redirect documentation](https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-angular/docs/redirects.md) for more details.
31
+
32
+ **Note:** As of MSAL v3.x, initialization of the application object is now required. See the [v2-v3 upgrade guide](https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-angular/docs/v2-v3-upgrade-guide.md) for more details.
33
+
34
+ ## MsalModule.forRoot
35
+
36
+ The `MsalModule` class contains a static method that can be called in your `app.module.ts` file:
37
+
38
+ ```typescript
39
+ import { NgModule } from '@angular/core';
40
+ import { HTTP_INTERCEPTORS } from '@angular/common/http';
41
+ import { AppComponent } from './app.component';
42
+ import { MsalModule, MsalService, MsalGuard, MsalInterceptor, MsalBroadcastService, MsalRedirectComponent } from "@azure/msal-angular";
43
+ import { PublicClientApplication, InteractionType, BrowserCacheLocation } from "@azure/msal-browser";
44
+
45
+ @NgModule({
46
+ imports: [
47
+ MsalModule.forRoot( new PublicClientApplication({ // MSAL Configuration
48
+ auth: {
49
+ clientId: "clientid",
50
+ authority: "https://login.microsoftonline.com/common/",
51
+ redirectUri: "http://localhost:4200/",
52
+ postLogoutRedirectUri: "http://localhost:4200/",
53
+ navigateToLoginRequestUrl: true
54
+ },
55
+ cache: {
56
+ cacheLocation : BrowserCacheLocation.LocalStorage,
57
+ storeAuthStateInCookie: true, // set to true for IE 11
58
+ },
59
+ system: {
60
+ loggerOptions: {
61
+ loggerCallback: () => {},
62
+ piiLoggingEnabled: false
63
+ }
64
+ }
65
+ }), {
66
+ interactionType: InteractionType.Popup, // MSAL Guard Configuration
67
+ authRequest: {
68
+ scopes: ['user.read']
69
+ },
70
+ loginFailedRoute: "/login-failed"
71
+ }, {
72
+ interactionType: InteractionType.Redirect, // MSAL Interceptor Configuration
73
+ protectedResourceMap
74
+ })
75
+ ],
76
+ providers: [
77
+ {
78
+ provide: HTTP_INTERCEPTORS,
79
+ useClass: MsalInterceptor,
80
+ multi: true
81
+ },
82
+ MsalGuard
83
+ ],
84
+ bootstrap: [AppComponent, MsalRedirectComponent]
85
+ })
86
+ export class AppModule {}
87
+ ```
88
+
89
+ ## Factory Providers
90
+
91
+ You may also provide the configuration options via factory providers.
92
+
93
+ ```typescript
94
+ import {
95
+ MsalModule,
96
+ MsalService,
97
+ MsalInterceptor,
98
+ MsalInterceptorConfiguration,
99
+ MsalGuard,
100
+ MsalGuardConfiguration,
101
+ MsalBroadcastService,
102
+ MsalRedirectComponent
103
+ } from "@azure/msal-angular";
104
+ import { IPublicClientApplication, PublicClientApplication, InteractionType, BrowserCacheLocation } from "@azure/msal-browser";
105
+
106
+ export function MSALInstanceFactory(): IPublicClientApplication {
107
+ return new PublicClientApplication({
108
+ auth: {
109
+ clientId: "6226576d-37e9-49eb-b201-ec1eeb0029b6",
110
+ redirectUri: "http://localhost:4200",
111
+ postLogoutRedirectUri: "http://localhost:4200"
112
+ },
113
+ cache: {
114
+ cacheLocation: BrowserCacheLocation.LocalStorage,
115
+ storeAuthStateInCookie: isIE, // set to true for IE 11
116
+ },
117
+ });
118
+ }
119
+
120
+ export function MSALInterceptorConfigFactory(): MsalInterceptorConfiguration {
121
+ const protectedResourceMap = new Map<string, Array<string>>();
122
+ protectedResourceMap.set("https://graph.microsoft.com/v1.0/me", ["user.read"]);
123
+
124
+ return {
125
+ interactionType: InteractionType.Redirect,
126
+ protectedResourceMap,
127
+ };
128
+ }
129
+
130
+ export function MSALGuardConfigFactory(): MsalGuardConfiguration {
131
+ return {
132
+ interactionType: InteractionType.Redirect,
133
+ authRequest: {
134
+ scopes: ['user.read']
135
+ },
136
+ loginFailedRoute: "./login-failed"
137
+ };
138
+ }
139
+
140
+ @NgModule({
141
+ imports: [
142
+ MsalModule
143
+ ],
144
+ providers: [
145
+ {
146
+ provide: HTTP_INTERCEPTORS,
147
+ useClass: MsalInterceptor,
148
+ multi: true
149
+ },
150
+ {
151
+ provide: MSAL_INSTANCE,
152
+ useFactory: MSALInstanceFactory
153
+ },
154
+ {
155
+ provide: MSAL_GUARD_CONFIG,
156
+ useFactory: MSALGuardConfigFactory
157
+ },
158
+ {
159
+ provide: MSAL_INTERCEPTOR_CONFIG,
160
+ useFactory: MSALInterceptorConfigFactory
161
+ },
162
+ MsalGuard,
163
+ MsalBroadcastService,
164
+ MsalService
165
+ ],
166
+ bootstrap: [AppComponent, MsalRedirectComponent]
167
+ })
168
+ export class AppModule { }
169
+ ```
170
+
171
+ ## platformBrowserDynamic
172
+
173
+ If you need to dynamically configure MSAL Angular (e.g. based on values returned from an API), you can use `platformBrowserDynamic`. `platformBrowserDyamic` is a platform factory, used to bootstrap the application, and is able to take in configuration options. `platformBrowserDynamic` should already be present when the Angular application is set up.
174
+
175
+ The following is an example of how to dynamically configure `@azure/msal-angular` with `platformBrowserDynamic` and a json file:
176
+
177
+ `app.module.ts`
178
+ ```typescript
179
+ import {
180
+ MsalModule,
181
+ MsalInterceptor,
182
+ MsalService,
183
+ } from '@azure/msal-angular';
184
+
185
+ @NgModule({
186
+ imports: [
187
+ MsalModule
188
+ ],
189
+ providers: [
190
+ {
191
+ provide: HTTP_INTERCEPTORS,
192
+ useClass: MsalInterceptor,
193
+ multi: true
194
+ },
195
+ MsalService
196
+ ],
197
+ bootstrap: [AppComponent]
198
+ })
199
+ export class AppModule { }
200
+ ```
201
+
202
+ `main.ts`
203
+ ```typescript
204
+ import { enableProdMode } from '@angular/core';
205
+ import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
206
+
207
+ import { AppModule } from './app/app.module';
208
+ import { environment } from './environments/environment';
209
+ import { MSAL_INSTANCE, MSAL_GUARD_CONFIG, MSAL_INTERCEPTOR_CONFIG } from '@azure/msal-angular';
210
+ import { PublicClientApplication, Configuration } from '@azure/msal-browser';
211
+
212
+ if (environment.production) {
213
+ enableProdMode();
214
+ }
215
+
216
+ function loggerCallback(logLevel: LogLevel, message: string) {
217
+ console.log("MSAL Angular: ", message);
218
+ }
219
+
220
+ fetch('/assets/configuration.json')
221
+ .then(response => response.json())
222
+ .then(json => {
223
+ platformBrowserDynamic([
224
+ { provide: MSAL_INSTANCE, useValue: new PublicClientApplication({
225
+ auth: json.msal.auth,
226
+ cache: json.msal.cache,
227
+ system: {
228
+ loggerOptions: {
229
+ loggerCallback,
230
+ logLevel: LogLevel.Info,
231
+ piiLoggingEnabled: false
232
+ }
233
+ }
234
+ }) },
235
+ { provide: MSAL_GUARD_CONFIG, useValue: {
236
+ interactionType: json.guard.interactionType,
237
+ authRequest: json.guard.authRequest,
238
+ loginFailedRoute: json.guard.loginFailedRoute
239
+ } as MsalGuardConfiguration },
240
+ { provide: MSAL_INTERCEPTOR_CONFIG, useValue: {
241
+ interactionType: json.interceptor.interactionType,
242
+ protectedResourceMap: new Map(json.interceptor.protectedResourceMap)
243
+ } as MsalInterceptorConfiguration },
244
+ ])
245
+ .bootstrapModule(AppModule)
246
+ .catch(err => console.error(err));
247
+ });
248
+ ```
249
+
250
+ `src/assets/configuration.json`
251
+ ```json
252
+ {
253
+ "msal": {
254
+ "auth": {
255
+ "clientId": "clientid",
256
+ "authority": "https://login.microsoftonline.com/common/",
257
+ "redirectUri": "http://localhost:4200/",
258
+ "postLogoutRedirectUri": "http://localhost:4200/",
259
+ "navigateToLoginRequestUrl": true
260
+ },
261
+ "cache": {
262
+ "cacheLocation": "localStorage",
263
+ "storeAuthStateInCookie": true
264
+ }
265
+ },
266
+ "guard": {
267
+ "interactionType": "redirect",
268
+ "authRequest": {
269
+ "scopes": ["user.read"]
270
+ },
271
+ "loginFailedRoute": "/login-failed"
272
+ },
273
+ "interceptor": {
274
+ "interactionType": "redirect",
275
+ "protectedResourceMap": [
276
+ ["https://graph.microsoft.com/v1.0/me", ["user.read"]]
277
+ ]
278
+ }
279
+ }
280
+ ```
281
+
282
+ ## Dynamic configurations using Factory Providers and APP_INITIALIZER
283
+
284
+ To dynamically configure MSAL Angular, you can use the Factory Providers with APP_INITIALIZER.
285
+
286
+ `src/app/config.service.ts`
287
+ ```typescript
288
+ import { Injectable } from '@angular/core';
289
+ import { HttpClient, HttpBackend } from '@angular/common/http';
290
+ import { map } from 'rxjs/operators';
291
+
292
+ @Injectable({
293
+ providedIn: 'root'
294
+ })
295
+ export class ConfigService {
296
+ private settings: any;
297
+ private http: HttpClient;
298
+
299
+ constructor(private readonly httpHandler: HttpBackend) {
300
+ this.http = new HttpClient(httpHandler);
301
+ }
302
+
303
+ init(endpoint: string): Promise<boolean> {
304
+ return new Promise<boolean>((resolve, reject) => {
305
+ this.http.get(endpoint).pipe(map(result => result))
306
+ .subscribe(value => {
307
+ this.settings = value;
308
+ resolve(true);
309
+ },
310
+ (error) => {
311
+ reject(error);
312
+ });
313
+ });
314
+ }
315
+
316
+ getSettings(key?: string | Array<string>): any {
317
+ if (!key || (Array.isArray(key) && !key[0])) {
318
+ return this.settings;
319
+ }
320
+
321
+ if (!Array.isArray(key)) {
322
+ key = key.split('.');
323
+ }
324
+
325
+ let result = key.reduce((account: any, current: string) => account && account[current], this.settings);
326
+
327
+ return result;
328
+ }
329
+ }
330
+ ```
331
+
332
+ `src/app/msal-config-dynamic.module.ts`
333
+ ```typescript
334
+ import { InjectionToken, NgModule, APP_INITIALIZER } from '@angular/core';
335
+ import { IPublicClientApplication, PublicClientApplication,
336
+ LogLevel } from '@azure/msal-browser';
337
+ import { MsalGuard, MsalInterceptor, MsalBroadcastService,
338
+ MsalInterceptorConfiguration, MsalModule, MsalService,
339
+ MSAL_GUARD_CONFIG, MSAL_INSTANCE, MSAL_INTERCEPTOR_CONFIG,
340
+ MsalGuardConfiguration } from '@azure/msal-angular';
341
+ import { HTTP_INTERCEPTORS } from '@angular/common/http';
342
+ import { ConfigService } from './config.service';
343
+
344
+ const AUTH_CONFIG_URL_TOKEN = new InjectionToken<string>('AUTH_CONFIG_URL');
345
+
346
+ export function initializerFactory(env: ConfigService, configUrl: string): any {
347
+ const promise = env.init(configUrl).then((value) => {
348
+ console.log('finished getting configurations dynamically.');
349
+ });
350
+ return () => promise;
351
+ }
352
+
353
+ const isIE = window.navigator.userAgent.indexOf("MSIE ") > -1 || window.navigator.userAgent.indexOf("Trident/") > -1; // Remove this line to use Angular Universal
354
+
355
+ export function loggerCallback(logLevel: LogLevel, message: string) {
356
+ console.log(message);
357
+ }
358
+
359
+ export function MSALInstanceFactory(config: ConfigService): IPublicClientApplication {
360
+ return new PublicClientApplication({
361
+ auth: config.getSettings('msal').auth,
362
+ cache: config.getSettings('msal').cache,
363
+ system: {
364
+ loggerOptions: {
365
+ loggerCallback,
366
+ logLevel: LogLevel.Info,
367
+ piiLoggingEnabled: false
368
+ }
369
+ }
370
+ });
371
+ }
372
+
373
+ export function MSALInterceptorConfigFactory(config: ConfigService): MsalInterceptorConfiguration {
374
+ const protectedResourceMap = new Map<string, Array<string>>(config.getSettings('interceptor').protectedResourceMap)
375
+
376
+ return {
377
+ interactionType: config.getSettings('interceptor').interactionType,
378
+ protectedResourceMap
379
+ };
380
+ }
381
+
382
+ export function MSALGuardConfigFactory(config: ConfigService): MsalGuardConfiguration {
383
+ return {
384
+ interactionType: config.getSettings('guard').interactionType,
385
+ authRequest: config.getSettings('guard').authRequest,
386
+ loginFailedRoute: config.getSettings('guard').loginFailedRoute
387
+ };
388
+ }
389
+
390
+ @NgModule({
391
+ providers: [],
392
+ imports: [MsalModule]
393
+ })
394
+ export class MsalConfigDynamicModule {
395
+
396
+ static forRoot(configFile: string) {
397
+ return {
398
+ ngModule: MsalConfigDynamicModule,
399
+ providers: [
400
+ ConfigService,
401
+ { provide: AUTH_CONFIG_URL_TOKEN, useValue: configFile },
402
+ { provide: APP_INITIALIZER, useFactory: initializerFactory,
403
+ deps: [ConfigService, AUTH_CONFIG_URL_TOKEN], multi: true },
404
+ {
405
+ provide: MSAL_INSTANCE,
406
+ useFactory: MSALInstanceFactory,
407
+ deps: [ConfigService]
408
+ },
409
+ {
410
+ provide: MSAL_GUARD_CONFIG,
411
+ useFactory: MSALGuardConfigFactory,
412
+ deps: [ConfigService]
413
+ },
414
+ {
415
+ provide: MSAL_INTERCEPTOR_CONFIG,
416
+ useFactory: MSALInterceptorConfigFactory,
417
+ deps: [ConfigService]
418
+ },
419
+ MsalService,
420
+ MsalGuard,
421
+ MsalBroadcastService,
422
+ {
423
+ provide: HTTP_INTERCEPTORS,
424
+ useClass: MsalInterceptor,
425
+ multi: true
426
+ }
427
+ ]
428
+ };
429
+ }
430
+ }
431
+ ```
432
+ `src/app/app.module.ts`
433
+ ```typescript
434
+ import { BrowserModule } from '@angular/platform-browser';
435
+ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
436
+ import { NgModule } from '@angular/core';
437
+
438
+ import { MatButtonModule } from '@angular/material/button';
439
+ import { MatToolbarModule } from '@angular/material/toolbar';
440
+ import { MatListModule } from '@angular/material/list';
441
+
442
+ import { AppRoutingModule } from './app-routing.module';
443
+ import { AppComponent } from './app.component';
444
+ import { HomeComponent } from './home/home.component';
445
+ import { ProfileComponent } from './profile/profile.component';
446
+
447
+ import { HttpClientModule } from '@angular/common/http';
448
+ import { MsalRedirectComponent } from '@azure/msal-angular';
449
+ import { DetailComponent } from './detail/detail.component';
450
+ import { MsalConfigDynamicModule } from './msal-config-dynamic.module';
451
+
452
+ @NgModule({
453
+ declarations: [
454
+ AppComponent,
455
+ HomeComponent,
456
+ ProfileComponent,
457
+ DetailComponent
458
+ ],
459
+ imports: [
460
+ BrowserModule,
461
+ BrowserAnimationsModule,
462
+ AppRoutingModule,
463
+ MatButtonModule,
464
+ MatToolbarModule,
465
+ MatListModule,
466
+ HttpClientModule,
467
+ MsalConfigDynamicModule.forRoot('assets/configuration.json')
468
+ ],
469
+ providers: [],
470
+ bootstrap: [AppComponent, MsalRedirectComponent]
471
+ })
472
+ export class AppModule { }
473
+ ```
474
+
475
+ `src/assets/configuration.json`
476
+ ```json
477
+ {
478
+ "msal": {
479
+ "auth": {
480
+ "clientId": "clientid",
481
+ "authority": "https://login.microsoftonline.com/common/",
482
+ "redirectUri": "http://localhost:4200/",
483
+ "postLogoutRedirectUri": "http://localhost:4200/",
484
+ "navigateToLoginRequestUrl": true
485
+ },
486
+ "cache": {
487
+ "cacheLocation": "localStorage",
488
+ "storeAuthStateInCookie": true
489
+ }
490
+ },
491
+ "guard": {
492
+ "interactionType": "redirect",
493
+ "authRequest": {
494
+ "scopes": ["user.read"]
495
+ },
496
+ "loginFailedRoute": "/login-failed"
497
+ },
498
+ "interceptor": {
499
+ "interactionType": "redirect",
500
+ "protectedResourceMap": [
501
+ ["https://graph.microsoft.com/v1.0/me", ["user.read"]]
502
+ ]
503
+ }
504
+ }
505
+ ```
506
+
507
+ ### MsalGuard - Dynamic auth request
508
+
509
+ The **MsalGuard** also allows you to dynamically change the **authRequest** at runtime. This allow you to pick a different authority for a route, or to dynamically add scopes based on the **RouterStateSnapshot**.
510
+
511
+ ```js
512
+ export function MSALGuardConfigFactory(): MsalGuardConfiguration {
513
+ return {
514
+ interactionType: InteractionType.Redirect,
515
+ authRequest: (authService, state) => {
516
+ return {
517
+ scopes: state.root.url.some(x => x.path === 'calendar')
518
+ ? ['user.read', ' Calendars.Read']
519
+ : ['user.read']
520
+ }
521
+ },
522
+ loginFailedRoute: "./login-failed"
523
+ };
524
+ }
525
+ ```
package/docs/errors.md ADDED
@@ -0,0 +1,89 @@
1
+ # MSAL Angular Errors
2
+
3
+ ***
4
+
5
+ **[BrowserAuthErrors](#browserautherrors)**
6
+
7
+ 1. [interaction_in_progress](#interaction_in_progress)
8
+
9
+ **[Additional Errors](https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/errors.md)**
10
+
11
+ ***
12
+
13
+ ## BrowserAuthErrors
14
+
15
+ ### Interaction_in_progress
16
+
17
+ **Error Message**: Interaction is currently in progress. Please ensure that this interaction has been completed before calling an interactive API.
18
+
19
+ This error is thrown when an interactive API (`loginPopup`, `loginRedirect`, `acquireTokenPopup`, `acquireTokenRedirect`) is invoked while another interactive API is still in progress. The login and acquireToken APIs are async so you will need to ensure that the resulting promises have resolved before invoking another one.
20
+
21
+ In `@azure/msal-angular` there are 2 common scenarios when this can happen:
22
+
23
+ 1. Your application is not handling redirects correctly. The error then occurs when either the app or the user tries to call an interactive API.
24
+ 1. Your application is calling one of the above APIs without first checking if interaction is already in progress elsewhere.
25
+
26
+ Redirects **must** be handled either with the `MsalRedirectComponent` or with calling `handleRedirectObservable()`. Not handling redirects explicitly with either of these two approaches will result in the error above. See our docs on redirects [here](https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-angular/docs/redirects.md) for more information on both approaches.
27
+
28
+ Additionally, any interaction should be done after subscribing to the `inProgress$` observable and filtering for `InteractionStatus.None`. Attempting interaction while another is in progress is not supported and will result in the error above. Checking for `InteractionStatus.None` is how you ensure this does not happen. Please see our [events doc](https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-angular/docs/events.md#the-inprogress-observable) for more details.
29
+
30
+ Please see the [`@azure/msal-browser` error doc](https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/errors.md) for more information on this error.
31
+
32
+ ❌ The following example will throw this error when another component has already invoked an interactive API that is in progress:
33
+
34
+ ```javascript
35
+ import { Component, OnInit } from '@angular/core';
36
+ import { MsalService, MsalBroadcastService, InteractionStatus } from '@azure/msal-angular';
37
+ import { filter } from 'rxjs/operators';
38
+
39
+ @Component()
40
+ export class ExampleComponent implements OnInit {
41
+
42
+ constructor(
43
+ private msalBroadcastService: MsalBroadcastService,
44
+ private authService: MsalService
45
+ ) {}
46
+
47
+ ngOnInit(): void {
48
+ this.authService.loginRedirect();
49
+ }
50
+ ```
51
+
52
+ ✔️ To fix the previous example, check that no other interaction is in progress before invoking `loginRedirect`:
53
+
54
+ ```javascript
55
+ import { Component, OnInit } from '@angular/core';
56
+ import { InteractionStatus } from '@azure/msal-browser';
57
+ import { MsalService, MsalBroadcastService } from '@azure/msal-angular';
58
+ import { filter } from 'rxjs/operators';
59
+
60
+ @Component()
61
+ export class ExampleComponent implements OnInit {
62
+
63
+ constructor(
64
+ private msalBroadcastService: MsalBroadcastService,
65
+ private authService: MsalService
66
+ ) {}
67
+
68
+ ngOnInit(): void {
69
+ this.msalBroadcastService.inProgress$
70
+ .pipe(
71
+ filter((status: InteractionStatus) => status === InteractionStatus.None),
72
+ )
73
+ .subscribe(() => {
74
+ this.authService.loginRedirect();
75
+ })
76
+ }
77
+ ```
78
+
79
+ #### Troubleshooting Steps
80
+
81
+ - [Enable verbose logging](https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/configuration.md#using-the-config-object) and trace the order of events. Verify that an interactive API is not invoked before another has resolved.
82
+ - If using the redirect flow make sure `MsalRedirectComponet` is correctly bootstrapped or `handleRedirectObservable` is being called on every page which may be redirected to.
83
+
84
+ If you are unable to figure out why this error is being thrown please [open an issue](https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/new/choose) and be prepared to share the following information:
85
+
86
+ - Verbose logs
87
+ - A sample app and/or code snippets that we can use to reproduce the issue
88
+ - Refresh the page. Does the error go away?
89
+ - Open your application in a new tab. Does the error go away?