Azure Cloud
Angular Azure AD MS Graph Office 365 SPA Tenants Visual Code

How to use the MS Graph API in Angular Applications

Welcome to today’s blog.

Today I will be showing how we can use the Microsoft Graph API to help us interact with Azure cloud applications in Office 365.

The Microsoft Graph API has a rich set of API methods, which are publicly available from HTTP REST (GET, POST, PUT, DELETE, and PATCH) calls and in addition, have the capability to flexibly run queries within the HTTO REST calls using OData. In a previous post I showed how to implement a .NET Core Web API with OData API methods. All methods within MS Graph are accessible with an existing MS account which is linked to either an Office 365 subscription or access to MS Outlook online email (free) and the associated applications that are accessible within Outlook (Excel, Calendar, Word etc.)

To be able to connect to Office 365 services from our Angular application, we use the Microsoft Authentication Library, also known as MSAL. The Microsoft Identity Platform has many components of which MSAL is part of.

MSAL allows us to authenticate to Azure resources using either an Azure identity account or a Microsoft account.

To make use of MSAL in a web client application, we will need to provide the following configuration parameters:

ClientId: The application id of the application we register in an Azure tenant.

Authority: The endpoint URI we use to authenticate a particular tenant of users. These endpoints are:

Personal Microsoft accounts:

https://login.microsoftonline.com/consumers/

Work and schools accounts, personal Microsoft accounts:

https://login.microsoftonline.com/common/

Work and schools accounts:

https://login.microsoftonline.com/organizations/

Organization only with <tenant> the tenant ID for the AD Azure tenant:

https://login.microsoftonline.com/<tenant>/

Redirect URL: This is the URL endpoint the MSAL authentication will route to after signing in to the MS identity provider (Azure AD). The URL will be one of the following:

Native client (a web client application or desktop application):

https://login.microsoftonline.com/common/oauth2/nativeclient

.NET Core application or API (with no user interface):

https://localhost

In my previous post I showed how to register a web application under an Azure AD tenant and provision the above parameters.

In the following example I will use a personal MS account to demonstrate use of the Microsoft identity library for Angular. If you have an account within an organization, work, or Azure AD identity within a tenant premium you can experiment with the other endpoints.

Excerpts of the code I will be showing are taken from the following tutorial on the Microsoft Azure AD website.

First, ensure that you have installed the MSAL packages for Angular. This is done from the VS Code terminal prompt:

npm install

ng new angular-msgraph-app

cd angular-msgraph-app

npm install msal @azure/msal-angular

Your package.json will now contain the following package definitions:

"dependencies": {
    …
    "@azure/msal-angular": "^1.1.2",
    "msal": "^1.4.4",
    …
},

MSAL.js version 1.3 and above supports auth flow.

Below is the configuration required within app.module.ts:

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

import { MaterialModule } from './material/material.module';
import { AuthService } from './services/auth.service';
import { AppRoutingModule } from './app-routing.module';
    …
import { ProfileComponent } from './profile/profile.component';

import { MsalModule, MsalInterceptor } from '@azure/msal-angular';
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { CalendarComponent } from './calendar/calendar.component';

export const protectedResourceMap: [string, string[]][] = [
  ['https://graph.microsoft.com/v1.0/me', ['user.read']]
];

const isIE = window.navigator.userAgent.indexOf('MSIE ') > -1 || window.navigator.userAgent.indexOf('Trident/') > -1;

@NgModule({
  declarations: [
    AppComponent,
    …
    ProfileComponent,
    CalendarComponent
  ],
  imports: [
    HttpClientModule,
    BrowserModule,
    BrowserAnimationsModule,
    NoopAnimationsModule, 
    AppRoutingModule,
    MaterialModule,
    MsalModule.forRoot({
      auth: {
        clientId: '529d1c87-1253-41dc-9aff-e20e55111c9c',
        authority: 'https://login.microsoftonline.com/consumers/',
        validateAuthority: true,
        redirectUri: 'http://localhost:4200/',
        postLogoutRedirectUri: 'http://localhost:4200/',
        navigateToLoginRequestUrl: true,
      },
      cache: {
        cacheLocation: 'localStorage',
        storeAuthStateInCookie: isIE, // set to true for IE 11
      },
    },
    {
      popUp: !isIE,
      consentScopes: [
        'user.read',
        'openid',
        'profile'
      ],
      unprotectedResources: ['https://www.microsoft.com/en-us/'],
      protectedResourceMap,
      extraQueryParameters: {}
    })
  ],
  providers: [
    AuthService, 
    {
      provide: HTTP_INTERCEPTORS,
      useClass: MsalInterceptor,
      multi: true
    }
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

There are a few things to note:

The protected resource is the MS Graph API endpoint we wish to access along with the requested scope(s). To be able to access MS Graph user profile in read only mode this is shown below:

['https://graph.microsoft.com/v1.0/me', ['user.read']]

The MSAL configuration for authentication for the above parameters I mentioned above is shown below:

MsalModule.forRoot({
  auth: {
    clientId: '529d1c87-1253-40ac-9aae-e20e55994c9c',
    authority: 'https://login.microsoftonline.com/consumers/',
    validateAuthority: true,
    redirectUri: 'http://localhost:4200/',
    postLogoutRedirectUri: 'http://localhost:4200/',
    navigateToLoginRequestUrl: true,
  },
  cache: {
    cacheLocation: 'localStorage',
    storeAuthStateInCookie: isIE, // set to true for IE 11
  },
},

The cache key is used to determine the browser key that will store the contents of the token that is received from Azure AD authority after signing in.

The we define the consent scopes which are the permissions we will give for the application to access our account.

consentScopes: [
  'user.read',
  'openid',
  'profile'
],
unprotectedResources: ['https://www.microsoft.com/en-us/'],

The authentication service which allows us to handle URL redirection, login, and logout is shown below:

import { Injectable } from '@angular/core'
import { BroadcastService, MsalService } from '@azure/msal-angular';
import { Logger, CryptoUtils } from 'msal';

@Injectable()
export class AuthService {
    isIframe = false;
    loggedIn = false;
  
    constructor(private broadcastService: BroadcastService, 
        private authService: MsalService) { }
  
    ngOnInit() {
      this.isIframe = window !== window.parent && !window.opener;
  
      this.checkoutAccount();
  
      this.broadcastService.subscribe('msal:loginSuccess', () => {
        this.checkoutAccount();
      });
  
      this.authService.handleRedirectCallback((authError, response) => {
        if (authError) {
          console.error('Redirect Error: ', authError.errorMessage);
          return;
        }
  
        console.log('Redirect Success: ', response);
      });
  
      this.authService.setLogger(new Logger((logLevel, message, piiEnabled) => 
      {
        console.log('MSAL Logging: ', message);
      }, {
        correlationId: CryptoUtils.createNewGuid(),
        piiLoggingEnabled: false
      }));
    }
  
    checkoutAccount() {
      this.loggedIn = !!this.authService.getAccount();
    }
  
    login() {
      const isIE = window.navigator.userAgent.indexOf('MSIE ') > -1 || window.navigator.userAgent.indexOf('Trident/') > -1;
  
      if (isIE) {
        this.authService.loginRedirect();
      } else {
        this.authService.loginPopup();
      }
    }
  
    logout() {
      this.authService.logout();
    }
}

To provide an authentication guard we use an interceptor from the Angular MSAL library, MsalInterceptor

AuthService, 
{
    provide: HTTP_INTERCEPTORS,
    useClass: MsalInterceptor,
    multi: true
}

This injects the tokens we receive from the MS sign-in dialog into the authentication service AuthService.

In our router definition app.router.ts, we will need to import the MsalGuard and activate it within each profile we wish to protect. In this case they will be pages that will depend on MS Graph calls:

import { MsalGuard } from '@azure/msal-angular';
    …
const routes: Routes = [
  {path: '', redirectTo: 'home', pathMatch: 'prefix'},
  {path: 'home', component: HomeComponent },
  {path: 'about', component: AboutComponent },
  {path: 'profile', component: ProfileComponent, canActivate: [MsalGuard]},
  {path: 'calendar', component: CalendarComponent, canActivate: [MsalGuard]}  
];

When the application is run the MSAL service calls handleRedirectCallback() to authenticate the user.

After the initial authentication, the next dialog will open to confirm authorization scopes for MS Graph Explorer to access the user account. 

The next dialog will request consent to confirm authorization scopes for the registered app to access the user account:

Once the authorization is complete the interceptor will open access to the protected profile page.

The profile component is shown below.

import { MsalService } from '@azure/msal-angular';
…
const GRAPH_ENDPOINT = 'https://graph.microsoft.com/v1.0/me';

@Component({
  selector: 'app-profile',
  templateUrl: './profile.component.html',
  styleUrls: ['./profile.component.css']
})
export class ProfileComponent implements OnInit {
  profile;

  constructor(private authService: MsalService, private http: HttpClient) { }

  ngOnInit() {
    this.getProfile();
  }

  getProfile() {
    this.http.get(GRAPH_ENDPOINT)
      .toPromise().then(profile => {
        this.profile = profile;
      });
  }
}

The MS Graph endpoint then obtains the user profile from the MS Graph endpoint:

https://graph.microsoft.com/v1.0/me

using getProfile().

The user’s name from their profile is then displayed as shown:

That’s all for today’s post.

In the next post I will show how to amend the same application to retrieve Outlook calendar events.

I hope you found this post useful and informative.

Social media & sharing icons powered by UltimatelySocial