PROWAREtech
ASP.NET Core: Add Local Authentication
It is very easy to add local authentication to ASP.NET Core Web Applications. It is easier to configure a REST API to authorize requests with JSON Web Tokens; see this article if using JSON web tokens.
To avoid using a database in this example, the filesystem is used to store user email and password. It is very simple. Probably, users will be stored in a database in production.
NOTE: this example uses .NET 6/.NET 8. There are some fundamental differences between these versions and .
Create a new ASP.NET Core MVC Web Application Project named AuthWebSite.
Create a new C# class file and save it to the project's root folder as User.cs.
// User.cs
namespace AuthWebSite
{
public class User
{
public string Email { get; }
public User(string email)
{
Email = email;
}
}
}
Create a C# class file and save it to the project's root folder as IUserDatabase.cs.
// IUserDatabase.cs
using System.Threading.Tasks;
namespace AuthWebSite
{
public interface IUserDatabase
{
Task<User?> AuthenticateUser(string email, string password);
Task<User?> AddUser(string email, string password);
}
}
Now, create a folder named "Users" in the project's folder and then create another new C# class file as UserDatabase.cs and save it to the same project root folder. This is the user database implementation.
// UserDatabase.cs
using Microsoft.AspNetCore.Hosting;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace AuthWebSite
{
public class UserDatabase : IUserDatabase
{
private readonly IWebHostEnvironment env;
public UserDatabase(IWebHostEnvironment env)
{
this.env = env;
}
private static string CreateHash(string str)
{
var salt = "997eff51db1544c7a3c2ddeb2053f051";
var md5 = new HMACSHA256(Encoding.UTF8.GetBytes(salt + str));
byte[] data = md5.ComputeHash(Encoding.UTF8.GetBytes(str));
str = string.Empty;
foreach (var x in data)
str += x.ToString("X2");
return str;
}
public async Task<User?> AuthenticateUser(string email, string password)
{
if (string.IsNullOrEmpty(email) || string.IsNullOrEmpty(password))
return null;
var path = System.IO.Path.Combine(env.ContentRootPath, "Users");
if (!System.IO.Directory.Exists(path))
return null;
path += System.IO.Path.DirectorySeparatorChar + email;
if (!System.IO.File.Exists(path))
return null;
if (await System.IO.File.ReadAllTextAsync(path) != CreateHash(password))
return null;
return new User(email);
}
public async Task<User?> AddUser(string email, string password)
{
try
{
if (string.IsNullOrEmpty(email) || string.IsNullOrEmpty(password))
return null;
var path = System.IO.Path.Combine(env.ContentRootPath, "Users"); // CREATE THE "USERS" FOLDER IN THE PROJECT'S FOLDER!!!
if (!System.IO.Directory.Exists(path))
System.IO.Directory.CreateDirectory(path);
path += System.IO.Path.DirectorySeparatorChar + email;
if (System.IO.File.Exists(path))
return null;
await System.IO.File.WriteAllTextAsync(path, CreateHash(password));
return new User(email);
}
catch
{
return null;
}
}
}
}
Modify the Program.cs file as follows. The comments note where code has been added.
// Program.cs
using AuthWebSite;
using Microsoft.AspNetCore.Authentication.Cookies;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllersWithViews();
// NOTE: LOCAL AUTHENTICATION CODE IS BELOW THIS LINE BUT INSIDE THIS BLOCK
builder.Services.AddTransient<IUserDatabase, UserDatabase>(); // NOTE: LOCAL AUTHENTICATION ADDED HERE; AddTransient() IS OK TO USE BECAUSE STATE IS SAVED TO THE DRIVE
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options =>
{
options.ExpireTimeSpan = TimeSpan.FromDays(1); // NOTE: EXPIRES AFTER ONE DAY OF INACTIVITY
options.LoginPath = "/Home/Login"; // NOTE: THIS IS THE PAGE THAT UNAUTHORIZED REQUESTS WILL BE REDIRECTED TO
options.AccessDeniedPath = "/Home/AccessDenied";
});
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.UseAuthentication(); // <-- NOTE: LOCAL AUTHENTICATION ADDED HERE
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
This is just the Login & Register Model. Create a new C# class file in the models folder named LoginModel.cs and add this code:
// LoginModel.cs
using System.ComponentModel.DataAnnotations;
namespace AuthWebSite.Models
{
public class LoginModel
{
[Required]
public string Email { get; set; }
[Required]
public string Password { get; set; }
public string? ReturnUrl { get; set; }
}
}
In this example, the home controller will be the one that handles authentication (alternatively, create an entire controller that handles authentication). Modify HomeController.cs to look like this:
// HomeController.cs
using System.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
using System.Security.Claims;
using System.Collections.Generic;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication;
using AuthWebSite.Models;
namespace AuthWebSite.Controllers
{
public class HomeController : Controller
{
private readonly IUserDatabase userdb;
public HomeController(IUserDatabase userdb)
{
this.userdb = userdb;
}
private async Task<IActionResult> SignIn(User user, string ReturnUrl) // NOTE: this is used by Register and Login methods
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, user.Email),
new Claim(ClaimTypes.Name, user.Email),
new Claim(ClaimTypes.Email, user.Email)
};
var id = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
var principal = new ClaimsPrincipal(id);
await HttpContext.SignInAsync(principal);
if (string.IsNullOrEmpty(ReturnUrl))
return RedirectToAction("Index", "Home");
return LocalRedirect(ReturnUrl);
}
public IActionResult Login()
{
return View();
}
[ValidateAntiForgeryToken]
[HttpPost]
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] // NOTE: don't forget this
public async Task<IActionResult> Login(LoginModel model)
{
if (!ModelState.IsValid)
return View(model);
var user = await userdb.AuthenticateUser(model.Email, model.Password);
if (user == null)
return View(model);
return await SignIn(user, model.ReturnUrl);
}
public IActionResult Register()
{
return View();
}
[ValidateAntiForgeryToken]
[HttpPost]
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] // NOTE: don't forget this
public async Task<IActionResult> Register(LoginModel model)
{
if (!ModelState.IsValid)
return View(model);
var user = await userdb.AddUser(model.Email, model.Password);
if (user == null)
return View(model);
return await SignIn(user, null);
}
[ValidateAntiForgeryToken]
[HttpPost]
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] // NOTE: don't forget this
public async Task<IActionResult> Logout()
{
await HttpContext.SignOutAsync();
return RedirectToAction("Index", "Home");
}
[Authorize] // NOTE: don't forget this; it makes this endpoint accessible only by logged in users
public IActionResult Privacy()
{
return View();
}
// NOTE: the following code is unmodified
public IActionResult Index()
{
return View();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
}
Modify Privacy.cshtml:
@{
ViewData["Title"] = "Privacy Policy";
}
<h1>@ViewData["Title"]</h1>
<p>Use this page to detail your site's privacy policy.</p>
<p>NOTE: this page can only be viewed by registered users.</p>
Create Register.cshtml, modify it as follows and save it in the Views/Home folder.
@{
ViewData["Title"] = "Register";
}
<h1>@ViewData["Title"]</h1>
<form action="/Home/Register" method="post">
@Html.AntiForgeryToken()
<input type="email" placeholder="EMAIL" name="Email" value="@Model?.Email" required />
<input type="password" placeholder="PASSWORD" name="Password" required />
<button type="submit">REGISTER</button>
</form>
Create Login.cshtml, modify it as follows and save it in the Views/Home folder.
@{
ViewData["Title"] = "Login";
}
<h1>@ViewData["Title"]</h1>
@if(!string.IsNullOrEmpty(Context.Request.Query["ReturnUrl"]))
{
<p>You must login to access <b>@Context.Request.Query["ReturnUrl"]</b></p>
}
<form action="/Home/Login" method="post">
@Html.AntiForgeryToken()
<input type="email" placeholder="EMAIL" name="Email" value="@Model?.Email" required />
<input type="password" placeholder="PASSWORD" name="Password" required />
<input type="hidden" name="ReturnUrl" value="@Context.Request.Query["ReturnUrl"]" />
<button type="submit">LOGIN</button>
</form>
Modify _Layout.cshtml to have these links in the navbar:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - AuthWebSite</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
<link rel="stylesheet" href="~/AuthWebSite.styles.css" asp-append-version="true" />
</head>
<body>
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container-fluid">
<a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">AuthWebSite</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</li>
<!-- NOTE: IF ELSE STATEMENT ADDED -->
@if (User.Identity.IsAuthenticated)
{
<li class="nav-item">
<!-- NOTE: NEW ITEM ADDED -->
<form action="/Home/Logout" method="post" id="logout-form" style="display:none;">@Html.AntiForgeryToken()</form>
<a class="nav-link text-dark" href="javascript:document.getElementById('logout-form').submit();">Logout</a>
</li>
<li class="nav-item">
<!-- NOTE: NEW ITEM ADDED -->
<span class="nav-link text-dark"><small>Hello, @User.Identity.Name</small></span>
</li>
}
else
{
<li class="nav-item">
<!-- NOTE: NEW ITEM ADDED -->
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Register">Register</a>
</li>
<li class="nav-item">
<!-- NOTE: NEW ITEM ADDED -->
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Login">Login</a>
</li>
}
</ul>
</div>
</div>
</nav>
</header>
<div class="container">
<main role="main" class="pb-3">
@RenderBody()
</main>
</div>
<footer class="border-top footer text-muted">
<div class="container">
© 2022 - AuthWebSite - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</div>
</footer>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
@await RenderSectionAsync("Scripts", required: false)
</body>
</html>
Now, run the project and try to access the /Home/Privacy path. To access this URL, the user must be registered and logged in. To secure a REST API controller, simply adding the [Authorize]
attribute to each endpoint is one solution, or add the [Authorize]
atttribute to the whole controller.
NOTE: this example uses ASP.NET Core 3.x.
Create a new ASP.NET Core Web Application Project named AuthWebSite or open an existing one.
Create a new C# class file and save it to the project's root folder as User.cs.
// User.cs
namespace AuthWebSite
{
public class User
{
public string Email { get; }
public User(string email)
{
Email = email;
}
}
}
Create a C# class file and save it to the project's root folder as IUserDatabase.cs.
// IUserDatabase.cs
using System.Threading.Tasks;
namespace AuthWebSite
{
public interface IUserDatabase
{
Task<User> AuthenticateUser(string email, string password);
Task<User> AddUser(string email, string password);
}
}
Now, create a folder named "Users" in the project's folder and then create another new C# class file as UserDatabase.cs and save it to the same project root folder. This is the user database implementation.
// UserDatabase.cs
using Microsoft.AspNetCore.Hosting;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
namespace AuthWebSite
{
public class UserDatabase : IUserDatabase
{
private readonly IWebHostEnvironment env;
public UserDatabase(IWebHostEnvironment env)
{
this.env = env;
}
private static string CreateHash(string str)
{
var salt = "997eff51db1544c7a3c2ddeb2053f051";
var md5 = new HMACSHA256(Encoding.UTF8.GetBytes(salt + str));
byte[] data = md5.ComputeHash(Encoding.UTF8.GetBytes(str));
str = string.Empty;
foreach(var x in data)
str += x.ToString("X2");
return str;
}
public async Task<User> AuthenticateUser(string email, string password)
{
return await Task.Run(() =>
{
if (string.IsNullOrEmpty(email) || string.IsNullOrEmpty(password))
return null;
var path = System.IO.Path.Combine(env.ContentRootPath, "Users");
if (!System.IO.Directory.Exists(path))
return null;
path += System.IO.Path.DirectorySeparatorChar + email;
if (!System.IO.File.Exists(path))
return null;
if (System.IO.File.ReadAllText(path) != CreateHash(password))
return null;
return new User(email);
});
}
public async Task<User> AddUser(string email, string password)
{
return await Task.Run(() =>
{
try
{
if (string.IsNullOrEmpty(email) || string.IsNullOrEmpty(password))
return null;
var path = System.IO.Path.Combine(env.ContentRootPath, "Users"); // CREATE THE "USERS" FOLDER IN THE PROJECT'S FOLDER!!!
if (!System.IO.Directory.Exists(path))
System.IO.Directory.CreateDirectory(path);
path += System.IO.Path.DirectorySeparatorChar + email;
if (System.IO.File.Exists(path))
return null;
System.IO.File.WriteAllText(path, CreateHash(password));
return new User(email);
}
catch
{
return null;
}
});
}
}
}
Modify the Startup.cs file as follows (this is an ASP.NET Core 3.0 file). The comments note where code has been added.
// Startup.cs
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace AuthWebSite
{
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.AddControllersWithViews();
// NOTE: LOCAL AUTHENTICATION CODE IS BELOW THIS LINE BUT INSIDE THIS BLOCK
services.AddTransient<IUserDatabase, UserDatabase>(); // NOTE: LOCAL AUTHENTICATION ADDED HERE; AddTransient() IS OK TO USE BECAUSE STATE IS SAVED TO THE DRIVE
// NOTE: LOCAL AUTHENTICATION ADDED HERE
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options =>
{
options.ExpireTimeSpan = TimeSpan.FromDays(1); // NOTE: EXPIRES AFTER ONE DAY OF INACTIVITY
options.LoginPath = "/Home/Login"; // NOTE: THIS IS THE PAGE THAT UNAUTHORIZED REQUESTS WILL BE REDIRECTED TO
options.AccessDeniedPath = "/Home/AccessDenied";
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseAuthentication(); // <-- NOTE: LOCAL AUTHENTICATION ADDED HERE
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}
This is just the Login & Register Model. Create a new C# class file in the models folder named LoginModel.cs and add this code:
// LoginModel.cs
using System.ComponentModel.DataAnnotations;
namespace AuthWebSite.Models
{
public class LoginModel
{
[Required]
public string Email { get; set; }
[Required]
public string Password { get; set; }
}
}
In this example, the home controller will be the one that handles authentication (alternatively, create an entire controller that handles authentication). Modify HomeController.cs to look like this:
// HomeController.cs
using System.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
using System.Security.Claims;
using System.Collections.Generic;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication;
using AuthWebSite.Models;
namespace AuthWebSite.Controllers
{
public class HomeController : Controller
{
private readonly IUserDatabase userdb;
public HomeController(IUserDatabase userdb)
{
this.userdb = userdb;
}
private async Task<IActionResult> SignIn(User user, string ReturnUrl) // this is used by Register and Login methods
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, user.Email),
new Claim(ClaimTypes.Name, user.Email),
new Claim(ClaimTypes.Email, user.Email)
};
var id = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
var principal = new ClaimsPrincipal(id);
await HttpContext.SignInAsync(principal);
if(string.IsNullOrEmpty(ReturnUrl))
return RedirectToAction("Index", "Home");
return LocalRedirect(ReturnUrl);
}
public IActionResult Login()
{
return View();
}
[ValidateAntiForgeryToken]
[HttpPost]
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] // NOTE: don't forget this
public async Task<IActionResult> Login(LoginModel model, string ReturnUrl)
{
if (!ModelState.IsValid)
return View(model);
var user = await userdb.AuthenticateUser(model.Email, model.Password);
if (user == null)
return View(model);
return await SignIn(user, ReturnUrl);
}
public IActionResult Register()
{
return View();
}
[ValidateAntiForgeryToken]
[HttpPost]
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] // NOTE: don't forget this
public async Task<IActionResult> Register(LoginModel model)
{
if (!ModelState.IsValid)
return View(model);
var user = await userdb.AddUser(model.Email, model.Password);
if (user == null)
return View(model);
return await SignIn(user, null);
}
[ValidateAntiForgeryToken]
[HttpPost]
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] // NOTE: don't forget this
public async Task<IActionResult> Logout()
{
await HttpContext.SignOutAsync();
return RedirectToAction("Index", "Home");
}
[Authorize] // NOTE: don't forget this; it makes this endpoint accessible only by logged in users
public IActionResult Privacy()
{
return View();
}
// NOTE: the following code is unmodified
public IActionResult Index()
{
return View();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
}
Modify Privacy.cshtml:
@{
ViewData["Title"] = "Privacy Policy";
}
<h1>@ViewData["Title"]</h1>
<p>Use this page to detail your site's privacy policy.</p>
<p>NOTE: this page can only be viewed by registered users.</p>
Create Register.cshtml, modify it as follows and save it in the Views/Home folder.
@{
ViewData["Title"] = "Register";
}
<h1>@ViewData["Title"]</h1>
<form action="/Home/Register" method="post">
@Html.AntiForgeryToken()
<input type="email" placeholder="EMAIL" name="Email" value="@Model?.Email" required />
<input type="password" placeholder="PASSWORD" name="Password" required />
<button type="submit">REGISTER</button>
</form>
Create Login.cshtml, modify it as follows and save it in the Views/Home folder.
@{
ViewData["Title"] = "Login";
}
<h1>@ViewData["Title"]</h1>
@if(!string.IsNullOrEmpty(Context.Request.Query["ReturnUrl"]))
{
<p>You must login to access <b>@Context.Request.Query["ReturnUrl"]</b></p>
}
<form action="/Home/Login?ReturnUrl=@Html.Raw(Uri.EscapeDataString("" + Context.Request.Query["ReturnUrl"]))" method="post">
@Html.AntiForgeryToken()
<input type="email" placeholder="EMAIL" name="Email" value="@Model?.Email" required />
<input type="password" placeholder="PASSWORD" name="Password" required />
<button type="submit">LOGIN</button>
</form>
Modify _Layout.cshtml to have these links in the navbar:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>@ViewData["Title"] - AuthWebSite</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/site.css" />
</head>
<body>
<header>
<nav class="navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3">
<div class="container">
<a class="navbar-brand" asp-area="" asp-controller="Home" asp-action="Index">AuthWebSite</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-controls="navbarSupportedContent"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
<ul class="navbar-nav flex-grow-1">
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
</li>
<li class="nav-item">
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</li>
<!-- NOTE: IF ELSE STATEMENT ADDED -->
@if (User.Identity.IsAuthenticated)
{
<li class="nav-item">
<!-- NOTE: NEW ITEM ADDED -->
<form action="/Home/Logout" method="post" id="logout-form" style="display:none;">@Html.AntiForgeryToken()</form>
<a class="nav-link text-dark" href="javascript:document.getElementById('logout-form').submit();">Logout</a>
</li>
<li class="nav-item">
<!-- NOTE: NEW ITEM ADDED -->
<span class="nav-link text-dark"><small>Hello, @User.Identity.Name</small></span>
</li>
}
else
{
<li class="nav-item">
<!-- NOTE: NEW ITEM ADDED -->
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Register">Register</a>
</li>
<li class="nav-item">
<!-- NOTE: NEW ITEM ADDED -->
<a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Login">Login</a>
</li>
}
</ul>
</div>
</div>
</nav>
</header>
<div class="container">
<main role="main" class="pb-3">
@RenderBody()
</main>
</div>
<footer class="border-top footer text-muted">
<div class="container">
© 2019 - AuthWebSite - <a asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
</div>
</footer>
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/js/site.js" asp-append-version="true"></script>
@RenderSection("Scripts", required: false)
</body>
</html>
Now, run the project and try to access the /articles/current/proware/privacy-policy path. To access this URL, the user must be registered and logged in. To secure a REST API controller, simply adding the [Authorize]
attribute to each endpoint is one solution, or add the [Authorize]
atttribute to the whole controller.