Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Develop a custom authentication and authorization system in consistence with web form application

I am creating a new ASP.NET MVC 4 application (actually my first MVC application) that is a part of my previous ASP.NET web forms application. I have never used ASP.NET inbuilt authentication methods in any of my project. This new MVC 4 app will be published on a sub-domain of previous app. Login will be done from previous app. A return url should be provided from MVC app to return back to current page if not logged in. However, New User Registration, Account Recovery options are already developed in previous web forms application and I don't want to replicate them in my new MVC application.

A cookie token with token number will be issued from web form application on the event of successful login which will be shared to all domain like *.maindomain.com.

Now I want to merge my own token validation method with ASP.NET inbuilt methods so that I can make use of Authorize and other security related options in my new MVC application.

In my previous application I have developed my custom user validation system in following way.

First, I have following related SQL Server tables

enter image description here

and following classes

public class Token
{
    public static uint GenerateToken(string userEmail, string password, bool isPersistent)
    {
        // this static function generates a uint type unique token number
        // and put this in the cookie "token" using HttpContext.Current.Response object.
        // if isPersistent is set to true then cookie will be persistent otherwise not
        // if there is any problem in creating token then it will throw an Exception with proper message
        // Possible causes of not generating a token are
        // 1. Invalid useremail or password
        // 2. 'State' value in 'Member' table is 'EmailPending' or 'Suspended' (there is an enum for MemberState
    }

    public Token(uint tokenNo, bool validateImmediately = false)
    {
        // simply load token details with a few filed from member table from database
        // Call validate function if validateImmediately is set to true
        // Throws an exception if token does not exists in the database
    }

    public void Validate()
    {
        // Checks for everything like MemberState is Active and Token status is also Active and throws exception if anything wrong
        // and then check (LastAccessedOn.AddSeconds(TokenLife) < AppSettings.Now) is not true
        // Call UpdateStatus function with new token status and current page from HttpContext in comment parameter
    }

    public void UpdateStatus((TokenStatus newStatus, string comment = "")
    {
        // simply write both newStatus and Comment in Token table
        // and remove the token cookie if newStatus is not set to Active
    }

    public uint TokenNumber { get; private set; }
    public uint MemberNumber { get; private set; } // from Member table
    public string Name { get; private set; } // from Member table
    public MemberState MemberState { get; private set; } // from Member table
    public string MemberEmail { get; private set; } // from member table
    public uint BusinsessNo { get; private set; } // from Business table
    public DateTime CreatedOn { get; private set; }
    public DateTime LastAccessedOn { get; private set; }
    public uint TokenLife { get; private set; } // from member
    public string CreatedIP { get; private set; }
    public string LastIP { get; private set; }
    public bool IsPersistent { get; private set; }
    public TokenStatus Status { get; private set; }
    public string Comment { get; private set; }
    public static Token Current
    {
        get
        {
            if (_t == null)
                _t = new Token(uint.Parse(HttpContext.Current.Request.Cookies["token"].Value));
            return _t;
        }
    }
    private static Token _t;
}

public class Member
{
     // all member related operations like new member, send verification email and verify email
}

For logging out user I simply call UpdateStatus like (TokenSatus.Closed, "User logged out"). This method will take care of cookie removal.

Note: Member class has a property bool IsAdmin. You know why its for.

Please suggest me a best solution to develop authentication system according to my needs in MVC application. I am telling you again that options like New User, Account Recovery and Email Verification will be done in my previous ASP.NET web forms application. All I need to just put my Validate() method of Token class on right place in MVC application. I am really confused with several solution available on internet.

like image 942
shashwat Avatar asked Dec 20 '22 01:12

shashwat


1 Answers

If you hand-roll your own authentication, the security can only be the as strong as how you store Ticket in client side cookie securely.

Normally, you want to encrypt the auth ticket/token and access via SSL. As long as you store the cookie securely at client side, it should not be an issue.

I also would like to suggest to take a look at how ASP.Net creates Form Authentication Ticket.

Note: If you use ASP.Net Form Authentication Ticket you do not need to store ticket/token in database, because user will send the auth ticket to server on every page request.

var now = DateTime.UtcNow.ToLocalTime();

var ticket = new FormsAuthenticationTicket(
                1, /*version*/
                MemberID,
                now,
                now.Add(FormsAuthentication.Timeout),
                createPersistentCookie,
                TokenID, /*custom data*/
                FormsAuthentication.FormsCookiePath);

var encryptedTicket = FormsAuthentication.Encrypt(ticket);

var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encryptedTicket)
{
   HttpOnly = true,
   Secure = FormsAuthentication.RequireSSL,
   Path = FormsAuthentication.FormsCookiePath
};

if (ticket.IsPersistent)
{
   cookie.Expires = ticket.Expiration;
}
if (FormsAuthentication.CookieDomain != null)
{
   cookie.Domain = FormsAuthentication.CookieDomain;
}

_httpContext.Response.Cookies.Add(cookie);

How to create Principal Object

Once authenticated user is requested a page, you need to retrieve auth ticket from cookie, and create a Principal object.

// In Global.asax.cs
void Application_AuthenticateRequest(object sender, EventArgs e)
{
   HttpCookie decryptedCookie = 
      Context.Request.Cookies[FormsAuthentication.FormsCookieName];

   FormsAuthenticationTicket ticket = 
      FormsAuthentication.Decrypt(decryptedCookie.Value);

   var identity = new GenericIdentity(ticket.Name);
   var principal = new GenericPrincipal(identity, null);

   HttpContext.Current.User = principal;
   Thread.CurrentPrincipal =HttpContext.Current.User;
}

// In action method, how to check whether user is logged in 
if (User.Identity.IsAuthenticated)
{

}

Do I need to extend cookie expiration?

If you leave slidingExpiration as true (which is true by default), it will increase the expiration time automatically. (Read more on article)

like image 199
Win Avatar answered May 01 '23 21:05

Win