Cookies in ASP.NET Core MVC

Cookies in ASP.NET Core MVC

In this article, I will discuss Cookies in ASP.NET Core MVC Applications with Examples. In web development, cookies are a way for web applications to remember information about users as they move from page to page. Because the HTTP protocol is stateless and does not keep track of previous requests, cookies help the server and browser share small pieces of data that make features like user login, shopping carts, and personalized settings possible. ASP.NET Core MVC makes it easy to use cookies for managing user sessions, remembering preferences, and providing a smoother, more personalized experience for users.

Why Do We Need Sessions and Cookies in Web Applications?

The core challenge in web development arises from the nature of the HTTP protocol. HTTP is a stateless protocol, which means that each request sent from a client (such as a web browser) to a server is completely independent of any other request. The server treats every request as new and unrelated, with no memory of previous interactions.

Because of this statelessness:

  • The server does not automatically know whether two requests come from the same user, the same browser, or even the same device.
  • Without additional mechanisms, every request is processed in isolation, making it impossible to maintain continuity or context across multiple requests.

This is where sessions and cookies become essential. For a better understanding, please refer to the following image.

Why Do We Need Sessions and Cookies in Web Applications?

Understanding the Stateless Nature of HTTP
  • Statelessness means every HTTP request stands alone: When you visit a website, your browser sends an HTTP request to the server. Once the server responds, it forgets everything about that interaction. If you click a link or submit a form (which sends a new HTTP request), the server has no built-in way to know you are the same person who made the previous request.
  • No automatic tracking or memory: Because of this, important user-specific data, like whether you are logged in, what items are in your shopping cart, or your site preferences, cannot be stored or retrieved by the server on its own across multiple requests.
The Role of Sessions and Cookies in State Management

To overcome HTTP’s stateless limitation, web applications use sessions and cookies as tools to remember information about users across multiple requests. This enables web apps to behave more like traditional stateful applications.

  • Sessions: These are server-side storage spaces where the server keeps user-specific data. Each session is identified by a unique session ID, which the server associates with a particular user.
  • Cookies: These are small pieces of data stored on the client’s browser. Cookies hold information such as the session ID or other key data, and are sent back to the server with every subsequent request, allowing the server to identify and link requests from the same user.
Real-time Use Cases of Sessions and Cookies in Web Applications

The following are some common types of information that sessions and cookies help remember:

Real-time Use Cases of Sessions and Cookies in Web Applications

User Authentication Status
  • Whether the user is logged in or logged out is tracked using sessions or cookies.
  • After a successful login, the server stores a session ID linked to the user’s identity.
  • The browser sends this session ID in cookies with each request, allowing the server to identify the user without requiring login credentials every time.
  • This ensures secure access to personalized content, profile pages, or protected resources.
User Preferences (e.g., Language, Theme)
  • Many websites allow users to choose their preferred language or display theme (such as dark mode or light mode).
  • This preference is stored in cookies or sessions so that the user’s settings persist across visits and page reloads.
  • Without this, users would have to set their preferences on every new page or visit, resulting in a poor user experience.
Shopping Cart Contents
  • In e-commerce applications, sessions or cookies store information about items users add to their shopping carts.
  • This allows users to browse different pages or even leave and return later without losing their selected items.
  • The server uses the session ID from the cookie to retrieve the cart contents from the session storage and display it accordingly.
Form Data Temporarily Saved Between Pages
  • For multi-step forms or wizards, sessions can store form data temporarily as users navigate between pages.
  • This prevents loss of information if users need to go back and forth between steps or if a page reload happens.
  • It also helps pre-fill forms with previously entered data, reducing user effort and errors.
Built-in Support in ASP.NET Core MVC and Other Frameworks

Modern web frameworks, including ASP.NET Core MVC, provide built-in, easy-to-use support for handling sessions and cookies. This means:

  • Developers can easily create, read, update, and delete cookies.
  • Sessions can be managed efficiently on the server, with the framework handling the complexity of associating cookies with session data.
  • Security features, including secure cookies, HTTP-only cookies, and consent management, are available to protect user data.
What is a Cookie?

A cookie is a small piece of information that a web server sends to a user’s web browser, which the browser then stores on the user’s device (such as a computer, phone, or tablet). These cookies are stored as simple text data and consist of name-value pairs, such as a label and its associated value. Every time you revisit a website, your browser automatically sends these cookies back to the server with each subsequent request. For a better understanding, please have a look at the following image:

What is a Cookie?

Without cookies, every interaction with a website would be “anonymous”. The server would not be able to distinguish between multiple requests from the same user and those from different users. Cookies enable web applications to provide a continuous, personalized experience for each user, despite the underlying HTTP protocol being stateless.

What Are Cookies Used For?

Cookies are commonly used to remember information about a user or their interactions with a website. This can include details such as:

  • User Identification: For example, a user ID or username that enables the server to recognize the user making the request.
  • Authentication Tokens: Saving login tokens allows users to avoid re-entering their credentials every time they visit the site.
  • User Preferences: Settings like language choice, theme (dark or light mode), or font size to personalize the experience.
  • Session Management: Keeping track of items in a shopping cart or the progress in a multi-step form.
How Do Cookies Work in Web Applications?

Cookies enable web applications to remember users and their preferences across multiple requests by exchanging small pieces of data between the client (browser) and the server. For a better understanding of how cookies work in a web application, refer to the following diagram.

How Do Cookies Work in Web Applications?

The following is a detailed step-by-step explanation of how cookies work in a web application involving interactions between the client (browser) and the server.

Step 1: Client Makes an Initial Request:

When you visit a website for the first time, your browser sends a request to the web server. At this point, your browser usually does not have any cookies related to that website, so the request contains no cookie data.

Step 2: Server Responds and Sets a Cookie:

The server receives the request, processes it, and decides to create one or more cookies to remember information about your visit or session.

  • The server includes these cookies in the response by adding one or more Set-Cookie headers in the HTTP response.
  • If there are multiple cookies to set, the server can send several Set-Cookie headers (one per cookie).
  • Each Set-Cookie header defines a cookie’s name, value, and optional attributes such as expiration time, path, domain, security flags (HttpOnly), etc.
Step 3: Client Stores the Cookie:

Your browser receives the response and stores the cookie(s) locally on your device according to the instructions specified in the Set-Cookie headers. It stores each cookie according to the attributes specified by the server, such as:

  • Name and Value (what the cookie stores)
  • Expiration Date (how long the cookie should last)
  • Path (which URLs on the site can access the cookie)
  • Domain (which domain(s) the cookie applies to)
  • Secure/HttpOnly (security flags controlling how the cookie can be accessed)
Step 4: Client Makes Subsequent Requests with the Cookie:

When you navigate to other pages on the same website or make further requests, your browser automatically includes all relevant cookies in the HTTP request headers under the Cookie header.

  • This happens only if the cookie is not expired and if the requested URL matches the cookie’s domain and path rules.
  • Even if multiple cookies exist, the browser sends them together in one Cookie header (as a semicolon-separated list).
  • This happens “behind the scenes”, the user doesn’t have to do anything.
Step 5: Server Receives and Reads the Cookie:

The server examines the incoming HTTP request, looks for the Cookie header, and reads any cookie values sent by the client.

  • The server uses the cookie data to identify the user or session, retrieve stored preferences, or decide what content to serve.
Step 6: Server Uses the Cookie Data:

Using the data stored in the cookies, the server can maintain continuity, such as keeping the user logged in, loading their shopping cart, or providing personalized content. So, based on the cookie values, the server can:

  • Maintain user session and authentication status.
  • Personalize content, such as greeting the user by name or showing saved preferences.
  • Track user activity for analytics or functionality.

Cookies help the server distinguish between different users and even between different sessions from the same user.

Step 7: Server Updates or Deletes the Cookie (Optional):

If the server needs to change or remove a cookie:

  • To update: The server sends another Set-Cookie header with the same name but a new value or different attributes (e.g., new expiration date).
  • To delete: The server sends a Set-Cookie header with the same name but sets the expiration date to a time in the past. The browser will then delete the cookie.

This process allows web applications to identify returning users, maintain sessions, keep users logged in, remember preferences, and much more, all while overcoming the stateless nature of the HTTP protocol.

Types of Cookies in Web Applications

Cookies can be broadly categorized into two main types based on their lifespan and purpose: Persistent Cookies and Non-Persistent Cookies (also known as session cookies). Understanding these types helps clarify how websites remember users and manage data. For a better understanding, please refer to the following image.

Types of Cookies in Web Applications

Persistent Cookies

Persistent cookies are cookies that remain stored on the user’s device even after the browser is closed. These cookies have a specified expiration date set by the website.

When a server sets a persistent cookie, it includes an expiration date or maximum age attribute that instructs the browser on how long to retain the cookie. The browser saves the cookie on the device’s storage and continues to send it back to the server with each relevant request until it expires or is manually deleted.

Common Uses:
  • Remembering Login Credentials: So, users don’t have to log in every time they visit the site.
  • User Preferences: These include language selection, theme settings, and layout preferences.

Example: When you check “Remember Me” on a login page, the website typically uses persistent cookies to keep you logged in for days, weeks, or even months.

Non-Persistent Cookies (Session Cookies)

Non-persistent or session cookies exist only during the current browsing session. They are stored temporarily in the browser’s memory and are deleted automatically when the browser is closed (or the specific tab, depending on the browser’s implementation).

The server sets a session cookie without an expiration date. The browser keeps this cookie in temporary memory and includes it with every request to the server during that browsing session.

Common Uses:
  • Session Management: Keeping users logged in while they navigate through different pages of a site in the same session.
  • Shopping Carts: Remembering items added to the cart until the user checks out or closes the browser.
  • Temporary Form Data: Holding information entered in multi-step forms until submission.

Example: When you log in to an online banking site, a session cookie keeps you logged in only until you close your browser.

How Do We Read, Write, and Delete Cookies in ASP.NET Core MVC?

In ASP.NET Core MVC, working with cookies is straightforward thanks to the built-in API provided by the framework. You can write (create), read (retrieve), and delete cookies easily using the Request.Cookies and Response.Cookies collections available in controllers.

Writing a Cookie in ASP.NET Core MVC

To create (write) a cookie, follow these steps:

  • Create an instance of CookieOptions: The CookieOptions class enables us to configure key settings for your cookie, including its expiration, security, and scope.
  • Set your desired properties: For example, set the expiration date to make the cookie persistent, specify the path, and set security flags.
  • Add the cookie to the response: Use the Response.Cookies.Append() method, providing the cookie’s name, value, and the options object.
Example:

Writing a Cookie in ASP.NET Core MVC

Explanation of Important CookieOptions Properties
  • Domain: Specifies the domain(s) where the cookie is valid. Setting this allows sharing cookies across subdomains if desired (e.g., .example.com makes it valid for sub.example.com too).
  • Expires: A fixed expiration date and time when the cookie will be automatically deleted by the browser.
  • Path: Defines the URL path that must exist in the requested URL for the cookie to be sent. Setting it to “/” means the cookie is sent on all requests within the entire domain.
  • Secure: When true, cookies are only sent over secure HTTPS connections, protecting cookies from interception by attackers during transmission.
  • HttpOnly: Prevents JavaScript running in the browser from accessing the cookie. This helps protect cookies from Cross-Site Scripting (XSS) attacks.
  • MaxAge: Sets the duration (time span) the cookie remains valid from the moment it is created. It’s an alternative to specifying an exact expiration date.
  • IsEssential: Indicates that the cookie is essential for the website to function properly, allowing it to be set even if the user has not consented to non-essential cookies (important for GDPR compliance).
Reading a Cookie in ASP.NET Core MVC

To retrieve a cookie’s value from the incoming HTTP request, use the Request.Cookies collection, providing the name (key) of the cookie. Since the cookie might not always exist (the user might have cleared it or it expired), it’s good practice to check for its presence to avoid errors.

Example:

Reading a Cookie in ASP.NET Core MVC

Note: Always validate the cookie’s existence and content before using it; cookies can be missing or may have been tampered with.

Deleting a Cookie in ASP.NET Core MVC

To delete a cookie, you need to call the Response.Cookies.Delete() method, passing the cookie name. You can optionally pass the same CookieOptions used when creating the cookie (if you used a custom Domain or Path when setting the cookie) to ensure the deletion targets the correct cookie scope.

Example:

Deleting a Cookie in ASP.NET Core MVC

Note:
  • Deleting a cookie actually means the browser is instructed to remove it; this is done by setting its expiration date to a time in the past.
  • If you set domain/path options when writing the cookie, you must specify the same options when deleting it; otherwise, the browser may not find and remove the correct cookie.
Example to Understand Cookies in ASP.NET Core MVC:

Let us implement a step-by-step, real-time example using an ASP.NET Core MVC application that demonstrates user login/Logout functionality using both Persistent and Non-Persistent Cookies with an in-memory user store. Let us start by creating a new ASP.NET Core Web Application using the MVC template:

  • Project name: CookieAuthDemo
  • Choose ASP.NET Core Web App (Model-View-Controller) as the template
Define User Model

Create a class file named User.cs within the Models folder and then copy and paste the following code. This class represents a user in the system, containing properties such as ID, Username, Password, and Full Name. It serves as the core data model for user information throughout the application, used to authenticate and personalize the user experience.

namespace CookieAuthDemo.Models
{
    public class User
    {
        public int Id { get; set; }
        public string Username { get; set; } = null!;
        public string Password { get; set; } = null!;
        public string FullName { get; set; } = string.Empty;
    }
}
Create an In-Memory User Store

Create a class file named UserStoreService.cs within the Models folder and then copy and paste the following code. This service acts as an in-memory user database. It holds a predefined list of users and provides methods for validating login credentials and retrieving user details by ID. The controller uses it to verify user credentials during login and retrieve user information as needed.

namespace CookieAuthDemo.Models
{
    public class UserStoreService
    {
        private List<User> _users = new List<User>
        {
            new User { Id = 1, Username = "john", Password = "pass123", FullName = "John Doe" },
            new User { Id = 2, Username = "jane", Password = "pass456", FullName = "Jane Smith" }
        };

        public User? ValidateUser(string username, string password)
        {
            return _users.FirstOrDefault(u =>
                u.Username.Equals(username, StringComparison.OrdinalIgnoreCase) &&
                u.Password == password
            );
        }

        public User? GetUserById(int id)
        {
            return _users.FirstOrDefault(u => u.Id == id);
        }
    }
}
Create ViewModels for Login

Create a class file named LoginViewModel.cs within the Models folder and then copy and paste the following code. This class defines the structure of data for the login form, including Username, Password, and a RememberMe option. It also includes validation attributes to ensure that the login form receives all required information from the user.

using System.ComponentModel.DataAnnotations;

namespace CookieAuthDemo.Models
{
    public class LoginViewModel
    {
        [Required]
        public string Username { get; set; } = null!;

        [Required]
        [DataType(DataType.Password)]
        public string Password { get; set; } = null!;

        public bool RememberMe { get; set; }
    }
}
Create the Account Controller

Create an empty MVC controller named AccountController within the Controllers folder, then copy and paste the following code. This controller manages all user authentication actions, such as login, logout, and displaying the dashboard. It uses the UserStoreService to validate users, sets authentication cookies for login (either session or persistent), and deletes cookies on logout. It also ensures that only authenticated users can access the dashboard.

using CookieAuthDemo.Models;
using Microsoft.AspNetCore.Mvc;

namespace CookieAuthDemo.Controllers
{
    public class AccountController : Controller
    {
        private readonly UserStoreService _userStoreService;
        private const string CookieUserId = "UserId";
        private const string CookieUsername = "Username";

        public AccountController(UserStoreService userStoreService)
        {
            _userStoreService = userStoreService;
        }

        // GET: /Account/Login
        [HttpGet]
        public IActionResult Login()
        {
            if (Request.Cookies.ContainsKey(CookieUserId))
                return RedirectToAction("Dashboard", "Account");
            return View(new LoginViewModel());
        }

        // POST: /Account/Login
        [HttpPost]
        public IActionResult Login(LoginViewModel model)
        {
            if (!ModelState.IsValid)
                return View(model);

            var user = _userStoreService.ValidateUser(model.Username, model.Password);
            if (user == null)
            {
                ModelState.AddModelError("", "Invalid username or password");
                return View(model);
            }

            // Set Cookie Options
            var options = new CookieOptions
            {
                Path = "/",
                HttpOnly = true,
                IsEssential = true,
                Secure = true // Set to true in production (requires HTTPS)
            };

            if (model.RememberMe)
            {
                // Persistent cookie (expires in 7 days)
                options.Expires = DateTime.UtcNow.AddDays(7);
            }
            else
            {
                // Non-persistent (Session cookie) - no expiration set, deleted on browser close
                options.Expires = null; // Or omit this line
            }

            // Create Cookies
            Response.Cookies.Append(CookieUserId, user.Id.ToString(), options);
            Response.Cookies.Append(CookieUsername, user.Username, options);
            return RedirectToAction("Dashboard", "Account");
        }

        // GET: /Account/Dashboard
        public IActionResult Dashboard()
        {
            if (!Request.Cookies.ContainsKey(CookieUserId))
                return RedirectToAction("Login");

            if (!int.TryParse(Request.Cookies[CookieUserId], out int userId))
                return RedirectToAction("Login");

            var user = _userStoreService.GetUserById(userId);
            if (user == null)
                return RedirectToAction("Login");

            return View(user);
        }

        // GET: /Account/Logout
        public IActionResult Logout()
        {
            Response.Cookies.Delete(CookieUserId);
            Response.Cookies.Delete(CookieUsername);
            return RedirectToAction("Login");
        }
    }
}
Create the Views

Now, let us create the required views.

Login Page (Login.cshtml)

Create a view named Login.cshtml within the Views/Account folder, then copy and paste the following code. This view renders the login form for users. It binds to the LoginViewModel, includes form validation, and offers a “Remember Me” checkbox to control cookie persistence.

@model LoginViewModel
@{
    ViewData["Title"] = "Login";
}

<div class="container mt-5" style="max-width: 400px;">
    <div class="card shadow rounded-3">
        <div class="card-body">
            <h3 class="text-center mb-4">Sign In</h3>
            <form asp-action="Login" method="post">
                <div class="mb-3">
                    <label asp-for="Username" class="form-label"></label>
                    <input asp-for="Username" class="form-control" />
                    <span asp-validation-for="Username" class="text-danger"></span>
                </div>
                <div class="mb-3">
                    <label asp-for="Password" class="form-label"></label>
                    <input asp-for="Password" class="form-control" />
                    <span asp-validation-for="Password" class="text-danger"></span>
                </div>
                <div class="mb-3 form-check">
                    <input asp-for="RememberMe" class="form-check-input" />
                    <label asp-for="RememberMe" class="form-check-label">Remember Me</label>
                </div>
                <button type="submit" class="btn btn-primary w-100">Login</button>
                <div class="text-danger mt-2">
                    @Html.ValidationSummary(false)
                </div>
            </form>
        </div>
    </div>
</div>
Dashboard Page (Dashboard.cshtml)

Create a view named Dashboard.cshtml within the Views/Account folder, then copy and paste the following code. This view is shown to users after they log in successfully. It welcomes the user by name, displays their username, and provides a logout button. It uses the User model to personalize the page.

@model User
@{
    ViewData["Title"] = "Dashboard";
}

<div class="container mt-5">
    <div class="card shadow rounded-3">
        <div class="card-body text-center">
            <h2>Welcome, @Model.FullName!</h2>
            <p>Your username: <b>@Model.Username</b></p>
            <a href="@Url.Action("Logout", "Account")" class="btn btn-danger mt-3">Logout</a>
        </div>
    </div>
</div>
Creating Cookie Controller

Create an empty MVC controller named CookieController within the Controllers folder, then copy and paste the following code. This controller provides actions to demonstrate how cookies work in ASP.NET Core MVC. It has actions for explaining cookies, setting and deleting a demo cookie, reading cookie values, and showing how to access cookies directly within a Razor view.

using Microsoft.AspNetCore.Mvc;

namespace CookieAuthDemo.Controllers
{
    public class CookieController : Controller
    {
        // About Cookies
        public IActionResult AboutCookies()
        {
            return View();
        }

        // Set a sample persistent cookie
        public IActionResult SetSampleCookie()
        {
            CookieOptions options = new CookieOptions
            {
                Expires = DateTime.UtcNow.AddDays(3),
                HttpOnly = true,
                IsEssential = true
            };
            Response.Cookies.Append("DemoCookie", "HelloFromCookieDemo", options);
            ViewBag.Message = "A persistent cookie named 'DemoCookie' has been set! (Expires in 3 days)";
            return View();
        }

        // Read the sample cookie
        public IActionResult ReadSampleCookie()
        {
            string? value = Request.Cookies["DemoCookie"];
            ViewBag.Message = value == null ? "Cookie not found." : $"Cookie value: {value}";
            return View();
        }

        // Delete the sample cookie
        public IActionResult DeleteSampleCookie()
        {
            Response.Cookies.Delete("DemoCookie");
            ViewBag.Message = "Cookie has been deleted!";
            return View();
        }

        // Accessing Cookies in a View using IHttpContextAccessor
        public IActionResult ViewCookiesInView()
        {
            return View();
        }
    }
}
AboutCookies.cshtml

Create a view named AboutCookies.cshtml within the Views/Cookie folder, then copy and paste the following code. This view presents a brief explanation of what cookies are, their purpose in web applications, and how they help maintain user sessions and preferences.

@{
    ViewData["Title"] = "About Cookies";
}
<h2 class="mb-3">About Cookies in Web Applications</h2>
<p>
    Cookies are small pieces of data stored by your browser. They help web apps remember who you are, keep you logged in, and personalize your experience.
</p>
<p>
    Use the navigation to set, read, and delete a sample cookie, and see how cookies can be accessed directly in views!
</p>
SetSampleCookie.cshtml

Create a view named SetSampleCookie.cshtml within the Views/Cookie folder, then copy and paste the following code. This view displays a confirmation message after a demo cookie has been set, guiding the user to try reading or deleting the cookie.

@{
    ViewData["Title"] = "Set Cookie";
}
<h2 class="mb-3">Set Sample Cookie</h2>
<div class="alert alert-success">@ViewBag.Message</div>
<p>
    Now go to <b>Read Cookie</b> to view the value, or <b>Delete Cookie</b> to remove it.
</p>
ReadSampleCookie.cshtml

Create a view named ReadSampleCookie.cshtml within the Views/Cookie folder, then copy and paste the following code. This view shows the current value of the demo cookie if it exists, or indicates if the cookie was not found.

@{
    ViewData["Title"] = "Read Cookie";
}
<h2 class="mb-3">Read Sample Cookie</h2>
<div class="alert alert-info">@ViewBag.Message</div>
<p>
    If you set a cookie, you should see its value above. If it's deleted, it will say 'not found'.
</p>
DeleteSampleCookie.cshtml

Create a view named DeleteSampleCookie.cshtml within the Views/Cookie folder, then copy and paste the following code. This view confirms to the user that the demo cookie has been successfully deleted and suggests further actions.

@{
    ViewData["Title"] = "Delete Cookie";
}
<h2 class="mb-3">Delete Sample Cookie</h2>
<div class="alert alert-warning">@ViewBag.Message</div>
<p>
    Try setting the cookie again, or reading it to confirm deletion.
</p>
ViewCookiesInView.cshtml

Create a view named ViewCookiesInView.cshtml within the Views/Cookie folder, then copy and paste the following code. This view demonstrates how to access cookies directly inside a Razor view using dependency injection with IHttpContextAccessor, displaying the current values of selected cookies.

@using Microsoft.AspNetCore.Http
@inject IHttpContextAccessor HttpContextAccessor

@{
    ViewData["Title"] = "Access Cookies in View";
    var demoCookie = HttpContextAccessor?.HttpContext?.Request.Cookies["DemoCookie"];
    var usernameCookie = HttpContextAccessor?.HttpContext?.Request.Cookies["Username"];
}

<h2 class="mb-3">Access Cookies in Razor View</h2>

<div class="mb-2">
    <b>DemoCookie:</b>
    @if (!string.IsNullOrEmpty(demoCookie))
    {
        @demoCookie
    }
    else
    {
        <span class="text-muted">not found</span>
    }
</div>

<div class="mb-2">
    <b>Username:</b>
    @if (!string.IsNullOrEmpty(usernameCookie))
    {
        @usernameCookie
    }
    else
    {
        <span class="text-muted">not found</span>
    }
</div>

<p class="text-muted">
    Here, we used <code>IHttpContextAccessor</code> to access cookies directly inside a Razor view.
</p>
Modifying the Layout Page (_Layout.cshtml)

Please modify _Layout.cshtml view which you will find inside Views/Shared folder as follows. This is the shared layout for all views in the application. It provides a consistent Bootstrap-based design, including a dark navbar, footer, and navigation links for all major pages and cookie demos.

@{
    var isLoggedIn = Context.Request.Cookies.ContainsKey("UserId");
    string currentAction = ViewContext.RouteData.Values["action"]?.ToString() ?? "";
}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>@ViewData["Title"] - Cookie Demo</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" />
    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
</head>
<body class="bg-light d-flex flex-column min-vh-100">

    <!-- Navbar -->
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark shadow-sm">
        <div class="container">
            <a class="navbar-brand fw-bold" href="@Url.Action("Dashboard", "Account")">Cookie Demo</a>
            <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#mainNavbar" aria-controls="mainNavbar" aria-expanded="false" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>
            <div class="collapse navbar-collapse" id="mainNavbar">
                <ul class="navbar-nav ms-auto mb-2 mb-lg-0">
                    <li class="nav-item">
                        <a class="nav-link @(currentAction == "AboutCookies" ? "active" : "")" href="@Url.Action("AboutCookies", "Cookie")">About Cookies</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link @(currentAction == "SetSampleCookie" ? "active" : "")" href="@Url.Action("SetSampleCookie", "Cookie")">Set Cookie</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link @(currentAction == "ReadSampleCookie" ? "active" : "")" href="@Url.Action("ReadSampleCookie", "Cookie")">Read Cookie</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link @(currentAction == "DeleteSampleCookie" ? "active" : "")" href="@Url.Action("DeleteSampleCookie", "Cookie")">Delete Cookie</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link @(currentAction == "ViewCookiesInView" ? "active" : "")" href="@Url.Action("ViewCookiesInView", "Cookie")">Cookies In View</a>
                    </li>
                    @if (isLoggedIn)
                    {
                        <li class="nav-item">
                            <a class="nav-link @(currentAction == "Dashboard" ? "active" : "")" href="@Url.Action("Dashboard", "Account")">Dashboard</a>
                        </li>
                        <li class="nav-item">
                            <a class="nav-link text-danger" href="@Url.Action("Logout", "Account")">Logout</a>
                        </li>
                    }
                    else
                    {
                        <li class="nav-item">
                            <a class="nav-link @(currentAction == "Login" ? "active" : "")" href="@Url.Action("Login", "Account")">Login</a>
                        </li>
                    }
                </ul>
            </div>
        </div>
    </nav>

    <!-- Main Content -->
    <main class="container my-5 flex-grow-1">
        <div class="row justify-content-center">
            <div class="col-lg-8 col-md-10 col-12">
                @RenderBody()
            </div>
        </div>
    </main>

    <!-- Footer -->
    <footer class="footer bg-dark text-light text-center py-3 mt-auto">
        &copy; @DateTime.Now.Year - Cookie Demo | Powered by ASP.NET Core MVC
    </footer>

    <!-- Bootstrap & Validation Scripts -->
    <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/jquery-validation@1.19.5/dist/jquery.validate.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/jquery-validation-unobtrusive@4.0.0/dist/jquery.validate.unobtrusive.min.js"></script>
    @RenderSection("Scripts", required: false)
</body>
</html>
Register the Services in the Program Class:

Please modify the Program class as follows. This class configures and launches the ASP.NET Core application. It registers required services, sets up dependency injection, configures middleware for routing and static files, and defines the default route for controllers.

using CookieAuthDemo.Models;

namespace CookieAuthDemo
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var builder = WebApplication.CreateBuilder(args);

            // Add services to the container.
            builder.Services.AddControllersWithViews();

            builder.Services.AddSingleton<UserStoreService>();

            //This Service is required to access the cookies in a view file
            builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

            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.UseAuthorization();

            app.MapControllerRoute(
                name: "default",
                pattern: "{controller=Home}/{action=Index}/{id?}");

            app.Run();
        }
    }
}

Here,

  • IHttpContextAccessor: This service is registered in the application to enable access to the current HTTP context (including cookies) from places outside the controller, such as Razor views.
How the Server Sets Cookies: The Response Header

When your ASP.NET Core MVC application sets a cookie, it instructs the browser to store this information by including a Set-Cookie header in the HTTP response. For every cookie you want to set, the server sends a separate Set-Cookie header.

Example – Response Headers after login:

  • Set-Cookie: UserId=1; path=/; secure; httponly
  • Set-Cookie: Username=john; path=/; secure; httponly
How to see this in Chrome/Edge/Firefox:

Open DevTools (press F12 or Ctrl+Shift+I). Navigate to the Network tab. Perform the action that triggers the cookie (such as logging in or clicking “Set Cookie”). Click the relevant network request (often a POST for login). Look under the Response Headers section. Find all the Set-Cookie headers set by the server as shown in the image below.

How the Server Sets Cookies: The Response Header

Note: If you log in without selecting the “Remember Me” option, your application creates a session cookie (no expiration attribute).

Where Cookies Are Stored in the Browser

After receiving the Set-Cookie response, your browser stores each cookie according to its attributes (domain, path, etc.), associating it with the relevant website.

How to View Cookies in Chrome/Edge/Brave:

In Chrome/Edge/Firefox, Open DevTools. Go to the Application tab (sometimes referred to as Storage in Firefox). In the left sidebar, under Storage, choose Cookies > [your site]. You will see a table with columns like Name, Value, Domain, Path, Expires/Max-Age, Size, HttpOnly, Secure, SameSite, as shown in the image below. You can manually add, edit, or delete cookies here.

Where Cookies Are Stored in the Browser

How the Browser Sends Cookies: The Request Header

On each subsequent request to the server (for the same domain and matching path), the browser automatically attaches all relevant cookies in the Cookie header of the HTTP request. All cookies are sent in a single Cookie header, separated by semicolons.

Example: Cookie: DemoCookie=HelloFromCookieDemo; Username=john; UserId=1

How to see this:

In DevTools, select the Network tab and choose any request that occurs after the cookie has been set (such as a page reload). Click the request. Under the Request Headers section, look for the “Cookie” header, as shown in the image below.

How the Browser Sends Cookies: The Request Header

Cookie Encryption and Decryption in ASP.NET Core MVC:

Cookies often store sensitive information such as:

  • User authentication tokens
  • Session identifiers
  • User preferences or roles

If left unencrypted, anyone intercepting these cookies (e.g., through network sniffing, or via client-side attacks) can:

  • Read sensitive data directly
  • Modify the cookie content to impersonate users or escalate privileges

Encrypting cookies is therefore crucial to:

  • Protect confidentiality: Ensure that unauthorized parties cannot read cookie data.
  • Maintain integrity: Prevent attackers from tampering with cookie values.
  • Compliance: Help meet legal standards such as GDPR, HIPAA, and PCI-DSS, which mandate protection of personal/sensitive data.

ASP.NET Core provides built-in mechanisms for implementing cookie encryption. Using the Data Protection API, we can encrypt Cookies in an ASP.NET Core MVC application.

What is the Data Protection API in ASP.NET Core?

ASP.NET Core provides a robust, built-in Data Protection API (DPAPI) that enables developers to encrypt and decrypt sensitive data with ease.

  • It handles cryptographic operations internally, including key management and rotation.
  • It is used by ASP.NET Core itself to protect authentication cookies, CSRF tokens, and other security-sensitive data.
  • You can use this API to encrypt your own cookie values or any other data before storing or transmitting.

The API exposes two main methods:

  • Protect(string plaintext): Encrypts the plaintext and returns a protected (ciphertext) string.
  • Unprotect(string protectedData): Decrypts the ciphertext back to the original plaintext.
How to Implement Cookie Encryption and Decryption Using Data Protection API?
Inject IDataProtectionProvider in your controller

You create an IDataProtector instance from the provider with a unique purpose string (more on this later). This protector performs the encryption and decryption.

How to Implement Cookie Encryption and Decryption Using Data Protection API?

What is the Purpose String?

The purpose string is a unique identifier that scopes your data protector instance.

  • It isolates encrypted data: Data encrypted with one purpose cannot be decrypted by a protector created with a different purpose.
  • It acts as a context or namespace for your encrypted data.
  • Helps prevent accidental data leakage between different parts of the app or between different applications sharing the same data protection keys.
Example:

_protector = dataProtectionProvider.CreateProtector(“CookieAuthDemo.UserCookies”);

Here, “CookieAuthDemo.UserCookies” is the purpose string.

  • Only protectors created with this exact string can decrypt the data.
  • You can create different protectors for different purposes (e.g., “PaymentTokens”, “UserSessionData”) to keep encrypted data separate.
Encrypt cookie values before storing them

In your login POST method or any other method, before writing to cookies, encrypt the cookies by using the Protect method:

Encrypt cookie values before storing them

Decrypt cookie values when reading them

When reading cookies, decrypt their values using the data protector Unprotect method:

Cookie Encryption and Decryption in ASP.NET Core MVC

You then validate and use the decrypted values.

Example to Understand Cookie Encryption and Decryption in ASP.NET Core MVC:

Let us enhance our application security by Encryption and Decryption the User ID and User Name Cookies as follows:

  • Encrypt cookie values before storing them.
  • Decrypt cookie values when reading them back.

So, please modify the AccountController as follows:

using CookieAuthDemo.Models;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Mvc;

namespace CookieAuthDemo.Controllers
{
    public class AccountController : Controller
    {
        private readonly UserStoreService _userStoreService;
        private const string CookieUserId = "UserId";
        private const string CookieUsername = "Username";
        private readonly IDataProtector _protector;

        public AccountController(UserStoreService userStoreService, IDataProtectionProvider dataProtectionProvider)
        {
            _userStoreService = userStoreService;

            // Create a protector for this purpose, with a unique purpose string
            _protector = dataProtectionProvider.CreateProtector("CookieAuthDemo.UserCookies");
        }

        // GET: /Account/Login
        [HttpGet]
        public IActionResult Login()
        {
            if (Request.Cookies.ContainsKey(CookieUserId))
                return RedirectToAction("Dashboard", "Account");
            return View(new LoginViewModel());
        }

        // POST: /Account/Login
        [HttpPost]
        public IActionResult Login(LoginViewModel model)
        {
            if (!ModelState.IsValid)
                return View(model);

            var user = _userStoreService.ValidateUser(model.Username, model.Password);
            if (user == null)
            {
                ModelState.AddModelError("", "Invalid username or password");
                return View(model);
            }

            // Set Cookie Options
            var options = new CookieOptions
            {
                Path = "/",
                HttpOnly = true,
                IsEssential = true,
                Secure = true // Set to true in production (requires HTTPS)
            };

            if (model.RememberMe)
            {
                // Persistent cookie (expires in 7 days)
                options.Expires = DateTime.UtcNow.AddDays(7);
            }
            else
            {
                // Non-persistent (Session cookie) - no expiration set, deleted on browser close
                options.Expires = null; // Or omit this line
            }

            // Encrypt values before storing
            var protectedUserId = _protector.Protect(user.Id.ToString());
            var protectedUsername = _protector.Protect(user.Username);

            // Create Cookies with encrypted values
            Response.Cookies.Append(CookieUserId, protectedUserId, options);
            Response.Cookies.Append(CookieUsername, protectedUsername, options);

            return RedirectToAction("Dashboard", "Account");
        }

        // GET: /Account/Dashboard
        public IActionResult Dashboard()
        {
            if (!Request.Cookies.ContainsKey(CookieUserId))
                return RedirectToAction("Login");

            var protectedUserId = Request.Cookies[CookieUserId];

            if (string.IsNullOrEmpty(protectedUserId))
            {
                // Delete Cookies and Redirect to login page
                Response.Cookies.Delete(CookieUserId);
                Response.Cookies.Delete(CookieUsername);
                return RedirectToAction("Login");
            }

            string userIdStr = _protector.Unprotect(protectedUserId);

            if (!int.TryParse(userIdStr, out int userId))
            {
                // Invalid user ID format, delete cookies and redirect
                Response.Cookies.Delete(CookieUserId);
                Response.Cookies.Delete(CookieUsername);
                return RedirectToAction("Login");
            }

            var user = _userStoreService.GetUserById(userId);
            if (user == null)
            {
                // User not found, delete cookies and redirect
                Response.Cookies.Delete(CookieUserId);
                Response.Cookies.Delete(CookieUsername);
                return RedirectToAction("Login");
            }

            return View(user);
        }


        // GET: /Account/Logout
        public IActionResult Logout()
        {
            Response.Cookies.Delete(CookieUserId);
            Response.Cookies.Delete(CookieUsername);
            return RedirectToAction("Login");
        }
    }
}

Now, run the application. Then, log in and verify that the token will be stored in encrypted format as shown in the image below:

Example to Understand Cookie Encryption and Decryption in ASP.NET Core MVC

What are the Limitations of Cookies in ASP.NET Core MVC applications?

What are the Limitations of Cookies in ASP.NET Core MVC applications?

The following are some key limitations of cookies in ASP.NET Core web applications:

  • Limited Size: Each individual cookie is limited to around 4 KB in size. Browsers also limit the total number of cookies per domain (usually 20–50). Trying to store too much data in cookies can lead to data loss or cookies not being set at all.
  • Security Risks: Cookies can be intercepted or manipulated if not handled properly. Sensitive data should never be stored in cookies unless they are encrypted and secured. Cookies are vulnerable to attacks such as Cross-Site Scripting (XSS) and Cross-Site Request Forgery (CSRF) if security flags (HttpOnly, Secure, etc.) are not utilized.
  • Client-Side Storage: Cookies are stored on the user’s browser, allowing users to view, modify, or delete them. You cannot rely on cookies for highly secure or critical data, as users have complete control over them.
  • Performance Impact: Cookies are sent with every HTTP request to the same domain, including for static resources such as images or CSS files. This can increase network traffic and affect performance if cookies are large or numerous.
  • Browser Restrictions and User Preferences: Users can disable cookies in their browser settings or utilize privacy features that automatically block or delete cookies. This may affect the functionality or features that rely on cookies.
  • Not Suitable for Storing Sensitive or Large Amounts of Data: Cookies are not designed for storing sensitive data (such as passwords) or large amounts of information. Server-side storage (such as sessions and databases) should be used in these cases.

Cookies play a fundamental role in modern web applications by enabling the server and browser to maintain user-specific state across multiple requests, despite the stateless nature of the HTTP protocol. They allow web apps to store user preferences, manage authentication, and personalize experiences by exchanging and storing small pieces of data between the server and client. By understanding how cookies are set in HTTP response headers, stored within the browser, and sent back to the server in request headers, developers can effectively use cookies to build secure, user-friendly, and interactive web applications.

In the next article, I will discuss Sessions in an ASP.NET Core MVC Application with an Example. In this article, I try to explain Cookies in ASP.NET Core MVC Applications with Examples. I hope you enjoy this article on creating a cookie in an ASP.NET Core MVC application.

5 thoughts on “Cookies in ASP.NET Core MVC”

  1. blank

    Not probably SUPER important, but these lines of code APPEAR to use the wrong cookie names:

    string? UserName = Request.Cookies[“UserId”];
    int? UserId = Convert.ToInt32(Request.Cookies[“UserName”]);

    They appear to be “backward” (UserId cookie for UserName string, UserName cookie for UserId int).

    Also, am I incorrect in believing it’s probably better to use TryParse, rather than Convert.ToInt32 to do your UserId conversion?

  2. blank

    Want to master how cookies work in ASP.NET Core MVC?
    Check out our detailed video tutorial where we explain everything from session management to secure cookie handling with real-time examples. Whether you’re a beginner or looking to deepen your understanding, this step-by-step guide will help you build better, more secure web applications.

    👉 Watch now: https://www.youtube.com/watch?v=19Zr32WUZWU

    Stay updated with Dot Net Tutorials for more expert content!

Leave a Reply

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