-
Implement Azure AD Authentication through MSAL to connect Angular App to Asp.net Core web API
Here we are going to explain steps by step process implementation of the Azure AD Authentication to connect Angular application to Asp.net Core Web API using Microsoft Authentication Library (MSAL).
We consider that you already have Azure AD account and already know how to create Angular app and Asp.net Core Web API.
Now first we will start with Azure AD Registration. We require two app registrations in Azure portal for AD authentication.
Create App registration in Azure portal:
Step 1: Login to Azure portal:
Step 2: Go to Azure Active Directory
Step 3: Click on “App registrations”
Step 4: Click on “+ New Registration”
Step 5: Give Valid name and redirect uri (Angular Redirect URI). And Click on “Register” button to Create new App.
Step 6: You will the client Id and Tenant Id from the dashboard of the App.
Step 7: Click Authentication tab in the left side and select Access Token and Id tokens and click Save button. We will use these tokens for our Authentication and Authorization purpose later. We can click App registration blade again and create a new app registration for Web API.
Step 8: Create Another app registration for the API and here we only need to give app name no need for redirect uri
Step 9: After the app registration will be populated, please click “Expose an API” and Click Add Scope button
Step 10: After Click on “Save and Continue” One new window will be appeared and we can enter scope name, consent name and consent description in this window.
We will use this consent scope later in our Angular application.
Step 11: We can link our previously created client application (Demo-FrontEnd) to this API app registration.
Step 12: Click + Add a client application button and give correct client id from previously created app registration. Don’t forget to select consent scope along with client id.
We have successfully created app registration for both UI and API.
Here consider that API project has already been created.
Asp.net core web application:
Step 1: In Visual Studio 2019, Create Asp.net core web application using API template.
Step 2: Install the Microsoft.AspNetCore.Authentication.AzureAD.UI Nuget Package.
This library used for AD authentication.
Step 3: Now open the appsettings.json file and add the following code.
------------------------- appsettings.json----------------------------- {
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"AzureActiveDirectory": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "<your domain>c.onmicrosoft.com",
"TenantId": "YOUR_TENATID",
"ClientId": "api://YOUR_CLIENTID"
}
}
Step 4: Now open the Startup.cs file and add the following code for register authentication service and also add CORS service.
------------------------- Startup.cs----------------------------- using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.AzureAD.UI;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
namespace AzureADAPI
{
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)
{
services.AddControllers();
services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme).AddAzureADBearer(options => Configuration.Bind("AzureActiveDirectory", options));
string corsDomains = "http://localhost:4200";
string[] domains = corsDomains.Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
services.AddCors(o => o.AddPolicy("AppCORSPolicy", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials()
.WithOrigins(domains);
}));
}
// 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.UseCors("AppCORSPolicy");
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
Step 5: Now create Employee class. This will be used in controller to return some data to front end(angular) app.
------------------------- Employee.cs--------------------------- namespace AzureADAPI
{
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public string Designation { get; set; }
}
}
Step 6: Create EmployeesController and add the get method. This method will be called from angular application to test AD authentication.
Add the following code in EmployeesController.cs file.
------------------------- EmployeesController.cs--------------------------- using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
namespace AzureADAPI.Controllers
{
[Authorize]
[Route("api/[controller]")]
public class EmployeesController : Controller
{
[HttpGet]
public IEnumerableGet()
{
Listemployees = new List
{
new Employee { Id = 1, Name = "Hiren Andharia", Designation = "Full Stack Developer"},
new Employee { Id = 2, Name = "Bhavika Panchal", Designation = "Business analyst" }
};
return employees;
}
}
}
Here we have done with Asp.net Core API, now in next steps we are going to create angular Application.
Angular web application:
Step 1: Create workspace and initial application
Run the following command in terminal / command line
ng new example
PS E:\> ng new example
Step 2: We are using “@azure/msal-angular” library for AD authentication. So run below command in terminal/command line to install.
npm i @azure/msal-angular
PS E:\> npm i @azure/msal-angular
We need RxJs in our application. So, run below command in terminal/command line to install.
npm i rxjs-compat
PS E:\> npm i rxjs-compat
Step 3: Now we can add client id, tenant id, and consent scope details inside the environment.ts file.
------------------------- environment.ts--------------------------- export const environment = {
production: false,
baseUrl: "http://localhost:58980/",
scopeUri: ["api://API_CLIENTID/api-access"],
tenantId: "FRONTEND_TENNATID",
uiClienId: "FRONT_ENDCLIENTID",
redirectUrl: "http://localhost:4200",
};
Step 4: Add service (msaluser service)
Run the following command in terminal / command line
ng g s msaluser
PS E:\> ng g s msaluser
Step 5: Now open the msaluser.service.ts file and add the following code.
------------------------- msaluser.service.ts------------------------ import { Injectable } from "@angular/core";
import * as Msal from "msal";
import { environment } from "src/environments/environment";
import { Observable } from "rxjs";
@Injectable()
export class MsalUserService {
private accessToken: any;
public clientApplication: Msal.UserAgentApplication = null;
constructor() {
this.clientApplication = new Msal.UserAgentApplication(
environment.uiClienId,
"https://login.microsoftonline.com/" + environment.tenantId,
this.authCallback,
{
storeAuthStateInCookie: true,
}
);
}
public GetAccessToken(): Observable{
if (
sessionStorage.getItem("msal.idtoken") !== undefined &&
sessionStorage.getItem("msal.idtoken") != null
) {
this.accessToken = sessionStorage.getItem("msal.idtoken");
}
return this.accessToken;
}
public authCallback(errorDesc, token, error, tokenType) {
if (token) {
} else {
console.log(error + ":" + errorDesc);
}
}
public getCurrentUserInfo() {
const user = this.clientApplication.getUser();
alert(user.name);
}
public logout() {
this.clientApplication.logout();
}
}
Step 6: Add class (employee class)
Run the following command in terminal / command line
ng g class employee
PS E:\> ng g class employee
Step 7: Now open the employee.ts file and add the following code
-----------------------------employee.ts-------------------------------------- export class Employee {
id: number;
name: string;
designation: string;
}
Step 8: Add service (data service)
Run the following command in terminal / command line
ng g s data
PS E:\> ng g s data
Step 9: Now open the data.service.ts file and add the following code
-----------------------------data.service.ts-------------------------------------- import { Injectable } from "@angular/core";
import { HttpClient, HttpHeaders } from "@angular/common/http";
import { Observable } from "rxjs";
import { environment } from "src/environments/environment";
import { MsalUserService } from "./msaluser.service";
import { Employee } from "./employee";
@Injectable({
providedIn: "root",
})
export class DataService {
private url = environment.baseUrl + "api/employees";
httpOptions = {
headers: new HttpHeaders({
"Content-Type": "application/json",
}),
};
constructor(private http: HttpClient, private msalService: MsalUserService) {}
getEmployees(): Observable<Employee[]> {
this.httpOptions = {
headers: new HttpHeaders({
"Content-Type": "application/json",
Authorization: "Bearer " + this.msalService.GetAccessToken(),
}),
};
return this.http.get(this.url, this.httpOptions).pipe((response: any) => {
return response;
});
}
getCurrentUserInfo() {
this.msalService.getCurrentUserInfo();
}
logout() {
this.msalService.logout();
}
}
Step 10: Now open the app-routing-module.ts file and add the following code.
-----------------------------app-routing.module.ts-------------------------------------- import { NgModule } from "@angular/core";
import { Routes, RouterModule } from "@angular/router";
import { AppComponent } from "./app.component";
import { MsalGuard } from "@azure/msal-angular";
const routes: Routes = [
{
path: "",
component: AppComponent,
canActivate: [MsalGuard],
},
];
@NgModule({
imports: [RouterModule.forRoot(routes, { useHash: true })],
exports: [RouterModule],
})
export class AppRoutingModule {}
Step 11: Now open the app-module.ts file and add the following code
-----------------------------app.module.ts-------------------------------------- import { BrowserModule } from "@angular/platform-browser";
import { NgModule } from "@angular/core";
import { AppRoutingModule } from "./app-routing.module";
import { AppComponent } from "./app.component";
import { environment } from "src/environments/environment";
import { MsalModule, MsalInterceptor } from "@azure/msal-angular";
import {
HttpClientModule,
HttpClient,
HTTP_INTERCEPTORS,
} from "@angular/common/http";
import { MsalUserService } from "./services/msaluser.service";
export const protectedResourceMap: any = [
[environment.baseUrl, environment.scopeUri],
];
@NgModule({
declarations: [AppComponent],
imports: [
MsalModule.forRoot({
clientID: environment.uiClienId,
authority: "https://login.microsoftonline.com/" + environment.tenantId,
protectedResourceMap: protectedResourceMap,
redirectUri: environment.redirectUrl,
}),
BrowserModule,
AppRoutingModule,
HttpClientModule,
],
providers: [
HttpClient,
MsalUserService,
{
provide: HTTP_INTERCEPTORS,
useClass: MsalInterceptor,
multi: true,
},
],
bootstrap: [AppComponent],
})
export class AppModule {}
Step 12: Now open the app-component.ts file and add the following code.
-----------------------------app.component.ts-------------------------------------- import { Component } from "@angular/core";
import { Employee } from "./services/employee";
import { DataService } from "./services/data.service";
@Component({
selector: "app-root",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"],
})
export class AppComponent {
title = "AzureMSALAngular";
employees: Employee[];
errorMessage: any;
isLoggedIn: boolean = false;
constructor(private dataService: DataService) {}
ngOnInit(): void {
if (
sessionStorage["msal.idtoken"] != "" &&
sessionStorage["msal.idtoken"] != undefined
) {
this.isLoggedIn = true;
}
this.dataService.getEmployees().subscribe(
(values) => {
this.employees = values;
},
(error) => (this.errorMessage =error)
);
}
logout() {
this.dataService.logout();
}
}
Step 13: Now open the app-component.html file and add the following code.
-----------------------------app.component.html-------------------------------------- <h3>Azure AD Authentication demo</h3>
<table *ngIf="isLoggedIn">
<thead>
<tr>
<th>Id</th>
<th>Name</th>
<th>Designation</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let employee of employees">
<td>{{ employee.id }}</td>
<td>{{ employee.name }}</td>
<td>{{ employee.designation }}</td>
</tr>
</tbody>
</table>
<hr />
<button *ngIf="isLoggedIn" (click)="logout()">Logout</button>
Step 14: Now open the app-component.css file for table style and add the following code.
>-----------------------------app.component.css-------------------------------------- table,
th,
td {
border: 1px solid black;
border-collapse: collapse;
}
th,
td {
padding: 5px;
text-align: left;
}
Step 15: Run the angular application using below command :
npm start
PS E:\> npm start
Output 1:
Login with your Azure AD account, after successfully authenticate your angular app call the Authorized app which get the list of employee list which is as below.