Basic Authentication
Demo: Auth Authz
Start a new MVC project
dotnet new mvc
Model
Create a new model file
LoginViewModel.cs
in the /Models directory/Models/LoginViewModel.cs
using System.ComponentModel.DataAnnotations; namespace AuthDemo.Models; public class LoginViewModel { [Required] public string? Username { get; set; } [Required] [DataType(DataType.Password)] public string? Password { get; set; } }
Controller
Create a new controller
AccountController.cs
and add a new actionLogin()
/Controllers/AccountController.cs
using Microsoft.AspNetCore.Mvc; using AuthDemo.Models; namespace AuthDemo.Controllers; public class AccountController : Controller { // Mocked user data private const string MockedUsername = "demo"; private const string MockedPassword = "pass"; // Note: NEVER hard-code passwords in real applications. public IActionResult Login() { return View(); } [HttpPost] [ValidateAntiForgeryToken] // This ensures that the form is submitted with a valid anti-forgery token to prevent CSRF attacks. public IActionResult Login(LoginViewModel model) { // Check model validators if (!ModelState.IsValid) { return View(model); } // Mocked user verification if (model.Username == MockedUsername && model.Password == MockedPassword) { // Normally, here you'd set up the session/cookie for the authenticated user. return RedirectToAction("Index", "Home"); // Redirect to a secure area of your application. } ModelState.AddModelError(string.Empty, "Invalid login attempt."); // Generic error message for security reasons. return View(model); } }
View
Create a new directory
Account
and a new view fileLogin.cshtml
/Views/Account/Login.cshtml
@model AuthDemo.Models.LoginViewModel <h2>Login</h2> <form asp-action="Login" asp-controller="Account" method="post" asp-antiforgery="true"> <div asp-validation-summary="ModelOnly" class="text-danger"></div> <div> <label asp-for="Username">Username:</label> <input type="text" id="Username" asp-for="Username" required /> <span asp-validation-for="Username" class="text-danger"></span> </div> <div> <label asp-for="Password">Password:</label> <input type="password" id="Password" asp-for="Password" required /> <span asp-validation-for="Password" class="text-danger"></span> </div> <div> <button type="submit">Login</button> </div> </form> @section Scripts { @{await Html.RenderPartialAsync("_ValidationScriptsPartial");} }
Navigation
Add a button to the menu bar in the file
_Layout.cshtml
in the/Views/Shared
directory/Views/Shared/_Layout.cshtml
<li class="nav-item"> <a class="nav-link text-dark" asp-area="" asp-controller="Account" asp-action="Login">Login</a> </li>
Add Authentication
Controller
Add a new action
SecretInfo()
to the AccountController/Controllers/AccountController.cs
... using Microsoft.AspNetCore.Authorization; ... public IActionResult SecretInfo() { return View(); }
View
Create a new view file
SecretInfo.cshtml
/Views/Account/SecretInfo.cshtml
<h2>Authentication Info</h2> <table class="table"> <thead> <tr> <th>Claim Type</th> <th>Claim Value</th> </tr> </thead> <tbody> @foreach (var claim in User.Claims) { <tr> <td>@claim.Type</td> <td>@claim.Value</td> </tr> } </tbody> </table>
Navigation
Add a button to the menu bar in the file
_Layout.cshtml
in the/Views/Shared
directory/Views/Shared/_Layout.cshtml
<li class="nav-item"> <a class="nav-link text-dark" asp-area="" asp-controller="Account" asp-action="SecretInfo">Secret Info</a> </li>
Authenticate and login the user
Add and configure Authentication middleware in
Program.cs
/Program.cs
using Microsoft.AspNetCore.Authentication.Cookies; ... builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie(); ... app.UseAuthentication(); // Before UseAuthorization() ...
Protect the SecretInfo page in the
AccountController.cs
The login page will be found based on naming convention
/Account/Login
/Controllers/AccountController.cs
... using Microsoft.AspNetCore.Authorization; ... [Authorize] // This attribute ensures that only authenticated users can access this action. public IActionResult SecretInfo() { return View(); } ...
Set up the session/cookie for the authenticated user in the
AccountController.cs
/Controllers/AccountController.cs
... using System.Security.Claims; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication; ... [HttpPost] [ValidateAntiForgeryToken] // This ensures that the form is submitted with a valid anti-forgery token to prevent CSRF attacks. public async Task<IActionResult> LoginAsync(LoginViewModel model) { // Check model validators if (!ModelState.IsValid) { return View(model); } // Mocked user verification if (model.Username == MockedUsername && model.Password == MockedPassword) { // Set up the session/cookie for the authenticated user. var claims = new[] { new Claim(ClaimTypes.Name, model.Username) }; var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme); var principal = new ClaimsPrincipal(identity); await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal); // Normally, here you'd set up the session/cookie for the authenticated user. return RedirectToAction("Index", "Home"); // Redirect to a secure area of your application. } ModelState.AddModelError(string.Empty, "Invalid login attempt."); // Generic error message for security reasons. return View(model); } ...
Log out the user
Change the login button in the menu bar in the file
_Layout.cshtml
in the/Views/Shared
directory so that it toggles betweenLogin
andLogout
/Views/Shared/_Layout.cshtml
... @if (Context.User.Identity.IsAuthenticated) { <a class="nav-link text-dark" asp-area="" asp-controller="Account" asp-action="Logout">Logout</a> } else { <a class="nav-link text-dark" asp-area="" asp-controller="Account" asp-action="Login">Login</a> } ...
Add a new action
Logout()
to the AccountController/Controllers/AccountController.cs
... [Authorize] public IActionResult Logout() { return SignOut( new AuthenticationProperties { RedirectUri = Url.Action("Index", "Home") }, CookieAuthenticationDefaults.AuthenticationScheme); }