WebApiConfiguration Example with RequestHandlers?

Topics: Web Api
Sep 27, 2011 at 6:05 PM

In Preview 4, I have been using this:

HttpHostConfiguration.Create()
    .SetResourceFactory(new UnityFactory(_unityContainer))
    .AddRequestHandlers(c => c.Add(new BasicAuthenticationOperationHandler("request", _unityContainer.Resolve<IApiUserRepository>())));

What is the equivalent in Preview 5?  I have been trying something like this, but the RequestHandlers piece is not clicking...

_httpConfiguration = new WebApiConfiguration
                     {
                         CreateInstance = (serviceType, context, request) => _unityContainer.Resolve(serviceType),
                         EnableTestClient = true,
                         RequestHandlers =  ???
                     };

Thanks,

Noah



Sep 27, 2011 at 6:12 PM

Ah, I think I got it:

_httpConfiguration = new WebApiConfiguration
                     {
                         CreateInstance = (serviceType, context, request) => _unityContainer.Resolve(serviceType),
                         EnableTestClient = true,
                         RequestHandlers = (requestHandlers, serviceEndpoint, operationDescriptions) =>
                             requestHandlers.Add(new BasicAuthenticationOperationHandler("request", _unityContainer.Resolve<IApiUserRepository>()))
                     };

Nov 17, 2011 at 3:24 AM

Hi there nheldman, any chance you could share the inner workings of your "BasicAuthenticationOperationHandler" - going down this road and myself...

Thanks

Nov 26, 2011 at 9:05 PM

Sure Matthew.  Here is the BasicAuthenticationOperationHandler class.  The key here is that I have a placeholder attribute called "RequireBasicAuthentication" that has no code in it, but because the attribute is placed on methods or classes that require authentication, when this Handler kicks in, it only authenticates if the attribute is present.  It also references some other classes (for Base64 conversion), and the injected Repository (we use StructureMap), but those are pretty straightforward.

public class BasicAuthenticationOperationHandler : HttpOperationHandler<HttpRequestMessage, HttpRequestMessage>
    {
        private readonly IApiUserRepository _apiUserRepository;

        public BasicAuthenticationOperationHandler(string outputParameterName, IApiUserRepository apiUserRepository)
            : base(outputParameterName)
        {
            _apiUserRepository = apiUserRepository;
        }

        protected override HttpRequestMessage OnHandle(HttpRequestMessage request)
        {
            if (!Helpers.HasAttribute(typeof(RequireBasicAuthenticationAttribute)))
                return request;
           
            Authenticate(request.Headers);

            return request;
        }

        private void Authenticate(HttpRequestHeaders headers)
        {
            try
            {
                if (headers.Authorization == null || headers.Authorization.Scheme != "Basic")
                    throw new Exception("You must provide Basic Authorization credentials in the HTTP Header");

                var encoded = headers.Authorization.Parameter;
                var encoding = Encoding.GetEncoding("iso-8859-1");
                var userPass = encoding.GetString(Convert.FromBase64String(encoded));
                var sep = userPass.IndexOf(':');
                var username = userPass.Substring(0, sep);
                var password = userPass.Substring(sep + 1);

                if (!ValidateCredentials(username, password))
                    throw new Exception("Invalid Basic Authentication credentials");
            }
            catch (Exception ex)
            {
                // TODO: Log
                var response = new HttpResponseMessage(HttpStatusCode.Unauthorized) { Content = new StringContent(ex.Message) };
                throw new HttpResponseException(response);
            }
           
        }

        private bool ValidateCredentials(string username, string password)
        {
            // Retrieve password & salt from database
            var apiUser = _apiUserRepository.FindByUsername(username);

            // Create byte arrays for the salt & password
            var arraySalt = Convert.FromBase64String(apiUser.PasswordSalt);
            var arrayPassword = Encoding.Unicode.GetBytes(password);

            // Concat the salt & password byte arrays
            var arraySaltAndPassword = new byte[arraySalt.Length + arrayPassword.Length];
            Buffer.BlockCopy(arraySalt, 0, arraySaltAndPassword, 0, arraySalt.Length);
            Buffer.BlockCopy(arrayPassword, 0, arraySaltAndPassword, arraySalt.Length, arrayPassword.Length);

            // Hash password
            HashAlgorithm algorithm = new SHA256CryptoServiceProvider();
            var arrayHashedPassword = algorithm.ComputeHash(arraySaltAndPassword);

            // Convert to base 64 string
            var hashedPassword = Convert.ToBase64String(arrayHashedPassword);

            // Compare with database password
            return hashedPassword == apiUser.Password;
        }

        private static string CreateSalt()
        {
            // Generate a cryptographic random number using the cryptographic
            // service provider
            var rng = new RNGCryptoServiceProvider();
            var buff = new byte[32];
            rng.GetBytes(buff);
           
            // Return a Base64 string representation of the random number
            return Convert.ToBase64String(buff);
        }
    }