Introduction
This blog is going to showcase to you how we can implement Azure AD with web API and Angular application.

To connect azure ad with angular and asp.net core web API 5.0.
There are four-step processes to implement Azure AD in angular and asp.net core web API.
Step 1
Create Azure AD Account and Register the SPA(Single Page Application ) application in Azure AD App Registration blade.


First, click on the App Registration button and then click on New Registration Button.

Fill in the Register Application Details.


After clicking on the Register button. SPA application successfully gets registered and on the overview page, you get the register application details like Client ID, Tenant ID, etc.

Step 2
Register Web API applications in the same way. But in the API registration process, we do not need to provide the application redirect URL because our SPA Application is used to redirect the page.


Then need to Expose An API,


After successfully exposing the API and scope has been added you need to go into the SPA application and add the permission for this API. Follow the process and click as per the number in below pic.

Step 3
Go to VS Code and Create New application in Angular, do the below changes. Add the below packages and do the npm install.

Add Auth-Config File and do the below settings as per the SPA application Registered.

Add below setting in App.Module.ts,
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { HomeComponent } from './home/home.component';
import { TodoEditComponent } from './todo-edit/todo-edit.component';
import { TodoViewComponent } from './todo-view/todo-view.component';
import { TodoService } from './todo.service';
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { IPublicClientApplication, PublicClientApplication, InteractionType } from '@azure/msal-browser';
import { MsalGuard, MsalInterceptor, MsalBroadcastService, MsalInterceptorConfiguration, MsalModule, MsalService, MSAL_GUARD_CONFIG, MSAL_INSTANCE, MSAL_INTERCEPTOR_CONFIG, MsalGuardConfiguration, MsalRedirectComponent } from '@azure/msal-angular';
import { msalConfig, loginRequest, protectedResources } from './auth-config';
export function MSALInstanceFactory(): IPublicClientApplication {
return new PublicClientApplication(msalConfig);
}
export function MSALInterceptorConfigFactory(): MsalInterceptorConfiguration {
const protectedResourceMap = new Map < string,
Array < string >> ();
protectedResourceMap.set(protectedResources.todoListApi.endpoint, protectedResources.todoListApi.scopes);
return {
interactionType: InteractionType.Redirect,
protectedResourceMap
};
}
export function MSALGuardConfigFactory(): MsalGuardConfiguration {
return {
interactionType: InteractionType.Redirect,
authRequest: loginRequest
};
}
@NgModule({
declarations: [
AppComponent,
HomeComponent,
TodoViewComponent,
TodoEditComponent
],
imports: [
BrowserModule,
BrowserAnimationsModule,
AppRoutingModule,
HttpClientModule,
FormsModule,
MsalModule
],
providers: [{
provide: HTTP_INTERCEPTORS,
useClass: MsalInterceptor,
multi: true
}, {
provide: MSAL_INSTANCE,
useFactory: MSALInstanceFactory
}, {
provide: MSAL_GUARD_CONFIG,
useFactory: MSALGuardConfigFactory
}, {
provide: MSAL_INTERCEPTOR_CONFIG,
useFactory: MSALInterceptorConfigFactory
},
MsalService,
MsalGuard,
MsalBroadcastService,
TodoService
],
bootstrap: [AppComponent, MsalRedirectComponent]
})
export class AppModule {}
App.Component Settings,
import { Component, OnInit, Inject, OnDestroy } from '@angular/core';
import { MsalService, MsalBroadcastService, MSAL_GUARD_CONFIG, MsalGuardConfiguration } from '@azure/msal-angular';
import { AuthenticationResult, InteractionStatus, InteractionType, PopupRequest, 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 = 'Azure Ad Testing Demo';
isIframe = false;
loginDisplay = false;
private readonly _destroying$ = new Subject < void > ();
constructor(@Inject(MSAL_GUARD_CONFIG) private msalGuardConfig: MsalGuardConfiguration, private authService: MsalService, private msalBroadcastService: MsalBroadcastService) {}
ngOnInit(): void {
this.isIframe = window !== window.parent && !window.opener;
/**
* You can subscribe to MSAL events as shown below. For more info,
* visit: https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-angular/docs/v2-docs/events.md
*/
this.msalBroadcastService.inProgress$.pipe(filter((status: InteractionStatus) => status === InteractionStatus.None), takeUntil(this._destroying$)).subscribe(() => {
this.setLoginDisplay();
});
}
setLoginDisplay() {
this.loginDisplay = this.authService.instance.getAllAccounts().length > 0;
}
login() {
if (this.msalGuardConfig.interactionType === InteractionType.Popup) {
if (this.msalGuardConfig.authRequest) {
this.authService.loginPopup({
...this.msalGuardConfig.authRequest
}
as PopupRequest).subscribe((response: AuthenticationResult) => {
this.authService.instance.setActiveAccount(response.account);
});
} else {
this.authService.loginPopup().subscribe((response: AuthenticationResult) => {
this.authService.instance.setActiveAccount(response.account);
});
}
} else {
if (this.msalGuardConfig.authRequest) {
this.authService.loginRedirect({
...this.msalGuardConfig.authRequest
}
as RedirectRequest);
} else {
this.authService.loginRedirect();
}
}
}
logout() {
this.authService.logout();
}
// unsubscribe to events when component is destroyed
ngOnDestroy(): void {
this._destroying$.next(undefined);
this._destroying$.complete();
}
}
app.component.html settings,
<mat-toolbar color="primary">
<a class="title" href="/">{{ title }}</a>
<div class="toolbar-spacer"></div>
<a mat-button [routerLink]="['todo-view']">TodoList</a>
<button mat-raised-button *ngIf="!loginDisplay" (click)="login()">Login</button>
<button mat-raised-button color="accent" *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>
</div>
<footer *ngIf="loginDisplay">
<mat-toolbar>
<div class="footer-text"> How did we do? </div>
</mat-toolbar>
</footer>
Index.html settings,
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>AzureDemoSPA</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.svg">
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
</head>
<body>
<app-root></app-root>
<app-redirect></app-redirect>
</body>
</html>
Step 4 - Web API settings
In AppSettings.json file add your Registered API tenant and Client ID and Domain name information.

From Nuget Package manager add microsoft.identity.web package and do the changes in startup’s file.
using AzureAdAPIDemo.Models;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Identity.Web;
using Microsoft.OpenApi.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace AzureAdAPIDemo {
public class Startup {
public Startup(IConfiguration configuration) {
Configuration = configuration;
}
public IConfiguration Configuration {
get;
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services) {
// Setting configuration for protected web api
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddMicrosoftIdentityWebApi(Configuration);
// Creating policies that wraps the authorization requirements
services.AddAuthorization();
services.AddDbContext < TodoContext > (opt => opt.UseInMemoryDatabase("TodoList"));
services.AddControllers();
// Allowing CORS for all domains and methods for the purpose of the sample
// In production, modify this with the actual domains you want to allow
services.AddCors(o => o.AddPolicy("default", builder => {
builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader();
}));
services.AddSwaggerGen(c => {
c.SwaggerDoc("v1", new OpenApiInfo {
Title = "AzureAdAPIDemo", Version = "v1"
});
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
if (env.IsDevelopment()) {
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "AzureAdAPIDemo v1"));
}
app.UseCors("default");
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints => {
endpoints.MapControllers();
});
}
}
}
Then use [Authorize ] on any controller.


After clicking the login button,

Select the account and log in successfully.

Summary
Follow the above steps and secure your solutions.

vou xiongPosted May 29, 2024, 5:31 PM
I'm getting and error Argument of type 'MonoTypeOperatorFunction<InteractionStatus>' is not assignable to parameter of type 'OperatorFunction<InteractionStatus, InteractionStatus>'. on line this.msalBroadcastService.inProgress$.pipe(filter((status: InteractionStatus) => status === InteractionStatus.None), takeUntil(this._destroying$)).subscribe(() => { this.setLoginDisplay();});
dany sebPosted Feb 6, 2023, 3:32 PM
Hi and very good work . but how to get :put here you ad domain name like xxx.onmicrosoft.com in api step?
Craig HinkelPosted Oct 26, 2022, 12:25 AM
Very easy to follow and saved me a ton of time. Thanks!
Rajesh ChPosted Sep 13, 2022, 9:24 AM
Excellent article!!
Shaiju RajanPosted Aug 4, 2022, 11:55 PM
Excellent article!! really helped
ariel caroPosted Jul 21, 2022, 2:31 PM
Hola estoy utilizando angular y msal 2.0, se logea correctamente pero al momento de generar de generar el redirect ui al http//:localhost:4200 genera un error Cors y e sun endpoint de tipo PO
S GeorgePosted Nov 3, 2021, 5:22 AM
Logout doesn't clear server cache.. next time when user logs in, they are not presented with challenge/pwd. They can login without pwd.
Timeware DevelopersPosted Oct 6, 2021, 4:20 PM
Thanks for posting this, it worked!
Vitthal ShindePosted Oct 4, 2021, 7:54 AM
Thanks. very nice.
MANAS SANTRAPosted Sep 15, 2021, 4:26 PM
Far Better then Microsoft Docs.
Amiya PadhiPosted Aug 26, 2021, 3:26 AM
Very detailed information. Thanks for posting this. I am sure it will help both beginners and advance level developers.
AKASH CHAVANPosted Aug 18, 2021, 5:28 AM
Nicely explained