March 8th, 2019

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

Developer Support
Cloud Solution Architects

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.

 

Author

Developer Support
Cloud Solution Architects

Microsoft Developer Support helps software developers rapidly build and deploy quality applications for Microsoft platforms.

22 comments

Discussion is closed. Login to edit/delete existing comments.

  • Jeff Positano

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

  • Bhuvanesh S

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

  • 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

    • Joshy Joseph

      Did you find any solution for this(refresh the token)?

  • 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 🙂

  • Abe IsleemMicrosoft employee

    For those of you having trouble getting this to work as a result of the reject(`Could not load file '${jsonFile}': ${JSON.stringify(response)}`) error triggering, I figured it out.

    What was happening is the AppConfig class is making an Http get() request which triggered the HTTP_INTERCEPTORS and therefore calls the InsertAuthTokenInterceptor class. The InsertAuthTokenInterceptor class tries to utilize the MsAdalAngular6Service to inject the bearer token into the request but MsAdalAngular6Service is not set up yet since it's waiting on the configurations that the AppConfig class is collecting. So, we have a classic circular dependency issue here.

    To reiterate:
    - MsAdalAngular6Service is depending on...

    Read more
  • Roman Olkhovsky

    Why do you need this code ?

    if (!resource || !this.adal.isAuthenticated) {
    return next.handle(req);
    }

    Couldn’t get my application work with this.
    But it works after this part has been removed.

  • Martin Johansson

    Thank you for this post!
    It was exactly what I needed and it works perfectly!

  • Liraz Amir

    First, Thank you for the awesome guides.

    I have a problem, I have implemented everything exactly the same and it works, but, after a few Http calls it gets automatically logged out and isAuthenticated returns false.

    do you have any idea about what this could be?

    • Liraz Amir

      I Got the answer – I used:
      “cacheLocation”: “localStorage”

      I changed it to:
      “cacheLocation”: “sessionStorage”

  • Prashant Ubale

    I have found number of articles for AD integration with Angular and Dot Net Core:
    1. Active Directory Authorization
    2. microsoft-authentication-library-for-js
    3. angular-oauth2-oidc
    4. angular-with-azure-active-directory-authentication-adal-angular4.

    I have implemented first one: 1. Active Directory Authorization
    but it does not work with separate 2 projects as front-end in angular and backend in dot net core. It gives 'Unauthorized', so I made it one project in visual studio. but still it is not working.
    After studding all above articles I have one question: In your implementation, Will only this library works for ADAL authentication and it does not require any backend/API chabges?
    If...

    Read more
    • Shwetha Nayak

      Hi Prashant,

      Even i am getting 401 Unauthorised Error.

      I have a query – how do u get correct access token i.e – his.adal.acquireToken(resource) : Here what should be the resource .
      Should it be backend API or https://graph.microsoft.com or what.