SharePoint, Forms Based Authentication (FBA), and viewing the Password Answer 

Tags:

Email based communications can often be problematic and a source of frustration for users. With the increased use of spam blockers both at the mail server level and email client level important communications, such as password changes, can often be blocked. One way around this is to use a combination of Question and Answer to provide verification of your user without relying on an email based workflow for password retrieval. The ASP.NET Membership Provider, specifically the SQL Membership Provider, provides a baseline set of functionality and controls for managing and storage of secret question and answer combinations within Forms Based Authenticated SharePoint (and ASP.NET for that matter).

Several settings in the membership provider section of the web.config drive some of the behavior for password retrieval. Three specifically are of concern to our scenario:

  • enablePasswordReset - Controls whether or not the password can be reset. Used in conjunction with requiresQuestionAndAnswer
  • requiresQuestionAndAnswer - Controls whether or not a question and and answer are required
  • passwordFormat - Specifies the password format including clear, encrypted, hashed.

In addition to not requiring email notifications following a successful challenge response our client requested the ability for a user to view their password answer. Unfortunately neither the Membership nor the MembershipUser classes provide an interface to be able to retrieve the password answer. To add some additional complications the password answer is stored in the same format as the password itself. If the passwordFormat setting is set to Hashed you will be unable to retrieve the answer but if it is set to either clear or encrypted you will be able to view the password answer. Considering that the password answer is just as important as the password itself its no wonder that the answer is protected in the same manner as the password. I recommend using encrypted and for my sample I assume that the password format is set to encrypted.

The key to our solution is decrypting the password answer and fortunately we have access to the MembershipProvider base class that has a protected method DecryptPassword that provides the desired functionality. Don't let the method name fool you though. It's actually a generic function that can decrypt the password answer as well. Unfortunately in order to gain access to this method we have to implement the SqlMembershipProvider class. Lastly we'll need to query the Membership SQL database directly to get the PasswordAnswer field back from the database.

 

public class CustomMembership : SqlMembershipProvider 
{        
    public string GetUserSecretAnswer(Guid providerKey)
    {
        string answer = string.Empty;
    
        MembershipUser user = Membership.GetUser(providerKey);
    
        string connectionString = ConfigurationManager.ConnectionStrings["CredentialStore"].ToString();
        SqlConnection connection = new SqlConnection(connectionString);
        connection.Open();
    
        SqlCommand command = new SqlCommand("Select CAST(PasswordAnswer as nvarchar(128)) from aspnet_membership where UserID=@UserID", connection);
        command.CommandType = CommandType.Text;
    
        SqlParameter param = new SqlParameter("@UserID", user.ProviderUserKey);
        command.Parameters.Add(param);
    
        SqlDataReader reader = command.ExecuteReader();
        string encryptedAnswer = string.Empty;
    
        if (reader.Read())
        {
           encryptedAnswer = reader.GetString(0);
        }
    
        byte[] encodedBytes= Convert.FromBase64String(encryptedAnswer);
        byte[] decodedBytes = this.DecryptPassword(encodedBytes);
    
        answer = System.Text.Encoding.Unicode.GetString(decodedBytes, 0x10, decodedBytes.Length - 0x10);
           
        return answer;
    }
}

 

You may want to put some checks in for your password format types to make the function more universal and additionally retrieve the connection string from the membership provider configuration section as opposed to a hardcoded connection string key. Lastly this solution isn't universal for just SharePoint but any ASP.NET application that is making use of the membership provider plumbing.

 
 
 
Posted by Joshua D. Carlisle on 28-May-08
0 Comments  |  Trackback Url  |  Link to this post | Bookmark this post with:        
 

Links to this post

Comments

Name:
URL:
Email:
Comments: