Monday, 29 May 2023

A Detailed Guide on Custom Authentication and Authorization in ASP.NET MVC

 

A Detailed Guide on Custom Authentication and Authorization in ASP.NET MVC

Introduction


In this blog, we'll show you how to make your authentication application. Authentication and authorization are indispensable aspects for any website project to grant users access based on their roles, as you may know. To build custom authentication, we utilize the membership provider class to check user credentials (username and password) and the role provider class to validate user authorization based on roles.

Our authentication system's scenario is as follows:

  1. After the user enters his or her credentials (Login and Password), we must use the ValidateUser method from our custom membership provider class.

  2. We need to choose a bespoke membership provider that will be utilized. We must make this change within the Web.config file.

  3. When a user is successfully authenticated, the Authorize Attribute filter is automatically invoked to determine whether the user has access to the requested resource, and the role provider is the class that is responsible for doing so depending on the user role.

  4. Note that we must also define a role provider in the Web.config file.

Create Your MVC application


First, open the Visual Studio and select File -> New Project

This will pop up the New Project window. Select ASP.NET Web Application(.NET Framework) and then click OK.

NewASP.NETWebApplication
Figure: Create New ASP.NET Web Application

Now, give the name of the project like CustomAuthenticationMVC, and select the appropriate location where you want to save your project and then click on the create button to create your project.

choosethepathandcreateaproject
Figure : Name the project, choose the path and create a project

Then, choose MVC and click on create to add the project.

SelectMVC
Figure : Select MVC project template and create the project

We will establish a database using the entity framework (Code first approach) once our project is created.

SQL Database part


Entity framework takes a variety of approaches to map databases, including database first, model first, and code first.

To construct our database, simply follow the steps below. First and foremost, we'll establish a folder called Data Access.

To accomplish this, right-click on the project name in Solution Explorer >> Add >> New Folder.

Then we'll add the User and Role entities.

Example

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace CustomAuthenticationMVC.DataAccess
{
public class User
{
public int UserId { get; set; }
public string Username { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public bool IsActive { get; set; }
public Guid ActivationCode { get; set; }
public virtual ICollection<role> Roles { get; set; }
}
}
</role>

Example

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace CustomAuthenticationMVC.DataAccess
{
public class Role
{
public int RoleId { get; set; }
public string RoleName { get; set; }
public virtual ICollection<user> Users { get; set; }
}
}
</user>

As the last step, we're going to add our AuthenticationDB context, which will allow us to access database data. Normally, the context class derives from the dbcontext class.

Example

using CustomAuthenticationMVC.DataAccess;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Configuration;
namespace CustomAuthenticationMVC.DataAccess
{
public class AuthenticationDB : DbContext
{
public AuthenticationDB()
:base("AuthenticationDB")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<user>()
.HasMany(u => u.Roles)
.WithMany(r => r.Users)
.Map(m =>
{
m.ToTable("UserRoles");
m.MapLeftKey("UserId");
m.MapRightKey("RoleId");
});
}
public DbSet<user> Users { get; set; }
public DbSet<role> Roles { get; set; }
}
}
</role></user></user>
Note

Make sure you've included your database's connection string in the Web.config file.

<connectionstrings>
<add connectionstring=" Data Source=.;Initial Catalog=CustomAuthenticationDB;Integrated Security=True" name="AuthenticationDB" providername="System.Data.SqlClient">
</add></connectionstrings>

Now enter the following command into the Package Manager console.

 

Enable-migrations


After performing above aspects, now we're ready to start building our database. Executes the commands in the order listed below.

add-migration "initial_migration"

update-database –verbose

CustomAuthenticationDB
Figure: Create a CustomAuthenticationDB by enabling migration

As you can see, all of the tables have been successfully added.

Implementing Membership Provider and Role Provider


Let's get started with the custom MemberShip Provider.

The first step is to construct the CustomMembership class, which should inherit from MembershipProvider.

After that, we'll override the following methods based on our requirements:

  • ValidateUser is a function that takes two parameters: username and password, and checks whether the user exists or not.
  • The GetUser method is in charge of returning user info based on the username input.
  • GetUserNameByEmail takes an email address as an input and returns a username.

Example

using CustomAuthenticationMVC.DataAccess;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
namespace CustomAuthenticationMVC.CustomAuthentication
{
public class CustomMembership : MembershipProvider
{
public override bool ValidateUser(string username, string password)
{
if(string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
{
return false;
}
using (AuthenticationDB dbContext = new AuthenticationDB())
{
var user = (from us in dbContext.Users
where string.Compare(username, us.Username, StringComparison.OrdinalIgnoreCase) == 0
&& string.Compare(password, us.Password, StringComparison.OrdinalIgnoreCase) == 0
&& us.IsActive == true
select us).FirstOrDefault();
return (user != null) ? true : false;
}
}
password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, object providerUserKey, out MembershipCreateStatus status)
{
throw new NotImplementedException();
}
public override MembershipUser GetUser(string username, bool userIsOnline)
{
using (AuthenticationDB dbContext = new AuthenticationDB())
{
var user = (from us in dbContext.Users
where string.Compare(username, us.Username, StringComparison.OrdinalIgnoreCase) == 0
select us).FirstOrDefault();
if(user == null)
{
return null;
}
var selectedUser = new CustomMembershipUser(user);
return selectedUser;
}
}
public override string GetUserNameByEmail(string email)
{
using (AuthenticationDB dbContext = new DataAccess.AuthenticationDB())
{
string username = (from u in dbContext.Users
where string.Compare(email, u.Email) == 0
select u.Username).FirstOrDefault();
return !string.IsNullOrEmpty(username) ? username : string.Empty;
}
}
public override string ApplicationName
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public override bool EnablePasswordReset
{
get
{
throw new NotImplementedException();
}
}
public override bool EnablePasswordRetrieval
{
get
{
throw new NotImplementedException();
}
}
public override int MaxInvalidPasswordAttempts
{
get
{
throw new NotImplementedException();
}
}
public override int MinRequiredNonAlphanumericCharacters
{
get
{
throw new NotImplementedException();
}
}
public override int MinRequiredPasswordLength
{
get
{
throw new NotImplementedException();
}
}
public override int PasswordAttemptWindow
{
get
{
throw new NotImplementedException();
}
}
public override MembershipPasswordFormat PasswordFormat
{
get
{
throw new NotImplementedException();
}
}
public override string PasswordStrengthRegularExpression
{
get
{
throw new NotImplementedException();
}
}
public override bool RequiresQuestionAndAnswer
{
get
{
throw new NotImplementedException();
}
}
public override bool RequiresUniqueEmail
{
get
{
throw new NotImplementedException();
}
}
public override bool ChangePassword(string username, string oldPassword, string newPassword)
{
throw new NotImplementedException();
}
public override bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion, string newPasswordAnswer)
{
throw new NotImplementedException();
}
public override bool DeleteUser(string username, bool deleteAllRelatedData)
{
throw new NotImplementedException();
}
public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
{
throw new NotImplementedException();
}
public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
{
throw new NotImplementedException();
}
public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
{
throw new NotImplementedException();
}
public override int GetNumberOfUsersOnline()
{
throw new NotImplementedException();
}
public override string GetPassword(string username, string answer)
{
throw new NotImplementedException();
}
public override MembershipUser GetUser(object providerUserKey, bool userIsOnline)
{
throw new NotImplementedException();
}
public override string ResetPassword(string username, string answer)
{
throw new NotImplementedException();
}
public override bool UnlockUser(string userName)
{
throw new NotImplementedException();
}
public override void UpdateUser(MembershipUser user)
{
throw new NotImplementedException();
}
}
}

As you can see, MembershipProvider offers a lot of methods like Create User, ChangePassword, GetPassword, and so on, but we only need ValidateUser, GetUser, and GetUserNameByEmail.

Here we can point out that the GetUser method makes use of the CustomMemberShipUser class to retrieve only the information we require about the user.

Example

using System;
using CustomAuthenticationMVC.DataAccess;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
namespace CustomAuthenticationMVC.CustomAuthentication
{
public class CustomMembershipUser : MembershipUser
{
#region User Properties
public int UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public ICollection<role> Roles { get; set; }
#endregion
public CustomMembershipUser(User user):base("CustomMembership", user.Username, user.UserId, user.Email, string.Empty, string.Empty, true, false, DateTime.Now, DateTime.Now, DateTime.Now, DateTime.Now, DateTime.Now)
{
UserId = user.UserId;
FirstName = user.FirstName;
LastName = user.LastName;
Roles = user.Roles;
}
}
}
</role>

The second step, as previously said, is to include our customerMembership in the Web.config file. Now we'll update the web.config file and add the following snippet of code.

<membership defaultprovider="CustomMembership">
<providers>
<clear>
<add name="CustomMembership" type="CustomAuthenticationMVC.CustomAuthentication.CustomMembership">
</add></clear></providers>
</membership>

We'll now use Custom Role Provider to implement it.

In this case, we'll construct a CustomRole class that inherits from RoleProvider and then override the GetRolesForUser and IsUserInRole methods.

Example

using CustomAuthenticationMVC.DataAccess;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
namespace CustomAuthenticationMVC.CustomAuthentication
{
public class CustomRole : RoleProvider
{
public override bool IsUserInRole(string username, string roleName)
{
var userRoles = GetRolesForUser(username);
return userRoles.Contains(roleName);
}
public override string[] GetRolesForUser(string username)
{
if (!HttpContext.Current.User.Identity.IsAuthenticated)
{
return null;
}
var userRoles = new string[] { };
using (AuthenticationDB dbContext = new AuthenticationDB())
{
var selectedUser = (from us in dbContext.Users.Include("Roles")
where string.Compare(us.Username, username, StringComparison.OrdinalIgnoreCase) == 0
select us).FirstOrDefault();
if(selectedUser != null)
{
userRoles = new[] { selectedUser.Roles.Select(r=>r.RoleName).ToString() };
}
return userRoles.ToArray();
}
}
public override string ApplicationName
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
public override void AddUsersToRoles(string[] usernames, string[] roleNames)
{
throw new NotImplementedException();
}
public override void CreateRole(string roleName)
{
throw new NotImplementedException();
}
public override bool DeleteRole(string roleName, bool throwOnPopulatedRole)
{
throw new NotImplementedException();
}
public override string[] FindUsersInRole(string roleName, string usernameToMatch)
{
throw new NotImplementedException();
}
public override string[] GetAllRoles()
{
throw new NotImplementedException();
}
public override string[] GetUsersInRole(string roleName)
{
throw new NotImplementedException();
}
public override void RemoveUsersFromRoles(string[] usernames, string[] roleNames)
{
throw new NotImplementedException();
}
public override bool RoleExists(string roleName)
{
throw new NotImplementedException();
}
}
}

Note that the GetRolesForUser method takes a username as a parameter and returns all roles associated with that username. The IsUserInRole method takes two parameters: username and rolename, and verifies whether the user has a role that grants him access to the desired resource.

After that, add the following code snippet to the web.config file.

<rolemanager defaultprovider="CustomRole" enabled="true">
<providers>
<clear>
<add name="CustomRole" type="CustomAuthenticationMVC.CustomAuthentication.CustomRole">
</add></clear></providers>
</rolemanager>

Custom principals and identities are now being implemented. We have a user property that contains basic user data by default to receive user information from HTTP requests. User information is accessed deeply using the IPrincipal interface. In reality, the Identity property of this interface encompasses all user data.

As previously stated, the user property only contains basic user information; however, the goal is to expand this property to include more useful data.

Now, we'll develop the CustomPrincipal class, which will inherit from the IPrincipal interface.

Example

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
using System.Web;
namespace CustomAuthenticationMVC.CustomAuthentication
{
public class CustomPrincipal : IPrincipal
{
#region Identity Properties
public int UserId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string[] Roles { get; set; }
#endregion
public IIdentity Identity
{
get; private set;
}
public bool IsInRole(string role)
{
if (Roles.Any(r => role.Contains(r)))
{
return true;
}
else
{
return false;
}
}
public CustomPrincipal(string username)
{
Identity = new GenericIdentity(username);
}
}
}
Note

We'll add the following code snippet within Global.asax to replace the default user property from HttpContext.

Example

protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
HttpCookie authCookie = Request.Cookies["Cookie1"];
if (authCookie != null)
{
FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
var serializeModel = JsonConvert.DeserializeObject<customserializemodel>(authTicket.UserData);
CustomPrincipal principal = new CustomPrincipal(authTicket.Name);
principal.UserId = serializeModel.UserId;
principal.FirstName = serializeModel.FirstName;
principal.LastName = serializeModel.LastName;
principal.Roles = serializeModel.RoleName.ToArray<string>();
HttpContext.Current.User = principal;
}
}
</string></customserializemodel>

Create a controller


Following the implementation of Custom Membership Provider and Custom Role Provider, I think it is now time to design an Account Controller with all of the required actions to assist us in authenticating users.

We're going to make a controller now. Right-click the controllers folder> > Add >> Controller>> MVC 5 Controller - Empty>> Add. Name the controller “AccountController” in the following dialogue, then click on the Add to add controller successfully.

MVC5Controller-Empty
Figure: Choose “MVC5 Controller-Empty” from the above controller
AccountController
Figure: Choose “MVC5 Controller-Empty” from the above controller

Example

using CustomAuthenticationMVC.CustomAuthentication;
using CustomAuthenticationMVC.DataAccess;
using CustomAuthenticationMVC.Models;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Mail;
using System.Web;
using System.Web.Mvc;
using System.Web.Security;
namespace CustomAuthenticationMVC.Controllers
{
[AllowAnonymous]
public class AccountController : Controller
{
public ActionResult Index()
{
return View();
}
[HttpGet]
public ActionResult Login(string ReturnUrl = "")
{
if (User.Identity.IsAuthenticated)
{
return LogOut();
}
ViewBag.ReturnUrl = ReturnUrl;
return View();
}
[HttpPost]
public ActionResult Login(LoginView loginView, string ReturnUrl = "")
{
if (ModelState.IsValid)
{
if (Membership.ValidateUser(loginView.UserName, loginView.Password))
{
var user = (CustomMembershipUser)Membership.GetUser(loginView.UserName, false);
if (user != null)
{
CustomSerializeModel userModel = new Models.CustomSerializeModel()
{
UserId = user.UserId,
FirstName = user.FirstName,
LastName = user.LastName,
RoleName = user.Roles.Select(r => r.RoleName).ToList()
};
string userData = JsonConvert.SerializeObject(userModel);
FormsAuthenticationTicket authTicket = new FormsAuthenticationTicket
(
1, loginView.UserName, DateTime.Now, DateTime.Now.AddMinutes(15), false, userData
);
string enTicket = FormsAuthentication.Encrypt(authTicket);
HttpCookie faCookie = new HttpCookie("Cookie1", enTicket);
Response.Cookies.Add(faCookie);
}
if (Url.IsLocalUrl(ReturnUrl))
{
return Redirect(ReturnUrl);
}
else
{
return RedirectToAction("Index");
}
}
}
ModelState.AddModelError("", "Something Wrong : Username or Password invalid ^_^ ");
return View(loginView);
}
[HttpGet]
public ActionResult Registration()
{
return View();
}
[HttpPost]
public ActionResult Registration(RegistrationView registrationView)
{
bool statusRegistration = false;
string messageRegistration = string.Empty;
if (ModelState.IsValid)
{
string userName = Membership.GetUserNameByEmail(registrationView.Email);
if (!string.IsNullOrEmpty(userName))
{
ModelState.AddModelError("Warning Email", "Sorry: Email already Exists");
return View(registrationView);
}
//Save User Data
using (AuthenticationDB dbContext = new AuthenticationDB())
{
var user = new User()
{
Username = registrationView.Username,
FirstName = registrationView.FirstName,
LastName = registrationView.LastName,
Email = registrationView.Email,
Password = registrationView.Password,
ActivationCode = Guid.NewGuid(),
};
dbContext.Users.Add(user);
dbContext.SaveChanges();
}
VerificationEmail(registrationView.Email, registrationView.ActivationCode.ToString());
messageRegistration = "Your account has been created successfully. ^_^";
statusRegistration = true;
}
else
{
messageRegistration = "Something Wrong!";
}
ViewBag.Message = messageRegistration;
ViewBag.Status = statusRegistration;
return View(registrationView);
}
[HttpGet]
public ActionResult ActivationAccount(string id)
{
bool statusAccount = false;
using (AuthenticationDB dbContext = new DataAccess.AuthenticationDB())
{
var userAccount = dbContext.Users.Where(u => u.ActivationCode.ToString().Equals(id)).FirstOrDefault();
if (userAccount != null)
{
userAccount.IsActive = true;
dbContext.SaveChanges();
statusAccount = true;
}
else
{
ViewBag.Message = "Something Wrong !!";
}
}
ViewBag.Status = statusAccount;
return View();
}
public ActionResult LogOut()
{
HttpCookie cookie = new HttpCookie("Cookie1", "");
cookie.Expires = DateTime.Now.AddYears(-1);
Response.Cookies.Add(cookie);
FormsAuthentication.SignOut();
return RedirectToAction("Login", "Account", null);
}
[NonAction]
public void VerificationEmail(string email, string activationCode)
{
var url = string.Format("/Account/ActivationAccount/{0}", activationCode);
var link = Request.Url.AbsoluteUri.Replace(Request.Url.PathAndQuery, url);
var fromEmail = new MailAddress("mehdi.rami2012@gmail.com", "Activation Account - AKKA");
var toEmail = new MailAddress(email);
var fromEmailPassword = "******************";
string subject = "Activation Account !";
string body = "
Please click on the following link in order to activate your account" + "
<a href=""> Activation Account ! </a>";
var smtp = new SmtpClient
{
Host = "smtp.gmail.com",
Port = 587,
EnableSsl = true,
DeliveryMethod = SmtpDeliveryMethod.Network,
UseDefaultCredentials = false,
Credentials = new NetworkCredential(fromEmail.Address, fromEmailPassword)
};
using (var message = new MailMessage(fromEmail, toEmail)
{
Subject = subject,
Body = body,
IsBodyHtml = true
})
smtp.Send(message);
}
}
}

Account controller has three major actions, as you can see above.

  • Check to see if the person who is going to create a new account has already been created. To do so, we'll use the CustomMembershipProvider's GetUserNameByEmail function.
  • After that, we'll save user information.
  • We must activate the user account by sending a verification email to the user, informing him that he must activate his account by clicking on the activation link.
  • Login action takes a loginView model as a parameter, which has a username and password properties, and then uses the ValidateUser method from custom Membership to check user credentials. If user validation is true, the GetUser function is used to retrieve user data.
  • Following that, we'll create an authentication ticket that will be encrypted using the term FormsAuthentication. Finally, we'll encrypt (authTicket) and create a faCookie object with our ticket's encrypted value as the value.
  • The registration action is used to create a new account for a user. This action will do three things on a deep level:
  • The LogOut action allows the user to log out of his or her session, as the name suggests.

We must now add account views for login, registration, and activation.

Example

@model CustomAuthenticationMVC.Models.LoginView
@{
ViewBag.Title = "Login";
}<h2>Login</h2>
@using (Html.BeginForm(null, null, new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post))
{
@Html.AntiForgeryToken()
<div class="form-horizontal"><h4>LoginView</h4>
<hr>
@Html.ValidationSummary(true, "", new { @class = "text-danger" })<div class="form-group">
@Html.LabelFor(model => model.UserName, htmlAttributes: new { @class = "control-label col-md-2" })<div class="col-md-10">
@Html.EditorFor(model => model.UserName, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.UserName, "", new { @class = "text-danger" })</div></div>
<div class="form-group">
@Html.LabelFor(model => model.Password, htmlAttributes: new { @class = "control-label col-md-2" })<div class="col-md-10">
@Html.EditorFor(model => model.Password, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Password, "", new { @class = "text-danger" })</div></div>
<div class="form-group">
@Html.LabelFor(model => model.RememberMe, htmlAttributes: new { @class = "control-label col-md-2" })<div class="col-md-10"><div class="checkbox">
@Html.EditorFor(model => model.RememberMe)
@Html.ValidationMessageFor(model => model.RememberMe, "", new { @class = "text-danger" })</div></div></div>
<div class="form-group"><div class="col-md-offset-2 col-md-10">
<input class="btn btn-default" type="submit" value="Log In"></div></div></div>
}
<div>
@Html.ActionLink("Back to List", "Index")</div>
<script src="~/Scripts/jquery-1.10.2.min.js"></script>
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>

Example

@model CustomAuthenticationMVC.Models.RegistrationView
@{
ViewBag.Title = "Registration";
}<h2>Registration</h2>
@if (ViewBag.Status != null && Convert.ToBoolean(ViewBag.Status))
{
if (ViewBag.Message != null)
{
<div class="alert alert-success">
<strong>Success!</strong> @ViewBag.Message</div>
}
}
else
{
using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal"><h4>RegistrationView</h4>
<hr>
@Html.ValidationSummary(true, "", new { @class = "text-danger" })<div class="form-group">
@Html.LabelFor(model => model.Username, htmlAttributes: new { @class = "control-label col-md-2" })<div class="col-md-10">
@Html.EditorFor(model => model.Username, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Username, "", new { @class = "text-danger" })</div></div>
<div class="form-group">
@Html.LabelFor(model => model.FirstName, htmlAttributes: new { @class = "control-label col-md-2" })<div class="col-md-10">
@Html.EditorFor(model => model.FirstName, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.FirstName, "", new { @class = "text-danger" })</div></div>
<div class="form-group">
@Html.LabelFor(model => model.LastName, htmlAttributes: new { @class = "control-label col-md-2" })<div class="col-md-10">
@Html.EditorFor(model => model.LastName, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.LastName, "", new { @class = "text-danger" })</div></div>
<div class="form-group">
@Html.LabelFor(model => model.Email, htmlAttributes: new { @class = "control-label col-md-2" })<div class="col-md-10">
@Html.EditorFor(model => model.Email, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Email, "", new { @class = "text-danger" })
@Html.ValidationMessage("ErrorEmail", new { @class = "text-danger" })</div></div>
<div class="form-group">
@Html.LabelFor(model => model.Password, htmlAttributes: new { @class = "control-label col-md-2" })<div class="col-md-10">
@Html.EditorFor(model => model.Password, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Password, "", new { @class = "text-danger" })</div></div>
<div class="form-group">
@Html.LabelFor(model => model.ConfirmPassword, htmlAttributes: new { @class = "control-label col-md-2" })<div class="col-md-10">
@Html.EditorFor(model => model.ConfirmPassword, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.ConfirmPassword, "", new { @class = "text-danger" })</div></div>
<div class="form-group"><div class="col-md-offset-2 col-md-10">
<input class="btn btn-default" type="submit" value="Create"></div></div></div>
if(ViewBag.Message != null)
{
<div class="alert alert-danger">
<strong>Error!</strong> @ViewBag.Message</div>
}
}
}
<div>
@Html.ActionLink("Login", "Login")</div>
@section Scripts{
<script src="~/Scripts/jquery.validate.min.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.min.js"></script>
}

Example

@{
ViewBag.Title = "Activation Account ^_^";
}<h2>Activation Account</h2>
@if(ViewBag.Status != null && Convert.ToBoolean(ViewBag.Status))
{
<div class="alert alert-success">
<strong>Success!</strong> Your account has been activated successfully.</div>
}
else
{
<div class="alert alert-danger">
<strong>Error!</strong>@ViewBag.Message</div>
}

Authorization filter


We'll implement a custom authorization filter in this section.

We want to create a filter that denies access to the user controller if the connected user does not have a user role.

Let's take it step by step.

First, make a CustomAuthorizeAttribute class that is derived from AuthorizeAttribute.

Example

using CustomAuthenticationMVC.CustomAuthentication;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace CustomAuthenticationMVC.CustomAuthentication
{
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
protected virtual CustomPrincipal CurrentUser
{
get { return HttpContext.Current.User as CustomPrincipal; }
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
return ((CurrentUser != null && !CurrentUser.IsInRole(Roles)) || CurrentUser == null) ? false : true;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
RedirectToRouteResult routeData = null;
if(CurrentUser == null)
{
routeData = new RedirectToRouteResult
(new System.Web.Routing.RouteValueDictionary
(new
{
controller = "Account",
action = "Login",
}
));
}
else
{
routeData = new RedirectToRouteResult
(new System.Web.Routing.RouteValueDictionary
(new
{
controller = "Error",
action = "AccessDenied"
}
));
}
filterContext.Result = routeData;
}
}
}

Example

using CustomAuthenticationMVC.CustomAuthentication;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace CustomAuthenticationMVC.Controllers
{
[CustomAuthorize(Roles = "User")]
public class UserController : Controller
{
// GET: User
public ActionResult Index()
{
return View();
}
}
}

When a user is successfully authenticated but does not have a user role, we should alert him that his or her access is denied. In the HandleUnauthorizedRequest method, we did something like this.

Example

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace CustomAuthenticationMVC.Controllers
{
public class ErrorController : Controller
{
// GET: Error
public ActionResult AccessDenied()
{
return View();
}
}
}

Example

@{
ViewBag.Title = "AccessDenied";
}<h2>AccessDenied</h2>

Conclusion


In this blog, we have learned about authentication and authorization in Asp.Net MVC using an example. It will assist you comprehending the importance of validation for any website.

Content source: https://www.ifourtechnolab.com/blog/a-detailed-guide-on-custom-authentication-and-authorization-in-asp-net-mvc



PowerApps Consulting Services - Driving Innovation in Enterprise Solutions

Driving innovation while keeping costs in check is something that every executive looks for. Microsoft Power Apps is a leading low-code app ...