Step-by-Step how to add Secure Cookie Authentication to your .NET Web App
I recently need to implement authorisation and roles to a .NET Web App, it was a bit easier than I thought but still required some time and research. Here's a summary of my findings.
Step 1: Add Authentication Middleware
Update your your program.cs to configure cookie authentication
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllersWithViews();
// Configure Entity Framework and DbContext
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));
builder.Services.AddAuthentication("MyCookieAuth")
.AddCookie("MyCookieAuth", options =>
{
options.LoginPath = "/Account/Login"; // Redirect here for unauthenticated users
options.LogoutPath = "/Account/Logout"; // Optional logout redirection
//options.AccessDeniedPath = "/Account/AccessDenied"; // For unauthorized users
options.Cookie.HttpOnly = true; // Prevent access via JavaScript
options.Cookie.SecurePolicy = CookieSecurePolicy.Always; // Require HTTPS
options.Cookie.SameSite = SameSiteMode.Strict; // Prevent cross-site cookie use
});
var app = builder.Build();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers(); // For MVC
app.MapRazorPages(); // If using Razor Pages
app.Run();
The key parts above are highlighted in bold. I have included my ApplicationDbContext here so this can be injected into my controllers, this may not required if you are using some other source of User data.
Step 2: Add an AccountController
Under your Controllers folder create an AccountController.cs (name as you require)
public class AccountController : Controller
{
private readonly ApplicationDbContext _dbContext;
public AccountController(ApplicationDbContext context)
{
_dbContext = context;
}
[AllowAnonymous]
public IActionResult Index()
{
return View();
}
[AllowAnonymous]
[HttpGet]
public IActionResult Login() => View();
[AllowAnonymous]
[HttpPost]
public async Task<IActionResult> Login(string username, string password)
{
// Hardcoded credentials for simplicity
// Use the injected AppDbContext to check the database
var user = await _dbContext.Contacts
.FirstOrDefaultAsync(u => u.Firstname == username && u.Password == password);
if (user != null)
{
// Create claims based on user data
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, user.Firstname),
new Claim(ClaimTypes.Role, (bool)user.IsAdmin ? "IsAdmin" : "User") // Add IsAdmin claim
};
// Create identity and principal
var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
var principal = new ClaimsPrincipal(identity);
// Sign in the user
await HttpContext.SignInAsync("MyCookieAuth", principal);
// Redirect to the home page
return RedirectToAction("Index", "Home");
}
ViewBag.Error = "Invalid credentials";
return View();
}
public IActionResult Logout()
{
HttpContext.SignOutAsync("MyCookieAuth");
return RedirectToAction("Login");
}
}
NOTE: the AccountController picks up the incoming username and password, attempts to locate the user in the databse, if a user record is found we create the claims, an identity and a principal then we call SignInAsync() passing MyCookieAuth as the scheme name, we will use this as the cookie name, change this name to suit your preferences, we then navigae to the home page.
Step 3: Home Controller
for completeness I show below my initial HomeController.cs