Peter Munnings
Peter Munnings

Reputation: 3329

What is the best way to do Custom Username Password Authentication in WCF?

I have the following code (based on numerous examples on the web)

public class UserNameValidator : UserNamePasswordValidator
{
    /// <summary>
    /// Validates the user name and password combination.
    /// </summary>
    /// <param name="userName">The user name.</param>
    /// <param name="password">The password.</param>
    public override void Validate(string userName, string password)
    {
        // validate arguments
        if (string.IsNullOrEmpty(userName))
            throw new ArgumentNullException("userName");
        if (string.IsNullOrEmpty(password))
            throw new ArgumentNullException("password");

        UserCredential user = InMemoryUserStore.Get(userName);
        if (user == null)
        {
            using (DataAccessAdapter da = new DataAccessAdapter())
            {
                LinqMetaData db = new LinqMetaData(da);
                var newUserCredential = (from u in db.User
                                         where u.Username == userName
                                         select new UserCredential
                                         {
                                             UserName = u.Username,
                                             PasswordHash = u.PasswordHash,
                                             PasswordSalt = u.PasswordSalt
                                         }).FirstOrDefault();
                if (newUserCredential == null)
                {
                    throw new SecurityTokenException("Unknown username or password");
                }
                else
                {
                    InMemoryUserStore.Add(newUserCredential);
                    user = newUserCredential;
                }
            }
        }

        //Validate Password
        PasswordHash p = new PasswordHash(user.PasswordSalt, user.PasswordHash);
        if (!p.Verify(password))
        {
            throw new SecurityTokenException("Unknown username or password");
        }
    }
}

Is this the best way of doing it?

Upvotes: 1

Views: 550

Answers (1)

Peter Munnings
Peter Munnings

Reputation: 3329

Seeing as the Custom Validator is only called once, the InMemoryStore is not needed. The code below is what we are using, and it is working great in production.

    public override void Validate(string userName, string password)
    {
        // validate arguments
        if (string.IsNullOrEmpty(userName))
            throw new ArgumentNullException("userName");
        if (string.IsNullOrEmpty(password))
            throw new ArgumentNullException("password");

        using (DataAccessAdapter da = new DataAccessAdapter())
        {
            LinqMetaData db = new LinqMetaData(da);
            var userCredential = (from u in db.User
                                  where u.Username == userName
                                  select new UserCredential
                                  {
                                      UserName = u.Username,
                                      PasswordHash = u.PasswordHash,
                                      PasswordSalt = u.PasswordSalt
                                  }).FirstOrDefault();
            if (userCredential == null)
            {
                throw new SecurityTokenException("Unknown username or password");
            }

            //Validate Password
            PasswordHash p = new PasswordHash(userCredential.PasswordSalt, userCredential.PasswordHash);
            if (!p.Verify(password))
            {
                throw new SecurityTokenException("Unknown username or password");
            }
        }
    }

Once the call is authenticated, you can create a Custom Principal using the following:

public bool Evaluate(EvaluationContext evaluationContext, ref object state)
    {
        // get the authenticated client identity
        IIdentity client = GetClientIdentity(evaluationContext);            

        // add roles etc
        ....

        evaluationContext.Properties["Principal"] = new CustomPrincipal(client, roles.ToArray(), userId, email, client.Name);

        return true;
    }

Upvotes: 1

Related Questions