Search code examples
c#asp.netvisual-studiomembership-providerasp.net-4.5

Where should I define a MembershipValidatePasswordEventHandler in a legacy ASP.NET Website Project?


I am working on a legacy ASP.NET website project that utilizes a custom MembershipProvider. I want to take advantage of the existing functionality that this provides to implement password validation. I want to ensure that all future passwords are of a certain length and contain a certain number of uppercase, lowercase, and non-alphanumeric characters.

What I have done so far is register a MembershipValidatePasswordEventHandler in the page_load function of the default page of the website based on the example from the docs, here. I currently have the event handler set up to reject everything just as a proof of concept, and it works. Despite that, it seems like an odd place to register the event handler considering there are multiple pages in the site, and the default page doesn't have anything to do with password creation/management.

public partial class _Default : System.Web.UI.Page
{
     protected void Page_Load(object sender, EventArgs e)
     {
          Membership.ValidatingPassword += new MembershipValidatePasswordEventHandler(ValidatePasswordEventHandler);
     }
     ...
     public void ValidatePasswordEventHandler(object sender, ValidatePasswordEventArgs e)
     {
          e.Cancel = true;
     }
}

I have considered registering the handler in the user creation or updating pages, but that still seems inappropriate. I would like to know if there is a more appropriate place to do this before I begin implementing the actual password checks, or is this the standard place to do this?

For additional context on the broader problem, I have also tried modifying the membership provider in the web.config file to get part of this functionality based on this previous answer, but when I provide an invalid password (smaller than 7 characters) it isn't rejected.

<membership defaultProvider="MyMembershipProvider" userIsOnlineTimeWindow="15">
  <providers>
    <add name="MyMembershipProvider" type="MyNamespace.Membership.MyMembershipProvider" applicationName="MyApp" connectionStringName="MyConnectionString" enablePasswordRetrieval="true" enablePasswordReset="true" requiresQuestionAndAnswer="true" writeExceptionsToEventLog="false" passwordFormat="Clear" maxInvalidPasswordAttempts="15" passwordAttemptWindow="10" minRequiredPasswordLength="7"/>
  </providers>
</membership>

Update: This question may also provide some additional justification for why I am interested in the is event handler. The top answer links to this question. The top answer for this question mentions doing what I have done here, but says it might be done "in a higher scope" this is more getting at my question. What would a higher/highest scope be in the context of a website project?

Solution: I ended up modifying my ChangePassword web control's NewPasswordRegularExpression field with the following regex: ^(?=.[a-z])(?=.[A-Z])(?=.\d)(?=.[^\da-zA-Z]).{8,}$, instead of implementing a custom event handler.


Solution

  • Hum, ok. Well, the logon page of course is specified in web.config.

    and on that logon page, you most likely used a asp.net "logon" control. so that logon control will have the membership provider - say like this:

    <asp:Login ID="Login1" runat="server"  Width="675px" Height="177px" 
     MembershipProvider="MySqlProvider" UserNameLabelText="Username:" 
     FailureText="Login attempt was not successful. Please try again." Font-Size="Small" 
     >
    
      <LayoutTemplate>
      <div style="margin-left:10px">
                <h>Please enter your Username and Password</h>
                <br />
    

    So, some place in your application - a membership provider was created, and will (should) exist in the applcation. So the above logon control will "consume" and use that memberships provider class. And in that class, there will be a routine "somthing" like this:

    Public Overrides Function ValidateUser(username As String, password As String) As Boolean
    
        Dim rst As DataTable
    
        Dim strSQL As String =
                 "SELECT * from dbo_ContactName WHERE Email = @user AND PasswordHash = @pass " &
                 "AND IsWebActive = 1"
    
        Dim cmdSQL As New SqlCommand(strSQL)
        cmdSQL.Parameters.Add("@Email", SqlDbType.NVarChar).Value = username
        cmdSQL.Parameters.Add("@pass", SqlDbType.NVarChar).Value = password
    
        rst = MyRstP(cmdSQL)
    

    So, the logon page has a logon control - but it WILL wind up calling the membership provider "ValidateUser" as per above.

    And so while it don't seem like there is "any" connection, or specifying of how the custom provider calls the above validate user? Well, the markup in the logon control specified that membership provider - and that's how the two are connected.

    In your logon page (the page with the asp.net logon control), if everything goes well, then this event fires:

    Protected Sub Login1_LoggedIn(sender As Object, e As EventArgs) Handles Login1.LoggedIn
    
        ' this event raises AFTER the user is authenticated
        '
        ' check if user is first time logon
        ' this means that user id = email address
    
         etc. etc. - do whatever you want for custom logon code.
    

    I'm not sure if the above helps, but it at least shows how the logon control and the membership provider are connected to each other. Now, it is possible that the standard asp.net logon control was never used - and that being the case, then the event model for the logon control thus would not have been used. Displaying the property sheet for the log on control, we see this:

    enter image description here

    So the logon control has a set of events - but the actual validation of the user occurs inside of the custom sqlmemberShipProivder class that I'm assuming was used here. That class thus should have a whole bunch of things like GetUser, ValidateUser, ChangePassword. The whole idea here is WHEN that provider is written correctly, then use of logon control, change password control etc will all JUST work for you, without having to build up the logon control(s) provided in the toolbox for this process.

    So, in that custom class (sqlMemberShipProivder), there is this: (and many more)

    enter image description here

    I don't see a Validate Password user - but there is a ValidateUser event.