PROWAREtech

articles » current » asp-net-core » add-local-authentication

ASP.NET Core: Add Local Authentication

Setup and add local authentication user accounts; written in C#.

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">
            &copy; 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">
			&copy; 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.

Coding Video

https://youtu.be/uoF1H1gHQwU


This site uses cookies. Cookies are simple text files stored on the user's computer. They are used for adding features and security to this site. Read the privacy policy.
CLOSE