MSAL Angular-Konfiguration

MSAL für Angular kann auf mehrere Arten konfiguriert werden. In diesem Artikel werden die verfügbaren Konfigurationsoptionen für MSAL Angular beschrieben, einschließlich statischer und dynamischer Ansätze, und es werden Codebeispiele für die Integration der Authentifizierung in Ihre App bereitgestellt. Verwenden Sie dieses Handbuch, um die Konfigurationsmethode auszuwählen, die den Anforderungen Ihrer Anwendung am besten entspricht, und bietet eine nahtlose Anmeldeerfahrung für Ihre Benutzer.

Konfigurationsoptionen

@azure/msal-angular akzeptiert drei Konfigurationsobjekte:

  1. Konfiguration: Dies ist dasselbe Konfigurationsobjekt, das für die Kernbibliothek @azure/msal-browser verwendet wird. Alle Konfigurationsoptionen finden Sie hier.
  2. MsalGuardConfiguration: Eine Reihe von Optionen speziell für den Angular Guard.
  3. MsalInterceptorConfiguration: Eine Reihe von Optionen speziell für den Angular-Interceptor.

Angular-spezifische Konfigurationen

  • Auf MsalGuardConfiguration und MsalInterceptorConfiguration muss ein interactionType angegeben werden und kann auf Popup oder Redirect festgelegt werden.
  • Das protectedResourceMap Objekt auf MsalInterceptorConfiguration wird verwendet, um Routen zu schützen.
  • Ein optionales authRequest-Objekt kann auf MsalGuardConfiguration und MsalInterceptorConfiguration angegeben werden, um zusätzliche Optionen festzulegen.
  • Eine optionale loginFailedRoute Zeichenfolge kann für MsalGuardConfiguration festgelegt werden. Msal Guard leitet zu dieser Route um, wenn die Anmeldung erforderlich ist und fehlschlägt.

Weitere Informationen zu Konfigurationen, Verwendung und Unterschieden zu MSAL Angular v1 finden Sie in unseren MsalInterceptor - und MsalGuard-Dokumenten .

Konfiguration für Umleitungen

Wir empfehlen, MsalRedirectComponent zu importieren und mit dem AppComponent zu bootstrappen, wenn Sie Weiterleitungen verwenden möchten. Weitere Details finden Sie in der Umleitungsdokumentation .

Hinweis: Ab MSAL v3.x ist jetzt die Initialisierung des Anwendungsobjekts erforderlich. Weitere Details finden Sie im v2-v3-Upgradehandbuch . Ausführliche Informationen zum Upgrade auf MSAL Angular v5 finden Sie im v4-v5-Upgradehandbuch.

MsalModule.forRoot

Die MsalModule Klasse enthält eine statische Methode, die in der app.module.ts Datei aufgerufen werden kann:

import { NgModule } from "@angular/core";
import { HTTP_INTERCEPTORS } from "@angular/common/http";
import { AppComponent } from "./app.component";
import { MsalModule, MsalService, MsalGuard, MsalInterceptor, MsalBroadcastService, MsalRedirectComponent } from "@azure/msal-angular";
import { PublicClientApplication, InteractionType, BrowserCacheLocation } from "@azure/msal-browser";

@NgModule({
  imports: [
    MsalModule.forRoot(
      new PublicClientApplication({
        // MSAL Configuration
        auth: {
          clientId: "clientid",
          authority: "https://login.microsoftonline.com/common/",
          redirectUri: "http://localhost:4200/",
          postLogoutRedirectUri: "http://localhost:4200/",
        },
        cache: {
          cacheLocation: BrowserCacheLocation.LocalStorage,
        },
        system: {
          loggerOptions: {
            loggerCallback: () => {},
            piiLoggingEnabled: false,
          },
        },
      }),
      {
        interactionType: InteractionType.Popup, // MSAL Guard Configuration
        authRequest: {
          scopes: ["user.read"],
        },
        loginFailedRoute: "/login-failed",
      },
      {
        interactionType: InteractionType.Redirect, // MSAL Interceptor Configuration
        protectedResourceMap,
      }
    ),
  ],
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: MsalInterceptor,
      multi: true,
    },
    MsalGuard,
  ],
  bootstrap: [AppComponent, MsalRedirectComponent],
})
export class AppModule {}

Werksanbieter

Sie können die Konfigurationsoptionen auch mithilfe von Factory-Providern bereitstellen.

import { MsalModule, MsalService, MsalInterceptor, MsalInterceptorConfiguration, MsalGuard, MsalGuardConfiguration, MsalBroadcastService, MsalRedirectComponent } from "@azure/msal-angular";
import { IPublicClientApplication, PublicClientApplication, InteractionType, BrowserCacheLocation } from "@azure/msal-browser";

export function MSALInstanceFactory(): IPublicClientApplication {
  return new PublicClientApplication({
    auth: {
      clientId: "00001111-aaaa-2222-bbbb-3333cccc4444",
      redirectUri: "http://localhost:4200",
      postLogoutRedirectUri: "http://localhost:4200",
    },
    cache: {
      cacheLocation: BrowserCacheLocation.LocalStorage,
    },
  });
}

export function MSALInterceptorConfigFactory(): MsalInterceptorConfiguration {
  const protectedResourceMap = new Map<string, Array<string>>();
  protectedResourceMap.set("https://graph.microsoft.com/v1.0/me", ["user.read"]);

  return {
    interactionType: InteractionType.Redirect,
    protectedResourceMap,
  };
}

export function MSALGuardConfigFactory(): MsalGuardConfiguration {
  return {
    interactionType: InteractionType.Redirect,
    authRequest: {
      scopes: ["user.read"],
    },
    loginFailedRoute: "./login-failed",
  };
}

@NgModule({
  imports: [MsalModule],
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: MsalInterceptor,
      multi: true,
    },
    {
      provide: MSAL_INSTANCE,
      useFactory: MSALInstanceFactory,
    },
    {
      provide: MSAL_GUARD_CONFIG,
      useFactory: MSALGuardConfigFactory,
    },
    {
      provide: MSAL_INTERCEPTOR_CONFIG,
      useFactory: MSALInterceptorConfigFactory,
    },
    MsalGuard,
    MsalBroadcastService,
    MsalService,
  ],
  bootstrap: [AppComponent, MsalRedirectComponent],
})
export class AppModule {}

platformBrowserDynamic

Wenn Sie MSAL Angular dynamisch konfigurieren müssen (z. B. basierend auf werten, die von einer API zurückgegeben werden), können Sie verwenden platformBrowserDynamic. platformBrowserDynamic ist eine Plattform-Factory, die zum Initialisieren der Anwendung verwendet wird und Konfigurationsoptionen entgegennehmen kann. platformBrowserDynamic sollte bereits vorhanden sein, wenn die Angular-Anwendung eingerichtet ist.

Im Folgenden sehen Sie ein Beispiel für die dynamische Konfiguration @azure/msal-angular mit platformBrowserDynamic und einer JSON-Datei:

app.module.ts

import { MsalModule, MsalInterceptor, MsalService } from "@azure/msal-angular";

@NgModule({
  imports: [MsalModule],
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: MsalInterceptor,
      multi: true,
    },
    MsalService,
  ],
  bootstrap: [AppComponent],
})
export class AppModule {}

main.ts

import { enableProdMode } from "@angular/core";
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";

import { AppModule } from "./app/app.module";
import { environment } from "./environments/environment";
import { MSAL_INSTANCE, MSAL_GUARD_CONFIG, MSAL_INTERCEPTOR_CONFIG } from "@azure/msal-angular";
import { PublicClientApplication, Configuration } from "@azure/msal-browser";

if (environment.production) {
  enableProdMode();
}

function loggerCallback(logLevel: LogLevel, message: string) {
  console.log("MSAL Angular: ", message);
}

fetch("/assets/configuration.json")
  .then((response) => response.json())
  .then((json) => {
    platformBrowserDynamic([
      {
        provide: MSAL_INSTANCE,
        useValue: new PublicClientApplication({
          auth: json.msal.auth,
          cache: json.msal.cache,
          system: {
            loggerOptions: {
              loggerCallback,
              logLevel: LogLevel.Info,
              piiLoggingEnabled: false,
            },
          },
        }),
      },
      {
        provide: MSAL_GUARD_CONFIG,
        useValue: {
          interactionType: json.guard.interactionType,
          authRequest: json.guard.authRequest,
          loginFailedRoute: json.guard.loginFailedRoute,
        } as MsalGuardConfiguration,
      },
      {
        provide: MSAL_INTERCEPTOR_CONFIG,
        useValue: {
          interactionType: json.interceptor.interactionType,
          protectedResourceMap: new Map(json.interceptor.protectedResourceMap),
        } as MsalInterceptorConfiguration,
      },
    ])
      .bootstrapModule(AppModule)
      .catch((err) => console.error(err));
  });

src/assets/configuration.json

{
  "msal": {
    "auth": {
      "clientId": "clientid",
      "authority": "https://login.microsoftonline.com/common/",
      "redirectUri": "http://localhost:4200/",
      "postLogoutRedirectUri": "http://localhost:4200/",
      "navigateToLoginRequestUrl": true
    },
    "cache": {
      "cacheLocation": "localStorage",
      "storeAuthStateInCookie": true
    }
  },
  "guard": {
    "interactionType": "redirect",
    "authRequest": {
      "scopes": ["user.read"]
    },
    "loginFailedRoute": "/login-failed"
  },
  "interceptor": {
    "interactionType": "redirect",
    "protectedResourceMap": [["https://graph.microsoft.com/v1.0/me", ["user.read"]]]
  }
}

Dynamische Konfigurationen mit Factory-Providern und APP_INITIALIZER

Um MSAL Angular dynamisch zu konfigurieren, können Sie die Factory-Provider mit APP_INITIALIZER verwenden.

src/app/config.service.ts

import { Injectable } from "@angular/core";
import { HttpClient, HttpBackend } from "@angular/common/http";
import { map } from "rxjs/operators";

@Injectable({
  providedIn: "root",
})
export class ConfigService {
  private settings: any;
  private http: HttpClient;

  constructor(private readonly httpHandler: HttpBackend) {
    this.http = new HttpClient(httpHandler);
  }

  init(endpoint: string): Promise<boolean> {
    return new Promise<boolean>((resolve, reject) => {
      this.http
        .get(endpoint)
        .pipe(map((result) => result))
        .subscribe(
          (value) => {
            this.settings = value;
            resolve(true);
          },
          (error) => {
            reject(error);
          }
        );
    });
  }

  getSettings(key?: string | Array<string>): any {
    if (!key || (Array.isArray(key) && !key[0])) {
      return this.settings;
    }

    if (!Array.isArray(key)) {
      key = key.split(".");
    }

    let result = key.reduce((account: any, current: string) => account && account[current], this.settings);

    return result;
  }
}

src/app/msal-config-dynamic.module.ts

import { InjectionToken, NgModule, APP_INITIALIZER } from "@angular/core";
import { IPublicClientApplication, PublicClientApplication, LogLevel } from "@azure/msal-browser";
import { MsalGuard, MsalInterceptor, MsalBroadcastService, MsalInterceptorConfiguration, MsalModule, MsalService, MSAL_GUARD_CONFIG, MSAL_INSTANCE, MSAL_INTERCEPTOR_CONFIG, MsalGuardConfiguration } from "@azure/msal-angular";
import { HTTP_INTERCEPTORS } from "@angular/common/http";
import { ConfigService } from "./config.service";

const AUTH_CONFIG_URL_TOKEN = new InjectionToken<string>("AUTH_CONFIG_URL");

export function initializerFactory(env: ConfigService, configUrl: string): any {
  const promise = env.init(configUrl).then((value) => {
    console.log("finished getting configurations dynamically.");
  });
  return () => promise;
}

export function loggerCallback(logLevel: LogLevel, message: string) {
  console.log(message);
}

export function MSALInstanceFactory(config: ConfigService): IPublicClientApplication {
  return new PublicClientApplication({
    auth: config.getSettings("msal").auth,
    cache: config.getSettings("msal").cache,
    system: {
      loggerOptions: {
        loggerCallback,
        logLevel: LogLevel.Info,
        piiLoggingEnabled: false,
      },
    },
  });
}

export function MSALInterceptorConfigFactory(config: ConfigService): MsalInterceptorConfiguration {
  const protectedResourceMap = new Map<string, Array<string>>(config.getSettings("interceptor").protectedResourceMap);

  return {
    interactionType: config.getSettings("interceptor").interactionType,
    protectedResourceMap,
  };
}

export function MSALGuardConfigFactory(config: ConfigService): MsalGuardConfiguration {
  return {
    interactionType: config.getSettings("guard").interactionType,
    authRequest: config.getSettings("guard").authRequest,
    loginFailedRoute: config.getSettings("guard").loginFailedRoute,
  };
}

@NgModule({
  providers: [],
  imports: [MsalModule],
})
export class MsalConfigDynamicModule {
  static forRoot(configFile: string) {
    return {
      ngModule: MsalConfigDynamicModule,
      providers: [
        ConfigService,
        { provide: AUTH_CONFIG_URL_TOKEN, useValue: configFile },
        { provide: APP_INITIALIZER, useFactory: initializerFactory, deps: [ConfigService, AUTH_CONFIG_URL_TOKEN], multi: true },
        {
          provide: MSAL_INSTANCE,
          useFactory: MSALInstanceFactory,
          deps: [ConfigService],
        },
        {
          provide: MSAL_GUARD_CONFIG,
          useFactory: MSALGuardConfigFactory,
          deps: [ConfigService],
        },
        {
          provide: MSAL_INTERCEPTOR_CONFIG,
          useFactory: MSALInterceptorConfigFactory,
          deps: [ConfigService],
        },
        MsalService,
        MsalGuard,
        MsalBroadcastService,
        {
          provide: HTTP_INTERCEPTORS,
          useClass: MsalInterceptor,
          multi: true,
        },
      ],
    };
  }
}

src/app/app.module.ts

import { BrowserModule } from "@angular/platform-browser";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { NgModule } from "@angular/core";

import { MatButtonModule } from "@angular/material/button";
import { MatToolbarModule } from "@angular/material/toolbar";
import { MatListModule } from "@angular/material/list";

import { AppRoutingModule } from "./app-routing.module";
import { AppComponent } from "./app.component";
import { HomeComponent } from "./home/home.component";
import { ProfileComponent } from "./profile/profile.component";

import { HttpClientModule } from "@angular/common/http";
import { MsalRedirectComponent } from "@azure/msal-angular";
import { DetailComponent } from "./detail/detail.component";
import { MsalConfigDynamicModule } from "./msal-config-dynamic.module";

@NgModule({
  declarations: [AppComponent, HomeComponent, ProfileComponent, DetailComponent],
  imports: [BrowserModule, BrowserAnimationsModule, AppRoutingModule, MatButtonModule, MatToolbarModule, MatListModule, HttpClientModule, MsalConfigDynamicModule.forRoot("assets/configuration.json")],
  providers: [],
  bootstrap: [AppComponent, MsalRedirectComponent],
})
export class AppModule {}

src/assets/configuration.json

{
  "msal": {
    "auth": {
      "clientId": "clientid",
      "authority": "https://login.microsoftonline.com/common/",
      "redirectUri": "http://localhost:4200/",
      "postLogoutRedirectUri": "http://localhost:4200/",
      "navigateToLoginRequestUrl": true
    },
    "cache": {
      "cacheLocation": "localStorage",
      "storeAuthStateInCookie": true
    }
  },
  "guard": {
    "interactionType": "redirect",
    "authRequest": {
      "scopes": ["user.read"]
    },
    "loginFailedRoute": "/login-failed"
  },
  "interceptor": {
    "interactionType": "redirect",
    "protectedResourceMap": [["https://graph.microsoft.com/v1.0/me", ["user.read"]]]
  }
}

MsalGuard – Dynamische Authentifizierungsanforderung

Mit MsalGuard können Sie die authRequest zur Laufzeit dynamisch ändern. Auf diese Weise können Sie eine andere Autorität für eine Route auswählen oder Bereiche basierend auf routerStateSnapshot dynamisch hinzufügen.

export function MSALGuardConfigFactory(): MsalGuardConfiguration {
  return {
    interactionType: InteractionType.Redirect,
    authRequest: (authService, state) => {
      return {
        scopes: state.root.url.some((x) => x.path === "calendar") ? ["user.read", "	Calendars.Read"] : ["user.read"],
      };
    },
    loginFailedRoute: "./login-failed",
  };
}

Konfigurationen für Angular-Apps mit eigenständigen Komponenten

Angular-Anwendungen, die eigenständige Komponenten verwenden, können wie oben in der Datei app.config.ts mit Factory-Providern verwendet werden, die dann zum Bootstrapping in main.ts importiert wird.

Bitte lesen Sie unser Angular Standalone-Beispiel für die Verwendung.

// app.config.ts
import { ApplicationConfig, importProvidersFrom } from "@angular/core";
import { provideRouter } from "@angular/router";
import { routes } from "./app.routes";
import { BrowserModule } from "@angular/platform-browser";
import { provideHttpClient, withInterceptorsFromDi, HTTP_INTERCEPTORS, withFetch, withInterceptors } from "@angular/common/http";
import { provideNoopAnimations } from "@angular/platform-browser/animations";
import { IPublicClientApplication, PublicClientApplication, InteractionType, BrowserCacheLocation, LogLevel } from "@azure/msal-browser";
import { MsalInterceptor, MSAL_INSTANCE, MsalInterceptorConfiguration, MsalGuardConfiguration, MSAL_GUARD_CONFIG, MSAL_INTERCEPTOR_CONFIG, MsalService, MsalGuard, MsalBroadcastService } from "@azure/msal-angular";

export function loggerCallback(logLevel: LogLevel, message: string) {
  console.log(message);
}

export function MSALInstanceFactory(): IPublicClientApplication {
  return new PublicClientApplication({
    auth: {
      clientId: "clientid",
      authority: "https://login.microsoftonline.com/common/",
      redirectUri: "/",
      postLogoutRedirectUri: "/",
    },
    cache: {
      cacheLocation: BrowserCacheLocation.LocalStorage,
    },
    system: {
      allowPlatformBroker: false, // Disables WAM Broker
      loggerOptions: {
        loggerCallback,
        logLevel: LogLevel.Info,
        piiLoggingEnabled: false,
      },
    },
  });
}

export function MSALInterceptorConfigFactory(): MsalInterceptorConfiguration {
  const protectedResourceMap = new Map<string, Array<string>>();
  protectedResourceMap.set("https://graph.microsoft.com/v1.0/me", ["user.read"]);

  return {
    interactionType: InteractionType.Redirect,
    protectedResourceMap,
  };
}

export function MSALGuardConfigFactory(): MsalGuardConfiguration {
  return {
    interactionType: InteractionType.Redirect,
    authRequest: {
      scopes: ["user.read"],
    },
    loginFailedRoute: "/login-failed",
  };
}

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    importProvidersFrom(BrowserModule),
    provideNoopAnimations(),
    provideHttpClient(withInterceptorsFromDi(), withFetch()),
    {
      provide: HTTP_INTERCEPTORS,
      useClass: MsalInterceptor,
      multi: true,
    },
    {
      provide: MSAL_INSTANCE,
      useFactory: MSALInstanceFactory,
    },
    {
      provide: MSAL_GUARD_CONFIG,
      useFactory: MSALGuardConfigFactory,
    },
    {
      provide: MSAL_INTERCEPTOR_CONFIG,
      useFactory: MSALInterceptorConfigFactory,
    },
    MsalService,
    MsalGuard,
    MsalBroadcastService,
  ],
};
// main.ts
import { bootstrapApplication } from "@angular/platform-browser";
import { appConfig } from "./app/app.config";
import { AppComponent } from "./app/app.component";

bootstrapApplication(AppComponent, appConfig).catch((err) => console.error(err));