Tag Archives

3 Articles

Posted by .Ronald on

Creating an Angular Single Page Application with Azure Active Directory and adal.js that uses an ASP.NET WebAPI

This sample shows how to create a single page application (SPA) that uses Azure Active Directory (AAD) authentication with adal.js and uses an ASP.NET WebAPI with AAD.

The source code for this sample can be found in the angular2-adaljs-webapi GitHub repository.

Set up the applications

  1. Create an Angular application.
    I’ve started from the Angular QuickStart seed to bootstrap an easy to use SPA.
  2. Create a WebAPI application.
    I’ve started in Visual Studio by creating a new ASP.NET Web Application, using the Empty template with Web API folders and core references added to it.

Implementing the WebAPI

I’ll setup the WebAPI first to provide data to the SPA without any authentication

  1. Create a model
    public class Message
    {
        public int Id { get; set; }
        public string Title { get; set; }
        public string Body { get; set; }
        public string Author { get; set; }
        public DateTime PublishedAt { get; set; }
    }
  2. Create a WebAPI 2 controller
    public class MessageController : ApiController
    {
        private IList<Message> _messages = new List<Message>()
        {
            new Message { Id = 1, Title = "Lorem ipsum", Body = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", PublishedAt = DateTime.Now},
            new Message { Id = 2, Title = "Pellentesque convallis", Body = "Pellentesque convallis finibus erat, sed lacinia eros mattis quis.", PublishedAt = DateTime.Now},
            new Message { Id = 3, Title = "Maecenas scelerisque", Body = "Maecenas scelerisque pretium risus, eu gravida elit porttitor id.", PublishedAt = DateTime.Now}
        };
    
        public IHttpActionResult Get(int id)
        {
            var message = _messages.FirstOrDefault(m => m.Id == id);
    
            if (message == null)
            {
                return NotFound();
            }
    
            return Ok(message);
        }
    }

This result in a WebAPI that can be consumed like this:

Implementing the Angular single page application

The SPA will consume the WebAPI I’ve created before and show the data on the screen. I will try to follow at least a few of the standards and best practices in Angular development. But bear in mind that this application is not meant to serve as a sample application as such.

  1. Create a model to represent your data
    export class Message {
        Id: number;
        Title: string;
        Body: string;
        Author: string;
        PublishedAt: Date;
    }
  2. Create a service that consumes the WebAPI
    import { Injectable } from '@angular/core';
    import 'rxjs/add/operator/toPromise';
    import { Http } from '@angular/http';
    import { Message } from './message';
    
    @Injectable()
    export class MessageService {
        private messageUrl = 'http://localhost:50071/api/';
    
        constructor(private http: Http) {}
    
        getMessage(id: number): Promise<Message> { 
            return this.http.get(this.messageUrl + 'message/' + id)
                .toPromise()
                .then(response => response.json() as Message)
                .catch(this.handleError);
        }
    
        private handleError(error: any): Promise<any> {
            console.error('An error occurred', error); // for demo purposes only
            return Promise.reject(error.message || error);
        }
    }
  3. Create a component to call the service and display the data
    import { Component } from '@angular/core';
    import { Message } from './message'
    import { MessageService } from './message.service';
    
    @Component({
        selector: 'message',
        template: `<div *ngIf="message; else noMessage">
            <h2>{{message.Title}}</h2>
            <div>{{message.Body}}</div>
            <br />
            <div><label>Author: </label>{{message.Author}}</div>
            <div>{{message.PublishedAt | date:'fullDate'}}</div>
        </div>
        <button (click)="getMessage()">Get message</button>`
    })
    export class MessageComponent {
        messageId: number;
        message: Message;
    
        constructor(private messageService: MessageService) {
            this.messageId = 0;
            this.message = null;
        }
    
        getMessage() {
            this.messageId = Math.floor((Math.random() * 3) + 1);;
            this.messageService.getMessage(this.messageId).then(m => this.message = m);
        }
    }
  4. Add routing and declarations to the Angular app.
    This is what my app.module.ts looks like:

    import { NgModule }      from '@angular/core';
    import { BrowserModule } from '@angular/platform-browser';
    import { RouterModule } from '@angular/router';
    import { HttpModule } from '@angular/http';
    import { AppComponent }  from './app.component';
    import { MessageComponent } from './message.component';
    import { MessageService } from './message.service';
    
    var routeConfig = [
      {
        path: 'messages',
        component: MessageComponent
      }
    ];
    
    @NgModule({
      imports: [BrowserModule, RouterModule.forRoot(routeConfig), HttpModule ],
      declarations: [ AppComponent, MessageComponent ],
      providers: [ MessageService ],
      bootstrap:    [ AppComponent ]
    })
    export class AppModule { }

    And this is what my app.component.ts looks like:

    import { Component } from '@angular/core';
    
    @Component({
      selector: 'my-app',
      template: `<a routerLink="/">HOME</a> <a routerLink="messages">Messages</a>
        <h1>Hello {{name}}</h1>
        <router-outlet></router-outlet>`
    })
    export class AppComponent { 
      name = 'Angular'; 
    }

Enable CORS to allow cross origin web requests

As the Angular application and the WebAPI are served from different hosts, we need to explicitly allow the SPA to consume data from the WebAPI. We do this by enabling CORS in the WebAPI and allow requests originating from the SPA.

  1. Add the CORS package to the WebAPI
    Install-Package Microsoft.AspNet.WebApi.Cors
  2. Enalbe CORS in WebApiConfig.cs
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            config.EnableCors();
    
            // Web API configuration and services
    
            // Web API routes
            config.MapHttpAttributeRoutes();
    
            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }
  3. Allow GET request coming from the SPA to the controller
    [EnableCors(origins: "http://localhost:3000", headers: "*", methods: "get")]
    public class MessageController : ApiController
    {

Add Azure Active Directory Authentication to the Angular SPA

I’ve already described how to add AAD authentication to an existing Angular 2 application in a previous blog post Add Azure Active Directory to an existing Angular 2 Single Page Application. I will follow the steps outlined there. This is in short how this is done:

  1. Configure the app to use SSL
  2. Register the application in Azure Active Directory
  3. Configure it to use OAuth2
  4. Implement and configure adal.js
  5. Add login and (optionally) logout functionality to your app that logs in to AAD
  6. Add a route guard to protect routes from unauthorized access and force AAD authentication

I’ve now created an Angular2 SPA that requires Azure Active Directory authentication (in some parts of the application), and that consumes a WebAPI, which not yet requires authentication.

Set up the WebAPI to require authentication

Next step is to set up the ASP.NET WebAPI to require authentication on the service the Angular2 SPA is consuming. This step is also already described in a previous blog post. You can read it here: Add Azure Active Directory to an existing ASP.NET MVC web application.

I am using Visual Studio 2017, so the easiest way for me to add Azure Active Directory authentication is by right-clicking on the Connected Services item in the project

  1. Right-click on the Connected Services item and select Add Connected Service
  2. From the list of connected services select Authentication with Azure Active Directory to configure single sign-on in your application
  3. On the introduction screen of the wizard click Next
  4. On the next screen enter your Domain (tenant) and an App ID URI
    If your WebAPI wasn’t already configured to use SSL, the wizard will do that for you.
  5. Optionally you can click Next to enable Directory access, so the application can read profile information from AAD
  6. Click Finish and the wizard will make the necessary changes to your code like adding Owin middleware, packages the Authorize attribute to the controllers, configure authentication

Tie the ends together

Now with both a WebAPI and a SPA configured to require Azure Active Directory, all I have to do is have them work together. I do this by telling the Angular2 SPA to send a JSON Web Token with every request sent to the WebAPI.

  1. Add the angular2-jwt libraries to the Angular2 SPA
    npm install angular2-jwt --save
  2. Have the route guard acquire the token for the logged in user and store the token in the localStorage. Therefore it needs the App ID URI from the WebAPI service. You can find this in the Azure Portal.
    Add this App ID URI to your AdalJs settings.

    import { Injectable } from '@angular/core';
    @Injectable()
    export class SecretService {
        public get adalConfig(): any {
            return {
                tenant: '[your tenant]',
                clientId: '[a GUID, the application ID]',
                redirectUri: window.location.origin + '/',
                postLogoutRedirectUri: window.location.origin + '/',
                resourceId: "[App ID URI]]"
            };
        }
    }

    Use it to acquire the token in the route guard:

    if (this.adalService.userInfo.isAuthenticated) {
        this.adalService.acquireToken(this.secretService.adalConfig.resourceId)
            .subscribe(tokenOut => localStorage.setItem('id_token', tokenOut));
    
        return true;
    } else {
  3. Change the root URL of the API to its https counterpart and replaced the regular Http provider with the AuthHttp provider in the service
    import { AuthHttp, AuthConfig, AUTH_PROVIDERS, provideAuth } from 'angular2-jwt/angular2-jwt';
    constructor(private http: AuthHttp) {}
  4. Add the provideAuth configuration in app.module.ts to tell it that it needs to add the token from the adal service (from step 2) to each request to the WebAPI that requires authentication
    providers: [MessageService, SecretService, AdalService, RouteGuard, provideAuth({
      tokenGetter: (() => localStorage.getItem('id_token'))
    }) ],
  5. In the Azure Portal, add API access to the WebAPI application to the SPA application
Posted by .Ronald on

Add Azure Active Directory to an existing Angular 2 Single Page Application

This article will guide you through the process of configuring your Single Page Application (SPA) in TypeScript (or JavaScript) to use Azure Active Directory (AAD) authentication.

We will use adal.js Active Directory Authentication Library (ADAL) for JavaScript and ng2-adal (which is built upon adal-angular)

Prepare your application to use Azure Active Directory

  1. If your application doesn’t already use SSL, it is hightly recommended to do it now. AAD without SSL, thus running over an unsecure connection is not advisable and a real hassle to setup.
    Note the SSL url of your application, as you will need it later to register the application in AAD.

Register your application in Azure Active Directory

If you haven’t already registered your application in the Azure Portal, follow the steps below:

  1. Sign in to the Azure portal
  2. Choose Azure Active Directory from your services (search using More Services if it isn’t shown yet)
  3. Choose App registrations and Add
  4. Enter a Name, choose Web app / API for Application Type and enter the URL of your web application under Sign-on URL (without the trailing slash)
    The URL is the SSL url we got earlier when we enable SSL for our web application
  5. Click Create
  6. Still in your application registrations, choose your application, choose All settings and Properties
  7. Copy the Application ID
  8. Enter the Logout URL as the Sign-on URL you entered earlier, followed by /Account/EndSessionThis will link to the single sign out URL of our application
  9. Also from the Settings menu, add a Key with a duration of 1 or 2 years
    Note down the key, as you will not be able to retrieve it afterwards.

Additional steps required for your SPA

Authentication happens using OAuth2 protocol. Applications provisioned in AAD are not by default enabled to use OAuth2, so you need to explicitly opt-in to do so:

  1. Still in the Azure Portal and in the page of the application you created before, click on Manifest to open the manifest editor.
    Alternatively, you can download, edit and upload the manifest afterwards, but the inline manifest editor is much easier to use.
  2. Look for the oauth2AllowImplicitFlow setting, which by default is set to false. Set it to true and save the manifest
    "oauth2AllowImplicitFlow": true,

Implementing and configuring adal.js in your Angular 2 SPA – Overview

In general you will need to follow theses steps, I will explain them in detail further on:

  1. Acquire the adal.js resources
  2. Create a service that provides you with the AAD settings
  3. Create and use a routeguard
  4. Add the Adal services to your application and initialize them
  5. Create a component to login and logout

Acquire the adal.js resources

  1. If you’re using the Node Package Manager (npm) system, it’s as easy as executing 1 single command to pull in the ng2-adal package and all it’s dependencies
    npm install ng2-adal --saveYou can also pull in the ng2-adal package with another package manager or manually. Make sure to also pull in all the required dependencies.
  2. If you’re using a module loader like SystemJS, you will need to add the modules to its configuration file, like it is shown for the systemjs.config.js file for SystemJS:
    (function (global) {
      System.config({
        paths: {
          // paths configuration
        },
        map: {
          // existing map configuration
    
          // adal libraries
          'ng2-adal': 'npm:ng2-adal',
          'adal': 'npm:adal-angular/lib',
          'adal-angular': 'npm:adal-angular/lib',
        },
        packages: {
          // existing package configuration
    
          // adal packages
          'ng2-adal': { main: 'core.js', defaultExtension: 'js' },
          'adal-angular': { main: 'adal-angular', defaultExtension: 'js' },
          'adal': { main: 'adal.js', defaultExtension: 'js' }
        }
      });
    })(this);

Create a service that provides you with the AAD settings

This is a simple angular service that stores the AAD settings, so they are easily manageable and accessible

  1. create a file called secret.service.ts
    import {Injectable} from '@angular/core';
    
    @Injectable()
    export class SecretService {
        public get adalConfig(): any {
            return {
                tenant: '[your tenant]',
                clientId: '[a GUID, the application ID]',
                redirectUri: window.location.origin + '/',
                postLogoutRedirectUri: window.location.origin + '/'
            };
        }
    }

Create and use a routeguard

A route guard is used to control the routers behavior and returns true or false to indicate whether the route can be followed or not

  1. Create an authentication guard (LoggedInGuard.ts) and implement the canActivate() method that checks whether the user is authenticated over Adal, if authenticated returning true, otherwise navigating to a login page
    import { Injectable } from '@angular/core';
    import { Router, CanActivate } from '@angular/router';
    import { AdalService } from 'ng2-adal/core';
    
    @Injectable()
    export class LoggedInGuard implements CanActivate {
        constructor(private adalService: AdalService,
            private router: Router) { }
    
        canActivate() {
            if (this.adalService.userInfo.isAuthenticated) {
                return true;
            } else {
                this.router.navigate(['/login']);
                return false;
            }
        }
    }
  2. Protect the route with the authentication guard in your routing configuration (fragmented code sample shown):
    import { SecretService } from "./secret.service"; 
    import { AdalService } from "ng2-adal/core"; 
    import { LoggedInGuard } from './LoggedInGuard';
    
    // ...
    
    { path: 'protected', component: protectedComponent, canActivate: [LoggedInGuard] },
    
    // ...
    
    providers: [AdalService, SecretService, LoggedInGuard],

Add the Adal services to your application and initialize them

  1. In app.component.ts add following code to import the services
    import { Component, OnInit } from '@angular/core';
    import { SecretService } from './secret.service';
    import { AdalService } from "ng2-adal/core";
  2. Still in app.component.ts initialize the Adal service in the constructor with the settings stored in the Secret service
    export class AppComponent implements OnInit {
        profile: any;
      
        constructor(
            private adalService: AdalService,
            private secretService: SecretService) {
            this.adalService.init(this.secretService.adalConfig);
        }
    }
  3. To prevent the user having to log in every time again, the authentication token is stored in the browser cache. This allows us to try to retrieve this token and continue using the application without being redirected again to the Azure login page.
    Add following code to app.component.ts to get the user object from cache:

        ngOnInit(): void {
            this.adalService.handleWindowCallback();
            this.adalService.getUser();
        }

Create a component to login and logout

This is a very straightforward way to add a login and logout button to your application. The essence is to call the adalService.login() and adalService.logOut() functions. Integrate them in your application to meet your requirements:

  1. Create a login component (login.component.ts)
    import {Component, OnInit} from '@angular/core';
    import {Router} from "@angular/router";
    import {AdalService} from 'ng2-adal/core';
    
    @Component({
        selector: 'welcome',
        template: '<h1>You need to login first</h1><button (click)="logIn()">Login</button>'
    })
    export class LoginComponent {
    
        constructor(
            private router: Router,
            private adalService: AdalService
        ) {
            if (this.adalService.userInfo.isAuthenticated) {
                this.router.navigate(['/home']);
            }
        }
    
        public logIn() {
            this.adalService.login();
        }
    }

    If the user is already logged in with valid Azure Active Directory credentials, he will immediately be redirected to the /home page.
    Otherwise, the user is presented with the Azure login page to login first, and afterwards redirected to the home page URL you provided in the AAD application registration.

  2. Create a logout component (logout.component.ts)
    import {Component} from '@angular/core';
    import {AdalService} from 'ng2-adal/core';
    
    @Component({
        selector: 'logout',
        template: '<div protected><h1>This is the logout page.</h1><button (click)="logOut()">Logout</button></div>'
    })
    export class LogoutComponent {
    
        constructor(
            private adalService: AdalService
        ) {
        }
    
        public logOut() {
            this.adalService.logOut();
        }
    }

    This will sign out the user from Azure Active Directory, invalidate the users authentication token and redirect to the post logout URL.

  3. Add routes to the login and logout components
    import { LoginComponent} from './login.component';
    import { LogoutComponent} from './logout.component';
    { path: 'logout', component: LogoutComponent },
    { path: 'login', component: LoginComponent },
    declarations: [AppComponent, LoginComponent, LogoutComponent, /* ... */ ]

References

Posted by .Ronald on

Add Azure Active Directory to an existing ASP.NET MVC web application

There are 2 options to add Azure Active Directory to your existing ASP.NET MVC application.

The easiest one is in Visual Studio. Right-click on your web project, and you are presented with the possibility to configure Azure AD Authentication. This starts a wizard which will do some checks and configures your application for you. Prerequisites are described on the Diagnosing errors with the Azure Active Directory Connection Wizard page.

The other, and slightly more difficult option, is to configure your application yourself. And that is what’s described below.

Prepare your application to use Azure Active Directory

  1. If your application doesn’t already use SSL, you need to enable it now. AAD without SSL, thus running over an unsecure connection is not advisable and a real hassle to setup.
    Note the SSL url, as you will need it later to register the application in AAD.

Remove existing authentication (if any)

  1. If you have configured your application in web.config to use any form of authentication, remove it
    <system.web>
      <authentication mode="None" />
    </system.web>
  2. If you have any settings in web.config regarding AAD authentication, remove them also
    <add key="ida:ClientId" value="[some GUID]" />
    <add key="ida:AADInstance" value="https://login.microsoftonline.com/" />
    <add key="ida:Domain" value="[your domain]" />
    <add key="ida:TenantId" value="[some guid]" />
    <add key="ida:PostLogoutRedirectUri" value="https://localhost:44364/" />
  3. It might also be interesting to check the .csproj file for any left-over authentication elements. Disable them and only enable anonymous authentication
    <PropertyGroup>
      <IISExpressAnonymousAuthentication>enabled</IISExpressAnonymousAuthentication>
      <IISExpressWindowsAuthentication>disabled</IISExpressWindowsAuthentication>
    </PropertyGroup>
  4. Remove authentication NuGet packages

Register your application in Azure Active Directory

  1. Sign in to the Azure portal
  2. Choose Azure Active Directory from your services (search using More Services if it isn’t shown yet)
  3. Choose App registrations and Add
  4. Enter a Name, choose Web app / API for Application Type and enter the URL of your web application under Sign-on URL (without the trailing slash)
    The URL is the SSL url we got earlier when we enable SSL for our web application
  5. Click Create
  6. Still in your application registrations, choose your application, choose All settings and Properties
  7. Copy the Application ID
  8. Enter the Logout URL as the Sign-on URL you entered earlier, followed by /Account/EndSession
    This will link to the single sign out URL of our application
  9. Also from the Settings menu, add a Key with a duration of 1 or 2 years
    Note down the key, as you will not be able to retrieve it afterwards.

Configure your application to use your Azure AD tenant

  1. Open web.config and add appSettings for:
    <appSettings>
      <add key="ida:ClientId" value="[some GUID]" />
      <add key="ida:AppKey" value="[The key we created earlier]" />
      <add key="ida:Tenant" value="[Tenant name]" />
      <add key="ida:AADInstance" value="https://login.microsoftonline.com/{0}" />
      <add key="ida:RedirectUri" value="[Url of the application]" />
    </appSettings>
    
  2. Replace the AccountController with this code:
    public class AccountController : Controller
    {
        public void SignIn()
        {
            // Send an OpenID Connect sign-in request.
            if (!Request.IsAuthenticated)
            {
                HttpContext.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "/" }, OpenIdConnectAuthenticationDefaults.AuthenticationType);
            }
        }
        public void SignOut()
        {
            // Remove all cache entries for this user and send an OpenID Connect sign-out request.
            string userObjectID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
            AuthenticationContext authContext = new AuthenticationContext(Startup.Authority, new NaiveSessionCache(userObjectID));
            authContext.TokenCache.Clear();
    
            HttpContext.GetOwinContext().Authentication.SignOut(
                OpenIdConnectAuthenticationDefaults.AuthenticationType, CookieAuthenticationDefaults.AuthenticationType);
        }
    
        public void EndSession()
        {
            if (HttpContext.Request.IsAuthenticated)
            {
                // Remove all cache entries for this user and send an OpenID Connect sign-out request.
                string userObjectID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
                AuthenticationContext authContext = new AuthenticationContext(Startup.Authority, new NaiveSessionCache(userObjectID));
                authContext.TokenCache.Clear();
            }
    
            // If AAD sends a single sign-out message to the app, end the user's session, but don't redirect to AAD for sign out.
            HttpContext.GetOwinContext().Authentication.SignOut(CookieAuthenticationDefaults.AuthenticationType);
        }
    }

    (Credits for this code go to https://github.com/Azure-Samples/active-directory-dotnet-webapp-webapi-openidconnect/)

  3. Add a reference to Microsoft.AspNet.Identity, Microsoft.Owin.Security.OpenIdConnect, Microsoft.Owin.Security.Cookies, Microsoft.IdentityModel.Clients.ActiveDirectory
    PM> Install-Package Microsoft.AspNet.Identity.Owin
    PM> Install-Package Microsoft.Owin.Security.OpenIdConnect
    PM> Install-Package Microsoft.Owin.Security.Cookies
    PM> Install-Package Microsoft.IdentityModel.Clients.ActiveDirectory
  4. Replace the Startup class in Startup.Auth.cs with this code:
    public partial class Startup
    {
        //
        // The Client ID is used by the application to uniquely identify itself to Azure AD.
        // The App Key is a credential used to authenticate the application to Azure AD.  Azure AD supports password and certificate credentials.
        // The Metadata Address is used by the application to retrieve the signing keys used by Azure AD.
        // The AAD Instance is the instance of Azure, for example public Azure or Azure China.
        // The Authority is the sign-in URL of the tenant.
        // The Post Logout Redirect Uri is the URL where the user will be redirected after they sign out.
        //
        private static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
        private static string appKey = ConfigurationManager.AppSettings["ida:AppKey"];
        private static string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
        private static string tenant = ConfigurationManager.AppSettings["ida:Tenant"];
        private static string redirectUri = ConfigurationManager.AppSettings["ida:RedirectUri"];
    
        public static readonly string Authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant);
    
        // This is the resource ID of the AAD Graph API.  We'll need this to request a token to call the Graph API.
        string graphResourceId = ConfigurationManager.AppSettings["ida:GraphResourceId"];
    
        public void ConfigureAuth(IAppBuilder app)
        {
            app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
    
            app.UseCookieAuthentication(new CookieAuthenticationOptions());
    
            app.UseOpenIdConnectAuthentication(
                new OpenIdConnectAuthenticationOptions
                {
                    ClientId = clientId,
                    Authority = Authority,
                    PostLogoutRedirectUri = redirectUri,
                    RedirectUri = redirectUri,
    
                    Notifications = new OpenIdConnectAuthenticationNotifications()
                    {
                        //
                        // If there is a code in the OpenID Connect response, redeem it for an access token and refresh token, and store those away.
                        //
                        AuthorizationCodeReceived = OnAuthorizationCodeReceived,
                        AuthenticationFailed = OnAuthenticationFailed
                    }
    
                });
        }
    
        private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context)
        {
            context.HandleResponse();
            context.Response.Redirect("/Home/Error?message=" + context.Exception.Message);
            return Task.FromResult(0);
        }
    
        private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification context)
        {
            var code = context.Code;
    
            ClientCredential credential = new ClientCredential(clientId, appKey);
            string userObjectID = context.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
            AuthenticationContext authContext = new AuthenticationContext(Authority, new NaiveSessionCache(userObjectID));
    
            // If you create the redirectUri this way, it will contain a trailing slash.  
            // Make sure you've registered the same exact Uri in the Azure Portal (including the slash).
            Uri uri = new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path));
    
            AuthenticationResult result = await authContext.AcquireTokenByAuthorizationCodeAsync(code, uri, credential, graphResourceId);
        }
    

    (Credits for this code go to https://github.com/Azure-Samples/active-directory-dotnet-webapp-webapi-openidconnect/)

  5. Add the NaiveSessionCache utility class from https://github.com/Azure-Samples/active-directory-dotnet-webapp-webapi-openidconnect/blob/master/TodoListWebApp/Utils/NaiveSessionCache.cs to your web application
  6. Replace the content of _LoginPartial.cshtml with this code:
    @if (Request.IsAuthenticated)
    {
        <text>
            <ul class="nav navbar-nav navbar-right">
                <li class="navbar-text">
                    Hello, @User.Identity.Name!
                </li>
                <li>
                    @Html.ActionLink("Sign out", "SignOut", "Account")
                </li>
            </ul>
        </text>
    }
    else
    {
        <ul class="nav navbar-nav navbar-right">
            <li>@Html.ActionLink("Sign in", "SignIn", "Account", routeValues: null, htmlAttributes: new { id = "loginLink" })</li>
        </ul>
    }
  7. Decorate the controllers that require authorization with the [Authorize] attribute
    [Authorize]
    public class HomeController : Controller
    {
        public ActionResult Index()

Now run your application. It should require you to sign in to your Azure AD account and ask your permission to read your user profile, so the application knows who you are.

References