Azure Active Directory is the Identity and Access Management (IAM) solution offered by Microsoft.
Azure AD can authenticate accounts from different sources, which are as follows:
We need two app registrations in Azure portal for AD authentication. One for Web API application and one for Angular application.
Click “Register” button to create the app.
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.
We have given app name only. No need to give redirect URI here because this is for API.
Click “Register” button to create app.
After the app registration will be populated, please click “Expose an API” blade.
Click Add Scope button
Click Save button.
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.
We can link our previously created client application to this API app registration.
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. Now we can create ASP.NET Core web API and Angular 8 application and enable Azure AD authentication.
Create ASP.NET Core Web API in Visual Studio 2019
We can create ASP.NET Core Web API application using default API template in Visual Studio.
We must install “Microsoft.AspNetCore.Authentication.AzureAD.UI” library using NuGet. This is used for AD authentication.
We have already created two app registrations in Azure active directory. We can use the client id and tenant id for API here in appsettings as given below.
appsettings.json
- {
- "Logging": {
- "LogLevel": {
- "Default": "Information",
- "Microsoft": "Warning",
- "Microsoft.Hosting.Lifetime": "Information"
- }
- },
- "AllowedHosts": "*",
- "AzureActiveDirectory": {
- "Instance": "https://login.microsoftonline.com/",
- "Domain": "<your domain>.onmicrosoft.com",
- "TenantId": "adbbbd82-76e5-4952-8531-3cc59f3c1fdd",
- "ClientId": "api://e283d8fb-22ad-4e2c-9541-14f6f118a08f"
- }
- }
We can register authentication service inside the ConfigureServices method in Startup class. Also add CORS service as well.
Staturp.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; }
-
-
- 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);
- }));
-
- }
-
-
- 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();
- });
- }
- }
- }
Create an Employee class. This will be used in our Employees controller class to return some dummy data to Angular application later.
Employee.cs
- namespace AzureADAPI
- {
- public class Employee
- {
- public int Id { get; set; }
- public string Name { get; set; }
- public string Company { get; set; }
- public string City { get; set; }
- }
- }
Create Employee controller with single Get method. This method will be called from Angular application to test AD authentication.
EmployeeController.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 IEnumerable<Employee> Get()
- {
- List<Employee> employees = new List<Employee>
- {
- new Employee { Id = 1, Name = "Sarathlal Saseendran", Company = "Orion Business Innovations", City = "Kochi" },
- new Employee { Id = 2, Name = "Anil Soman", Company = "Cognizant", City = "Bangalare" }
- };
- return employees;
- }
- }
- }
Please note, we have decorated above controller with [Authorize] decorator.
We have completed the API application enabled with AD authentication. We can create Angular application from scratch and add all the components and services.
Create Angular 8 application using Angular CLI
I am still using the stable version of Angular 8 LTS (8.3.26)
As we discussed earlier, we are using “@azure/msal-angular” library for AD authentication. Unfortunately, the latest version is still in preview mode. Hence, I am using the stable version 0.1.4 in our application.
Please use below command to install this stable version.
We need RxJs compatible version also in our application.
Please install that also.
npm i rxjs-compat
We can add AD related client id, tenant id, consent scope details inside the environment variable.
environment.ts
- export const environment = {
- production: false,
- baseUrl:'http://localhost:58980/',
- scopeUri: ['api://e283d8fb-22ad-4e2c-9541-14f6f118a08f/sarath'],
- tenantId: 'adbbbd82-76e5-4952-8531-3cc59f3c1fdd',
- uiClienId: '28a65047-6d13-4566-aba6-bd6d6dcd170b',
- redirectUrl: 'http://localhost:4200'
- };
We can create a services folder and create a MsalUserService class.
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<any> {
- 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();
- }
- }
We have used this service for getting access token (for authentication), logout function and also for getting logged in user name.
We can create an employee class
Employee.ts
- export class Employee {
- id: number;
- name: string;
- company: string;
- city: string;
- }
We can create a DataService for getting employee data from Web API application.
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();
- }
- }
For every request, we are calling access token from MsalUserService and added to request header.
We can add default MsalGuard inside the AppRoutingModule class.
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)],
- exports: [RouterModule]
- })
- export class AppRoutingModule { }
Whenever we route to App component, MsalGuard will protect the component with AD authentication.
We can modify the AppModule class with below 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 { }
We have registered MsalModule and MsalInterceptor inside the AppModule.
We can modify the default App component with below 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;
-
- constructor(private dataService: DataService) { }
-
- ngOnInit(): void {
- this.dataService.getEmployees().subscribe(
- values => {
- this.employees = values;
- },
- error => this.errorMessage = <any>error
- );
- }
-
- getUser(){
- this.dataService.getCurrentUserInfo();
- }
-
- logout(){
- this.dataService.logout();
- }
- }
Also modify the template file.
app.component.html
- <h3>Azure AD Authentication with Azure Angular MSAL library</h3>
-
- <hr>
-
- <table>
- <thead>
- <tr>
- <th>Id</th>
- <th>Name</th>
- <th>Company</th>
- <th>City</th>
- </tr>
- </thead>
- <tbody>
- <tr *ngFor="let employee of employees">
- <td>{{ employee.id }}</td>
- <td>{{ employee.name }}</td>
- <td>{{ employee.company }}</td>
- <td>{{ employee.city }}</td>
- </tr>
- </tbody>
- </table>
-
- <hr>
-
- <button (click)="getUser()">User Name</button>
- <button (click)="logout()">Logout</button>
We have completed the coding part of Angular application as well.
We can run both Web API and Angular application.
Application will immediately ask your AD credentials.
After the successful login, it will ask your consent to access the AD app registration. Once you approved the consent, it will not ask again.
We can see the dummy Employee data has been taken from Web API successfully.
If you click the User Name button, you can see the logged in user name.