Authentication Token Service for WCF Services (Part 6 – A JavaScript client)

Drum roll please . . . This is the moment you’ve all been waiting for. The JavaScript client has finally arrived. In the past articles we have taken control of Authentication in WCF. The token authentication service was designed specifically for ReST like WCF services to be used by modern web and mobile apps. For modern web, that means the Basic Token Service for WCF Services has to work with JavaScript! Of course, it does. That is what it was designed for.

As for the WCF Services, I made a few improvements and fixed some bugs. I am not going to go over those changes. Just know it is a better example than what was delivered in part 6, but not much different.

Download this project here: WCF BTS JS Client

OK. So here is my little html and javascript example. I created a single html file, mostly. I added jquery and knockoutjs from NuGet. The rest is all in the TestPage/Index.html. Really, all you need to know is that there are three buttons. One to test authentication, one to test using the token for calling the test service, and one for using Basic Authentication instead of the token to call the test service.

Here is an image of the page rendered in a browser.

AuthenticationTokenService html and JavaScript

Here is the source code.

<!DOCTYPE html>
<html>
<head>
    <title>JavaScript Client</title>
    <meta charset="utf-8" />
    <script type="text/javascript" src="/Scripts/jquery-2.1.4.js"></script>
    <script type="text/javascript" src="/Scripts/knockout-3.4.0.debug.js"></script>
    <script type="text/javascript">
        var ViewModel = function () {
            var _vm = this;
            _vm.user = ko.observable();
            _vm.password = ko.observable();
            _vm.basicAuth = ko.computed(function () {
                return "Basic " + btoa(_vm.user() + ":" + _vm.password());
            });
            // I am just sticking the token in a local variable,
            // but you might want to save it in a cookie.
            _vm.token = ko.observable();
            _vm.getResponse = ko.observable();
            _vm.postResponse = ko.observable();
            _vm.onAuthClick = function () {
                $.ajax({
                    method: "POST",
                    url: "/Services/AuthenticationTokenService.svc/Authenticate",
                    contentType: "application/json",
                    context: document.body,
                    data: JSON.stringify({
                        User: _vm.user(),
                        Password: _vm.password()
                    }),
                    success: function (data) {
                        _vm.token(data);
                    },
                    failure: function (err) { alert(err.responseText); },
                    error: function (err) { alert(err.responseText); }
                });
            };
            _vm.onTestGetWithTokenClick = function () {
                $.ajax({
                    url: "/Services/Test1Service.svc/TestGet",
                    contentType: "application/json",
                    context: document.body,
                    beforeSend: function (request) { request.setRequestHeader("Token", _vm.token()); },
                    success: function (data) {
                        _vm.getResponse(data);
                    },
                    failure: function (err) { alert(err.responseText); },
                    error: function (err) { alert(err.responseText); }
                });
            };
            _vm.onTestPostWithBasicAuthClick = function () {
                $.ajax({
                    method: "POST",
                    url: "/Services/Test1Service.svc/TestPost",
                    contentType: "application/json",
                    context: document.body,
                    beforeSend: function (request) { request.setRequestHeader("Authorization", _vm.basicAuth()); },
                    success: function (data) {
                        _vm.postResponse(data);
                    },
                    failure: function (err) { alert(err.responseText); },
                    error: function (err) { alert(err.responseText); }
                });
            };
        };
        $(function () {
            ko.applyBindings(new ViewModel());
        });
    </script>
</head>
<body>
    <div>
        <input type="text" data-bind="value: user" placeholder="Enter your username here . . ." />
        <input type="password" data-bind="value: password" placeholder="Enter your password here . . ." />
        <input type="button" value="Authenticate" data-bind="click: onAuthClick" />
    </div>
    <p>Token: <span data-bind="text: token"></span></p>
    <input type="button" value="Test Get w/ Token" data-bind="click: onTestGetWithTokenClick" />
    <p>Test Get Response: <span data-bind="text: getResponse"></span></p>
    <input type="button" value="Test Post w/ Basic Auth" data-bind="click: onTestPostWithBasicAuthClick" />
    <p>Test Post Response: <span data-bind="text: postResponse"></span></p>
</body>
</html>

Authentication Token Service for WCF Services (Part 5 – Adding SSL)

In the previous article, Basic Token Service for WCF Services (Part 4 – Supporting Basic Authentication), we implemented Basic Authentication. And in the articles before that, our credentials were in the body of the http request. That means we have a huge security issue. Credentials are passing as clear text. This is very, very, very (insert a few thousand more very’s) bad. We need to enabled SSL.

I am going to assume that you know how to do this in production on IIS. I am going to show you how to do this in your development environment.

See this project on GitHub here: https://github.com/rhyous/Auth.TokenService

Setting Up Visual Studio for SSL

First, let’s get this working in your project. Visual Studio uses needs to launch your project in IIS Express as an SSL site.

  1. In Visual Studio, highlight your project in Solution Explorer.
  2. Press F4 to get the project properties.
  3. Set SSL to true. Notice an SSL url is created on a new port.
ProjectProperties

Setting Up Web Services for SSL

The web.config is where the WCF endpoints are configured. They are currently configured only for HTTP and not HTTPS. So let’s make some web.config edits.

  1. Add an Binding configuration with the security mode set to Transport.
  2. So set the clientCredentialType to none. Remember, we are not using IIS to handle authentication, but instead, we are handling authentication in the service.
  3. use webHttpBinding because We are using JSON and ReST-like (not full ReST) WCF services.
  4. Configure the endpoints to use the newly created Binding configuration.

Here is the complete web.config. The changed or added lines are highlighted.
Changed: Lines 17, 20
Added: Lines 47-55

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
  </configSections>
  <appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
  </appSettings>
  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" />
  </system.web>
  <system.serviceModel>
    <services>
      <service name="WcfSimpleTokenExample.Services.AuthenticationTokenService" behaviorConfiguration="ServiceBehaviorHttp">
        <endpoint address="" behaviorConfiguration="AjaxEnabledBehavior" binding="webHttpBinding" bindingConfiguration="webBindingSSL" contract="WcfSimpleTokenExample.Services.AuthenticationTokenService" />
      </service>
      <service name="WcfSimpleTokenExample.Services.Test1Service" behaviorConfiguration="ServiceRequiresTokenBehaviorHttp">
        <endpoint address="" behaviorConfiguration="AjaxEnabledBehavior" binding="webHttpBinding" bindingConfiguration="webBindingSSL" contract="WcfSimpleTokenExample.Services.Test1Service" />
      </service>
    </services>
    <behaviors>
      <endpointBehaviors>
        <behavior name="AjaxEnabledBehavior">
          <webHttp helpEnabled="true" />
        </behavior>
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior name="ServiceBehaviorHttp">
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
        </behavior>
        <behavior name="ServiceRequiresTokenBehaviorHttp">
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
          <TokenValidationBehaviorExtension />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <extensions>
      <behaviorExtensions>
        <add name="TokenValidationBehaviorExtension"
          type="WcfSimpleTokenExample.Behaviors.TokenValidationBehaviorExtension, WcfSimpleTokenExample, Version=1.0.0.0, Culture=neutral"/>
      </behaviorExtensions>
    </extensions>
    <bindings>
      <webHttpBinding>
        <binding name="webBindingSSL">
          <security mode="Transport">
            <transport clientCredentialType="None"/>
          </security>
        </binding>
      </webHttpBinding>
    </bindings>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="false" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true" />
    <directoryBrowse enabled="true" />
  </system.webServer>
  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
      <parameters>
        <parameter value="v11.0" />
      </parameters>
    </defaultConnectionFactory>
    <providers>
      <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
    </providers>
  </entityFramework>
  <connectionStrings>
    <add name="BasicTokenDbConnection" connectionString="data source=(LocalDB)\v11.0;attachdbfilename=|DataDirectory|\BasicTokenDatabase.mdf;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework" providerName="System.Data.SqlClient" />
  </connectionStrings>
</configuration>

Configuring the SSL Certificate

An SSL certification was generated for me when I built and ran the project. I was able to choose via a pop-up to trust the certificate.

Go on and check out part 6 here: Basic Token Service for WCF Services (Part 6 – A JavaScript client)

Authentication Token Service for WCF Services (Part 4 – Supporting Basic Authentication)

In Authentication Token Service for WCF Services (Part 3 – Token Validation in IDispatchMessageInspector) we showed how to verify our token against a database. The token is a great tool. The authentication service also provides the token based on a post of credentials.

In this article, we are going to add support for Basic Authentication. We aren’t going to do it the standard WCF way, using Transport security. We will keep our security at none, expect the deployment to be https and roll our own code to handle Basic Authentication.

Download this project here: WCF Basic Auth

There are two features we want in order claim support Basic Authentication.

  1. Allow AuthenticationTokenService.svc to create the token by optionally using Basic Authentication.
  2. Allow Basic Authentication as an option to providing a token.

To provide these two features, first we have to understand Basic Authentication. Basic Authentication is a well-known standard that is defined.

Basic Authentication is an html request header. The header is named “Authorization” and the value is as follows:

Basic amFyZWQ6dGVzdHB3

The first part of the Authorization header value is just the word “Basic” followed by a space.
The second part is the username and password concatenated together with a semicolon separator and then Base64 encoded.

jared:testpw
Basic amFyZWQ6dGVzdHB3

Let’s start with a simple class to manage the Basic authentication header, and encoding and decoding it.

using System;
using System.Text;
using WcfSimpleTokenExample.Model;

namespace WcfSimpleTokenExample.Business
{
    public class BasicAuth
    {
        private readonly string _User;
        private readonly string _Password;
        private const string Prefix = "Basic ";

        #region Constructors
        public BasicAuth(string encodedHeader)
            : this(encodedHeader, Encoding.UTF8)
        {
        }

        public BasicAuth(string encodedHeader, Encoding encoding)
        {
            HeaderValue = encodedHeader;
            var decodedHeader = encodedHeader.StartsWith(Prefix, StringComparison.OrdinalIgnoreCase)
                ? encoding.GetString(Convert.FromBase64String(encodedHeader.Substring(Prefix.Length)))
                : encoding.GetString(Convert.FromBase64String(encodedHeader));
            var credArray = decodedHeader.Split(':');
            if (credArray.Length > 0)
                _User = credArray[0];
            if (credArray.Length > 1)
                _Password = credArray[1];
        }

        public BasicAuth(string user, string password)
            : this(user, password, Encoding.UTF8)
        {
        }

        public BasicAuth(string user, string password, Encoding encoding)
        {
            _User = user;
            _Password = password;
            HeaderValue = Prefix + Convert.ToBase64String(encoding.GetBytes(string.Format("{0}:{1}", _User, _Password)));
        }
        #endregion

        public Credentials Creds
        {
            get { return _Creds ?? (_Creds = new Credentials { User = _User, Password = _Password }); }
        }
        private Credentials _Creds;

        public string HeaderValue { get; }
    }
}

BasicAuth.cs has constructors that allow for encoding by passing in a username and password and encoding it, as well as constructors that allow for passing in the header value and decoding it to get the username and password.

If we add BasicAuth.cs to our existing WcfSimpleTokenExample project, we can easily use it to support Basic Authentication.

Feature 1 – Basic Authentication for AuthenticationTokenService.svc/Authenticate

By using the BasicAuth.cs class, we can provide support for Basic Authentication in our token service using only 3 lines of code. Below is our new AuthenticationTokenService.svc.cs. Lines 18-20 our the new lines we add.

using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using WcfSimpleTokenExample.Business;
using WcfSimpleTokenExample.Database;
using WcfSimpleTokenExample.Model;

namespace WcfSimpleTokenExample.Services
{
    [ServiceContract]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class AuthenticationTokenService
    {
        [WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare)]
        [OperationContract]
        public string Authenticate(Credentials creds)
        {
            if (creds == null && WebOperationContext.Current != null)
            {
                creds = new BasicAuth(WebOperationContext.Current.IncomingRequest.Headers["Authorization"]).Creds;
            }
            using (var dbContext = new BasicTokenDbContext())
            {
                return new DatabaseTokenBuilder(dbContext).Build(creds);
            }
        }
    }
}

Feature 2 – Using Basic Authentication instead of a token

In our TokenValidationInspector.cs file, we are already validating the token using DatabaseTokenValidator, Now we need to validate the crendentials. We can validate credentials using the DatabaseCrendentialsValidator object that is already being used by AuthenticationTokenBuilder. However, we have to add some conditionaly code to test if a token is provided or if Basic Authorization is provided. If both are ignored, the token takes priority.

To do this, I wrapped the existing lines calling DatabaseTokenValidator into a method called ValidateToken. THen I created a new method called ValidateBasicAuthentication, which we only attempt to call a token isn’t provided.

using System.Net;
using System.Security.Authentication;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Web;
using WcfSimpleTokenExample.Business;
using WcfSimpleTokenExample.Database;
using WcfSimpleTokenExample.Interfaces;

namespace WcfSimpleTokenExample.Behaviors
{
    public class TokenValidationInspector : IDispatchMessageInspector
    {
        public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
        {
            // Return BadRequest if request is null
            if (WebOperationContext.Current == null) { throw new WebFaultException(HttpStatusCode.BadRequest); }

            // Get Token from header
            var token = WebOperationContext.Current.IncomingRequest.Headers["Token"];
            if (!string.IsNullOrWhiteSpace(token))
            {
                ValidateToken(token);
            }
            else
            {
                ValidateBasicAuthentication();
            }
            return null;
        }
        
        private static void ValidateToken(string token)
        {
            using (var dbContext = new BasicTokenDbContext())
            {
                ITokenValidator validator = new DatabaseTokenValidator(dbContext);
                if (!validator.IsValid(token))
                {
                    throw new WebFaultException(HttpStatusCode.Forbidden);
                }
                // Add User ids to the header so the service has them if needed
                WebOperationContext.Current.IncomingRequest.Headers.Add("User", validator.Token.User.Username);
                WebOperationContext.Current.IncomingRequest.Headers.Add("UserId", validator.Token.User.Id.ToString());
            }
        }


        private static void ValidateBasicAuthentication()
        {
            var authorization = WebOperationContext.Current.IncomingRequest.Headers["Authorization"];
            if (string.IsNullOrWhiteSpace(authorization))
            {
                using (var dbContext = new BasicTokenDbContext())
                {
                    var basicAuth = new BasicAuth(authorization);
                    if (!new DatabaseCredentialsValidator(dbContext).IsValid(basicAuth.Creds))
                    {
                        throw new AuthenticationException();
                    }
                }
            }
        }

        public void BeforeSendReply(ref Message reply, object correlationState)
        {
        }
    }
}

The web.config

There are not changes needed for the web.config. Here is a copy of it though, for reference.

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
  </configSections>
  <appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
  </appSettings>
  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" />
  </system.web>
  <system.serviceModel>
    <services>
      <service name="WcfSimpleTokenExample.Services.AuthenticationTokenService" behaviorConfiguration="ServiceBehaviorHttp">
        <endpoint address="" behaviorConfiguration="AjaxEnabledBehavior" binding="webHttpBinding" contract="WcfSimpleTokenExample.Services.AuthenticationTokenService" />
      </service>
      <service name="WcfSimpleTokenExample.Services.Test1Service" behaviorConfiguration="ServiceRequiresTokenBehaviorHttp">
        <endpoint address="" behaviorConfiguration="AjaxEnabledBehavior" binding="webHttpBinding" contract="WcfSimpleTokenExample.Services.Test1Service" />
      </service>
    </services>
    <behaviors>
      <endpointBehaviors>
        <behavior name="AjaxEnabledBehavior">
          <webHttp helpEnabled="true" />
        </behavior>
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior name="ServiceBehaviorHttp">
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
        </behavior>
        <behavior name="ServiceRequiresTokenBehaviorHttp">
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
          <TokenValidationBehaviorExtension />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <extensions>
      <behaviorExtensions>
        <add name="TokenValidationBehaviorExtension"
          type="WcfSimpleTokenExample.Behaviors.TokenValidationBehaviorExtension, WcfSimpleTokenExample, Version=1.0.0.0, Culture=neutral"/>
      </behaviorExtensions>
    </extensions>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="false" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true" />
    <directoryBrowse enabled="true" />
  </system.webServer>
  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
      <parameters>
        <parameter value="v11.0" />
      </parameters>
    </defaultConnectionFactory>
    <providers>
      <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
    </providers>
  </entityFramework>
  <connectionStrings>
    <add name="BasicTokenDbConnection" connectionString="data source=(LocalDB)\v11.0;attachdbfilename=|DataDirectory|\BasicTokenDatabase.mdf;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework" providerName="System.Data.SqlClient" />
  </connectionStrings>
</configuration>

Testing Basic Authentication with PostMan

Now we an test that this is working using PostMan. Our PostMan call is similar to what we did in previous articles, but instead of passing a token header, we set Basic Authentication, which sets the Authorization header for us (yes, you could have set the Authorization header manually.)

You could create the Authorization header yourself, but PostMan will create it for you if you click the Authorization and select Basic Auth. Enter your username and password and click update.

PostManBasicAuth

All this does it create an Authorization header for you. You can see this by clicking on the Headers tab in PostMan.

PostManBasicAuthHeader

Go ahead and click Send and you will get your authentication.

Notice the url is https in the image. I haven’t shown you how to do that yet. That is in part 5 here: Authentication Token Service for WCF Services (Part 5 – Adding SSL)

AutoMapper versus Extension Methods versus Implicit Casts

Download Project
Imagine the database code is legacy, can’t be changed, and the Database Person object from the database namespace looks like this:

using System;
using System.ComponentModel.DataAnnotations;

namespace WcfToEntityAutomapperExample.DAL.Model
{
    /// <summary>
    /// Example of an Person object where properties are not defined in a way you want 
    /// to expose via WCF.
    /// </summary>
    class PersonRow
    {
        public int Id { get; set; }

        [Required]
        [StringLength(50)]
        public string FName { get; set; }

        [Required]
        [StringLength(50)]
        public string MName { get; set; }

        [Required]
        [StringLength(50)]
        public string LName { get; set; }

        [Required]
        public DateTime BD { get; set; }

        public DateTime? DD { get; set; }
    }
}

You can’t use this in your WCF service, and not just beccause PersonRow, FName and LName just look tacky, though that is reason alone. No, the real problem is ambiguity and confusion. MName isn’t exactly clear. Is it Maiden name, Mother’s last name, or middle name? And what is BD and DD? Such confusion and ambiguity isn’t acceptable in an exposed API.

So you create a Person DataContract to expose with WCF that looks like this:

using System;
using System.Runtime.Serialization;

namespace WcfToEntityAutomapperExample.Services.Model
{
    [DataContract]
    public class Person
    {
        [DataMember]
        public string FirstName { get; set; }
        [DataMember]
        public string MiddleName { get; set; }
        [DataMember]
        public string LastName { get; set; }
        [DataMember]
        public DateTime DateOfBirth { get; set; }
        [DataMember]
        public DateTime? DateOfDeath { get; set; }
    }
}

Wow. That will look much better in your exposed WCF API.

So now there is a problem. We need to convert Person to PersonRow.

Solving with extension methods

It would be easy to write extension methods to do this:

  1. Add an extension method
    using WcfToEntityAutomapperExample.DAL.Model;
    using WcfToEntityAutomapperExample.Services.Model;
    
    namespace WcfToEntityAutomapperExample.Extensions
    {
        static class PersonExtensions
        {
            public static PersonRow ToPersonRow(this Person person)
            {
                return new PersonRow
                {
                    FName = person.FirstName,
                    MName = person.MiddleName,
                    LName = person.LastName,
                    BD = person.DateOfBirth,
                    DD = person.DateOfDeath
                };
            }
        }
    }
    
  2. Add a reverse mapping extension method.

    OK. Now we have this extension method and we can use it anywhere we want. However, we forgot. We need to do this in reverse too. We need an extension method for PersonRow to Person. So add this method to PersonExtensions class.

    using WcfToEntityAutomapperExample.DAL.Model;
    using WcfToEntityAutomapperExample.Services.Model;
    
    namespace WcfToEntityAutomapperExample.Extensions
    {
        static class PersonExtensions
        {
            public static PersonRow ToPersonRow(this Person person)
            {
                return new PersonRow
                {
                    FName = person.FirstName,
                    MName = person.MiddleName,
                    LName = person.LastName,
                    BD = person.DateOfBirth,
                    DD = person.DateOfDeath
                };
            }
    
            public static Person ToPerson(this PersonRow personRow)
            {
                return new Person
                {
                    FirstName = personRow.FName,
                    MiddleName = personRow.MName,
                    LastName = personRow.LName,
                    DateOfBirth = personRow.BD,
                    DateOfDeath = personRow.DD
                };
            }
        }
    }
    
  3. Now, you have 40 other objects to do this too.
    Hint: Take a moment to compare this to the AutoMapper method below and ask yourself which is better.

  4. Use the extension method in the web service call.
    using System.Collections.Generic;
    using System.Linq;
    using WcfToEntityAutomapperExample.DAL;
    using WcfToEntityAutomapperExample.DAL.Model;
    using WcfToEntityAutomapperExample.Extensions;
    using WcfToEntityAutomapperExample.Services.Interfaces;
    using WcfToEntityAutomapperExample.Services.Model;
    
    namespace WcfToEntityAutomapperExample.Services
    {
         public class Service1 : IService1
        {
            public void AddPersonExtensionMethod(Person person)
            {
                using (var dbContext = new PersonDbContext())
                {
                    dbContext.People.Add(person.ToPersonRow());
                    dbContext.SaveChanges();
                }
            }
    
            public List<Person> FindExtensionMethod(string lastName)
            {
                using (var dbContext = new PersonDbContext())
                {
                    var foundPeopleFromDb = dbContext.People.Where(p => p.LName == lastName).ToList();
                    return foundPeopleFromDb.Select(p => p.ToPerson()).ToList();
                }
            }
        }
    }
    

Extension Method Conclusion

Simple. Easy to use. Easy to read. Makes sense. The extension method name is an important part of this clarity. I used ToPerson and ToPersonRow. But it would also work with AsPerson and AsPersonRow.

Anybody can read this code and understand it.

If another field is added it is easy to add to the extension method on a single place so code isn’t strewn about.

Using AutoMapper

Why is AutoMapper better than the above extension method? Let’s do the same thing with AutoMapper. You be the judge of whether it is a better solution.

Well, so far, I can’t find any benefit from AutoMapper.

Here is what I need to do:

  1. Add AutoMapper library from NuGet. That adds a dll and another dependency to maintain.
  2. Create a static class to configure AutoMapper mappings: AutoMapperConfig.cs.
  3. Add mappings both ways: From Person to PersonRow and from PersonRow to Person.
    using AutoMapper;
    using WcfToEntityAutomapperExample.DAL.Model;
    using WcfToEntityAutomapperExample.Services.Model;
    
    namespace WcfToEntityAutomapperExample.Map
    {
        public static class AutoMapperConfig
        {
            internal static void RegisterMappings()
            {
                Mapper.CreateMap<Person, PersonRow>()
                    .ForMember(dest => dest.FName, opt => opt.MapFrom(src => src.FirstName))
                    .ForMember(dest => dest.MName, opt => opt.MapFrom(src => src.MiddleName))
                    .ForMember(dest => dest.LName, opt => opt.MapFrom(src => src.LastName))
                    .ForMember(dest => dest.BD, opt => opt.MapFrom(src => src.DateOfBirth))
                    .ForMember(dest => dest.DD, opt => opt.MapFrom(src => src.DateOfDeath)).ReverseMap();
            }
        }
    }
    
    

    Now, you have 40 other objects to do this too.
    Hint: Take a moment to compare this to the extension method above and ask yourself which is better.

  4. Find a global location to call AutoMapperConfig.cs: Global.asax/Global.asax.cs.
    Note: If you don’t have a Global.asax/Global.asax.cs, then you need to add this.

    using System;
    using System.Web;
    using WcfToEntityAutomapperExample.Map;
    
    namespace WcfToEntityAutomapperExample
    {
        public class Global : HttpApplication
        {
            protected void Application_Start(object sender, EventArgs e)
            {
                AutoMapperConfig.RegisterMappings();
            }
    
            protected void Session_Start(object sender, EventArgs e)
            {
    
            }
    
            protected void Application_BeginRequest(object sender, EventArgs e)
            {
    
            }
    
            protected void Application_AuthenticateRequest(object sender, EventArgs e)
            {
    
            }
    
            protected void Application_Error(object sender, EventArgs e)
            {
    
            }
    
            protected void Session_End(object sender, EventArgs e)
            {
    
            }
    
            protected void Application_End(object sender, EventArgs e)
            {
    
            }
        }
    }
    
  5. Call it in your service.
    using System.Collections.Generic;
    using System.Linq;
    using AutoMapper;
    using WcfToEntityAutomapperExample.DAL;
    using WcfToEntityAutomapperExample.DAL.Model;
    using WcfToEntityAutomapperExample.Extensions;
    using WcfToEntityAutomapperExample.Services.Interfaces;
    using WcfToEntityAutomapperExample.Services.Model;
    
    namespace WcfToEntityAutomapperExample.Services
    {
         public class Service1 : IService1
        {
            public void AddPersonAutoMapper(Person person)
            {
                using (var dbContext = new PersonDbContext())
                {
                    dbContext.People.Add(Mapper.Map<PersonRow>(person));
                    dbContext.SaveChanges();
                }
            }
    
            public List<Person> FindAutoMapper(string lastName)
            {
                using (var dbContext = new PersonDbContext())
                {
                    var foundPeopleFromDb = dbContext.People.Where(p => p.LName == lastName).ToList();
                    return foundPeopleFromDb.Select(Mapper.Map<Person>).ToList();
                }
            }
        }
    }
    

AutoMapper conclusion
Something just isn’t right here. You have to call more complex code to get an object converted. The ReverseBack() method saves us from having to create the reverse copy manually. Still, there are two methods and three lambda’s per property to copy. Hardly saving code or making life easier.

The configuration code looks way more complex than the extension method code. I had a bug in my mapper config, and I couldn’t see it because the code is so busy.

The AutoMapper code also isn’t intuitive. The methods aren’t obvious and it is not clear what it is doing without reading the documentation. Mapper.Map(p) doesn’t not clearly tell me that I am converting from an object of type Person to an object of type PersonRow. To me a Map is a HashTable or a Dictionary. I assume at first glance, that I am calling some type of Dictionary. The syntax breaks the “code should be clear” and the “code should be self documenting” rules. Any developer not familiar with AutoMapper will have no idea what your code is doing.

Note: AutoMapper supposedly adds a feature that allows for a copy if the properties are the same with just one line of code: Mapper.CreateMap();

I can see how if you had a lot of objects with identical properties that AutoMapper would be tempting. Still, the naming and lack of readability gets to me. Mapping.Map(p) just isn’t clear. If all the properties of all the objects match, scripting a clear, self-documenting extension method pre-build would be the way to go. We need a pre-build solution, not a runtime solution.

If a field is added and named the same, nothing has to be done and AutoMapper works. However, if the fields are named differently, then you still have to add it to the Mapper config.

Implicit Casts

You could do this with Implicit casts.

  1. Add an implicit cast tot he object under your control, Person.
    using System;
    using System.Runtime.Serialization;
    using WcfToEntityAutomapperExample.DAL.Model;
    
    namespace WcfToEntityAutomapperExample.Services.Model
    {
        [DataContract]
        public class Person
        {
            [DataMember]
            public string FirstName { get; set; }
            [DataMember]
            public string MiddleName { get; set; }
            [DataMember]
            public string LastName { get; set; }
            [DataMember]
            public DateTime DateOfBirth { get; set; }
            [DataMember]
            public DateTime? DateOfDeath { get; set; }
    
            // User-defined conversion from Digit to double 
            public static implicit operator Person(PersonRow personRow)
            {
                return new Person
                {
                    FirstName = personRow.FName,
                    MiddleName = personRow.MName,
                    LastName = personRow.LName,
                    DateOfBirth = personRow.BD,
                    DateOfDeath = personRow.DD
                };
            }
            //  User-defined conversion from double to Digit 
            public static implicit operator PersonRow(Person person)
            {
                return new PersonRow
                {
                    FName = person.FirstName,
                    MName = person.MiddleName,
                    LName = person.LastName,
                    BD = person.DateOfBirth,
                    DD = person.DateOfDeath
                };
            }
        }
    }
    

    The implicit cast is not included in the client code so it is fine to add to the Person DataContract.

  2. Use it in your services.
    using System.Collections.Generic;
    using System.Linq;
    using AutoMapper;
    using WcfToEntityAutomapperExample.DAL;
    using WcfToEntityAutomapperExample.DAL.Model;
    using WcfToEntityAutomapperExample.Extensions;
    using WcfToEntityAutomapperExample.Services.Interfaces;
    using WcfToEntityAutomapperExample.Services.Model;
    
    namespace WcfToEntityAutomapperExample.Services
    {
        public class Service1 : IService1
        {
            public void AddPersonImplicitCast(Person person)
            {
                using (var dbContext = new PersonDbContext())
                {
                    dbContext.People.Add(person);
                    dbContext.SaveChanges();
                }
            }
    
            public List<Person> FindImplicitCast(string lastName)
            {
                using (var dbContext = new PersonDbContext())
                {
                    var foundPeopleFromDb = dbContext.People.Where(p => p.LName == lastName).ToList();
                    return foundPeopleFromDb.Select(p => (Person)p).ToList();
                }
            }
        }
    }
    

Implicit Cast Conclusion

Implicit Cast was pretty simple. I didn’t need any other classes. However, it muddied up a DataContract model class.

It is not obvious why you can add a Person where a PersonRow is needed, but it makes sense.

If I add a property or field, I’d have to add it to the cast.

My Winner

To me it is the extension method, with implicit casts a close second. I just like the simplicity of the code. A first year developer can understand and use it. I also like that it doesn’t muddy up the Model object iself like implicit operators do. Nor does it require me to create a mapping config, and initialize the mapping config.

A read a unit testing argument that unit tests won’t fail when a field is added. I had to disagree. I can put refection code in my unity test fail a test if a property is not copied. Now the reflection code is in a test project not in the production project.

My Loser

AutoMapper. It just doesn’t add the simplicity that it claims to. It by far the most complex in this scenario. Complexity != better. The gains of auto mapping Properties and Fields with the same name doesn’t outweigh the losses in readability.

Also, extension methods are far faster than AutoMapper. I didn’t do benchmarks but 7 times is what other have found. I have some data sets that take a couple of seconds to return. Times a couple of seconds by 7 and you will quickly see that such performance matters. The cost to use reflection when looping through can’t be good for you.

Also, I don’t buy into the argument that performance doesn’t matter. Performance issues pile up over time. I agree that you should not write unreadable code to optimize before you know know that readable code performs poorly. However, if two pieces of code are clear and readable and one is more performant, use the more performant. You shouldn’t make your code more complex and harder to understand to get unnecessary optimization. But with AutoMapper, you are making your code more complex and harder to understand to get less performance? How does that make sense?

Script the creation of the extension methods for objects with members named the same. You’ll be better off for it. You could even add the script to a pre-build command so the extension method is updated pre-build whenever a property is added.

Please comment and tell me which is your winner vs loser?

Authentication Token Service for WCF Services (Part 3 – Token Validation in IDispatchMessageInspector)

In Authentication Token Service for WCF Services (Part 2 – Database Authentication) we showed how to verify our token. However, we verified the token in the service itself.

WCF BTS Message Inspector

This is not ideal.

    [OperationContract]
    [WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare)]
    public string Test()
    {
        var token = HttpContext.Current.Request.Headers["Token"];
        using (var dbContext = new BasicTokenDbContext())
        {
            ITokenValidator validator = new DatabaseTokenValidator(dbContext);
            if (validator.IsValid(token))
            {
                // Do service work here . . . 
            }
        }
    }

This is fine for a one or two services. But what if there are going to have many services? The Don’t Repeat Yourself (DRY) principle would be broken if we repeated the same lines of code at the top of every service. If only we could validate the token in one place, right? Well, we can.

We could make a method that we could call at the top of every service, but even if we did that, we would still have to repeat one line for every service. Is there a way where we wouldn’t even have to repeat a single line of code? Yes, there is. Using Aspect-oriented programming (AOP). It turns out WCF services have some AOP capabilities built in.

IDispatchMessageInspector can be configured to do this.

To enable this, your really need to implement three Interfaces and configure it in the web.config. I am going to use separate classes for each interface.

The web config extension class:

using System;
using System.ServiceModel.Configuration;

namespace WcfSimpleTokenExample.Behaviors
{
    public class TokenValidationBehaviorExtension : BehaviorExtensionElement
    {
        #region BehaviorExtensionElement

        public override Type BehaviorType
        {
            get { return typeof(TokenValidationServiceBehavior); }
        }

        protected override object CreateBehavior()
        {
            return new TokenValidationServiceBehavior();
        }

        #endregion
    }
}

The Service Behavior class:

using System.Collections.ObjectModel;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;

namespace WcfSimpleTokenExample.Behaviors
{
    public class TokenValidationServiceBehavior : IServiceBehavior
    {
        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            foreach (var t in serviceHostBase.ChannelDispatchers)
            {
                var channelDispatcher = t as ChannelDispatcher;
                if (channelDispatcher != null)
                {
                    foreach (var endpointDispatcher in channelDispatcher.Endpoints)
                    {
                        endpointDispatcher.DispatchRuntime.MessageInspectors.Add(new TokenValidationInspector());
                    }
                }
            }
        }

        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
        }
    }
}

The message inspector class

using System.Net;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Web;
using WcfSimpleTokenExample.Business;
using WcfSimpleTokenExample.Database;
using WcfSimpleTokenExample.Interfaces;

namespace WcfSimpleTokenExample.Behaviors
{
    public class TokenValidationInspector : IDispatchMessageInspector
    {
        public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
        {
            // Return BadRequest if request is null
            if (WebOperationContext.Current == null) { throw new WebFaultException(HttpStatusCode.BadRequest); }

            // Get Token from header
            var token = WebOperationContext.Current.IncomingRequest.Headers["Token"];

            // Validate the Token
            using (var dbContext = new BasicTokenDbContext())
            {
                ITokenValidator validator = new DatabaseTokenValidator(dbContext);
                if (!validator.IsValid(token))
                {
                    throw new WebFaultException(HttpStatusCode.Forbidden);
                }
                // Add User ids to the header so the service has them if needed
                WebOperationContext.Current.IncomingRequest.Headers.Add("User", validator.Token.User.Username);
                WebOperationContext.Current.IncomingRequest.Headers.Add("UserId", validator.Token.User.Id.ToString());
            }
            return null;
        }

        public void BeforeSendReply(ref Message reply, object correlationState)
        {
        }
    }
}

Basically, what happens is AfterReceiveRequest is called somewhere between when the actual packets arrive at the server and just before the service is called. This is perfect. We can validate our token here in a single place.

So let’s populate our AfterReceiveRequest.

        public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
        {
            // Return BadRequest if request is null
            if (WebOperationContext.Current == null) { throw new WebFaultException(HttpStatusCode.BadRequest); }

            // Get Token from header
            var token = WebOperationContext.Current.IncomingRequest.Headers["Token"];

            // Validate the Token
            using (var dbContext = new BasicTokenDbContext())
            {
                ITokenValidator validator = new DatabaseTokenValidator(dbContext);
                if (!validator.IsValid(token))
                {
                    throw new WebFaultException(HttpStatusCode.Forbidden);
                }
                // Add User ids to the header so the service has them if needed
                WebOperationContext.Current.IncomingRequest.Headers.Add("User", validator.Token.User.Username);
                WebOperationContext.Current.IncomingRequest.Headers.Add("UserId", validator.Token.User.Id.ToString());
            }
            return null;
        }

You might have noticed we made one change to the ITokenValidator. See the changes below. It now has a Token property, as does its implementation, DatabaseTokenValidator. Mostly I am getting Token.UserId, but since EF gets the User object for me too, I went ahead an added the User name as well.

using WcfSimpleTokenExample.Database;
namespace WcfSimpleTokenExample.Interfaces
{
    public interface ITokenValidator
    {
        bool IsValid(string token);
        Token Token { get; set; }
    }
}
using System;
using System.Linq;
using WcfSimpleTokenExample.Database;
using WcfSimpleTokenExample.Interfaces;

namespace WcfSimpleTokenExample.Business
{
    public class DatabaseTokenValidator : ITokenValidator
    {
        // Todo: Set this from a web.config appSettting value
        public static double DefaultSecondsUntilTokenExpires = 1800;

        private readonly BasicTokenDbContext _DbContext;

        public DatabaseTokenValidator(BasicTokenDbContext dbContext)
        {
            _DbContext = dbContext;
        }

        public bool IsValid(string tokentext)
        {
            Token = _DbContext.Tokens.SingleOrDefault(t => t.Text == tokentext);
            return Token != null && !IsExpired(Token);
        }

        internal bool IsExpired(Token token)
        {
            var span = DateTime.Now - token.CreateDate;
            return span.TotalSeconds > DefaultSecondsUntilTokenExpires;
        }

        public Token Token { get; set; }
    }
}

Now we don’t need all that Token validation code in our Service. We can clean it up. In fact, since all it does right now is return a string, our service only needs a single line of code. I also added the UserId and User to the output for fun.

    [ServiceContract]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class Test1Service
    {
        [OperationContract]
        [WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare)]
        public string Test()
        {
            return string.Format("Your token worked! User: {0} User Id: {1}",
                WebOperationContext.Current.IncomingRequest.Headers["UserId"],
                WebOperationContext.Current.IncomingRequest.Headers["User"]);
        }
    }

Well, now that it is all coded up, it won’t work until we enable the new behavior in the web.config. So let’s look at the new web.config. We create a new ServiceBehavior (lines 34-38) for all the services that validate the token. We leave the AuthenticationTokenService the same as we don’t have a token when we hit it because we hit it to get the token. We also need to make sure to add the behavior extension (lines 41-46). Then we need to tell our ServiceBehavior to use the new extension (line 37).

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 -->
    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
  </configSections>
  <appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
  </appSettings>
  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" />
  </system.web>
  <system.serviceModel>
    <services>
      <service name="WcfSimpleTokenExample.Services.AuthenticationTokenService" behaviorConfiguration="ServiceBehaviorHttp">
        <endpoint address="" behaviorConfiguration="AjaxEnabledBehavior" binding="webHttpBinding" contract="WcfSimpleTokenExample.Services.AuthenticationTokenService" />
      </service>
      <service name="WcfSimpleTokenExample.Services.Test1Service" behaviorConfiguration="ServiceAuthBehaviorHttp">
        <endpoint address="" behaviorConfiguration="AjaxEnabledBehavior" binding="webHttpBinding" contract="WcfSimpleTokenExample.Services.Test1Service" />
      </service>
    </services>
    <behaviors>
      <endpointBehaviors>
        <behavior name="AjaxEnabledBehavior">
          <webHttp helpEnabled="true" />
        </behavior>
      </endpointBehaviors>
      <serviceBehaviors>
        <behavior name="ServiceBehaviorHttp">
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
        </behavior>
        <behavior name="ServiceAuthBehaviorHttp">
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
          <TokenValidationBehaviorExtension />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <extensions>
      <behaviorExtensions>
        <add name="TokenValidationBehaviorExtension"
          type="WcfSimpleTokenExample.Behaviors.TokenValidationBehaviorExtension, WcfSimpleTokenExample, Version=1.0.0.0, Culture=neutral"/>
      </behaviorExtensions>
    </extensions>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="false" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true" />
    <directoryBrowse enabled="true" />
  </system.webServer>
  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
      <parameters>
        <parameter value="v11.0" />
      </parameters>
    </defaultConnectionFactory>
    <providers>
      <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
    </providers>
  </entityFramework>
  <connectionStrings>
    <add name="BasicTokenDbConnection" connectionString="data source=(LocalDB)\v11.0;attachdbfilename=|DataDirectory|\BasicTokenDatabase.mdf;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework" providerName="System.Data.SqlClient" />
  </connectionStrings>
</configuration>

Go on and read part 4 here: Authentication Token Service for WCF Services (Part 4 – Supporting Basic Authentication)