FreeBSD FridayProgramming / DevelopmentWPF Databinding TutorialMy Posts

Welcome to my technology learning website. The goal of this site is to learn and retain as much information as possible. I consider this blog my knowledge base and hope that you both find and share knowledge here as well.Also for those wondering, Rhyous is pronounced ‘rī-əs. As in the words ‘Rye‘ and ‘us‘ compounded into a two syllable word.

Please take a moment to subscribe, or setup an RSS feed so you never miss a post.

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

In the previous segment, Basic 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.
using System;
using System.Linq;
using System.Security.Authentication;
using WcfSimpleTokenExample.Database;
using WcfSimpleTokenExample.Interfaces;

namespace WcfSimpleTokenExample.Business
{
    public class DatabaseTokenBuilder : ITokenBuilder
    {
        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 = Guid.NewGuid().ToString();
            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;
        }
    }
}

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"];
            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.

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.

Leave a Reply

Powered by sweet Captcha