In this article, the purpose of the code is to create Login and Logout Functionality in MVC, using Form Authentication. We will discuss about the best way to store the password in the database using HASHING too.
So, here we go.
The most important question is how passwords are protected. If you are storing the password in a plain-text or using encryption/decryption (2-way), then it is a horrible idea. If you store the password in encryption format, then also there is a possibility to revert to the pain-text value using encrypted output.
Here is the best solution for storing the password in database. We encrypt the password using one-way hashing algorithms.
First of all, we create a HASH Value of combination of Passwords, One Unique Field (username, or mobile, or email) and SALT Key using SHA512 Algorithm (bcrypt/PBKDF2/scrypt are also best algorithms for hashing). Also create a unique SALT Key using CSPRNG. Then, we store HASH Value & SALT Key in database.
We don't need to know the password but we just verify the entered password. So, when the user attempts to login, we create one HASH Value of password and one unique field (which is entered by user) and SALT. Then, it is checked against the hash of their real password which are retrieved from the database. If the hashes match, the user is granted access. If not, the user is told that they have entered invalid login credentials.
First of all, we need to create a database & data table which contains users` information. Here, we start the code.
STEP 1 - Create A Database with Name "DemoLoginFunctionality"
  1. CREATE DATABASE DemoLoginFunctionality;
The following script is used to create a Datatable with Data Entries.
  1. USE [DemoLoginFunctionality]
  2. GO
  3. SET ANSI_NULLS ON
  4. GO
  5. SET QUOTED_IDENTIFIER ON
  6. GO
  7. SET ANSI_PADDING ON
  8. GO
  9. CREATE TABLE [dbo].[UserMaster](
  10. [UserID] [bigint] IDENTITY(1,1) NOT NULL,
  11. [Username] [nvarchar](50) NOT NULL,
  12. [HASH] [nvarchar](max) NOT NULL,
  13. [SALT] [varbinary](512) NOT NULL,
  14. CONSTRAINT [PK_UserMaster] PRIMARY KEY CLUSTERED
  15. (
  16. [UserID] ASC
  17. )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
  18. ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
  19. GO
  20. SET ANSI_PADDING OFF
  21. GO
  22. SET IDENTITY_INSERT [dbo].[UserMaster] ON
  23. GO
  24. INSERT [dbo].[UserMaster] ([UserID], [Username], [HASH], [SALT]) VALUES (1, N'****', N'***', 0xE2215FF02C584A4F9252F62E504C171B178AF8B39C758E31BFCE8DC4C35A133A)
  25. GO
  26. SET IDENTITY_INSERT [dbo].[UserMaster] OFF
  27. GO
NOTE
We save the password hashing & SALT in database.
First, we need to create a SALT Key. We use CSPRNG (cryptographically secure pseudo-random number generator) for creating a SALT Key.
What is SALT key?
A SALT is random data that is used as an additional input to a one-way function that "hashes" a password.
What is CSPRNG?
A cryptographically secure pseudo-random number generator (CSPRNG) is a pseudo-random number generator (PRNG) with properties that make it suitable for use in cryptography. It uses mathematical formulas to produce sequences of random numbers.
  1. #region --> Generate SALT Key
  2. private static byte[] Get_SALT()
  3. {
  4. return Get_SALT(saltLengthLimit);
  5. }
  6. private static byte[] Get_SALT(int maximumSaltLength)
  7. {
  8. var salt = new byte[maximumSaltLength];
  9. //Require NameSpace: using System.Security.Cryptography;
  10. using (var random = new RNGCryptoServiceProvider())
  11. {
  12. random.GetNonZeroBytes(salt);
  13. }
  14. return salt;
  15. }
  16. #endregion
2) Now, the second step is to create a HASH Value. We use SHA512 for that.
What is Password Hashing?
Hashing performs a one-way transformation on a password, turning the password into another String, called the hashed password. “One-way” means it is practically impossible to go the other way to turn the hashed password back into the original password. They also have the property that if the input changes by even a tiny bit, the resulting hash is completely different.
i.e.
What is SHA512?
The Secure Hash Algorithm (SHA512) is a set of cryptographic hash functions designed by the National Security Agency (NSA).
  1. #region --> Generate HASH Using SHA512
  2. public static string Get_HASH_SHA512(string password, string username, byte[] salt)
  3. {
  4. try
  5. {
  6. //required NameSpace: using System.Text;
  7. //Plain Text in Byte
  8. byte[] plainTextBytes = Encoding.UTF8.GetBytes(password + username);
  9. //Plain Text + SALT Key in Byte
  10. byte[] plainTextWithSaltBytes = new byte[plainTextBytes.Length + salt.Length];
  11. for (int i = 0; i < plainTextBytes.Length; i++)
  12. {
  13. plainTextWithSaltBytes[i] = plainTextBytes[i];
  14. }
  15. for (int i = 0; i < salt.Length; i++)
  16. {
  17. plainTextWithSaltBytes[plainTextBytes.Length + i] = salt[i];
  18. }
  19. HashAlgorithm hash = new SHA512Managed();
  20. byte[] hashBytes = hash.ComputeHash(plainTextWithSaltBytes);
  21. byte[] hashWithSaltBytes = new byte[hashBytes.Length + salt.Length];
  22. for (int i = 0; i < hashBytes.Length; i++)
  23. {
  24. hashWithSaltBytes[i] = hashBytes[i];
  25. }
  26. for (int i = 0; i < salt.Length; i++)
  27. {
  28. hashWithSaltBytes[hashBytes.Length + i] = salt[i];
  29. }
  30. return Convert.ToBase64String(hashWithSaltBytes);
  31. }
  32. catch
  33. {
  34. return string.Empty;
  35. }
  36. }
  37. #endregion
Now, save the HASH Value & SALT Key in database.
STEP 2 - Create New MVC Application Project.
1) On the File menu, click New Project.
2) In the New Project dialog box under Project types, expand Visual C#, and then click Web. In the Name box, type "LoginLogout" and click on OK.
3) Now, in the dialog box, click on the "MVC" under the ASP.NET 4.5.2 Templates. Then, click on Change Authentication in the center of the right side.
STEP 3
So, here is the new new MVC Application created. Now, we need to create an EDMX & bind our database "DemoLogin" with EDMX.
1) On the right side, you can find the Solution Explorer.

2) In Solution Explorer, right click on "Models" folder. Then, click on the "Add". Now, click on the "New Item..."

3) Now, click on the Visual C# and select ADO.NET Entity Data Model. Name it "DBModel" and click on OK.
4) Select EF Designer from Database and click on "Next".
5) Now, click on new connection. Define "server name" and select authentication mode to either Windows or SQL Server. If you select SQL server, then enter username or password. Finally, select database "DemoLogin" under Connect to a Database. Click on OK.
6) Now, declare a name of connection string as "DBEntities" under Save connection setting in Web.config. Then, click on Next.
7) Select the version of Entity Framework. Select "Entity Framework 6.x" and click on Next.
8) Expand the "Tables", then expand "dbo" and select your datatable "Users". Now, give the name space as "Models" under Model Namespace. Then, click on OK.
9) Now, build your Project by pressing CLTR + B for updating every entity perfectly.
STEP 4 - Add a new empty controller
1) To add a Controller, right click on "Controllers" folder and select "Add". Then, click on "Controller".
2) Now, in Add Scaffold Dialog box, select "MVC 5 Controller - Empty". Click on Add, and name it as "HomeController". Click on Add.
Create a new ActionResult method named as 'Login'.
  1. #region --> Login GET Method
  2. [HttpGet]
  3. public ActionResult Login(string returnURL)
  4. {
  5. var userinfo = new LoginVM();
  6. try
  7. {
  8. // We do not want to use any existing identity information
  9. EnsureLoggedOut();
  10. // Store the originating URL so we can attach it to a form field
  11. userinfo.ReturnURL = returnURL;
  12. return View(userinfo);
  13. }
  14. catch
  15. {
  16. throw;
  17. }
  18. }
  19. #endregion
STEP 5 - Add a View
1) For this, right click on your "Login" action method and then select "Add View".
2) Now, uncheck the "Use a layout page" and click on Add button.
3) Add the Model Class in your top of the View.
@model LoginFunctionalityMVC.Models.LoginVM
4) Add an Email, Password textbox, Checkbox (for Remember Me Option), and a Submit button in form tag on Index.cshtml (View).
  1. //Paste below code in your view
  2. @model LoginFunctionalityMVC.Models.LoginVM
  3. @{
  4. Layout = null;
  5. }
  6. <!DOCTYPE html>
  7. <html>
  8. <head>
  9. <meta name="viewport" content="width=device-width" />
  10. <title>Login</title>
  11. </head>
  12. <body>
  13. @using (Html.BeginForm("Login", "Home", FormMethod.Post))
  14. {
  15. @Html.AntiForgeryToken()
  16. @Html.HiddenFor(s => s.ReturnURL)
  17. <h1>Login Functionality In MVC</h1>
  18. <div>
  19. @if (TempData["ErrorMSG"] != null)
  20. {
  21. <label style="color:maroon;"> @TempData["ErrorMSG"] </label>
  22. <br /><br />
  23. }
  24. @Html.TextBoxFor(s => s.Username, new { @placeholder = "Username" })
  25. <br /> <br />
  26. @Html.PasswordFor(s => s.Password, new { @placeholder = "Password" })
  27. <br /> <br />
  28. @Html.CheckBoxFor(s => s.isRemember) Remember ME
  29. <br /> <br />
  30. <button type="submit">Login</button>
  31. </div>
  32. }
  33. </body>
  34. </html>
NOTE
Don`t forgot to declare AntiForgeryToken in your View.
AntiForgeryToken: The anti-forgery token can be used to help protect your application against cross-site request forgery.
STEP 6
When login page initializes, we need to check that current session is logged out. So, first we logout the existing user.
For this, we create two methods, "EnsureLoggedOut" and "Logout".
  1. //GET: EnsureLoggedOut
  2. private void EnsureLoggedOut()
  3. {
  4. // If the request is (still) marked as authenticated we send the user to the logout action
  5. if (Request.IsAuthenticated)
  6. Logout();
  7. }
  8. //POST: Logout
  9. [HttpPost]
  10. [ValidateAntiForgeryToken]
  11. public ActionResult Logout()
  12. {
  13. try
  14. {
  15. // First we clean the authentication ticket like always
  16. //required NameSpace: using System.Web.Security;
  17. FormsAuthentication.SignOut();
  18. // Second we clear the principal to ensure the user does not retain any authentication
  19. //required NameSpace: using System.Security.Principal;
  20. HttpContext.User = new GenericPrincipal(new GenericIdentity(string.Empty), null);
  21. Session.Clear();
  22. System.Web.HttpContext.Current.Session.RemoveAll();
  23. // Last we redirect to a controller/action that requires authentication to ensure a redirect takes place
  24. // this clears the Request.IsAuthenticated flag since this triggers a new request
  25. return RedirectToLocal();
  26. }
  27. catch
  28. {
  29. throw;
  30. }
  31. }
NOTE
Don`t forgot to declare ValidateAntiForgeryToken in your top of POST Method.
ValidateAntiForgeryToken: The feature doesn't prevent any other type of data forgery or tampering based attacks. To use it, decorate the action method or controller with the ValidateAntiForgeryToken attribute and place a call to @Html.AntiForgeryToken(), in the forms posting to the method.
STEP 7
We create one method as "SignInRemember" for setting authentication in cookie (Remember Me option) and one method for redirecting to page, named "RedirectToLocal"
  1. //GET: SignInAsync
  2. private void SignInRemember(string userName, bool isPersistent = false)
  3. {
  4. // Clear any lingering authencation data
  5. FormsAuthentication.SignOut();
  6. // Write the authentication cookie
  7. FormsAuthentication.SetAuthCookie(userName, isPersistent);
  8. }
  9. //GET: RedirectToLocal
  10. private ActionResult RedirectToLocal(string returnURL = "")
  11. {
  12. try
  13. {
  14. // If the return url starts with a slash "/" we assume it belongs to our site
  15. // so we will redirect to this "action"
  16. if (!string.IsNullOrWhiteSpace(returnURL) && Url.IsLocalUrl(returnURL))
  17. return Redirect(returnURL);
  18. // If we cannot verify if the url is local to our host we redirect to a default location
  19. return RedirectToAction("Index", "Dashboard");
  20. }
  21. catch
  22. {
  23. throw;
  24. }
  25. }
STEP 8
Now, all pre-required Methods are done. Finally, we move to create one method for validating username and password.
  1. #region --> Login POST Method
  2. [HttpPost]
  3. [ValidateAntiForgeryToken]
  4. public ActionResult Login(LoginVM entity)
  5. {
  6. string OldHASHValue = string.Empty;
  7. byte[] SALT = new byte[saltLengthLimit];
  8. try
  9. {
  10. using (db = new DBEntities())
  11. {
  12. // Ensure we have a valid viewModel to work with
  13. if (!ModelState.IsValid)
  14. return View(entity);
  15. //Retrive Stored HASH Value From Database According To Username (one unique field)
  16. var userInfo = db.UserMasters.Where(s => s.Username == entity.Username.Trim()).FirstOrDefault();
  17. //Assign HASH Value
  18. if (userInfo != null)
  19. {
  20. OldHASHValue = userInfo.HASH;
  21. SALT = userInfo.SALT;
  22. }
  23. bool isLogin = CompareHashValue(entity.Password, entity.Username, OldHASHValue, SALT);
  24. if (isLogin)
  25. {
  26. //Login Success
  27. //For Set Authentication in Cookie (Remeber ME Option)
  28. SignInRemember(entity.Username, entity.isRemember);
  29. //Set A Unique ID in session
  30. Session["UserID"] = userInfo.UserID;
  31. // If we got this far, something failed, redisplay form
  32. // return RedirectToAction("Index", "Dashboard");
  33. return RedirectToLocal(entity.ReturnURL);
  34. }
  35. else
  36. {
  37. //Login Fail
  38. TempData["ErrorMSG"] = "Access Denied! Wrong Credential";
  39. return View(entity);
  40. }
  41. }
  42. }
  43. catch
  44. {
  45. throw;
  46. }
  47. }
  48. #endregion
  49. #region --> Comapare HASH Value
  50. public static bool CompareHashValue(string password, string username, string OldHASHValue, byte[] SALT)
  51. {
  52. try
  53. {
  54. string expectedHashString = Get_HASH_SHA512(password, username, SALT);
  55. return (OldHASHValue == expectedHashString);
  56. }
  57. catch
  58. {
  59. return false;
  60. }
  61. }
  62. #endregion
STEP 9
Now, the last stage is to create a custom Authorize Attribute In MVC. Because it checks if user is logged in or not before any page initializes.
Create a class named "CheckAuthorization.cs" in "Models" folder.
  1. public class CheckAuthorization : AuthorizeAttribute
  2. {
  3. public override void OnAuthorization(AuthorizationContext filterContext)
  4. {
  5. if (HttpContext.Current.Session["UserID"] == null || !HttpContext.Current.Request.IsAuthenticated)
  6. {
  7. if (filterContext.HttpContext.Request.IsAjaxRequest())
  8. {
  9. filterContext.HttpContext.Response.StatusCode = 302; //Found Redirection to another page. Here- login page. Check Layout ajaxError() script.
  10. filterContext.HttpContext.Response.End();
  11. }
  12. else
  13. {
  14. filterContext.Result = new RedirectResult(System.Web.Security.FormsAuthentication.LoginUrl + "?ReturnUrl=" +
  15. filterContext.HttpContext.Server.UrlEncode(filterContext.HttpContext.Request.RawUrl));
  16. }
  17. }
  18. else
  19. {
  20. //Code HERE for page level authorization
  21. }
  22. }
  23. }
STEP 10
Now, I just have to put [CheckAuthorization] attribute on top of my controller to access my CheckAuthorization Function which is Custom Authorize Attribute.

BONUS POINT
How to create a new hash & salt (which are needed to save in our database) for new user at registration time.

For create SALT & HASH (Which are saved in database)
  1. //Create a salt
  2. var salt = Get_SALT();
  3. //Create a hash
  4. var hash = Get_HASH_SHA512("Your Password", "Your UserName", salt);
STEP 11
Add authentication mode to your web.config.
Add the following code to your web.confing.
  1. <system.web >
  2. <authentication mode = "Forms" >
  3. <forms loginUrl = "~/Home/Login" timeout = "2880" / > //Declare Your Return URL Here. Mean If Login Fail Then Page Redirect This URL
  4. </authentication>
  5. </system.web>