Cookie Authentication in ASP.NET Core MVC
Cookie authentication verifies a user and stores their authentication information in an encrypted cookie in the browser.
How it works
- User submits a username and password.
- Server validates the credentials.
- Server creates a
ClaimsPrincipalcontaining the user’s identity and roles. SignInAsync()generates an encrypted authentication cookie.- The browser sends this cookie with every subsequent request.
- Cookie Authentication Middleware validates it and sets
HttpContext.User. [Authorize]allows or denies access.
await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity));
[Authorize]
public IActionResult Dashboard()
{
return View();
}
Configuration
builder.Services.AddAuthentication("MyCookie")
.AddCookie("MyCookie", options =>
{
options.LoginPath = "/Account/Login";
options.AccessDeniedPath = "/Account/AccessDenied";
});
app.UseAuthentication();
app.UseAuthorization();
Library: Built into ASP.NET Core through Microsoft.AspNetCore.Authentication.Cookies; normally no separate NuGet installation is required.
Interview point: The cookie stores an encrypted authentication ticket, not the user’s password.
How ASP.NET Core Identity Works
ASP.NET Core Identity is a membership system that manages:
- User registration and login
- Password hashing
- Roles and claims
- Email confirmation
- Password reset
- Two-factor authentication
- Account lockout
Authentication flow
- User submits username/email and password.
UserManagerretrieves the user.SignInManagerverifies the hashed password.- Identity creates an encrypted authentication cookie.
- The browser sends the cookie with future requests.
[Authorize]checks the authenticated user’s roles or claims.
var result = await _signInManager.PasswordSignInAsync(
model.Email,
model.Password,
model.RememberMe,
lockoutOnFailure: true);
Main components
UserManager– manages users, passwords, roles and claims.SignInManager– handles login and logout.RoleManager– manages roles.IdentityDbContext– stores Identity data using EF Core.PasswordHasher– securely hashes passwords.
Configuration
builder.Services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
Required packages: Commonly Microsoft.AspNetCore.Identity.EntityFrameworkCore and the appropriate EF Core database provider, such as Microsoft.EntityFrameworkCore.SqlServer.
Interview point: Identity manages users and security features, while cookie authentication maintains the user’s signed-in session.
Authorization Types
| Type | Checks | Example |
|---|---|---|
| Role-based | User’s role | Admin, Manager |
| Claims-based | A user property/value | Department = HR |
| Policy-based | One or more custom requirements | Admin role + age above 18 |
1. Role-based authorization
Allows access based on the user’s role.
[Authorize(Roles = "Admin")]
public IActionResult Dashboard() => View();
2. Claims-based authorization
Allows access based on information attached to the user’s identity.
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("HRDepartment",
policy => policy.RequireClaim("Department", "HR"));
});
[Authorize(Policy = "HRDepartment")]
3. Policy-based authorization
Uses reusable rules that can combine roles, claims, and custom requirements.
options.AddPolicy("SeniorAdmin", policy =>
{
policy.RequireRole("Admin");
policy.RequireClaim("Experience", "Senior");
});
[Authorize(Policy = "SeniorAdmin")]
Key point: Role-based and claims-based authorization are simple checks. Policy-based authorization is more flexible and is preferred for complex business rules.
Library: Built into ASP.NET Core; normally no additional NuGet package is required.
How do you create a custom authorization policy and requirement?
Custom Authorization Policy and Requirement
A custom policy is used when authorization requires business logic that cannot be handled by a simple role or claim check.
1. Create a requirement
public class MinimumExperienceRequirement : IAuthorizationRequirement
{
public int Years { get; }
public MinimumExperienceRequirement(int years)
{
Years = years;
}
}
2. Create a handler
public class MinimumExperienceHandler
: AuthorizationHandler<MinimumExperienceRequirement>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
MinimumExperienceRequirement requirement)
{
var value = context.User.FindFirst("Experience")?.Value;
if (int.TryParse(value, out int years) &&
years >= requirement.Years)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
3. Register the policy and handler
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("ExperiencedEmployee", policy =>
policy.AddRequirements(
new MinimumExperienceRequirement(5)));
});
builder.Services.AddSingleton<
IAuthorizationHandler,
MinimumExperienceHandler>();
4. Apply the policy
[Authorize(Policy = "ExperiencedEmployee")]
public IActionResult Reports()
{
return View();
}
How it works
The policy contains the requirement. The handler evaluates the logged-in user and calls context.Succeed() when the rule is satisfied.
Library: Built into ASP.NET Core using Microsoft.AspNetCore.Authorization; no additional NuGet package is normally required.
How do you secure controller actions and Areas?
Securing Controller Actions and Areas
Use the [Authorize] attribute to restrict access to authenticated users, roles, or policies.
Secure a controller
All actions require login:
[Authorize]
public class OrdersController : Controller
{
}
Secure a specific action
[Authorize(Roles = "Admin")]
public IActionResult Delete(int id)
{
return View();
}
Allow public access
[AllowAnonymous] bypasses authorization:
[AllowAnonymous]
public IActionResult Login()
{
return View();
}
Secure an Area
Apply [Authorize] to the Area’s base controller:
[Area("Admin")]
[Authorize(Roles = "Admin")]
public class AdminBaseController : Controller
{
}
Inherit other Admin controllers from it:
public class DashboardController : AdminBaseController
{
}
For stronger protection, configure an authorization convention for the entire Area:
builder.Services.AddControllersWithViews(options =>
{
options.Conventions.Add(
new AuthorizeAreaFolderConvention("Admin", "/"));
});
Required middleware
app.UseAuthentication();
app.UseAuthorization();
UseAuthentication() must run before UseAuthorization() and before controller endpoints are mapped.
Interview point: Secure the entire controller or Area by default, then use [AllowAnonymous] only for intentionally public actions.
Library: Authorization is built into ASP.NET Core; no additional NuGet package is normally required.
How do you configure external authentication providers?
External Authentication Providers
External authentication allows users to log in using providers such as Google, Microsoft, Facebook, or GitHub.
Steps
- Register your application with the external provider.
- Obtain the Client ID and Client Secret.
- Install the provider’s NuGet package.
- Configure authentication in
Program.cs. - Add a login button and handle the callback.
Google example
Install package:
dotnet add package Microsoft.AspNetCore.Authentication.Google
Configure:
builder.Services
.AddAuthentication()
.AddGoogle(options =>
{
options.ClientId =
builder.Configuration["Authentication:Google:ClientId"]!;
options.ClientSecret =
builder.Configuration["Authentication:Google:ClientSecret"]!;
});
Configuration:
{
"Authentication": {
"Google": {
"ClientId": "your-client-id",
"ClientSecret": "your-client-secret"
}
}
}
Start external login:
return Challenge(
new AuthenticationProperties
{
RedirectUri = "/Account/ExternalLoginCallback"
},
"Google");
How it works
The user is redirected to Google → signs in → Google sends the user back to the application → the application reads the user’s claims and creates its own authentication cookie.
Security point: Store client secrets in User Secrets, environment variables, or Azure Key Vault, not directly in appsettings.json.
Interview point: The external provider verifies the user’s identity, while the MVC application manages its own local session using a cookie.
How do you implement logout and session expiration securely?
Secure Logout and Session Expiration
Logout
Sign out from cookie authentication and redirect to the login page:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Logout()
{
await HttpContext.SignOutAsync(
CookieAuthenticationDefaults.AuthenticationScheme);
return RedirectToAction("Login", "Account");
}
Use POST, not GET, and add an anti-forgery token to prevent forced logout attacks.
<form asp-action="Logout" method="post">
<button type="submit">Logout</button>
</form>
Configure authentication expiration
builder.Services.AddAuthentication("MyCookie")
.AddCookie("MyCookie", options =>
{
options.LoginPath = "/Account/Login";
options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
options.SlidingExpiration = true;
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.SameSite = SameSiteMode.Lax;
});
ExpireTimeSpan– logs the user out after the configured period.SlidingExpiration– renews the cookie when the active user passes halfway through the expiration period.HttpOnly– prevents JavaScript from accessing the cookie.Secure– sends the cookie only over HTTPS.SameSite– helps protect against CSRF.
Session expiration
If MVC session storage is also used:
builder.Services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(20);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
app.UseSession();
Interview point: Authentication-cookie expiration controls the user’s login. Session expiration controls server-side session data. They are separate.
Library: Cookie authentication and session support are built into ASP.NET Core; normally no additional NuGet package is required.
What is the difference between cookie authentication and JWT authentication?
Cookie Authentication vs JWT Authentication
| Cookie Authentication | JWT Authentication |
|---|---|
| Commonly used in MVC web applications | Commonly used in Web APIs and SPAs |
| Token is stored in a browser cookie | Token is usually sent in the Authorization header |
| Browser sends the cookie automatically | Client must explicitly attach the token |
| Can be stateful or ticket-based | Usually stateless |
| Built-in CSRF risk | Less exposed to CSRF when sent through headers |
| Easy to revoke using server-side validation | Harder to revoke before the token expires |
| Best for browser-based MVC applications | Best for APIs, mobile apps and distributed systems |
Cookie request
Cookie: MyAuthCookie=encrypted-ticket
JWT request
Authorization: Bearer eyJhbGciOi...
Important security difference
- Cookie: Protect against CSRF using anti-forgery tokens and
SameSite. - JWT: Protect the token from theft and XSS. Use HTTPS and short token expiration.
Interview answer
Cookie authentication is suitable for traditional MVC applications because the browser automatically manages the cookie. JWT authentication is suitable for APIs because the client sends a self-contained bearer token with each request.
Libraries:
- Cookie authentication: Built into ASP.NET Core.
- JWT: Usually requires
Microsoft.AspNetCore.Authentication.JwtBearer.
How do you protect sensitive cookies?
Protecting Sensitive Cookies
Use the following security settings:
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.SameSite = SameSiteMode.Lax;
options.Cookie.IsEssential = true;
Key protections
- HttpOnly: Prevents JavaScript from reading the cookie, reducing cookie theft through XSS.
- Secure: Sends the cookie only over HTTPS.
- SameSite: Reduces CSRF attacks. Use
LaxorStrictwhen possible. - Encryption and signing: ASP.NET Core Data Protection automatically protects authentication cookies.
- Short expiration: Limits the time a stolen cookie remains usable.
- Regenerate after login: Helps prevent session-fixation attacks.
- Avoid sensitive data: Never store passwords, connection strings or confidential information directly in cookies.
- Validate anti-forgery tokens: Protects state-changing form requests from CSRF.
Example
builder.Services.AddAuthentication("MyCookie")
.AddCookie("MyCookie", options =>
{
options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
options.SlidingExpiration = true;
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.SameSite = SameSiteMode.Lax;
});
Interview point: Sensitive data should normally remain on the server. Store only a protected identifier or authentication ticket in the cookie.
Library: Cookie protection uses ASP.NET Core Authentication and Data Protection; normally no additional NuGet package is required.