Authentication And Authorization In ASP.NET Core MVC Using Cookie

Authentication and Authorization are two major aspects while thinking about securing your application. Security is the main concern of modern applications because anyone can steal your data if it is not secured. So, if you are going to create an application where the data security is a primary concern, then think about Authentication and Authorization.

Authentication is the process to validate an anonymous user based on some credentials and Authorization process happens just after that and grants resources to this validated user. So, we can say, it's two-step validating process before providing the access of the resources or data.

We have many techniques to validate the users, like Windows Authentication, JWT Authentication, and Cookie Authentication etc. Today, we will learn how to implement and make ASP.NET Core MVC applications more secure using Cookie-based authentication and authorization. So, let's start the demonstration and create a fresh ASP.NET Core MVC project. You can refer to the following for the step by step process of creating an ASP.NET Core MVC application.

First Application in ASP.NET Core MVC 2.0

Be sure that while creating the project, your template should be Web Application (Model-View-Controller) and change the authentication as ‘No Authentication’.

You can download the code from here.

Here, you can choose the inbuilt Authentication functionality instead of ‘No Authentication’ and it will provide the readymade code. But we are choosing ‘No Authentication’ here because we are going to add our own Cookie-based authentication functionality in this demo and you will learn how to implement the Authentication and Authorization system from scratch.

We are choosing MVC template because we would like to see some Login and Logout functionality on UI along with Authentication and Authorization using Cookies. Now, click OK and it will take a few seconds and the project will be ready. Run it for checking if everything is working fine or not. Once everything is OK, you are ready to go.

Let’s move to the starting point of the ASP.NET Core application file which is “Startup.cs” where we configure the setting for the application like configuring the required services and configuring the middleware services etc. So, implementing the Authentication features, first, we have to add the authentication and then use it. So, let’s move to Startup.cs’s ConfigureService method and add the authentication feature using the following line of code, it will be just above services.AddMvc().

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
        .AddCookie();

Now move to Configure in the startup.cs method and use the authentication features using the following line of code, it will be just above routing.

app.UseAuthentication();

Following is the whole code for adding the Authentication and using it.

using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace CookieDemo
{
    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.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });
         services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie();
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }
            app.UseStaticFiles();
            app.UseCookiePolicy();
            app.UseAuthentication();
            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

We can implement Authentication through the Login feature. In most of the applications today, Authorization is decided internally based on your role. So, now we are going to create an account login and logout feature, so just create one more controller as ‘AccountController.cs’ inside the controller's folder and add two action methods, one for rendering the Login View and the other one for posting user credentials data for logging in to the system. Here is the code for AccountController where we have implemented Login functionality.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Mvc;
namespace CookieDemo.Controllers
{
    public class AccountController : Controller
    {
        public IActionResult Login()
        {
            return View();
        }
        [HttpPost]
        public IActionResult Login(string userName, string password)
        {
            if (!string.IsNullOrEmpty(userName) && string.IsNullOrEmpty(password))
            {
                return RedirectToAction("Login");
            }
            // Check the user name and password
            // Here can be implemented checking logic from the database
            if (userName == "Admin" && password == "password")
            {
                // Create the identity for the user
                var identity = new ClaimsIdentity(new[]
                {
                    new Claim(ClaimTypes.Name, userName)
                }, CookieAuthenticationDefaults.AuthenticationScheme);
                var principal = new ClaimsPrincipal(identity);
                var login = HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);

                return RedirectToAction("Index", "Home");
            }
            return View();
        }
    }
}

The first login action method is rendering the UI for the login page and once you fill in the data required for Login as username and password then the second action method Login will work and send the Post request to the server.

In this method, first, we will check whether the username and password are empty then we will validate the username and password. Here, in this demonstration, we are checking the username and password with some dummy data. You can implement a database login instead of this.

After validating the user information, if everything is correct then we create Identity for that user and create the cookie information for it. Based on this principle data, we try to Sign In using a generic function called "SignInAsync" and if everything goes in the right direction then we redirect to the Home page.

Now, let's create the Login view page from where we can give the functionality to the user to enter the username and password. So, right click on the Login action method and add a view without a model. It will automatically create the Account folder inside the Views under that will create the “login. cshtml” file. Just open it and create a container and add a form tag along with two textboxes for entering the username and password. Apart from this, create two separate buttons as “Submit” and “Reset”. Once you fill in the data and click on the submit button, it will call to the Login action method defined in the Account Controller using POST call. So, modify the code of “login. cshtml” as follows.

@{
    ViewData["Title"] = "Login";
}
<div class="container">
    <div class="row">
        <div class="col-md-3">
            <h2><strong>Login Page</strong></h2><br />
            <form asp-action="login" method="post">
                <div class="form-group">
                    <label>User Name</label>
                    <input type="text" class="form-control" id="userName" name="userName" placeholder="Enter user name">
                </div>
                <div class="form-group">
                    <label>Password</label>
                    <input type="password" class="form-control" name="password" id="password" placeholder="Password">
                </div>
                <div class="form-check">
                    <button class="btn btn-info" type="reset">Reset</button>
                    <button type="submit" class="btn btn-primary">Submit</button>
                </div>
            </form>
        </div>
    </div>
</div>

So far we have implemented the Cookie-based Authentication functionality in Asp.Net Core MVC project. But what about Authorization. Authorization means, providing access to the authenticated user to access a resource based on role.

So, let's first understand how we can implement the Authorization in Asp.Net Core MVC. For now, if you will try to access the HOME page without sign in, you can access it. So, let’s prevent the anonymous user from accessing the HOME page directly, if someone wants to access the HOME page then they should have to go through the Authentication process and then they will be able to access it.

So, to accomplish this, let’s open the Home Controller and put the [Authorize] attribute just above to controller. You can place it at the action level but here we would like to block the whole home controller functionality and if we want to access it, just go and log in. So, just do something like below.

using CookieDemo.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Diagnostics;
namespace CookieDemo.Controllers
{
    [Authorize]
    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }
        public IActionResult About()
        {
            ViewData["Message"] = "Your application description page.";

            return View();
        }
        public IActionResult Contact()
        {
            ViewData["Message"] = "Your contact page.";

            return View();
        }
        public IActionResult Privacy()
        {
            return View();
        }
        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public IActionResult Error()
        {
            return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
        }
    }
}

Note

Be sure you have cleared all cookies that have been created based on your previous login. If you do not do this, you will be accessing the HOME page, it is because an authenticated user cookie is available in the browser memory.

So, let's check how it works. Run the application and try to access the Home page. You will see here that your application automatically redirects to the Login page. Now let's try to provide the user information as username = "Admin" and password =" password". Once you will pass the correct credentials and log then you will be redirected to HOME page. So, let's add the feature that shows the logged-in username along with a logout button. If you click on the log-out button, your cookie value will be deleted and you will be redirected to the login page.

So, let's open the Account Controller and add the following logout action method.

[HttpPost]
public IActionResult Logout()
{
    var login = HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
    return RedirectToAction("Login");
}

And now open the Index.cshtml file from the Home folder inside the Views and modify the code as follows. Here, first of all, we are trying to show the Logged In username using @User.Identity.Name and apart from this adding a link for out.

@{
    ViewData["Title"] = "Home Page";
}
<div class="container">
    <div class="row">
        <div class="col-md-12">
            <h2><strong>Home Page</strong></h2><br /><br />
            Hello @User.Identity.Name
            <a asp-action="logout" asp-controller="account">
                Logout
            </a>
            <br /><br />
            <h4>Welcome to Asp.Net Core Authentication and Authorization Demo!!</h4>
        </div>
    </div>
</div>

So far, we are able to understand how to implement Authentication in Asp.Net Core MVC and how to implement Authorization and give access to validate the users. Now, let's understand how to work with multiple roles. Here we are doing everything manually with some static value, but you can change the logic and connect to the database for validating the user. So, just modify the Login method as follows where we are providing two different kinds of roles; one is Admin role and another is User role. Based on these roles, we will provide access to some of the pages.

[HttpPost]
public IActionResult Login(string userName, string password)
{
    if (!string.IsNullOrEmpty(userName) && string.IsNullOrEmpty(password))
    {
        return RedirectToAction("Login");
    }
    // Check the user name and password
    // Here can be implemented checking logic from the database
    ClaimsIdentity identity = null;
    bool isAuthenticated = false;
    if (userName == "Admin" && password == "password")
    {
        // Create the identity for the user
        identity = new ClaimsIdentity(new[]
        {
            new Claim(ClaimTypes.Name, userName),
            new Claim(ClaimTypes.Role, "Admin")
        }, CookieAuthenticationDefaults.AuthenticationScheme);
        isAuthenticated = true;
    }
    if (userName == "Mukesh" && password == "password")
    {
        // Create the identity for the user
        identity = new ClaimsIdentity(new[]
        {
            new Claim(ClaimTypes.Name, userName),
            new Claim(ClaimTypes.Role, "User")
        }, CookieAuthenticationDefaults.AuthenticationScheme);

        isAuthenticated = true;
    }
    if (isAuthenticated)
    {
        var principal = new ClaimsPrincipal(identity);
        var login = HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);
        return RedirectToAction("Index", "Home");
    }
    return View();
}

Now let's move to HomeController remove the [Authorize] attribute from the class level and put it in the action level as follows. Here we have two different action methods which point to two different views. One is pointing to the Index view and another one is pointing to the Setting page. The index page can be accessible to both types of roles, either it is Admin or User but the Setting page can be accessed only by the Admin role.

public class HomeController : Controller
{
    [Authorize(Roles = "Admin, User")]
    public IActionResult Index()
    {
        return View();
    }
    [Authorize(Roles = "Admin")]
    public IActionResult Setting()
    {
        return View();
    }
}

Now modify the Index.cshtml file and add one thing as Role, just modify the code as follows.

@{
    ViewData["Title"] = "Setting Page";
}  
<div class="container">
    <div class="row">
        <div class="col-md-12">
            <h2><strong>Setting Page </strong></h2><br /><br />
            Hello @User.Identity.Name !, Role @User.FindFirst(claim => claim.Type == System.Security.Claims.ClaimTypes.Role)?.Value
            <a asp-action="logout" asp-controller="account">
                Logout
            </a>
            <br />
            <br />
            <h4>Admin role user can only access this page!!</h4>
        </div>
    </div>
</div>

Now, we have added everything and it's time to run the application. So, just press F5 and it will run your application. First, go to the "Login" page and log in with the "User" role.

Login

Once you log in as a User role, definitely you will be redirected to the home page because the home page is accessible to both types the roles.

Home Page

Now, let's try to access the settings page, here you will get some Access Denied error. It is because the "User" role member does not allow you to access the settings page. By default, you will get the following error as per the browser. But you can customize your error and page as well. It totally depends on you.

HTTP

Now, let's log out of the application for the "User" role and try to log in for the Admin role. As follows, you can see, we are able to access the home page.

User

But let's try to access the settings page for the "Admin" role and yes, you will access the settings page.

Admin

Lastly, let me show how you can see the cookie information. For this demo, I am using the Microsoft Edge browser, you can use any other as per your choice. But cookie information is saved almost in the same place for every browser. So, just go to the Network tab and then the Cookie tab. Here you can see all the listed Cookies.

Microsoft Edge

Conclusion

So, today we have learned what authentication and authorization are and how to implement the Cookie Based Authentication and Authorization in Asp.Net Core MVC.

I hope this post will help you. Please leave your feedback using the comments which helps me to improve myself for the next post. If you have any doubts please ask your doubts or query in the comment section and If you like this post, please share it with your friends. Thanks.