Categories
Technical

Cookie Authentication in ASP.NET CORE MVC Web Application

Authentication is the process of identifying the user that is who you are. Another related term is Authorization is this process of identifying whether you have authorized to access something in web it will be website pages.

Authorization is the important module, most of the projects would not complete without covering this area.  Also, there are multiple number for authentication types available, and we are always confused about which one we can go for it.

Few are,

  1. ASP.NET Core Identity
  2. Cookie Authentication
  3. OAuth Authentication

In this topic, I will explain the ways to create simple ASP.NET Core application (Using MVC template) and add Cookie Authentication in that application.

Cookie Authentication would be the popular choice to developers to use to secure their web application.  Also, this involves easy steps to implement.

Prerequisites:

  1. Visual Studio 2017
  2. .NET Core 2.0 or Greater.


Download the Sample Project File,

Steps to Create Cookie Authentication in ASP.NET Core Application

Step 1:

Create New Project -> Web -> ASP.NET Core Web Application. I have named the application as “CookieAuthSample”.

Create New ASP.NET Core Application

Step 2:

Select .NET Core version you want. In this sample, I have used ASP.NET Core 2.2 and Web Application (Model-View-Controller) template.

Also make sure to select “No Authentication”.

Note: If you select any Authentication by clicking Change Authentication, it will create ASP.NET Core Identity instead of Cookie Authentication.

This step creates the empty ASP.NET Core MVC project with no authentication. After this, we need to add few codes to implement Cookie Authentication.

Step 3:

First in Startup.cs file, Add the following line inside the method

ConfigureServices(IServiceCollection services)

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

You should include the namespace

using Microsoft.AspNetCore.Authentication.Cookies;

After this, add the following line inside method,

Configure(IApplicationBuilder app, IHostingEnvironment env)

app.UseAuthentication();

Full Code of Startup.cs file,

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Authentication.Cookies;

namespace CookieAuthSample
{
    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_2);
        }

        // 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");
                // 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.UseCookiePolicy();
            app.UseAuthentication();
            
            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

Now, we have done services call to Cookie Authentication. Next, we need to create Controllers, Models, and Views to do login forms, and login and log out operations.

Since, we already know about creation of Controllers, Models and Views, I have not explained about this here.

Step 4

I have created new Controller called “AccountController, and the View respective to Login action inside this controller.

Also, added a new Model class “UserModel.cs” to define users’ properties under the Model Folder.

Full Code of UserModel.cs file

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace CookieAuthSample.Models
{
    public class UserModel
    {
        public string Email { get; set; }

        public string Password { get; set; }

        public string Role { get; set; }
    }
}

Here just created 3 simple properties – Email, Password and Roles to manage users.

Added 2 methods in AccountController.cs

  1. Login -> Get and Post Action
  2. Logout

Login Get Action

Created new view for Login, it has code block for Login Form where user gives email address and password as inputs and submit the form.

Full Code of Login.cshtml view file

@{
    ViewData["Title"] = "Login";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<div class="container">
    <div class="row">
        <div class="col-md-4">
            <h2><strong>Login Form </strong></h2><br />
            <form asp-action="login" method="post">
                <div class="form-group">
                    <label>Email Name</label>
                    <input type="text" class="form-control" id="email" name="email" placeholder="Enter email address">
                </div>
                <div class="form-group">
                    <label>Password</label>
                    <input type="password" class="form-control" name="password" id="password" placeholder="Password">
                </div>
                <div class="form-button">
                    <button type="submit" class="btn btn-primary">Submit</button>
                </div>
            </form>
        </div>
    </div>
</div>

Login Post Action

In Login Post action, we do the validation for user form input, and create the identity for the user once we found the user input is valid and exists with our system.

Note: We can add database operation for validating user data which maintained in your application database custom tables.

Logout Action

We have done code which makes logged in user to logout state once click on log out button. You can keep that logout button anywhere.

Full Code of AccountController.cs file,

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Security.Claims;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using CookieAuthSample.Models;

namespace CookieAuthSample.Controllers
{
    public class AccountController : Controller
    {

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

        [HttpPost]
        public IActionResult Login(string email, string password)
        {
            if (!string.IsNullOrEmpty(email) && string.IsNullOrEmpty(password))
            {
                //You can required server side validations, only empty validation handled.
                //You can apply model binding too.
                return RedirectToAction("Login");
            }

            //Write code to check the email and passwords from the Database.

            //Here, I have matched from local List the email and password get from user table in Database.

            List<UserModel> userModels = new List<UserModel>();

            UserModel user1 = new UserModel()
            {
                Email = "karthik@test.com",
                Password = "12345678",
                Role = "Admin"
            };
            userModels.Add(user1);

            UserModel user2 = new UserModel()
            {
                Email = "karthik@abc.com",
                Password = "qwerty",
                Role = "User"
            };
            userModels.Add(user2);

            var userResult = (from users in userModels
                              where users.Email == email && users.Password == password
                              select users).FirstOrDefault();

            ClaimsIdentity identity = null;
            bool isAuthenticated = false;

            if (userResult == null)
            {
                //Login Failed. No User found in DB.
                return RedirectToAction("Login");
            }
            else
            {
                //Create the identity for the user
                identity = new ClaimsIdentity(new[] {
                    new Claim(ClaimTypes.Name, userResult.Email),
                    new Claim(ClaimTypes.Role, userResult.Role)
                }, CookieAuthenticationDefaults.AuthenticationScheme);

                isAuthenticated = true;
            }

            if (isAuthenticated)
            {
                var principal = new ClaimsPrincipal(identity);

                var login = HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);

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

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

I have added Logout button link in Home controller’s Index page.

That’s it, we have done the code to Authentication process using simple login form in ASP.NET Core MVC application.

Now one more thing is pending, which is we should Authorize required pages.

How to Authorize page?

Just simple, adding Authorize action filter at the top of the controller or method do this job.

Adding this Authorize filter above the Controller applies to all the methods inside that controller.

If we add above the method, then this only applies to that specific method.

In this example, I have added the Authorize action filter above the Index and Privacy methods. So these two pages will be accessed only after done the authentication and have right authorization.

Here, Index page can be accessed to users in the role Admin and User. But Privacy page only can be authorized to access admin alone.

Full Code of HomeController.cs file,

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using CookieAuthSample.Models;

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

        [Authorize(Roles = "Admin")]
        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 });
        }
    }
}

Run the Application

You have done code do achieve Cookie Authentication in ASP.NET Core MVC application. Now you can build the application and Run it.

While running application, it will go to Home/Index. But since you have authorized this page, you will be redirected to Account/Login.

Once you have done the authentication, will be redirected to Home/Index page.

After Authentication done,

Conclusion

I hope this article explains the steps to implement Cookie Authentication in ASP.NET Core MVC application.  I prefer to use Cookie Authentication or OAUTH authentication if you have user-based web application which security is more concerns.

Compared to ASP.NET Core Identity, I prefer Cookie Authentication is more secure and easy to understand and convenient to apply the custom code.

I found the one security related problem while using with ASP.NET Core identity. This identity application is created by default if we select Authentication as Individual Authentication while creating new ASP.NET Core application.

.AspNetCore.Identity.Application cookie will be created once we do the login using ASP.NET Core Identity,  and after we do the logout, this cookie getting deleted. But its not actually deleting properly.

After I have done the logout, I tried to add the .AspNetCore.Identity.Application cookie with the same value I already get while in login state. Using cookie editing extension which helps to insert the new cookie into the current page. Once I added the same cookie again, I can access to any authorized pages without login again. I could not find right solution to this security issue. Also find some reference about the issue.

https://github.com/IdentityServer/IdentityServer4/issues/2087

If you guys, faced the same problem and get the solution, you can do comment here.

Individual Authentication creates Scaffolding identity codes which allows you do authentication without making any code changes by developer side except database migration to store identity users to your database tables.

And these Scaffolding Identity files are included in Razor Class Library itself. So, by default you do not find the files for login and logout functions. But you can create new identity scaffolding, which show those files.

References

https://docs.microsoft.com/en-us/aspnet/core/security/authentication/scaffold-identity?view=aspnetcore-3.1&tabs=visual-studio
https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity?view=aspnetcore-3.1&tabs=visual-studio

One reply on “Cookie Authentication in ASP.NET CORE MVC Web Application”

Leave a Reply

Your email address will not be published. Required fields are marked *