Archive for February 2015

Authentication Token Service for WCF Services (Part 2 – Database Authentication)

In the previous segment, Authentication Token Service for WCF Services (Part 1), we created a project that exposes an AuthenticationTokenService and a Test1Service. The object is to first authenticate using the AuthenticationTokenService. Authentication provides a token. Calls made to additional services should include the token as a header value.

We used concrete implementations of Interfaces to do our authentication, token creation, and token validation. The concrete implementations had stubbed-in code. I used interfaces because now to change this to use database authentication, I can create concrete implementation of the same interfaces. My implementation will be different but the methods and signatures should remain the same.

Download this project here: WCF BTS DB

So here is quick table that might help you visualize what is going on. We already have the interfaces, we already have the code example code. We need to write new classes that instead of using stub example code uses database code.

Interface Concrete Code Example Class Concrete Database Class
ICredentialsValidator CodeExampleCredentialsValidator DatabaseCredentialsValidator
ITokenBuilder CodeExampleTokenBuilder DatabaseTokenBuilder
ITokenValidator CodeExampleTokenValidator DatabaseTokenValidator

OK. So we have one task to create database implementation of the interfaces. However, before we do that, we have two tasks we must do first if we are going to use a database.

  1. A database (SQL Server)
  2. A data access layer (Entity Framework)

You may already have a database, in which case, skip to Step 5& – Add Entity Framework.

Step 1 – Create the database

For now, let’s keep everything in Visual Studio. So we will create the database as a file in Visual Studio.

Note: For production deployment, we will use a real database and change the connection string in the web.config to point to the real database.

  1. Right-click on App_Data and choose Add | New Item . . . | Installed > Visual C# > Data | SQL Server Database.
  2. I named this database BasicTokenDatabase.mdf.

Step 2 – Create the User table

We will create only two tables. A user table and a Token table. A user table is needed that has at least a user and a password. The user field should be unique. The password field should NOT store the password in clear text. Instead it should store a salted hash of the password. Since we are using a salt, we need a column to store the salt. If you don’t know what a salt is, read about it here: https://crackstation.net/hashing-security.htm

  1. Double-click the database in Visual Studio.
    The Data Connections widget should open with a connection to the BasicTokenDatabase.mdf.
  2. Right-click on Tables and choose Add New Table.
  3. Keep the first Id column but also make it an identity so it autoincrements.
  4. Add three columns: User, Password, and Hash.
    The table should look as follows:

    Name Data Type Allow Nulls Default
    Id int [ ]
    User nvarchar(50) [ ]
    Password nvarchar(250) [ ]
    Salt nvarchar(250) [ ]
  5. Add a Unique constraint for the User column. I do this just by adding it to the table creation code.The SQL to create the table should look like this:
    CREATE TABLE [dbo].[User] (
        [Id]       INT            IDENTITY (1, 1) NOT NULL,
        [User]     NVARCHAR (50)  NOT NULL,
        [Password] NVARCHAR (250) NOT NULL,
        [Salt]     NVARCHAR (250) NOT NULL,
        PRIMARY KEY CLUSTERED ([Id] ASC),
        CONSTRAINT [Unique_User] UNIQUE NONCLUSTERED ([User] ASC)
    );
    
  6. Click Update to create the table.
  7. Close the table designer window.

Step 3 – Create a Token table

For the purposes of our token service, we want to create a token and store it in the database. We need a table to store the token as well as some data about the token, such as create date, and which user the token belongs to, etc.

  1. Double-click the database in Visual Studio.
    The Data Connections widget should open with a connection to the BasicTokenDatabase.mdf.
  2. Right-click on Tables and choose Add New Table.
  3. Keep the first Id column but also make it an identity so it autoincrements.
  4. Add three columns: Token, UserId, CreateDateTime.
    The table should look as follows:

    Name Data Type Allow Nulls Default
    Id int [ ]
    Token nvarchar(250) [ ]
    UserId int [ ]
    CreateDate DateTime [ ]
  5. Add a foreign key constraint for the UserId column to the Id column of the User table. I do this just by adding it to the table creation code.
  6. Add a Unique constraint for the Token column. I do this just by adding it to the table creation code. The SQL to create the table should look like this:
    CREATE TABLE [dbo].[Token] (
        [Id]         INT            IDENTITY (1, 1) NOT NULL,
        [Token]      NVARCHAR (250) NOT NULL,
        [UserId]     INT            NOT NULL,
        [CreateDate] DATETIME       NOT NULL,
        PRIMARY KEY CLUSTERED ([Id] ASC),
        CONSTRAINT [Unique_Token] UNIQUE NONCLUSTERED ([Token] ASC),
        CONSTRAINT [FK_Token_ToUser] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id])
    );
    
  7. Click Update to create the table.
  8. Close the table designer window.

Step 4 – Add a default user to the database

We need a user to test. We are going to add a user as follows:

User: user1
Password: pass1
Salt: salt1

  1. Double-click the database in Visual Studio.
    The Data Connections widget should open with a connection to the BasicTokenDatabase.mdf.
  2. Right-click on SimpleTokenConnection and choose New Query.
  3. Add SQL to insert a user.
    The sql to insert the sample user is this:

    INSERT INTO [User] ([User],[Password],[Salt]) VALUES ('user1','63dc4400772b90496c831e4dc2afa4321a4c371075a21feba23300fb56b7e19c','salt1')
    

Step 5 – Add Entity Framework

  1. Right-click on the solution and choose Manage NuGet Packages for Solution.
  2. Click Online.
  3. Type “Entity” into the search.
  4. Click Install when EntityFramework comes up.
  5. You will be prompted to accept the license agreement.

Step 6 – Add a DBContext

Entity Framework has a lot of options. Because I expect you to already have a database, I am going to use Code First to an Existing database.

  1. Create a folder called Database in your project.
  2. Right-click on the Database folder and choose Add | New Item . . . | Installed > Visual C# > Data | ADO.NET Entity Data Model.
  3. Give it a name and click OK.
    I named mine SimpleTokenDbContext.
  4. Select Code First from database.
    Your BasicTokenDatabase should selected by default. If not, you have to browse to it.
  5. I named my connection in the web.config BasicTokenDbConnection and clicked next.
  6. Expand tables and expand dbo and check the User table and the Token table.
  7. Click Finish.

You should now have three new objects created:

  1. SimpleTokenDbContext.cs
  2. Token.cs
  3. User.cs

Entity Framework will allow us to use these objects when communicating with the database.

Note: I made one change to these. Because User is a table name and a column name, Entity Framework named the class object User and the property for the user column User1. That looked wierd to me, so I renamed the User1 property to Username but I left the table with and table column named User. Token and the Token property also had this issue. I changed the Token property to be Text.

[Column("User")]
[Required]
[StringLength(50)]
public string Username { get; set; }
        [Column("Token")]
        [Required]
        [StringLength(250)]
        public string Text { get; set; }

Step 7 – Implement ICredentialsValidator

  1. Create a new class called DatabaseCredentialsValidator.cs.
  2. Use Entity Framework and the Hash class to check if those credentials match what is in the User table of the database.
using System;
using System.Linq;
using WcfSimpleTokenExample.Database;
using WcfSimpleTokenExample.Interfaces;

namespace WcfSimpleTokenExample.Business
{
    public class DatabaseCredentialsValidator : ICredentialsValidator
    {
        private readonly BasicTokenDbContext _DbContext;

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

        public bool IsValid(Model.Credentials creds)
        {
            var user = _DbContext.Users.SingleOrDefault(u => u.Username.Equals(creds.User, StringComparison.CurrentCultureIgnoreCase));
            return user != null && Hash.Compare(creds.Password, user.Salt, user.Password, Hash.DefaultHashType, Hash.DefaultEncoding);
        }
    }
}

Step 8 – Implement ITokenBuilder

  1. Create a new class called DatabaseTokenBuilder.cs.
  2. Use Entity Framework to create a new token and add it to the Token table in the database.
  3. Instead of using Guid.NewGuid, which isn’t secure because it may not be cryptographically random, we will create a better random string generator using RNGCryptoServiceProvider. Se the BuildSecureToken() method below.
using System;
using System.Linq;
using System.Security.Authentication;
using System.Security.Cryptography;
using WcfSimpleTokenExample.Database;
using WcfSimpleTokenExample.Interfaces;

namespace WcfSimpleTokenExample.Business
{
    public class DatabaseTokenBuilder : ITokenBuilder
    {
        public static int TokenSize = 100;
        private readonly BasicTokenDbContext _DbContext;

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

        public string Build(Model.Credentials creds)
        {
            if (!new DatabaseCredentialsValidator(_DbContext).IsValid(creds))
            {
                throw new AuthenticationException();
            }
            var token = BuildSecureToken(TokenSize);
            var user = _DbContext.Users.SingleOrDefault(u => u.Username.Equals(creds.User, StringComparison.CurrentCultureIgnoreCase));
            _DbContext.Tokens.Add(new Token { Text = token, User = user, CreateDate = DateTime.Now });
            _DbContext.SaveChanges();
            return token;
        }

        private string BuildSecureToken(int length)
        {
            var buffer = new byte[length];
            using (var rngCryptoServiceProvider = new RNGCryptoServiceProvider())
            {
                rngCryptoServiceProvider.GetNonZeroBytes(buffer);
            }
            return Convert.ToBase64String(buffer);
        }
    }
}

Step 9 – Implement ITokenValidator

  1. Create a new class called DatabaseTokenValidator.cs.
  2. Read the token from the header data.
  3. Use Entity Framework to verify the token is valid.
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)
        {
            var 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;
        }
    }
}

Step 10 – Update the services code

Ideally we would have our services code automatically get the correct interface implementations. But for this example, we want to keep things as simple as possible.

using System.Security.Authentication;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using WcfSimpleTokenExample.Business;
using WcfSimpleTokenExample.Database;
using WcfSimpleTokenExample.Interfaces;
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)
        {
            using (var dbContext = new BasicTokenDbContext())
            {
                ICredentialsValidator validator = new DatabaseCredentialsValidator(dbContext);
                if (validator.IsValid(creds))
                    return new DatabaseTokenBuilder(dbContext).Build(creds);
                throw new InvalidCredentialException("Invalid credentials");
            }
        }
    }
}
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using System.Web;
using WcfSimpleTokenExample.Business;
using WcfSimpleTokenExample.Database;
using WcfSimpleTokenExample.Interfaces;

namespace WcfSimpleTokenExample.Services
{
    [ServiceContract]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class Test1Service
    {
        [OperationContract]
        [WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare)]
        public string Test()
        {
            var token = HttpContext.Current.Request.Headers["Token"]; // This works whether aspNetCompatibilityEnabled is true of false.
            using (var dbContext = new BasicTokenDbContext())
            {
                ITokenValidator validator = new DatabaseTokenValidator(dbContext);
                return validator.IsValid(token) ? "Your token worked!" : "Your token failed!";
            }
        }
    }
}

I didn’t make any changes to the web.config myself. However, the web.config was changed by adding Entity Framework and a database. Download the source code to see and example of it.

Testing using Postman

The steps for testing with Postman in Part 1 should still be valid for Part 2. Just remember to remove any escape characters from the returned string. For example, if a \/ is found, remove the \ as it is an escape character. If you look at the resulting token in Postman’s Pretty tab, the escape character is removed for you.

Well, by now, you should be really getting this down. Hopefully but this point, you can now take this code and implement your own Basic Token Service BTS. Hopefully you can use this where simple token authentication is needed and the bloat of an entire Secure Token Service framework is not.

Go on and read part 3 here: Authentication Token Service for WCF Services (Part 3 – Token Validation in IDispatchMessageInspector)

Authentication Token Service for WCF Services (Part 1)

I am setting out to create a thin web UI that consists of only HTML, CSS, and Javascript (HCJ) for the front end. For the back end, I have Ajax-enabled WCF services.

I have a couple of options for authentication.

Options:

  1. Authenticate with username and password every time a service is called.
  2. Store the username and password once, then store the credentials in the session or a cookie or a javascript variable and pass them every time I call a subsequent service.
  3. Authentication to one WCF service, then store a token.

Option 1 – Authenticate every time

This is not acceptable to the users. It would be a pain to type in credentials over and over again when clicking around a website.

Option 2 – Authenticate once and store credentials

This option is not acceptable because we really don’t want to be storing credentials in cookies and headers. You could alleviate the concern by hashing the password and only storing the hash, but that is still questionable. It seems this might cause the username and password to be passed around too often and eventually, your credentials will be leaked.

Option 3 – Authenticate once and store a token

This option seems the most secure. After authenticating, a token is returned to the user. The other web services can be accessed by using the token. Now the credentials are not stored. They are only passed over the network at authentication time.

Secure Token Service

This third idea is the idea around the Secure Token Service (STS). However, the STS is designed around the idea of having a 3rd party provide authentication, for example, when you login to a website using Facebook even though it isn’t a Facebook website.

STS service implementation is complex. There are entire projects built around this idea. What if you want something simpler?

Basic Token Service (BTS)

I decided that for simple authentication, there needs to be an example on the web of a Basic Token Service.

In the basic token service, there is a the idea of a single service that provides authentication. That service returns a token if authenticated, a failure otherwise. If authenticated, the front end is responsible for passing the token to any subsequent web services. This could be a header value, a cookie or a url parameter. I am going to use a header value in my project.

Here is the design.

Basic Token Service

Since this is “Basic” it should use basic code, right? It does.

The BTS Code

Download here: WCF BTS

In Visual Studio, I chose New | Project | Installed > Templates > Visual C# > WCF | WCF Service Application.

OK, so lets do some simple code. In this example, we will put everything in code. (In part 2, I will enhance the code to look to the database.)

Ajax-enabled WCF Services

Add the Authentication WCF Service first. In Visual Studio, I right-clicked on the project and chose Add | New Item … | Installed > Visual C# > Web | WCF Service (Ajax-enabled)

<%@ ServiceHost Language="C#" Debug="true" Service="WcfSimpleTokenExample.Services.AuthenticationTokenService" CodeBehind="AuthenticationTokenService.svc.cs" %>
using System.Security.Authentication;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using WcfSimpleTokenExample.Business;
using WcfSimpleTokenExample.Interfaces;
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)
        {
            ICredentialsValidator validator = new CodeExampleCredentialsValidator();
            if (validator.IsValid(creds))
                return new CodeExampleTokenBuilder().Build(creds);
            throw new InvalidCredentialException("Invalid credentials");
        }
    }
}

A second example service:

<%@ ServiceHost Language="C#" Debug="true" Service="WcfSimpleTokenExample.Services.Test1Service" CodeBehind="Test1Service.svc.cs" %>
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using System.Web;
using WcfSimpleTokenExample.Business;
using WcfSimpleTokenExample.Interfaces;

namespace WcfSimpleTokenExample.Services
{
    [ServiceContract]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class Test1Service
    {
        [OperationContract]
        [WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, BodyStyle = WebMessageBodyStyle.Bare)]
        public string Test()
        {
            var token = HttpContext.Current.Request.Headers["Token"];
            ITokenValidator validator = new CodeExampleTokenValidator();
            if (validator.IsValid(token))
            {
                return "Your token worked!";
            }
            else
            {
                return "Your token failed!";
            }
        }
    }
}
<?xml version="1.0"?>
<configuration>
  <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="ServiceBehaviorHttp" >
        <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>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
    <directoryBrowse enabled="true"/>
  </system.webServer>
</configuration>

Note: In the project, there is an xdt:Transform for the web.config.debug and the web.config.release if you use web deploy. These enforce that the web services that make them only use HTTPS. Check them out.

Models

Now we are going to have a single class in the Model for this basic example, a Credentials class.

namespace WcfSimpleTokenExample.Model
{
    public class Credentials
    {
        public string User { get; set; }
        public string Password { get; set; }
    }
}

Interfaces

using WcfSimpleTokenExample.Model;

namespace WcfSimpleTokenExample.Interfaces
{
    public interface ICredentialsValidator
    {
        bool IsValid(Credentials creds);
    }
}
using WcfSimpleTokenExample.Model;

namespace WcfSimpleTokenExample.Interfaces
{
    interface ITokenBuilder
    {
        string Build(Credentials creds);
    }
}
namespace WcfSimpleTokenExample.Interfaces
{
    public interface ITokenValidator
    {
        bool IsValid(string token);
    }
}

Business Implementations

using WcfSimpleTokenExample.Interfaces;
using WcfSimpleTokenExample.Model;

namespace WcfSimpleTokenExample.Business
{
    public class CodeExampleCredentialsValidator : ICredentialsValidator
    {
        public bool IsValid(Credentials creds)
        {
            // Check for valid creds here
            // I compare using hashes only for example purposes
            if (creds.User == "user1" && Hash.Get(creds.Password, Hash.HashType.SHA256) == Hash.Get("pass1", Hash.HashType.SHA256))
                return true;
            return false;
        }
    }
}
using System.Security.Authentication;
using WcfSimpleTokenExample.Interfaces;
using WcfSimpleTokenExample.Model;

namespace WcfSimpleTokenExample.Business
{
    public class CodeExampleTokenBuilder : ITokenBuilder
    {
        internal static string StaticToken = "{B709CE08-D2DE-4201-962B-3BBAC74C5952}";

        public string Build(Credentials creds)
        {
            if (new CodeExampleCredentialsValidator().IsValid(creds))
                return StaticToken;
            throw new AuthenticationException();
        }
    }
}
using WcfSimpleTokenExample.Interfaces;

namespace WcfSimpleTokenExample.Business
{
    public class CodeExampleTokenValidator : ITokenValidator
    {
        public bool IsValid(string token)
        {
            return CodeExampleTokenBuilder.StaticToken == token;
        }
    }
}

I also use the Hash.cs file from this post: A C# class to easily create an md5, sha1, sha256, or sha512 hash.

Demo

I use the Postman plugin for Chrome.
Postman

Step 1 – Authenticate and acquire token

  1. Set the url. In this example, it is a local debug url:
    http://localhost:49911/Services/AuthenticationTokenService.svc/Authenticate.
  2. Set a header value: Content-Type: application/json.
  3. Add the body: {“User”:”user1″,”Password”:”pass1″}
  4. Click Send.
  5. Copy the GUID returned for the next step.

PostmanAuthReceive

Step 2 – Call subsequent service

  1. Set the url. In this example, it is a local debug url:
    http://localhost:49911/Services/Test1Service.svc/Test
  2. Add a header called “Token” and paste in the value received from the authentication step

PostmanTestReceive

Part 1 uses examples that are in subbed in statically in the code. In Authentication Token Service for WCF Services (Part 2 – Database Authentication), we will enhance this to use a database for credentials validation and token storage and token validation.