MSAL Angular Yapılandırması

Angular için MSAL birden çok şekilde yapılandırılabilir. Bu makalede, statik ve dinamik yaklaşımlar da dahil olmak üzere MSAL Angular için kullanılabilir yapılandırma seçenekleri açıklanır ve kimlik doğrulamasını uygulamanızla tümleştirmek için kod örnekleri sağlanır. Uygulamanızın gereksinimlerine en uygun yapılandırma yöntemini seçmek ve kullanıcılarınız için sorunsuz bir oturum açma deneyimi sağlamak için bu kılavuzu kullanın.

Yapılandırma Seçenekleri

@azure/msal-angular üç yapılandırma nesnesi kabul eder:

  1. Yapılandırma: Bu, çekirdek @azure/msal-browser kitaplık için kullanılan yapılandırma nesnesiyle aynıdır. Tüm yapılandırma seçenekleri burada bulunabilir.
  2. MsalGuardConfiguration: Angular guard’a özgü bir seçenekler kümesi.
  3. MsalInterceptorConfiguration: Angular interceptor'ü için özel bir dizi seçenek.

Angular'a özgü yapılandırmalar

  • interactionType, MsalGuardConfiguration ve MsalInterceptorConfiguration üzerinde belirtilmelidir ve Popup veya Redirect olarak ayarlanabilir.
  • protectedResourceMap üzerindeki MsalInterceptorConfiguration nesnesi, yolları korumak için kullanılır.
  • Ek seçenekleri ayarlamak için, MsalGuardConfiguration ve MsalInterceptorConfiguration üzerinde isteğe bağlı bir authRequest nesnesi belirtilebilir.
  • MsalGuardConfiguration üzerinde isteğe bağlı bir loginFailedRoute dizesi ayarlanabilir. Oturum açılması gerekiyorsa ve oturum açma başarısız olursa Msal Guard bu rotaya yönlendirir.

Yapılandırmalar, kullanım ve MSAL Angular v1 farklılıkları hakkında daha fazla bilgi için lütfen MsalInterceptor ve MsalGuard belgelerimize bakın.

Yeniden yönlendirmeler için yapılandırma

Yeniden yönlendirmeleri kullanmayı düşünüyorsanız, MsalRedirectComponent içe aktarmanızı ve AppComponent ile başlatmanızı öneririz. Daha fazla ayrıntı için lütfen yeniden yönlendirme belgelerine bakın.

Not: MSAL v3.x itibarıyla uygulama nesnesinin başlatılması gerekir. Diğer ayrıntılar için v2-v3 yükseltme kılavuzuna bakın. MSAL Angular v5'e yükseltme hakkında ayrıntılı bilgi için v4-v5 yükseltme kılavuzuna bakın.

MsalModule.forRoot

MsalModule sınıfı, app.module.ts dosyanızda çağrılabilen statik bir yöntem içerir:

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 {}

Fabrika Sağlayıcıları

Yapılandırma seçeneklerini fabrika sağlayıcıları aracılığıyla da sağlayabilirsiniz.

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

MSAL Angular'ı dinamik olarak yapılandırmanız gerekiyorsa (örneğin, bir API'den döndürülen değerlere göre), kullanabilirsiniz platformBrowserDynamic. platformBrowserDynamic, uygulamayı önyüklemek için kullanılan ve yapılandırma seçeneklerini kabul edebilen bir platform fabrikasıdır. platformBrowserDynamic Angular uygulaması ayarlandığında zaten mevcut olmalıdır.

Aşağıda, @azure/msal-angular öğesinin platformBrowserDynamic ve bir JSON dosyası kullanılarak dinamik olarak nasıl yapılandırılacağına dair bir örnek verilmiştir:

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"]]]
  }
}

Fabrika Sağlayıcıları ve APP_INITIALIZER kullanan dinamik yapılandırmalar

MSAL Angular'ı dinamik olarak yapılandırmak için fabrika sağlayıcılarını APP_INITIALIZER kullanabilirsiniz.

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 - Dinamik kimlik doğrulama isteği

MsalGuard ayrıca çalışma zamanında authRequest'i dinamik olarak değiştirmenize de olanak tanır. Bu, bir yol için farklı bir yetkili seçmenize veya RouterStateSnapshot'ı temel alan kapsamları dinamik olarak eklemenize olanak sağlar.

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",
  };
}

Tek başına bileşenlere sahip Angular uygulamaları için yapılandırmalar

Standalone bileşenler kullanan Angular uygulamaları, app.config.ts dosyasında yukarıda olduğu gibi fabrika sağlayıcıları ile kullanılabilir; bu dosya daha sonra önyükleme için main.ts içine aktarılır.

Kullanım için lütfen Angular Tek Başına Örneğimize bakın.

// 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));