This browser is no longer supported.

Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.

Download Microsoft Edge More info about Internet Explorer and Microsoft Edge

In this tutorial, you build an Angular single-page application (SPA) that signs in users and calls the Microsoft Graph API by using the authorization code flow with PKCE. The SPA you build uses the Microsoft Authentication Library (MSAL) for Angular v2.

In this tutorial:

  • Create an Angular project with npm
  • Register the application in the Azure portal
  • Add code to support user sign-in and sign-out
  • Add code to call Microsoft Graph API
  • Test the app
  • MSAL Angular v2 improves on MSAL Angular v1 by supporting the authorization code flow in the browser instead of the implicit grant flow. MSAL Angular v2 does NOT support the implicit flow.

    Prerequisites

  • Node.js for running a local web server.
  • Visual Studio Code or other editor for modifying project files.
  • How the sample app works

    The sample application created in this tutorial enables an Angular SPA to query the Microsoft Graph API or a web API that accepts tokens issued by the Microsoft identity platform. It uses the Microsoft Authentication Library (MSAL) for Angular v2, a wrapper of the MSAL.js v2 library. MSAL Angular enables Angular 9+ applications to authenticate enterprise users by using Azure Active Directory (Azure AD), and also users with Microsoft accounts and social identities like Facebook, Google, and LinkedIn. The library also enables applications to get access to Microsoft cloud services and Microsoft Graph.

    In this scenario, after a user signs in, an access token is requested and added to HTTP requests through the authorization header. Token acquisition and renewal are handled by MSAL.

    Libraries

    This tutorial uses the following libraries:

    Library Description

    You can find the source code for all of the MSAL.js libraries in the AzureAD/microsoft-authentication-library-for-js repository on GitHub.

    Create your project

    Once you have Node.js installed, open up a terminal window and then run the following commands to generate a new Angular application:

    npm install -g @angular/cli                         # Install the Angular CLI
    ng new msal-angular-tutorial --routing=true --style=css --strict=false    # Generate a new Angular app
    cd msal-angular-tutorial                            # Change to the app directory
    npm install @angular/material @angular/cdk          # Install the Angular Material component library (optional, for UI)
    npm install @azure/msal-browser @azure/msal-angular # Install MSAL Browser and MSAL Angular in your application
    ng generate component home                          # To add a home page
    ng generate component profile                       # To add a profile page
    

    Register your application

    Follow the instructions to register a single-page application in the Azure portal.

    On the app Overview page of your registration, note the Application (client) ID value for later use.

    Register your Redirect URI value as http://localhost:4200/ and type as 'SPA'.

    Configure the application

  • In the src/app folder, edit app.module.ts and add MsalModule and MsalInterceptor to imports as well as the isIE constant. Your code should look like this:

    import { BrowserModule } from '@angular/platform-browser';
    import { NgModule } from '@angular/core';
    import { AppRoutingModule } from './app-routing.module';
    import { AppComponent } from './app.component';
    import { HomeComponent } from './home/home.component';
    import { ProfileComponent } from './profile/profile.component';
    import { MsalModule } from '@azure/msal-angular';
    import { PublicClientApplication } from '@azure/msal-browser';
    const isIE = window.navigator.userAgent.indexOf('MSIE ') > -1 || window.navigator.userAgent.indexOf('Trident/') > -1;
    @NgModule({
      declarations: [
        AppComponent,
        HomeComponent,
        ProfileComponent
      imports: [
        BrowserModule,
        AppRoutingModule,
        MsalModule.forRoot( new PublicClientApplication({
          auth: {
            clientId: 'Enter_the_Application_Id_here', // Application (client) ID from the app registration
            authority: 'Enter_the_Cloud_Instance_Id_Here/Enter_the_Tenant_Info_Here', // The Azure cloud instance and the app's sign-in audience (tenant ID, common, organizations, or consumers)
            redirectUri: 'Enter_the_Redirect_Uri_Here'// This is your redirect URI
          cache: {
            cacheLocation: 'localStorage',
            storeAuthStateInCookie: isIE, // Set to true for Internet Explorer 11
        }), null, null)
      providers: [],
      bootstrap: [AppComponent]
    export class AppModule { }
    

    Replace these values:

    Value name About Enter_the_Application_Id_Here On the Overview page of your application registration, this is your Application (client) ID value. Enter_the_Cloud_Instance_Id_Here This is the instance of the Azure cloud. For the main or global Azure cloud, enter https://login.microsoftonline.com. For national clouds (for example, China), see National clouds. Enter_the_Tenant_Info_Here Set to one of the following options: If your application supports accounts in this organizational directory, replace this value with the directory (tenant) ID or tenant name (for example, contoso.microsoft.com). If your application supports accounts in any organizational directory, replace this value with organizations. If your application supports accounts in any organizational directory and personal Microsoft accounts, replace this value with common. To restrict support to personal Microsoft accounts only, replace this value with consumers. Enter_the_Redirect_Uri_Here Replace with http://localhost:4200.

    For more information about available configurable options, see Initialize client applications.

  • Add routes to the home and profile components in the src/app/app-routing.module.ts. Your code should look like the following:

    import { NgModule } from '@angular/core';
    import { Routes, RouterModule } from '@angular/router';
    import { BrowserUtils } from '@azure/msal-browser';
    import { HomeComponent } from './home/home.component';
    import { ProfileComponent } from './profile/profile.component';
    const routes: Routes = [
        path: 'profile',
        component: ProfileComponent,
        path: '',
        component: HomeComponent
    const isIframe = window !== window.parent && !window.opener;
    @NgModule({
      imports: [RouterModule.forRoot(routes, {
        // Don't perform initial navigation in iframes or popups
       initialNavigation: !BrowserUtils.isInIframe() && !BrowserUtils.isInPopup() ? 'enabledNonBlocking' : 'disabled' // Set to enabledBlocking to use Angular Universal
      exports: [RouterModule]
    export class AppRoutingModule { }
    

    Replace base UI

  • Replace the placeholder code in src/app/app.component.html with the following:

    <mat-toolbar color="primary">
      <a class="title" href="/">{{ title }}</a>
      <div class="toolbar-spacer"></div>
      <a mat-button [routerLink]="['profile']">Profile</a>
      <button mat-raised-button *ngIf="!loginDisplay" (click)="login()">Login</button>
    </mat-toolbar>
    <div class="container">
      <!--This is to avoid reload during acquireTokenSilent() because of hidden iframe -->
      <router-outlet *ngIf="!isIframe"></router-outlet>
    
  • Add material modules to src/app/app.module.ts. Your AppModule should look like this:

    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 { MsalModule } from '@azure/msal-angular';
    import { PublicClientApplication } from '@azure/msal-browser';
    const isIE = window.navigator.userAgent.indexOf('MSIE ') > -1 || window.navigator.userAgent.indexOf('Trident/') > -1;
    @NgModule({
      declarations: [
        AppComponent,
        HomeComponent,
        ProfileComponent
      imports: [
        BrowserModule,
        BrowserAnimationsModule,
        AppRoutingModule,
        MatButtonModule,
        MatToolbarModule,
        MatListModule,
        MsalModule.forRoot( new PublicClientApplication({
          auth: {
            clientId: 'Enter_the_Application_Id_here',
            authority: 'Enter_the_Cloud_Instance_Id_Here/Enter_the_Tenant_Info_Here',
            redirectUri: 'Enter_the_Redirect_Uri_Here'
          cache: {
            cacheLocation: 'localStorage',
            storeAuthStateInCookie: isIE, 
        }), null, null)
      providers: [],
      bootstrap: [AppComponent]
    export class AppModule { }
    
  • (OPTIONAL) Add CSS to src/style.css:

    @import '~@angular/material/prebuilt-themes/deeppurple-amber.css';
    html, body { height: 100%; }
    body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }
    .container { margin: 1%; }
    
  • (OPTIONAL) Add CSS to src/app/app.component.css:

    .toolbar-spacer {
        flex: 1 1 auto;
      a.title {
        color: white;
    

    Sign in a user

    Add the code from the following sections to invoke login using a pop-up window or a full-frame redirect:

    Sign in using pop-ups

  • Change the code in src/app/app.component.ts to the following to sign in a user using a pop-up window:

    import { MsalService } from '@azure/msal-angular';
    import { Component, OnInit } from '@angular/core';
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    export class AppComponent implements OnInit {
      title = 'msal-angular-tutorial';
      isIframe = false;
      loginDisplay = false;
      constructor(private authService: MsalService) { }
      ngOnInit() {
        this.isIframe = window !== window.parent && !window.opener;
      login() {
        this.authService.loginPopup()
          .subscribe({
            next: (result) => {
              console.log(result);
              this.setLoginDisplay();
            error: (error) => console.log(error)
      setLoginDisplay() {
        this.loginDisplay = this.authService.instance.getAllAccounts().length > 0;
    

    The rest of this tutorial uses the loginRedirect method with Microsoft Internet Explorer because of a known issue related to the handling of pop-up windows by Internet Explorer.

    Sign in using redirects

  • Update src/app/app.module.ts to bootstrap the MsalRedirectComponent. This is a dedicated redirect component which will handle redirects. Your code should now look like this:

    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 { MsalModule, MsalRedirectComponent } from '@azure/msal-angular'; // Updated import
    import { PublicClientApplication } from '@azure/msal-browser';
    const isIE = window.navigator.userAgent.indexOf('MSIE ') > -1 || window.navigator.userAgent.indexOf('Trident/') > -1;
    @NgModule({
      declarations: [
        AppComponent,
        HomeComponent,
        ProfileComponent
      imports: [
        BrowserModule,
        BrowserAnimationsModule,
        AppRoutingModule,
        MatButtonModule,
        MatToolbarModule,
        MatListModule,
        MsalModule.forRoot( new PublicClientApplication({
          auth: {
            clientId: 'Enter_the_Application_Id_here',
            authority: 'Enter_the_Cloud_Instance_Id_Here/Enter_the_Tenant_Info_Here',
            redirectUri: 'Enter_the_Redirect_Uri_Here'
          cache: {
            cacheLocation: 'localStorage',
            storeAuthStateInCookie: isIE,
        }), null, null)
      providers: [],
      bootstrap: [AppComponent, MsalRedirectComponent] // MsalRedirectComponent bootstrapped here
    export class AppModule { }
    
  • Add the <app-redirect> selector to src/index.html. This selector is used by the MsalRedirectComponent. Your src/index.html should look like this:

    <!doctype html>
    <html lang="en">
      <meta charset="utf-8">
      <title>msal-angular-tutorial</title>
      <base href="/">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <link rel="icon" type="image/x-icon" href="favicon.ico">
    </head>
      <app-root></app-root>
      <app-redirect></app-redirect>
    </body>
    </html>
    
  • Replace the code in src/app/app.component.ts with the following to sign in a user using a full-frame redirect:

    import { MsalService } from '@azure/msal-angular';
    import { Component, OnInit } from '@angular/core';
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    export class AppComponent implements OnInit {
      title = 'msal-angular-tutorial';
      isIframe = false;
      loginDisplay = false;
      constructor(private authService: MsalService) { }
      ngOnInit() {
        this.isIframe = window !== window.parent && !window.opener;
      login() {
        this.authService.loginRedirect();
      setLoginDisplay() {
        this.loginDisplay = this.authService.instance.getAllAccounts().length > 0;
    
  • Replace existing code in src/app/home/home.component.ts to subscribe to the LOGIN_SUCCESS event. This will allow you to access the result from the successful login with redirect. Your code should look like this:

    import { Component, OnInit } from '@angular/core';
    import { MsalBroadcastService, MsalService } from '@azure/msal-angular';
    import { EventMessage, EventType, InteractionStatus } from '@azure/msal-browser';
    import { filter } from 'rxjs/operators';
    @Component({
      selector: 'app-home',
      templateUrl: './home.component.html',
      styleUrls: ['./home.component.css']
    export class HomeComponent implements OnInit {
      constructor(private authService: MsalService, private msalBroadcastService: MsalBroadcastService) { }
      ngOnInit(): void {
        this.msalBroadcastService.msalSubject$
          .pipe(
            filter((msg: EventMessage) => msg.eventType === EventType.LOGIN_SUCCESS),
          .subscribe((result: EventMessage) => {
            console.log(result);
    

    Conditional rendering

    In order to render certain UI only for authenticated users, components have to subscribe to the MsalBroadcastService to see if users have been signed in and interaction has completed.

  • Add the MsalBroadcastService to src/app/app.component.ts and subscribe to the inProgress$ observable to check if interaction is complete and an account is signed in before rendering UI. Your code should now look like this:

    import { Component, OnInit, OnDestroy } from '@angular/core';
    import { MsalService, MsalBroadcastService } from '@azure/msal-angular';
    import { InteractionStatus } from '@azure/msal-browser';
    import { Subject } from 'rxjs';
    import { filter, takeUntil } from 'rxjs/operators';
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    export class AppComponent implements OnInit, OnDestroy {
      title = 'msal-angular-tutorial';
      isIframe = false;
      loginDisplay = false;
      private readonly _destroying$ = new Subject<void>();
      constructor(private broadcastService: MsalBroadcastService, private authService: MsalService) { }
      ngOnInit() {
        this.isIframe = window !== window.parent && !window.opener;
        this.broadcastService.inProgress$
        .pipe(
          filter((status: InteractionStatus) => status === InteractionStatus.None),
          takeUntil(this._destroying$)
        .subscribe(() => {
          this.setLoginDisplay();
      login() {
        this.authService.loginRedirect();
      setLoginDisplay() {
        this.loginDisplay = this.authService.instance.getAllAccounts().length > 0;
      ngOnDestroy(): void {
        this._destroying$.next(undefined);
        this._destroying$.complete();
    
  • Update the code in src/app/home/home.component.ts to also check for interaction to be completed before updating UI. Your code should now look like this:

    import { Component, OnInit } from '@angular/core';
    import { MsalBroadcastService, MsalService } from '@azure/msal-angular';
    import { EventMessage, EventType, InteractionStatus } from '@azure/msal-browser';
    import { filter } from 'rxjs/operators';
    @Component({
      selector: 'app-home',
      templateUrl: './home.component.html',
      styleUrls: ['./home.component.css']
    export class HomeComponent implements OnInit {
      loginDisplay = false;
      constructor(private authService: MsalService, private msalBroadcastService: MsalBroadcastService) { }
      ngOnInit(): void {
        this.msalBroadcastService.msalSubject$
          .pipe(
            filter((msg: EventMessage) => msg.eventType === EventType.LOGIN_SUCCESS),
          .subscribe((result: EventMessage) => {
            console.log(result);
        this.msalBroadcastService.inProgress$
          .pipe(
            filter((status: InteractionStatus) => status === InteractionStatus.None)
          .subscribe(() => {
            this.setLoginDisplay();
      setLoginDisplay() {
        this.loginDisplay = this.authService.instance.getAllAccounts().length > 0;
    
  • Replace the code in src/app/home/home.component.html with the following conditional displays:

    <div *ngIf="!loginDisplay">
        <p>Please sign-in to see your profile information.</p>
    <div *ngIf="loginDisplay">
        <p>Login successful!</p>
        <p>Request your profile information by clicking Profile above.</p>
    

    Guarding routes

    Angular Guard

    MSAL Angular provides MsalGuard, a class you can use to protect routes and require authentication before accessing the protected route. The steps below add the MsalGuard to the Profile route. Protecting the Profile route means that even if a user does not sign in using the Login button, if they try to access the Profile route or click the Profile button, the MsalGuard will prompt the user to authenticate via pop-up or redirect before showing the Profile page.

    MsalGuard is a convenience class you can use improve the user experience, but it should not be relied upon for security. Attackers can potentially get around client-side guards, and you should ensure that the server does not return any data the user should not access.

  • Add the MsalGuard class as a provider in your application in src/app/app.module.ts, and add the configurations for the MsalGuard. Scopes needed for acquiring tokens later can be provided in the authRequest, and the type of interaction for the Guard can be set to Redirect or Popup. Your code should look like the following:

    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 { MsalModule, MsalRedirectComponent, MsalGuard } from '@azure/msal-angular'; // MsalGuard added to imports
    import { PublicClientApplication, InteractionType } from '@azure/msal-browser'; // InteractionType added to imports
    const isIE = window.navigator.userAgent.indexOf('MSIE ') > -1 || window.navigator.userAgent.indexOf('Trident/') > -1;
    @NgModule({
      declarations: [
        AppComponent,
        HomeComponent,
        ProfileComponent
      imports: [
        BrowserModule,
        BrowserAnimationsModule,
        AppRoutingModule,
        MatButtonModule,
        MatToolbarModule,
        MatListModule,
        MsalModule.forRoot( new PublicClientApplication({
          auth: {
            clientId: 'Enter_the_Application_Id_here',
            authority: 'Enter_the_Cloud_Instance_Id_Here/Enter_the_Tenant_Info_Here',
            redirectUri: 'Enter_the_Redirect_Uri_Here'
          cache: {
            cacheLocation: 'localStorage',
            storeAuthStateInCookie: isIE,
        }), {
            interactionType: InteractionType.Redirect, // MSAL Guard Configuration
            authRequest: {
              scopes: ['user.read']
        }, null)
      providers: [
        MsalGuard // MsalGuard added as provider here
      bootstrap: [AppComponent, MsalRedirectComponent]
    export class AppModule { }
    
  • Set the MsalGuard on the routes you wish to protect in src/app/app-routing.module.ts:

    import { NgModule } from '@angular/core';
    import { Routes, RouterModule } from '@angular/router';
    import { HomeComponent } from './home/home.component';
    import { ProfileComponent } from './profile/profile.component';
    import { MsalGuard } from '@azure/msal-angular';
    const routes: Routes = [
        path: 'profile',
        component: ProfileComponent,
        canActivate: [MsalGuard]
        path: '',
        component: HomeComponent
    const isIframe = window !== window.parent && !window.opener;
    @NgModule({
      imports: [RouterModule.forRoot(routes, {
        initialNavigation: !isIframe ? 'enabled' : 'disabled' // Don't perform initial navigation in iframes
      exports: [RouterModule]
    export class AppRoutingModule { }
    
  • Adjust the login calls in src/app/app.component.ts to take the authRequest set in the guard configurations into account. Your code should now look like the following:

    import { Component, OnInit, OnDestroy, Inject } from '@angular/core';
    import { MsalService, MsalBroadcastService, MSAL_GUARD_CONFIG, MsalGuardConfiguration } from '@azure/msal-angular';
    import { InteractionStatus, RedirectRequest } from '@azure/msal-browser';
    import { Subject } from 'rxjs';
    import { filter, takeUntil } from 'rxjs/operators';
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    export class AppComponent implements OnInit, OnDestroy {
      title = 'msal-angular-tutorial';
      isIframe = false;
      loginDisplay = false;
      private readonly _destroying$ = new Subject<void>();
      constructor(@Inject(MSAL_GUARD_CONFIG) private msalGuardConfig: MsalGuardConfiguration, private broadcastService: MsalBroadcastService, private authService: MsalService) { }
      ngOnInit() {
        this.isIframe = window !== window.parent && !window.opener;
        this.broadcastService.inProgress$
        .pipe(
          filter((status: InteractionStatus) => status === InteractionStatus.None),
          takeUntil(this._destroying$)
        .subscribe(() => {
          this.setLoginDisplay();
      login() {
        if (this.msalGuardConfig.authRequest){
          this.authService.loginRedirect({...this.msalGuardConfig.authRequest} as RedirectRequest);
        } else {
          this.authService.loginRedirect();
      setLoginDisplay() {
        this.loginDisplay = this.authService.instance.getAllAccounts().length > 0;
      ngOnDestroy(): void {
        this._destroying$.next(undefined);
        this._destroying$.complete();
    

    Acquire a token

    Angular Interceptor

    MSAL Angular provides an Interceptor class that automatically acquires tokens for outgoing requests that use the Angular http client to known protected resources.

  • Add the Interceptor class as a provider to your application in src/app/app.module.ts, with its configurations. Your code should now look like this:

    import { BrowserModule } from '@angular/platform-browser';
    import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
    import { NgModule } from '@angular/core';
    import { HTTP_INTERCEPTORS, HttpClientModule } from "@angular/common/http"; // Import 
    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 { MsalModule, MsalRedirectComponent, MsalGuard, MsalInterceptor } from '@azure/msal-angular'; // Import MsalInterceptor
    import { InteractionType, PublicClientApplication } from '@azure/msal-browser';
    const isIE = window.navigator.userAgent.indexOf('MSIE ') > -1 || window.navigator.userAgent.indexOf('Trident/') > -1;
    @NgModule({
      declarations: [
        AppComponent,
        HomeComponent,
        ProfileComponent
      imports: [
        BrowserModule,
        BrowserAnimationsModule,
        AppRoutingModule,
        MatButtonModule,
        MatToolbarModule,
        MatListModule,
        HttpClientModule,
        MsalModule.forRoot( new PublicClientApplication({
          auth: {
            clientId: 'Enter_the_Application_Id_Here',
            authority: 'Enter_the_Cloud_Instance_Id_Here/Enter_the_Tenant_Info_Here',
            redirectUri: 'Enter_the_Redirect_Uri_Here',
          cache: {
            cacheLocation: 'localStorage',
            storeAuthStateInCookie: isIE,
        }), {
          interactionType: InteractionType.Redirect,
          authRequest: {
            scopes: ['user.read']
          interactionType: InteractionType.Redirect, // MSAL Interceptor Configuration
          protectedResourceMap: new Map([ 
              ['Enter_the_Graph_Endpoint_Here/v1.0/me', ['user.read']]
      providers: [
          provide: HTTP_INTERCEPTORS,
          useClass: MsalInterceptor,
          multi: true
        MsalGuard
      bootstrap: [AppComponent, MsalRedirectComponent]
    export class AppModule { }
    

    The protected resources are provided as a protectedResourceMap. The URLs you provide in the protectedResourceMap collection are case-sensitive. For each resource, add scopes being requested to be returned in the access token.

    For example:

  • ["user.read"] for Microsoft Graph
  • ["<Application ID URL>/scope"] for custom web APIs (that is, api://<Application ID>/access_as_user)
  • Modify the values in the protectedResourceMap as described here:

    Value name About Enter_the_Graph_Endpoint_Here The instance of the Microsoft Graph API the application should communicate with. For the global Microsoft Graph API endpoint, replace both instances of this string with https://graph.microsoft.com. For endpoints in national cloud deployments, see National cloud deployments in the Microsoft Graph documentation.
  • Replace the code in src/app/profile/profile.component.ts to retrieve a user's profile with an HTTP request:

    import { Component, OnInit } from '@angular/core';
    import { HttpClient } from '@angular/common/http';
    const GRAPH_ENDPOINT = 'Enter_the_Graph_Endpoint_Here/v1.0/me';
    type ProfileType = {
      givenName?: string,
      surname?: string,
      userPrincipalName?: string,
      id?: string
    @Component({
      selector: 'app-profile',
      templateUrl: './profile.component.html',
      styleUrls: ['./profile.component.css']
    export class ProfileComponent implements OnInit {
      profile!: ProfileType;
      constructor(
        private http: HttpClient
      ) { }
      ngOnInit() {
        this.getProfile();
      getProfile() {
        this.http.get(GRAPH_ENDPOINT)
          .subscribe(profile => {
            this.profile = profile;
    
  • Replace the UI in src/app/profile/profile.component.html to display profile information:

    <p><strong>First Name: </strong> {{profile?.givenName}}</p> <p><strong>Last Name: </strong> {{profile?.surname}}</p> <p><strong>Email: </strong> {{profile?.userPrincipalName}}</p> <p><strong>Id: </strong> {{profile?.id}}</p>

    Sign out

    Update the code in src/app/app.component.html to conditionally display a Logout button:

    <mat-toolbar color="primary">
      <a class="title" href="/">{{ title }}</a>
      <div class="toolbar-spacer"></div>
      <a mat-button [routerLink]="['profile']">Profile</a>
      <button mat-raised-button *ngIf="!loginDisplay" (click)="login()">Login</button>
      <button mat-raised-button *ngIf="loginDisplay" (click)="logout()">Logout</button>
    </mat-toolbar>
    <div class="container">
      <!--This is to avoid reload during acquireTokenSilent() because of hidden iframe -->
      <router-outlet *ngIf="!isIframe"></router-outlet>
    

    Sign out using redirects

    Update the code in src/app/app.component.ts to sign out a user using redirects:

    import { Component, OnInit, OnDestroy, Inject } from '@angular/core';
    import { MsalService, MsalBroadcastService, MSAL_GUARD_CONFIG, MsalGuardConfiguration } from '@azure/msal-angular';
    import { InteractionStatus, RedirectRequest } from '@azure/msal-browser';
    import { Subject } from 'rxjs';
    import { filter, takeUntil } from 'rxjs/operators';
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    export class AppComponent implements OnInit, OnDestroy {
      title = 'msal-angular-tutorial';
      isIframe = false;
      loginDisplay = false;
      private readonly _destroying$ = new Subject<void>();
      constructor(@Inject(MSAL_GUARD_CONFIG) private msalGuardConfig: MsalGuardConfiguration, private broadcastService: MsalBroadcastService, private authService: MsalService) { }
      ngOnInit() {
        this.isIframe = window !== window.parent && !window.opener;
        this.broadcastService.inProgress$
        .pipe(
          filter((status: InteractionStatus) => status === InteractionStatus.None),
          takeUntil(this._destroying$)
        .subscribe(() => {
          this.setLoginDisplay();
      login() {
        if (this.msalGuardConfig.authRequest){
          this.authService.loginRedirect({...this.msalGuardConfig.authRequest} as RedirectRequest);
        } else {
          this.authService.loginRedirect();
      logout() { // Add log out function here
        this.authService.logoutRedirect({
          postLogoutRedirectUri: 'http://localhost:4200'
      setLoginDisplay() {
        this.loginDisplay = this.authService.instance.getAllAccounts().length > 0;
      ngOnDestroy(): void {
        this._destroying$.next(undefined);
        this._destroying$.complete();
    

    Sign out using pop-ups

    Update the code in src/app/app.component.ts to sign out a user using pop-ups:

    import { Component, OnInit, OnDestroy, Inject } from '@angular/core';
    import { MsalService, MsalBroadcastService, MSAL_GUARD_CONFIG, MsalGuardConfiguration } from '@azure/msal-angular';
    import { InteractionStatus, PopupRequest } from '@azure/msal-browser';
    import { Subject } from 'rxjs';
    import { filter, takeUntil } from 'rxjs/operators';
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    export class AppComponent implements OnInit, OnDestroy {
      title = 'msal-angular-tutorial';
      isIframe = false;
      loginDisplay = false;
      private readonly _destroying$ = new Subject<void>();
      constructor(@Inject(MSAL_GUARD_CONFIG) private msalGuardConfig: MsalGuardConfiguration, private broadcastService: MsalBroadcastService, private authService: MsalService) { }
      ngOnInit() {
        this.isIframe = window !== window.parent && !window.opener;
        this.broadcastService.inProgress$
        .pipe(
          filter((status: InteractionStatus) => status === InteractionStatus.None),
          takeUntil(this._destroying$)
        .subscribe(() => {
          this.setLoginDisplay();
      login() {
        if (this.msalGuardConfig.authRequest){
          this.authService.loginPopup({...this.msalGuardConfig.authRequest} as PopupRequest)
            .subscribe({
              next: (result) => {
                console.log(result);
                this.setLoginDisplay();
              error: (error) => console.log(error)
        } else {
          this.authService.loginPopup()
            .subscribe({
              next: (result) => {
                console.log(result);
                this.setLoginDisplay();
              error: (error) => console.log(error)
      logout() { // Add log out function here
        this.authService.logoutPopup({
          mainWindowRedirectUri: "/"
      setLoginDisplay() {
        this.loginDisplay = this.authService.instance.getAllAccounts().length > 0;
      ngOnDestroy(): void {
        this._destroying$.next(undefined);
        this._destroying$.complete();
    

    Test your code

  • Start the web server to listen to the port by running the following commands at a command-line prompt from the application folder:

    npm install
    npm start
    
  • In your browser, enter http://localhost:4200 or http://localhost:{port}, where port is the port that your web server is listening on. You should see a page that looks like the one below.

    Add scopes and delegated permissions

    The Microsoft Graph API requires the User.Read scope to read a user's profile. The User.Read scope is added automatically to every app registration you create in the Azure portal. Other APIs for Microsoft Graph, as well as custom APIs for your back-end server, might require additional scopes. For example, the Microsoft Graph API requires the Mail.Read scope in order to list the user's email.

    As you add scopes, your users might be prompted to provide additional consent for the added scopes.

    The user might be prompted for additional consents as you increase the number of scopes.

    Help and support

    If you need help, want to report an issue, or want to learn about your support options, see Help and support for developers.

    Next steps

    Delve deeper into single-page application (SPA) development on the Microsoft identity platform in our the multi-part article series.

    Scenario: Single-page application

  •