Click here to Skip to main content
15,867,308 members
Articles / Web Development / HTML

Login Functionality in MVC

Rate me:
Please Sign up or sign in to vote.
4.88/5 (29 votes)
15 Jul 2016CPOL6 min read 139.5K   4.9K   37   23
Custom login functionality in MVC using form authentication

Introduction

Let’s first establish what the purpose of code is in the first place.

For this article, the purpose of code is to create Login & Logout Functionality in MVC using Form Authentication. We also discuss about the best way to store the password in database using HASHING.

Background

The most important question is how password are protected. If you storing the password in a plain-text or using encryption/decrypation (2-way) then it is a horrible idea. If you store the password in ecryption format then its 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 Password, One Unique Field (i.e. username or mobile or email) and SALT Key using SHA512 Algorithm (bcrypt/PBKDF2/scrypt are also best alorithms for hashing) and also create a unique SALT Key using CSPRNG. Then we store HASH Value & SALT Key in database.

We dont need to know the password we just verify the inputed password. So, When the user attempts to login, we create a 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 they entered invalid login credentials.

Using the Code

First of all we need to create a database & datatable which contains users` information. HERE We start the CODE.

STEP 01: Create A Database with Name "DemoLoginFunctionality".

SQL
Script:

CREATE DATABASE DemoLoginFunctionality;

Below Script Is use to create a Datatable with Data Entries

SQL
USE [DemoLoginFunctionality]
GO
/****** Object:  Table [dbo].[UserMaster]    Script Date: 15-07-2016 11:30:49 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[UserMaster](
    [UserID] [bigint] IDENTITY(1,1) NOT NULL,
    [Username] [nvarchar](50) NOT NULL,
    [HASH] [nvarchar](max) NOT NULL,
    [SALT] [varbinary](512) NOT NULL,
 CONSTRAINT [PK_UserMaster] PRIMARY KEY CLUSTERED
(
    [UserID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

GO
SET ANSI_PADDING OFF
GO
SET IDENTITY_INSERT [dbo].[UserMaster] ON

GO
INSERT [dbo].[UserMaster] ([UserID], [Username], [HASH], [SALT]) VALUES (1, N'suchit', N'duD96EJIW3AhCKE9vcl8aev1M2yULlLS4dHqqyFISrpvAdjNvNlVjKHC2Xsi4wsDyHRWa/wxeGmNqd37nQdgS+IhX/AsWEpPklL2LlBMFxsXiviznHWOMb/OjcTDWhM6', 0xE2215FF02C584A4F9252F62E504C171B178AF8B39C758E31BFCE8DC4C35A133A)
GO
SET IDENTITY_INSERT [dbo].[UserMaster] OFF
GO

NOTE: We save the password hashing & salt in database.

--- At the time of Registration (Generate HASH Value & SALT Key) ---

1) First we need to Create A SALT Key. We use CSPRNG (cryptographically secure pseudo-random number generator) for create 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 is uses mathematical formulas to produce sequences of random numbers.

#region --> Generate SALT Key
    
private static byte[] Get_SALT()
{
      return Get_SALT(saltLengthLimit);
}

private static byte[] Get_SALT(int maximumSaltLength)
{
     var salt = new byte[maximumSaltLength];

      //Require NameSpace: using System.Security.Cryptography;
      using (var random = new RNGCryptoServiceProvider())
      {
           random.GetNonZeroBytes(salt);
      }

     return salt;
}

#endregion

2) Now, second step is to create a HASH Value. we use SHA512 For Create a HASH Value.

What is password hasing?

Hashing performs a one-way transformation on a password, turning the password into another String, called the hashed password. “One-way” means that 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.
hashSHA512("hello") = ekFc4y+0WZ/jHte2PNzvSLC/WLKel86uRb71r6JliSrgjEkKeFQkTL2Ltix+1mcybYsJZuaAp7x7dMUi+1sQCmzR/JoMziQU/XKv0YK92oYMywLb9QJYNYiZDITZNlNu

hashSHA512("hollo") = HntRl+7m3KQ6fDNhqGPYDz3tCAan9gtVL3ad/Z6Qm5aQ3bzDUXUBy+mfgmSVmzfQBYV+3K8A9pWwcRFJxpNYwwVKsB4HudvUsCGPE0JiDx+nX9ot/UU7fn5V1jOTos1g

hashSHA512("helle") = UXaSsPL0G0sY/jqccVMrjazoFgpeDihMu5xwSo3AYWqpHuozHee9zfRviHL1SF+6E/0nU/aV/rM+LLjbTUfBpNI4nPKFwCTmRYmvMrT2npHHsBaIO/w8cpIsJ7y5WiNv

What is SHA512?

The Secure Hash Algorithm (SHA512) is a set of cryptographic hash functions designed by the National Security Agency (NSA).

//CODE: Generate HASH Using SHA512
public static string Get_HASH_SHA512(string password, string username, byte[] salt)
{
            try
            {
                //required NameSpace: using System.Text;
                //Plain Text in Byte
                byte[] plainTextBytes = Encoding.UTF8.GetBytes(password + username);

                //Plain Text + SALT Key in Byte
                byte[] plainTextWithSaltBytes = new byte[plainTextBytes.Length + salt.Length];

                for (int i = 0; i < plainTextBytes.Length; i++)
                {
                    plainTextWithSaltBytes[i] = plainTextBytes[i];
                }

                for (int i = 0; i < salt.Length; i++)
                {
                    plainTextWithSaltBytes[plainTextBytes.Length + i] = salt[i];
                }

                HashAlgorithm hash = new SHA512Managed();
                byte[] hashBytes = hash.ComputeHash(plainTextWithSaltBytes);
                byte[] hashWithSaltBytes = new byte[hashBytes.Length + salt.Length];

                for (int i = 0; i < hashBytes.Length; i++)
                {
                    hashWithSaltBytes[i] = hashBytes[i];
                }

                for (int i = 0; i < salt.Length; i++)
                {
                    hashWithSaltBytes[hashBytes.Length + i] = salt[i];
                }

                return Convert.ToBase64String(hashWithSaltBytes);
            }
            catch
            {
                return string.Empty;
            }
}

Now, Save the HASH Value & SALT Key in database.

STEP 02: Create New MVC Application Project.

1) On the File menu, click New Project.

Image 1

2) In the New Project dialog box under Project types, expand Visual C#, and then click Web and In the Name box, type "LoginFunctionalityMVC" then 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 which is stay on center of the right side.

Image 2

Image 3

STEP 03: 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 found the solution explorer.

Image 4

2) In solution explorer right click on "Models" Folder. Then Click on the "Add" then click on the "New Item..."

Image 5

3) Now click on the Visual C# and Select ADO.NET Entity Data Model, Name it "DBModel" and click on Ok

Image 6

4) Select EF Designer from Database and click on "Next"

Image 7

5) Now click on new connection, then define "server name", select authentication mode either Windows or SQL Server if SQL server then enter username or password. And finally, select database "DemoLoginFunctionality" under Connect to a Database then click on OK

Image 8

Image 9

6) Now Declare a name of connection string as "DBEntities" under Save connection setting in Web.config as: then click on next

Image 10

7) select version of entity framework. select "Entity Framework 6.x" then click on next

Image 11


8) Expand "Table" then exand "dbo" and then select our datatable "UserMaster". now, Give the name space as "Models" under Model Namespace: and then click on OK

Image 12

9) Now Buid Your Project by pressing CLTR + B for updating every entities perfectly.

STEP 04: Add a new empty controller

1) To add a controller. Right click on "controller folder" select "Add" the click on "controller".

2) Now, In Add Scaffold Dialog box select "MVC 5 Controller - Empty" then Click on Add and name it as "HomeController" and click on Add

Create A New ActionResult method name as 'Login'.

C#
[HttpGet]
public ActionResult Login(string returnURL)
{
    var userinfo = new LoginVM();

    try
    {
        // We do not want to use any existing identity information
        EnsureLoggedOut();

        // Store the originating URL so we can attach it to a form field
        userinfo.ReturnURL = returnURL;

        return View(userinfo);
    }
    catch
    {
        throw;
    }
}
C#
//Create A New ViewModel Name AS "LoginVM" into Models Folders & Declare Below Properties.

public class LoginVM
    {
        public string Username { get; set; }

        public string Password { get; set; }

        public string ReturnURL { get; set; }

        public bool isRemember { get; set; }

    }

STEP 05: Add a View.

1) For this, Right click on your "Login" action method and then select a "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 a Email, Password textbox, Checkbox (for Remember Me Option) & submit button in form tag on Index.cshtml (view)

HTML
//Paste below code in your view
@model LoginFunctionalityMVC.Models.LoginVM

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Login</title>
</head>
<body>
    @using (Html.BeginForm("Login", "Home", FormMethod.Post))
    {
        @Html.AntiForgeryToken()
        @Html.HiddenFor(s => s.ReturnURL)

        <h1>Login Functionality In MVC</h1>
        <div>

            @if (TempData["ErrorMSG"] != null)
            {
                <label style="color:maroon;"> @TempData["ErrorMSG"] </label>
                <br /><br />
            }

            @Html.TextBoxFor(s => s.Username, new { @placeholder = "Username" })
            <br /> <br />
            @Html.PasswordFor(s => s.Password, new { @placeholder = "Password" })
            <br /> <br />
            @Html.CheckBoxFor(s => s.isRemember) Remember ME
            <br /> <br />
            <button type="submit">Login</button>
        </div>
    }
</body>
</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 06: When login page initialization, at that time we need to check that current session is logout. so fist we logout the existing user.

For this, we create a two methods as "EnsureLoggedOut" and "Logout"

C#
//GET: EnsureLoggedOut
private void EnsureLoggedOut()
{
    // If the request is (still) marked as authenticated we send the user to the logout action
    if (Request.IsAuthenticated)
        Logout();
}

//POST: Logout
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Logout()
{
    try
    {
        // First we clean the authentication ticket like always
        //required NameSpace: using System.Web.Security;
        FormsAuthentication.SignOut();

        // Second we clear the principal to ensure the user does not retain any authentication
        //required NameSpace: using System.Security.Principal;
        HttpContext.User = new GenericPrincipal(new GenericIdentity(string.Empty), null);

        Session.Clear();
        System.Web.HttpContext.Current.Session.RemoveAll();

        // Last we redirect to a controller/action that requires authentication to ensure a redirect takes place
        // this clears the Request.IsAuthenticated flag since this triggers a new request
        return RedirectToLocal();
    }
    catch
    {
        throw;
    }
}

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 07: We Create a one method as "SignInRemember" for Set Authentication in Cookie (Remeber ME Option) & one method for redirect to page. name as "RedirectToLocal"

C#
//GET: SignInAsync
private void SignInRemember(string userName, bool isPersistent = false)
{
    // Clear any lingering authencation data
    FormsAuthentication.SignOut();

    // Write the authentication cookie
    FormsAuthentication.SetAuthCookie(userName, isPersistent);
}
C#
//GET: RedirectToLocal
private ActionResult RedirectToLocal(string returnURL = "")
{
    try
    {
        // If the return url starts with a slash "/" we assume it belongs to our site
        // so we will redirect to this "action"
        if (!string.IsNullOrWhiteSpace(returnURL) && Url.IsLocalUrl(returnURL))
            return Redirect(returnURL);

        // If we cannot verify if the url is local to our host we redirect to a default location
        return RedirectToAction("Index", "Dashboard");
    }
    catch
    {
        throw;
    }
}

STEP 08: Now, all pre-required methods are done.. Finally we move to create a one method for validate username and password.

C#
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginVM entity)
{
    string OldHASHValue = string.Empty;
    byte[] SALT = new byte[saltLengthLimit];

    try
    {
        using (db = new DBEntities())
        {
            // Ensure we have a valid viewModel to work with
            if (!ModelState.IsValid)
                return View(entity);

            //Retrive Stored HASH Value From Database According To Username (one unique field)
            var userInfo = db.UserMasters.Where(s => s.Username == entity.Username.Trim()).FirstOrDefault();

            //Assign HASH Value
            if (userInfo != null)
            {
                OldHASHValue = userInfo.HASH;
                SALT = userInfo.SALT;
            }

            bool isLogin = CompareHashValue(entity.Password, entity.Username, OldHASHValue, SALT);

            if (isLogin)
            {
                //Login Success
                //For Set Authentication in Cookie (Remeber ME Option)
                SignInRemember(entity.Username, entity.isRemember);

                //Set A Unique ID in session
                Session["UserID"] = userInfo.UserID;

                // If we got this far, something failed, redisplay form
                // return RedirectToAction("Index", "Dashboard");
                return RedirectToLocal(entity.ReturnURL);
            }
            else
            {
                //Login Fail
                TempData["ErrorMSG"] = "Access Denied! Wrong Credential";
                return View(entity);
            }
        }
    }
    catch
    {
        throw;
    }

}
C#
public static bool CompareHashValue(string password, string username, string OldHASHValue, byte[] SALT)
{
    try
    {
        string expectedHashString = Get_HASH_SHA512(password, username, SALT);

        return (OldHASHValue == expectedHashString);
    }
    catch
    {
        return false;
    }
}

STEP 09: Now, the last stage is to create a Custom Authorize Attribute In MVC. Because it check that user is login or not before any page initialize.

Create a class name as "CheckAuthorization.cs" in "Models Folder"

public class CheckAuthorization : AuthorizeAttribute
   {
       public override void OnAuthorization(AuthorizationContext filterContext)
       {
           if (HttpContext.Current.Session["UserID"] == null || !HttpContext.Current.Request.IsAuthenticated)
           {
               if (filterContext.HttpContext.Request.IsAjaxRequest())
               {
                   filterContext.HttpContext.Response.StatusCode = 302; //Found Redirection to another page. Here- login page. Check Layout ajaxError() script.
                   filterContext.HttpContext.Response.End();
               }
               else
               {
                   filterContext.Result = new RedirectResult(System.Web.Security.FormsAuthentication.LoginUrl + "?ReturnUrl=" +
                        filterContext.HttpContext.Server.UrlEncode(filterContext.HttpContext.Request.RawUrl));
               }
           }
           else
           {

               //Code HERE for page level authorization

           }
       }
   }

STEP 10: Now, just I have to put [CheckAuthorization] attribute on top of my controller to access my CheckAuthorization Function which is Custom Authorize Attribute.

STEP 11: Add authentication mode to Your Web.config

//Add Below Code To Your Web.confing

<system.web >  
    <authentication mode = "Forms" >  
    <forms loginUrl = "~/Home/Login" timeout = "2880" / > //Declare Your Return URL Here. Mean If Login Fail Then Page Redirect This URL  
    </authentication>
</system.web>

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



Comments and Discussions

 
QuestionProvide the Password Pin
SDZafar Ali8-Dec-19 18:04
SDZafar Ali8-Dec-19 18:04 
QuestionRequire SALT and Hash generator code for the same Pin
abhimestry19-Sep-18 19:37
abhimestry19-Sep-18 19:37 
QuestionUsername & password PinPopular
abhimestry18-Sep-18 23:34
abhimestry18-Sep-18 23:34 
QuestionPerfect! Pin
Abdul Muqeet Khan20-Aug-18 11:00
Abdul Muqeet Khan20-Aug-18 11:00 
Questionhi thank u Pin
shervinmina17-Jan-18 3:54
shervinmina17-Jan-18 3:54 
Questionlogin credentials Pin
satyam gupta21-Mar-17 2:22
satyam gupta21-Mar-17 2:22 
QuestionVery best article + question Pin
Maurizio Felicella24-Aug-16 5:59
Maurizio Felicella24-Aug-16 5:59 
GeneralMy vote of 5 Pin
Dan Randolph9-Aug-16 18:20
Dan Randolph9-Aug-16 18:20 
GeneralRe: My vote of 5 Pin
Khunt Suchit9-Aug-16 19:30
professionalKhunt Suchit9-Aug-16 19:30 
PraiseNice work - To the point and clear Pin
ranadivyap@yahoo.co.in22-Jul-16 0:56
ranadivyap@yahoo.co.in22-Jul-16 0:56 
GeneralRe: Nice work - To the point and clear Pin
Khunt Suchit22-Jul-16 4:11
professionalKhunt Suchit22-Jul-16 4:11 
QuestionNice article Pin
t.alkahtiri18-Jul-16 23:35
t.alkahtiri18-Jul-16 23:35 
Thank you very much for this nice article
the application is running good but what is the password that entered in the table
and also could you please do part 2 of this article showing how to add user and password then save the info in the database from the application.
Again thank you very much, and best regards
AnswerRe: Nice article Pin
Khunt Suchit18-Jul-16 23:42
professionalKhunt Suchit18-Jul-16 23:42 
PraiseRe: Nice article Pin
t.alkahtiri18-Jul-16 23:56
t.alkahtiri18-Jul-16 23:56 
GeneralAwesome Post Pin
Sachin Makwana18-Jul-16 20:36
professionalSachin Makwana18-Jul-16 20:36 
GeneralRe: Awesome Post Pin
Khunt Suchit18-Jul-16 20:39
professionalKhunt Suchit18-Jul-16 20:39 
GeneralMy vote of 5 Pin
Carl Edwards In SA18-Jul-16 7:47
professionalCarl Edwards In SA18-Jul-16 7:47 
GeneralRe: My vote of 5 Pin
Khunt Suchit18-Jul-16 8:13
professionalKhunt Suchit18-Jul-16 8:13 
PraiseWritten very nicely ! Pin
DalalV18-Jul-16 0:24
DalalV18-Jul-16 0:24 
GeneralRe: Written very nicely ! Pin
Khunt Suchit29-Jul-16 3:37
professionalKhunt Suchit29-Jul-16 3:37 
QuestionVery nice article, thank you Pin
PHUNG TIEN TRIEU15-Jul-16 17:15
PHUNG TIEN TRIEU15-Jul-16 17:15 
AnswerRe: Very nice article, thank you Pin
Khunt Suchit15-Jul-16 17:51
professionalKhunt Suchit15-Jul-16 17:51 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.