Angular How-to: Microsoft ADAL for Angular 6+ with Configurable Settings

Developer

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

Comments are closed. Login to edit/delete your existing comments

  • Mark Deraeve

    I’m having trouble with Angular 9 and this implementation. Did somebody tried this before? I get this error:
    ERROR in The target entry-point “microsoft-adal-angular6” has missing dependencies:
    – @angular/core
    – rxjs
    – @angular/router
    Thanks.

    Ok, I could solve this by reinstalling the packages??? Strange, but ok, it works in Angular 9 🙂

  • rakesh handoo

    Hi,
    I have developed a SPA using Angular8 and .net core 3.0 and following Microsoft ADAL for Angular 6+ with Configurable Settings example.
    Could you please suggest how do we refresh the token, when it is about to expire i.e. !this.adal.isAuthenticated ?
    Thanks,
    Rakesh

  • Bhuvanesh S

    Getting 401 unauthorized after 1 hour and please let me know how to handle that in interceptor ? Thanks in advance.

  • Jeff Positano

    Would this work for NativeScript Angular, or is this strictly for web apps?