Implementing Token Based Security Pattern | Design Patterns Tutorial

With HTTPS in place, its safe to request user to provide credentials through our front end. But first, lets establish a pattern for out security mechanism. There are various patterns which could be used to address your application specific security requirements. In this post, we will use a token based security pattern. Once the user is successfully authenticated, server will issue a token which needs to be passed by client with every subsequent requests as a proof of user identity.
The security token needs to be strongly encrypted to prevent forgery. The token will be issued and consumed by the same party – our server, so symmetric key encryption like Rijndael/AEC or DES would work fine. However, here I am using public-key cryptography for ease of managing the keys. Common use case of public-key cryptography/x.509 certificates involve sharing your public keys with the collaborating systems which could send data encrypted using your public key, which could be decrypted only using your private key. In our case, we don’t need to and shouldn’t share the public key with any other party as we are the issuer and consumer of the token. So, both keys needs to be protected religiously to prevent forgery of the token.
Another security risk is session hijacking, someone could get hold of the token and could pose as a user. In order to avoid hijacking, we will associate the token to the user machine’s IP address, while validating the token we will be comparing it with the IP address from which the request is being made. Following is the token in pain text, with just two key value pairs separated by comma.
UserId=ninja,IP=192.78.68.90

Following the class representing the token
public class Token
{
    public Token(string userId, string fromIP)
    {
        UserId = userId;
        IP = fromIP;
    }

    public string UserId { get; private set; }
    public string IP { get; private set; }

    public string Encrypt()
    {
        CryptographyHelper cryptographyHelper = new CryptographyHelper();
        X509Certificate2 certificate = cryptographyHelper.GetX509Certificate("CN=WebAPI-Token");
        return cryptographyHelper.Encrypt(certificate, this.ToString());
    }

    public override string ToString()
    {
        return String.Format("UserId={0};IP={1}", this.UserId, this.IP);
    }

    public static Token Decrypt(string encryptedToken)
    {
        CryptographyHelper cryptographyHelper = new CryptographyHelper();
        X509Certificate2 certificate = cryptographyHelper.GetX509Certificate("CN=WebAPI-Token");
        string decrypted = cryptographyHelper.Decrypt(certificate, encryptedToken);

        //Splitting it to dictionary
        Dictionary<string, string> dictionary = decrypted.ToDictionary();
        return new Token(dictionary["UserId"], dictionary["IP"]);
    }
}

Create the x.509 certificate by running following command on the Visual Studio command prompt while running as administrator
makecert -sr LocalMachine -ss My sha1 -n CN=WebAPI-Token -sk y exchange -pe
CryptographyHelper provides helper methods for encryption and decryption, code could be found here .

Inspecting Security Token

Lets create a message handler which will check for token in the header of every incoming requests.

public class TokenInspector : DelegatingHandler
{
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        const string TOKEN_NAME = "X-Token";

        if (request.Headers.Contains(TOKEN_NAME))
        {
            string encryptedToken = request.Headers.GetValues(TOKEN_NAME).First();
            try
            {
                Token token = Token.Decrypt(encryptedToken);
                bool isValidUserId = IdentityStore.IsValidUserId(token.UserId);
                bool requestIPMatchesTokenIP = token.IP.Equals(request.GetClientIP());

                if (!isValidUserId || !requestIPMatchesTokenIP)
                {
                    HttpResponseMessage reply = request.CreateErrorResponse(HttpStatusCode.Unauthorized, "Invalid identity or client machine.");
                    return Task.FromResult(reply);
                }
            }
            catch (Exception ex)
            {
                HttpResponseMessage reply = request.CreateErrorResponse(HttpStatusCode.Unauthorized, "Invalid token.");
                return Task.FromResult(reply);
            }
        }
        else
        {
            HttpResponseMessage reply = request.CreateErrorResponse(HttpStatusCode.Unauthorized, "Request is missing authorization token.");
            return Task.FromResult(reply);
        }

        return base.SendAsync(request, cancellationToken);
    }

}

X-Token is the HTTP header in which we expect client to supply the token issued after authentication. Any request which doesn’t contain token is refused politely with HTTP 401 code. If the token is supplied, it is decrypted, user id is checked for validity against identity store. The IP address in the token is matched with the IP address of the request to prevent session hijacking. We need to register this message handler in out API config class as shown below.UsersController provides operations for authenticating the user, so of course we can’t demand token on that request, so the is excluded by explicitly specifying the route.

public static void Register(HttpConfiguration config)
{
    //Create and instance of TokenInspector setting the default inner handler
    TokenInspector tokenInspector = new TokenInspector() { InnerHandler = new HttpControllerDispatcher(config) };

  //Just exclude the users controllers from need to provide valid token, so they could authenticate
    config.Routes.MapHttpRoute(
        name: "Authentication",
        routeTemplate: "api/users/{id}",
        defaults: new { controller = "users" }
    );

    config.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional },
        constraints: null,
        handler: tokenInspector
    );

    config.MessageHandlers.Add(new HTTPSGuard()); //Global handler - applicable to all the requests
}

The code for Web API Authentication Using Security Token along with unit tests could be found here