Cookie Authentication and authorization with password encryption and decryption using ASP.NET core
Authentication refers to giving access to the specific system. It gives security from unwanted people from accessing data or site.
ASP.NET core | SQL Stored Procedure | SQL SERVER | Encryption and Decryption | Authentication and Authorization
Authorization refers to limiting the access of the system to the user according to their roles. There are different roles in an organization and we can provide access to the resources according to their position or role in the organization.
There are various technique for Authorization and authentication like JWT Authentication, Windows Authentication, Cookies Authentication, etc. So, we will be learning cookie based authentication and authorization with encrypt password using Guid data type key in ASP.NET core with MVC framework.
We will be using SQL SERVER Stored procedure for data base.
At First Lest create table in database and write stored procedure for insert operation and select operation.
Let’s create table Demo for now,
CREATE TABLE Demo(
UserID uniqueidentifier default NEWID(),
Name NVARCHAR(100),
Email NVARCHAR(100),
Password NVARCHAR(MAX),
Role NVARCHAR(20)
)
Stored Procedure for registering user,
CREATE PROCEDURE usp_RegisterUser(
@name NVARCHAR(100),
@email NVARCHAR(100),
@password NVARCHAR(MAX),
@role NVARCHAR(20)
)
AS
BEGIN
INSERT INTO Demo(Name,Email,Password,Role)
VALUES(@name,@email,@password,@role)
END
Stored Procedure for selecting login data from table Demo,
CREATE PROCEDURE usp_Login(@email NVARCHAR(100))
AS
BEGIN
SELECT Name,Email,Password,Role
FROM Demo where Email=@email
END
Now, time to create project in ASP.NET Core MVC with no authentication as we will be using cookie authentication ourselves.
After project is created, Right click on solution and add new project as class library. Add two class library as LogicalLayers which contain business controller, data access layer and SQL handler (You can download SQL handler from here, you need to add NuGet package for SQL Client ( used in this Version 4.4.1)) if you want to know more about SQL Handler then Click here. and ModelInfos for model.
Create folder as shown,
After that add class file in BusinessLayer and Data access layer and add existing file for handling SQL as shown:
Add LoginModel.cs in DemoLoginModel folder of class library ModelInfos as:
Create model: LoginModel.cs
using System.ComponentModel.DataAnnotations;
namespace ModelInfos.DemoLoginModel
{
public class LoginModel
{
public string Name { get; set; }
[Required]
public string Email { get; set; }
[Required]
public string Password { get; set; }
public string Role { get; set; }
public string ReturnUrl { get; set; }
}
}
Establish connection between your application and SQL server:
Open the SQL Helper folder and open file SQLHandler, and edit line21 _connectionString="Default Connection" as below
_connectionString = "Data Source =<ServerName>; Initial Catalog = <databasename>; Integrated Security = False; Persist Security Info = False; User ID = <datbaseID>; Password = <dbpassword>; Connect Timeout = 60 ";
Example:_connectionString = "Data Source =DESKTOP-LF3H4C5; Initial Catalog = DemoLogin; Integrated Security = False; Persist Security Info = False; User ID = sa; Password = dblogin; Connect Timeout = 60 ";
Create Data access Provider: DataAccessProvider.cs
All the SQL connection are handled by SQLHandler so no need to start connection or close connections.
using LogicLayers.SQLHelper;
using ModelInfos.DemoLoginModel;
using System;
using System.Collections.Generic;
using System.Text;
namespace LogicalLayers.DataAcessLayer
{
public class DataAccessProvider
{
internal void RegisterUser(LoginModel objLogin)
{
List<KeyValuePair<string, object>> parameter = new List<KeyValuePair<string, object>>()
{
new KeyValuePair<string, object>("@name",objLogin.Name),
new KeyValuePair<string, object>("@email",objLogin.Email),
new KeyValuePair<string, object>("@password",objLogin.Password),
new KeyValuePair<string, object>("@role",objLogin.Role),
};
SQLHandler objSQLH = new SQLHandler();
objSQLH.ExecuteNonQuery("usp_RegisterUser", parameter);
}
internal LoginModel GetLoginInfoByEmail(string Email)
{
List<KeyValuePair<string, object>> parameter = new List<KeyValuePair<string, object>>()
{
new KeyValuePair<string, object>("@email",Email)
};
SQLHandler SQLH = new SQLHandler();
LoginModel loginInfo = SQLH.ExecuteAsObject<LoginModel>("usp_Login", parameter);
return loginInfo;
}
}
}
Create Business Controller: LoginBusinessController.cs
Encryprion and Decryption is done in BusinessController and var key is used as encrypting and decrypting key of type Guid converted to string. i have used same key for every user.
using LogicalLayers.DataAcessLayer;
using ModelInfos.DemoLoginModel;
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace LogicalLayers.BusinessLayer
{
public class LoginBusinessController
{
public LoginModel GetLogin(LoginModel objLogin)
{
DataAccessProvider objLoginData = new DataAccessProvider();
LoginModel loginInfo = objLoginData.GetLoginInfoByEmail(objLogin.Email);
var key = Guid.Parse("7E3C0EF6-66C1-4FC6-916C-14A11ECBFEFE").ToString("N");
string mat = Decrypt(loginInfo.Password, key).ToString();
if (objLogin.Email == loginInfo.Email && mat == objLogin.Password)
{
return loginInfo;
}
return null;
}
public List<LoginModel> GetRegistered(LoginModel objRegisterInfo)
{
DataAccessProvider objLoginData = new DataAccessProvider();
List<LoginModel> objloginInfos = new List<LoginModel>();
var key = Guid.Parse("7E3C0EF6-66C1-4FC6-916C-14A11ECBFEFE").ToString("N");
objRegisterInfo.Password = Encrypt(objRegisterInfo.Password, key);
objLoginData.RegisterUser(objRegisterInfo);
return objloginInfos;
}
static string Encrypt(string text, string key)
{
var _key = Encoding.UTF8.GetBytes(key);
using (var aes = Aes.Create())
{
using (var encryptor = aes.CreateEncryptor(_key, aes.IV))
{
using (var ms = new MemoryStream())
{
using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write))
{
using (var sw = new StreamWriter(cs))
{
sw.Write(text);
}
}
var iv = aes.IV;
var encrypted = ms.ToArray();
var result = new byte[iv.Length + encrypted.Length];
Buffer.BlockCopy(iv, 0, result, 0, iv.Length);
Buffer.BlockCopy(encrypted, 0, result, iv.Length, encrypted.Length);
return Convert.ToBase64String(result);
}
}
}
}
static string Decrypt(string encrypted, string key)
{
var b = Convert.FromBase64String(encrypted);
var iv = new byte[16];
var cipher = new byte[16];
Buffer.BlockCopy(b, 0, iv, 0, iv.Length);
Buffer.BlockCopy(b, iv.Length, cipher, 0, iv.Length);
var _key = Encoding.UTF8.GetBytes(key);
using (var aes = Aes.Create())
{
using (var decryptor = aes.CreateDecryptor(_key, iv))
{
var result = string.Empty;
using (var ms = new MemoryStream(cipher))
{
using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read))
{
using (var sr = new StreamReader(cs))
{
result = sr.ReadToEnd();
}
}
}
return result;
}
}
}
}
}
Now, add controller type file in Controllers folder of project as
Create Controller: DemoLoginController.cs
Authentication and authorization is done in main controller. Authorization is done using roles Admin and user using Authorize attribute. Admin has access to every controller but user doesnot have access to Register( at first comment attribute above register to register admin). AllowAnonymous attribute give access to non user too bt if we use Authorize attribute above controller it doesnot give access until you login. [Authorize(Roles=("Admin"))] means authorized to admin only and [Authorize(Roles=("Admin,user"))] means admin and user both have access to controller. For more about Authentication and Authorization, Click here.
using System.Collections.Generic;
using System.Security.Claims;
using System.Threading.Tasks;
using LogicalLayers.BusinessLayer;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using ModelInfos.DemoLoginModel;
namespace DemoLogin.Controllers
{
public class DemoLoginController : Controller
{
[Authorize(Roles = ("user,Admin"))]
public IActionResult Index()
{
return View();
}
[Authorize(Roles=("Admin"))] //comment this line at first for registering Role as Admin
[HttpGet]
public IActionResult Register()
{
return View();
}
[HttpPost]
public IActionResult Register(LoginModel objLogin)
{
LoginBusinessController objLoginController = new LoginBusinessController();
if (!ModelState.IsValid)
{
return View(objLogin);
}
objLoginController.GetRegistered(objLogin);
return RedirectToAction("Index");
}
[AllowAnonymous]
[HttpGet]
public IActionResult Login(string returnUrl = "")
{
if (User.Identity.Name != null)
{
return RedirectToAction("Index", "DemoLogin");
}
var info = new LoginModel { ReturnUrl = returnUrl };
return View(info);
}
[HttpPost]
public async Task<IActionResult> Login(LoginModel objlogin)
{
LoginBusinessController objLogincontroller = new LoginBusinessController();
if (ModelState.IsValid)
{
LoginModel objloginInfo1 = objLogincontroller.GetLogin(objlogin);
if (objloginInfo1 != null)
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, objloginInfo1.Email),
new Claim(ClaimTypes.Role,objloginInfo1.Role),
};
ClaimsIdentity userIdentity = new ClaimsIdentity(claims,CookieAuthenticationDefaults.AuthenticationScheme);
ClaimsPrincipal principal = new ClaimsPrincipal(userIdentity);
await HttpContext.SignInAsync(principal);
return RedirectToAction("Index", "DemoLogin");
}
else
{
TempData["UserLoginFailed"] = "Login Failed.Please enter correct credentials";
return View();
}
}
else
return View();
}
[HttpGet]
public async Task<IActionResult> Logout()
{
await HttpContext.SignOutAsync();
return RedirectToAction("Login", "DemoLogin");
}
}
}
Now, add new folder in view as DemoLogin and add view Index.cshtml, Login.cshtml,Register.cshtml
Create View Login: Register.cshtml
@model ModelInfos.DemoLoginModel.LoginModel
@{
ViewData["Title"] = "Register";
}
<h2>Register</h2>
<h4>LoginModel</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Register">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Email" class="control-label"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Password" class="control-label"></label>
<input asp-for="Password" class="form-control" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Role" class="control-label"></label>
<input asp-for="Role" class="form-control" />
<span asp-validation-for="Role" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>
</div>
</div>
Create View Login: Login.cshtml
@model ModelInfos.DemoLoginModel.LoginModel
@{
ViewData["Title"] = "Login";
}
<h2>Login</h2>
<div class="row">
<div class="col-md-4">
<form asp-action="Login">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Email" class="control-label"></label>
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Password" class="control-label"></label>
<input asp-for="Password" class="form-control" />
<span asp-validation-for="Password" class="text-danger"></span>
</div>
<div class="form-group">
<input type="submit" value="Login" class="btn btn-default" />
</div>
</form>
</div>
</div>
Create View Index: Index.cshtml
@{
ViewData["Title"] = "Index";
}
Changes in Startup.cs file:
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace DemoLogin
{
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.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
//Start-Add these code for cookie authentication
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.LoginPath = "/DemoLogin/Login/";
});
//-End
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
//Start-add these
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=DemoLogin}/{action=Login}/{id?}");
});
}
}
}
Now at open shared folder inside the View and open _Layout.cshtml file and add following code in navbar.
By keeping if condition Resgister, login user data and signout button can be shown only if condition matches the role of the user. you can see difference in output.
@if(@User.FindFirst(claim => claim.Type == System.Security.Claims.ClaimTypes.Role)?.Value == "Admin")
{
<li><a asp-area="" asp-controller="DemoLogin" asp-action="Register">Register</a></li>
}
@if (User.Identity.Name != null)
{
<li>
<a> <p style="color:aqua;"> @User.FindFirst(claim => claim.Type == System.Security.Claims.ClaimTypes.Role)?.Value</p> </a>
</li>
<li>
<a asp-area="" asp-action="Logout" asp-controller="DemoLogin">Sign Out</a>
</li>
}
I have removed few lines form navbar and replaced above code.
Output:
For Admin:
For User:
Also Read: