Laurie Atkinson, Senior Consultant, Use the microsoft-adal-angular6 wrapper library to authenticate with Azure Active Directory in your Angular 6+ app.
Active Directory Authentication Library (ADAL) for Angular 6+ is a library for integrating Azure AD into your Angular app. However, its provided instructions and example application assume a hardcoded configuration and often your implementation needs to support configurable options. This post provides the modifications necessary to remove this limitation and offer a more realistic scenario.
Get the library
npm install microsoft-adal-angular6
Create a configuration file
Refer to this post for how to set up an editable configuration file that can be customized for multiple environments.
Include a node in your configuration file in the format expected by the microsoft-adal-angular6 library.
The endpoints property will be important for the Angular http interceptor to match which API calls should include the authentication token inserted into the header.
assets\config\config.dev.json
{ . . ., "adalConfig": { "clientId": "<client-id-here>", "tenant": "<tenant-guid-here>", "cacheLocation": "localStorage", "endpoints": { "api": "<client-id-here>" } } }
Reference the ADAL module in your app
Do not call forRoot() on the MsAdalAngular6Module as shown in the library’s documentation, because this forces you to provide the adalConfig object too soon in the bootstrapping process. The initialization of the modules listed in the imports section of the module declaration does not wait for the APP_INITIALIZER to complete. Instead declare it this way:
app.module.ts
@NgModule({ imports: [ MsAdalAngular6Module ], declarations: [ . . . ], providers: [ . . . ], bootstrap: [ AppComponent ] }) export class AppModule { }
Initialize the ADAL configuration
Instead of providing a hardcoded configuration object, retrieve the configuration settings from the JSON file illustrated above using Angular’s APP_INITIALIZER feature. Then specify an alternate provider for the adalConfig parameter to the MsAdalAngular6Service constructor, which returns the retrieved config data instead of a hardcoded parameter.
In addition, add the AuthenticationGuard service which is part of the microsoft-adal-angular6 library.
With these changes, the AppModule should now look as follows:
app.module.ts
import { MsAdalAngular6Module, MsAdalAngular6Service, AuthenticationGuard } from 'microsoft-adal-angular6'; let adalConfig: any; // will be initialized by APP_INITIALIZER export function msAdalAngular6ConfigFactory() { return adalConfig; // will be invoked later when creating MsAdalAngular6Service } // refer to: // https://devblogs.microsoft.com/premier-developer/angular-how-to-editable-config-files/ // for a description of the AppConfig service export function initializeApp(appConfig: AppConfig) { const promise = appConfig.load().then(() => { adalConfig = { tenant: AppConfig.settings.adalConfig.tenant, clientId: AppConfig.settings.adalConfig.clientId, redirectUri: window.location.origin, endpoints: AppConfig.settings.adalConfig.endpoints, navigateToLoginRequestUrl: false, cacheLocation: AppConfig.settings.adalConfig.cacheLocation }; }); return () => promise; } @NgModule({ imports: [ MsAdalAngular6Module ], declarations: [ . . . ], providers: [ { provide: APP_INITIALIZER, useFactory: initializeApp, deps: [AppConfig], multi: true }, MsAdalAngular6Service, { provide: 'adalConfig', useFactory: msAdalAngular6ConfigFactory, deps: [] }, AuthenticationGuard ], bootstrap: [ AppComponent ] }) export class AppModule { }
Create an HttpInterceptor to insert the bearer token into API requests
The purpose of the endpoints property on the adalConfig object is to automatically populate the requests matching those endpoints with the token obtained by AAD. My experience is that this insertion does not occur automatically and instead requires a custom interceptor be provided.
insert-auth-token-interceptor.ts
import { Injectable } from '@angular/core'; import { HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http'; import { mergeMap } from 'rxjs/operators'; import { MsAdalAngular6Service } from 'microsoft-adal-angular6'; @Injectable() export class InsertAuthTokenInterceptor implements HttpInterceptor { constructor(private adal: MsAdalAngular6Service) { } intercept(req: HttpRequest<any>, next: HttpHandler) { // get api url from adal config const resource = this.adal.GetResourceForEndpoint(req.url); if (!resource || !this.adal.isAuthenticated) { return next.handle(req); } // merge the bearer token into the existing headers return this.adal.acquireToken(resource).pipe( mergeMap((token: string) => { const authorizedRequest = req.clone({ headers: req.headers.set('Authorization', `Bearer ${token}`), }); return next.handle(authorizedRequest); })); } }
app.module.ts
@NgModule({ . . . providers: [ { provide: HTTP_INTERCEPTORS, useClass: InsertAuthTokenInterceptor }, . . . }) export class AppModule { }
With these modifications, your Angular app should be ready to start using Azure AD and the ADAL library for authentication. For more details on library usage, refer to the documentation here: https://www.npmjs.com/package/microsoft-adal-angular6.
22 comments