Thursday, February 7, 2008

Custom Security MembershipProvider & Login attempts (MaxInvalidPasswordAttempts)

The Problem : Have a limit on the amount of possible logins when using a Custom provider.

Let's say that you write your own MembershipProvider (quite common if you want to use your own object model ..)

   11   public class MyMemberShipProvider : System.Web.Security.MembershipProvider
   12     {

Like many other articles explain, the most important Method is ValidateUser.
What very people often "forget"to mention is that all other features are not present in your provider. For example, the MaxInvalidPasswordAttempts is a property that you can set it to any number but nothing is going to happen.

In a Nutshell : you need to implement it yourself.

What I am showning below is a quite simple version but it's proves the point.

  180     public override System.Boolean ValidateUser(System.String pUsername, System.String pPassword)
  181         {
  182             if (pUsername.Equals("demo") && pPassword.Equals("demo"))
  183             {
  184                 return LoginSuccess();
  185             }
  186             return LoginFailed();
  188         }

LoginFailed() will basically return false but also it would do the check if another attempt should be granted or not:

  211     private bool LoginFailed()
  212         {
  213             //WebFailureAuditEvent.Raise(WebEventCodes.AuditFormsAuthenticationFailure);
  214             LoginFailuresCount++;
  215             if (LoginFailuresCount >= this.MaxInvalidPasswordAttempts)
  216                 throw new Exception("OK, that's it. Bye Bye");
  218             return false;
  220         }

You need to keep track of how many login failures :

  197  protected int LoginFailuresCount
  198         {
  199             get
  200             {
  201                 if (HttpContext.Current.Session["Failures"] == null)
  202                     return 0;
  203                 return (int)HttpContext.Current.Session["Failures"];
  204             }
  205             set
  206             {
  207                 HttpContext.Current.Session["Failures"] = value;
  208             }
  209         }

In the commented line where I was trying to Raise the "WebFailureAuditEvent", I read somewhere that I was a good idea to raise it but could not find any reference or example. If anybody knows how to do it --> please let me know.

Note: An improvement to this model would be to support "PasswordAttemptWindow" which in this case I did not implement.

Note: New Browser == new Session so it's not really state-of-the-art security technique it's a basic