Azure Active Directory(AD) Authentication Using ASP.Net Core 6

Azure Active Directory(AD) Authentication using Asp.Net Core 6

What is Azure Active Directory?

Azure Active Directory (Azure AD) is a cloud-based identity and access management service. The service helps employees access external resources such as his Microsoft 365, Azure portal, and thousands of other SaaS applications. Azure Active Directory also helps you access internal resources such as apps on your corporate intranet or cloud apps developed for your own organization. Learn more about creating a tenant for your organization.

Who is using Azure AD?

Azure AD is intended for:

  • IT Admin
    As an IT administrator, you use Azure AD to control access to apps and app resources based on your business needs. For example, you can use Azure AD to require multi-factor authentication when accessing critical corporate resources. You can also use Azure AD to automate user provisioning between your existing Windows Server AD and cloud apps such as Microsoft 365. Finally, Azure AD automatically protects user identities and credentials and provides powerful tools to meet your access control needs. First, sign up for a 30-day free trial of Azure Active Directory Premium.
  • App developer
    App developers can use Azure AD as a standards-based approach to adding single sign-on (SSO) to their apps, allowing them to work with users' existing credentials. Azure AD also provides APIs that you can use to create personalized app experiences using your existing enterprise data. First, sign up for a 30-day free trial of Azure Active Directory Premium. See also Azure Active Directory for developers for more information.
  • Microsoft 365, Office 365, Azure, or Dynamics CRM Online subscribers
    As a subscriber, I'm already using Azure AD. All Microsoft 365, Office 365, Azure, and Dynamics CRM Online tenants are automatically Azure AD tenants. Start managing access to your integrated cloud apps today.

Let's start with a practical example

First, I followed this blog to add cookie authentication to my code. It provides a login page where you can log in. The AccountController also provides some dummy user accounts that can be used to log in for testing purposes.

I then added code to the login page to provide the option to log in using AAD.

Modify the Program.cs file to add multiple authentication schemes.

Here is my code, Program.cs

using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.UI;

var builder = WebApplication.CreateBuilder(args);

//set CookieAuthenticationDefaults.AuthenticationScheme as the default authentication scheme
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(x => x.LoginPath = "/account/login");
builder.Services.AddAuthentication()
    .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"), OpenIdConnectDefaults.AuthenticationScheme, "ADCookies");

// Add microsoft sign in page
builder.Services.AddControllersWithViews().AddMicrosoftIdentityUI();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();

Note: If you have not installed any required NuGetPackage then please install NugetPackage according to the requirement 

So, see the above code and understand here I set the CookieAuthenticationDefaults.AuthenticationScheme as the default authentication scheme and also added Microsoft sign-in page see the above program.cs code and set exactly what I did.

Let's add HomeController and put the below code

using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Diagnostics;
using WebAppMvcCookieAuthAad.Models;

namespace WebAppMvcCookieAuthAad.Controllers
{
    [AllowAnonymous]
    public class HomeController : Controller
    {
        /// <summary>
        /// Added By Rajesh Gami
        /// </summary>

        public IActionResult Index()
        {
            return View();
        }

        [Authorize]
        public async Task<IActionResult> ConfidentialDataAsync()
        {
            return View();
        }
    }
}

In the next step we have to add a Model class, So let's add two models LoginModel and UserModel as shown below. 

please create two separate model LoginModel and UserModel

using System.ComponentModel.DataAnnotations;

namespace WebAppMvcCookieAuthAad.Models
{
    public class LoginModel
    {
        [Required]
        [Display(Name = "Username")]
        public string UserName{get;set;}
        [Required]
        [DataType(DataType.Password)]
        public string Password{get;set;}
        public bool RememberLogin{get;set;}
        public string ReturnUrl{get;set;}
    }
}

namespace WebAppMvcCookieAuthAad.Models
{
    public class UserModel
    {
        public int UserId { get; set; }
        public string Username { get; set; }
        public string Password { get; set; }
        public string Role { get; set; }
    }
}

Now add the below code into AccountController (Create new AccountController if not created) 

using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;
using WebAppMvcCookieAuthAad.Models;

namespace WebAppMvcCookieAuthAad.Controllers
{
    public class AccountController : Controller
    { 
        public List<UserModel> users = null;
        public AccountController()
        {
            users = new List<UserModel>();
            users.Add(new UserModel()
            {
                UserId = 1,
                Username = "Tiny",
                Password = "123",
                Role = "Admin"
            });
            users.Add(new UserModel()
            {
                UserId = 2,
                Username = "Other",
                Password = "123",
                Role = "User"
            });
        }
        public IActionResult Login(string ReturnUrl = "/")
        {
            LoginModel objLoginModel = new LoginModel();
            objLoginModel.ReturnUrl = ReturnUrl;
            return View(objLoginModel);
        }
        [HttpPost]
        public async Task<IActionResult> Login(LoginModel objLoginModel)
        {
            if (ModelState.IsValid)
            {
                var user = users.Where(x => x.Username == objLoginModel.UserName && x.Password == objLoginModel.Password).FirstOrDefault();
                if (user == null)
                { 
                    ViewBag.Message = "Invalid Credential";
                    return View(objLoginModel);
                }
                else
                {
                    var claims = new List<Claim>() {
                    new Claim(ClaimTypes.NameIdentifier, Convert.ToString(user.UserId)),
                        new Claim(ClaimTypes.Name, user.Username),
                        new Claim(ClaimTypes.Role, user.Role),
                        new Claim("FavoriteDrink", "Tea")
                    };
                    var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
                    var principal = new ClaimsPrincipal(identity);
                    await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal, new AuthenticationProperties()
                    {
                        IsPersistent = objLoginModel.RememberLogin
                    });
                    return LocalRedirect(objLoginModel.ReturnUrl);
                }
            }
            return View(objLoginModel);
        }
        public async Task<IActionResult> LogOut()
        {
            await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
            return LocalRedirect("/");
        }
    }
}

Here you can see I have added the Login action method, In this method, I added the required code so please add the code as mine.

So,need to add one view under the Login action post method. Add view and put the below code 

View >> Account >>Login.cshtml:

@model WebAppMvcCookieAuthAad.Models.LoginModel
@{
    ViewData["Title"] = "Login";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>Login</h2>
<hr />
<div class="row">
    <div class="col-md-4">
        <form asp-action="Login">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            @if (!string.IsNullOrEmpty(ViewBag.Message))
            {
                <span class="text-danger">
                    @ViewBag.Message
                </span>
            }
            @Html.HiddenFor(x => x.ReturnUrl)
            <div class="form-group">
                <label asp-for="UserName" class="control-label"></label>
                <input asp-for="UserName" class="form-control" />
                <span asp-validation-for="UserName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Password" class="control-label"></label>
                <input asp-for="Password" class="form-control" />
                <span asp-validation-for="Password" class="text-danger"></span>
            </div>
            <div class="form-group">
                <div class="checkbox">
                    <label>
                        <input asp-for="RememberLogin" /> @Html.DisplayNameFor(model => model.RememberLogin)
                    </label>
                </div>
            </div>
            <div class="form-group">
                <input type="submit" value="Login" />
            </div>
        </form>
    </div>
</div>

<div>
    <label>sign in with aad</label>
    <a asp-area="MicrosoftIdentity" asp-controller="Account" asp-action="SignIn">Sign in with aad</a>
</div>

And in the next step need to add some view code into  ConfidentialData.cshtml,  (If not created view please add view under Home)

View -> Home -> ConfidentialData.cshtml

@if(User.Identity.IsAuthenticated){
    <table>
        @foreach (var item in User.Claims)
        {
            <tr><td>@item.Type</td><td>@item.Value</td></tr>
        }
    </table>
}

Now the most important part is to add one partial view that we use as a layout for our AD login process. 

Please don't forget to add this partial view under the shared folder.

View -> Shared -> _LoginPartial.cshtml

@using System.Security.Principal

<ul class="navbar-nav">
    @if (User.Identity.IsAuthenticated)
    {
        <li class="nav-item">
            <span class="navbar-text text-dark">Hello @User.Identity.Name!</span>
        </li>
        <li class="nav-item">
            <a class="nav-link text-dark" asp-controller="Account" asp-action="LogOut">log out</a>
        </li>
        @*asp - area = "MicrosoftIdentity"*@
    }
    else
    {
        <li class="nav-item">
            <a class="nav-link text-dark" asp-controller="Account" asp-action="Login">log in</a>
        </li>
    }
</ul>

So now almost the code is done just need to add some AD configuration keys into the appsetting.json file

appsetting.json

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "Domain": "tenant_name",
    "TenantId": "tenant_id",
    "ClientId": "azure_ad_app_id",
    "ClientSecret": "azure_ad_client_secret",
    "CallbackPath": "/home", //don't forget to set redirect url in azure 
    "SignedOutCallbackPath ": "/signout-callback-oidc"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}

Azure Active Directory(AD) Authentication using Asp.Net Core 6

This code worked fine on my side and allowed me to login with cookie auth and aad. I noticed that @User.Identity.Name doesn't show my username after signing in with aad. However, the login flow actually succeeds.

For a better understanding please set all the code as mine and run the application and try to login with AD please use a debugger for better understanding.

If you have any queries or something else please feel free to contact me at my email id [email protected] or comment here.