Todo with Basic Authentication

4/26/2013
In .NET

Purpose


I wrote in an earlier article that one of the changes that I see happening in the computer industry is the diversification of clients and the rise of JavaScript as a client-side scripting language. These clients need to interact with a service-based architecture in order to work with data. The Microsoft .Net environment is a very rich environment for creating that service-based architecture to meet the needs of a diverse client base.

In this article we will create a service architecture that is suitable for serving data to many different kinds of clients. We will build on the standard Todo example that can be installed in Visual Studio via the NuGet Package Manager.

The Todo sample project is interesting because it is built as a single page application using the Backbone JavaScript library. However, the existing example is lacking several basic capabilities.

  1. It doesn’t track Todos per user – all users share the same list!
  2. It doesn’t persist the Todos in a database, it just stores them in memory on the server. That’s fine for those of you who have servers that never go down and have infinite amounts of memory, but for the rest of us mere mortals, we need to persist that information on disk via a database.

In this article we are going to change the existing Todo project to meet the two deficiencies noted above. Users will be identified via HTTP Basic authentication, a well understood and universally supported part of the HTTP protocol.

Please note there will still be several needs that need to be met if this application is going to be moved to production. First, the ajax calls between client and server are not encrypted, so usernames and passwords will be passed in clear text. The service would have to be implemented via HTTPS to encrypt the communication channel between client and server for security. Second, the usernames and passwords are going to be embedded in JavaScript on the client at the end of the article. Obviously we would need to update the client code to allow the user to enter a username and password on startup.

So, consider this article a starting point, learning how to configure a service architecture to track users via HTTP Basic authentication. We offer a number of courses which will help you learn how to build on this foundation, including creating client applications via HTML5 for Visual Studio 2010 (10953) and Visual Studio 2012 (20480). We also offer courses showing how to set up an architecture on the server for serving web pages (10267, 10264) and data (10263).

Prerequisites


This section lists the software required to complete this example.

  1. Visual Studio 2010 or 2012.
  2. NuGet Package Manager (I used version 2.2.40116.9051).

Overview


This section lists the major steps that make up this exercise.

  1. Set up default Todo project.
  2. Cleanup and refactor existing code.
  3. Create entries in the web.config file for Users and the database.
  4. Setup Basic authentication with appropriate user accounts.
  5. Create a POCO for storing Todo by UserId.
  6. Update the Repository Code to Track Todos by UserId.
  7. Implement Basic Authentication.
  8. Add a Repository for Storing Todos in the Database.

Set up default Todo project.


  1. Open Visual Studio 2010 or 2012.
  2. Create a new ASP.NET Empty Web Application named SSTodoAuth.
  3. In Solution Explorer, right-click the project, select “Manage NuGet Packages…”.
  4. Install the package titled “Starter ASP.NET Website Template – ServiceStack at /” (I used version 3.9.43).
  5. Close NuGet Package Manager.

Cleanup and refactor existing code.


The existing code structure needs to organized. After this series of steps, we will have the same functionality as before but the code will be in a structure much more conducive to expansion.

  1. Open App_StartWebServiceExample.cs file.
  2. Delete the classes named Hello, HelloResponse and HelloService, we won’t be using these.
  3. Open App_StartAppHost.cs file.
  4. On line 28, change the statement
    typeof(HelloService).Assembly

    to

    typeof(TodosService).Assembly
  5. Comment out the exising Routes.Add… statements on lines 36-38.
  6. I like to keep all my routing statements in the AppHost.cs file instead of setting them up as attributes on the request and response DTOs. To do this, add the following new statements just below your commented code (be careful of <Todos> (plural) versus <Todo> (singular)):
    Routes
    	.Add<Todos>("/todos")
    	.Add<Todos>("/todos/{Ids}")
    	.Add<Todo>("/todos", "POST")
    	.Add<Todo>("/todos/{Id}", "PUT");
  7. Change back to App_StartWebServiceExample.cs.
  8. Remove all the [Route(…)] attributes from the Todos and Todo class definitions.
  9. We’re going to differentiate between a TodoDTO and a TodoEntity in our project (we’ll discuss the reason why in a later section). Find the definition of the Todo class on line 30. Right-click, Refactor->Rename to TodoDTO, then click OK, then click Apply.
  10. Go to the bottom of the file and delete all the existing comments and commented code relating to ServiceStack’s C# clients (lines 102-117). We won’t need these since we will be interacting with our service via Backbone/JQuery.
  11. We’re going to put all of our models in one file and our repository definitions in another file. Add a new class file to the App_Start folder named Models.cs.
  12. In the Models.cs file, change the namespace to SSTodoAuth (ie. remove the .App_Start from the namespace name) and remove the Models class definition.
  13. Add the following statement at the top of the Models.cs file.
    using ServiceStack.ServiceHost;
    using ServiceStack.DataAnnotations;
  14. From the App_StartWebServiceExamples.cs file, move the definitions of the existing Todos and TodoDTO classes to the Models.cs file.
  15. Add a new class file to the App_Start folder named Repositories.cs.
  16. In the Repositories.cs file, change the namespace to SSTodoAuth (ie. remove the .App_Start from the namespace name) and remove the Repositories class definition.
  17. Add the following statement at the top of the Repositories.cs file:
    using ServiceStack.Common;
    using ServiceStack.OrmLite;
    using System.Data;
  18. Move the definition of the class named TodoRepository from the WebServiceExample.cs file to the Repositories.cs file.
  19. The existing list of Todo items is stored in a List<TodoDTO> in memory. While adequate for demonstration purposes, we will need to persist this and other data to a database. In preparation for that portion of the exercise, we’ll create a new interface based on the existing TodoRepository class. Find the class definition, Right-Click, choose Refactor->Extract Interface. In the “Extract Interface” dialog box, Click “Select All”, then click “OK”. A new file is created for you with the interface definition.
  20. Move the interface definition from the ITodoRepository.cs file and place it in the Repositories.cs file, before the existing TodoRepository class definition.
  21. Change the TodoRepository class definition to implement ITodoRepository directly (ie. change SSTodoAuth.App_Start.ITodoRepository to ITodoRepository).
  22. Compile and run the project, make sure everything works.

Create Entries in the web.config file for Users and the Database


When we get Basic authentication working, we’ll need some accounts to use. I am going to set up an infrastructure so that a single default Admin and User account are created when the project starts.

  1. Open the project’s web.config file.
  2. Add the following xml right below the existing element, substituting your preferred usernames and passwords if my lame examples are not satisfactory.
    <appSettings>
        <add key="AdminUserName" value="Admin"/>
        <add key="AdminPassword" value="IAmAdmin"/>
        <add key="StdUserName" value="User"/>
        <add key="StdPassword" value="IAmUser"/>
      </appSettings>
  3. Add xml configuration to create a database connection. This database will eventually store both our user account information and the Todos for each user. Here are my configuration settings.
    <connectionStrings>
        <add name="SSTodoAuth"
             connectionString="Data Source=.SqlExpress;Initial Catalog=SSTodoAuth;Integrated Security=SSPI;"
             providerName="System.Data.SqlClient" />
      </connectionStrings>
  4. Create the empty database that matches the information supplied above. You will not need to create any tables, these will be created for you by code in later sections.
  5. Again, compile and run the code to make sure everything is still working.

Setup Basic Authentication with Appropriate User Accounts


There is existing sample code set up for authentication. However, it is commented out. We’ll need to uncomment the relevant sections and add some code that creates the user accounts based on the information we added to the web.config file. Also, we’re going to refactor the code a bit to extract the database registration and ignore some of the authentication providers that we’re not using in this exercise.

There’s a lot of code to write in this section, but this is the worst of it. Once we get done with this section, we should be able to run our project and verify that the tables to store user account information, and the default user accounts, have been created in our database.

  1. Open the file App_StartAppHost.cs.
  2. Make sure the following compendium of using statements is at the top of the code file.
    using System;
    using System.Linq;
    using System.Configuration;
    using System.Collections.Generic;
    using ServiceStack.Configuration;
    using ServiceStack.OrmLite;
    using ServiceStack.ServiceInterface;
    using ServiceStack.ServiceInterface.Auth;
    using ServiceStack.ServiceInterface.ServiceModel;
    using ServiceStack.WebHost.Endpoints;
    using System.Data;
  3. Create a new method named RegisterDatabase, as shown below, inside the existing AppHost class definition.
    private void RegisterDatabase(Funq.Container container)
    {
    	var connectionString = ConfigurationManager
    		.ConnectionStrings["SSTodoAuth"]
    		.ConnectionString;
    
    	container.Register<IDbConnectionFactory>(
    		new OrmLiteConnectionFactory(connectionString, 
    			SqlServerDialect.Provider));
    }
  4. In the existing Configure method, add a call to the method you just created. This should be after the Routes.Add statements you created in an earlier exercise. It should look like the following.
    RegisterDatabase(container);
  5. Uncomment the method named ConfigureAuth. The easiest way is to remove the single commented line just above the method definition, and the single comment termination character (“*/”) at the end of the method definition.
  6. Comment out the lines for the CredentialsAuthProvider, FacebookAuthProvider, and TwitterAuthProvider in the list of existing plugins used for authentication. However, leave the BasicAuthProvider uncommented. That section of code should look like the following when you are done.
    Plugins.Add(new AuthFeature(() => new CustomUserSession(),
    	new IAuthProvider[] {
    		//new CredentialsAuthProvider(appSettings), 
    		//new FacebookAuthProvider(appSettings), 
    		//new TwitterAuthProvider(appSettings), 
    		new BasicAuthProvider(appSettings), 
    	}));
  7. Since we have a separate method that configures our database connection, we can remove the following lines of code in the ConfigureAuth method.
    //Requires ConnectionString configured in Web.Config
    var connectionString = 	ConfigurationManager.ConnectionStrings["AppDb"].ConnectionString;
    container.Register<IDbConnectionFactory>(c =>
        new OrmLiteConnectionFactory(connectionString, SqlServerDialect.Provider));
  8. Back in the Configure method, remove the comment characters from the line that calls the ConfigureAuth method.
  9. Add a method to the existing AppHost class named CreateUser that is able to add a user to the UserAuth table. The code is shown below.
    private void CreateUser(Funq.Container container, 
    int id, 
    string username, 
    string email, string password, 
    List<string> roles = null, 
    List<string> permissions = null)
    {
    	string hash;
    	string salt;
    	new SaltedHash().GetHashAndSaltString(password, out hash, out salt);
    
    	((OrmLiteAuthRepository)container
    		.Resolve<IUserAuthRepository>())
    		.CreateUserAuth(new UserAuth
    	{
    		Id = id,
    		DisplayName = username,
    		Email = email ?? string.Format("user{0}@invalid.net", id),
    		UserName = username,
    		FirstName = "FirstName",
    		LastName = "LastName",
    		PasswordHash = hash,
    		Salt = salt,
    		Roles = roles,
    		Permissions = permissions
    	}, password);
    }
  10. We’ll need one more utility method, named GetNextAvailableId, to get unique Ids for our users as they are created. Add the method to the existing AppHost class as shown below.
    private int GetNextAvailableId(Funq.Container container)
    {
    	var repo = container.Resolve<IDbConnectionFactory>();
    	using (var db = repo.OpenDbConnection())
    	{
    		return db.GetScalar<UserAuth, int>(e => Sql.Max(e.Id)) + 1;
    	}           
    }
  11. There’s an extension method which used to exist in earlier versions of .Net which I still use. We’ll add our own definition of the method in the App_StartModels.cs file. Add the following class and method definition inside the namespace but after the existing Todos and TodoDTO class definitions.
    public static class ModelExtensions
    {
        public static bool IsNullOrEmpty(this string s)
        {
            return (s == null) || (s.Trim().Equals(string.Empty));
        }
    }
  12. The end’s in sight! Move back to the App_StartWebServiceExamples.cs file. We’ll need a method that reads the user account information out of our web.config file and creates the necessary users if they haven’t been created already. Add the following method to the existing AppHost class.
    private void SetupUsers(Funq.Container container)
    {
    	IUserAuthRepository repo = container.Resolve<IUserAuthRepository>();
    
    	string adminUserName = ConfigurationManager.AppSettings["AdminUserName"];
    	string adminUserPassword = ConfigurationManager.AppSettings["AdminPassword"];
    
    	if (!adminUserName.IsNullOrEmpty() && !adminUserPassword.IsNullOrEmpty())
    	{
    		UserAuth userAuth = null;
    		if (!repo.TryAuthenticate(adminUserName, adminUserPassword, out userAuth))
    		{
    			List<string> roles = new List<string>();
    			roles.Add("Admin");
    
    			var newId = GetNextAvailableId(container);
    			CreateUser(container, newId, adminUserName, null, adminUserPassword, roles);
    
    			repo.TryAuthenticate(adminUserName, adminUserPassword, out userAuth);
    		}
    
    		if (userAuth != null && !userAuth.Roles.Contains("Admin"))
    		{
    			userAuth.Roles.Add("Admin");
    			repo.SaveUserAuth(userAuth);
    		}
    	}
    
    	string userName = ConfigurationManager.AppSettings["StdUserName"];
    	string userPassword = ConfigurationManager.AppSettings["StdPassword"];
    
    	if (!userName.IsNullOrEmpty() && !userPassword.IsNullOrEmpty())
    	{
    		UserAuth userAuth = null;
    		if (!repo.TryAuthenticate(userName, userPassword, out userAuth))
    		{
    			var newId = GetNextAvailableId(container);
    			CreateUser(container, newId, userName, null, userPassword);
    		}
    	}
    }
  13. Back in the Configure method, uncomment the line of code that runs the ConfigureAuth(container) method.
  14. Last bit of code to change in this section! After the line that invokes ConfigureAuth (which you just uncommented), write a line of code the runs the SetupUsers method like so.
    SetupUsers(container);
  15. You should be able to successfully compile and run the code. If the code runs, you should be able to open the database and see the UserAuth table which has been created. The contents of the table should contain two entries for the two users the system creates by default. If you’re having problems, make sure your database has been created with the proper name (step 4 of the previous section) and that the strings in the SetupUsers method match the entries in the <appSettings> section of the web.config file.

Create a POCO for Storing Todo Information in the Database


We are setting up our application to serve multiple users. The current TodoDTO class does not have any ability to track Todos by user. We are therefore going to have to create a new C# class that corresponds to the structure of data in the database. This new class, called TodoEnt, is going to be different from TodoDTO in that it will have a UserId field which will track which Todo belongs to which user.

For security reasons, however, we don’t want to send the UserId field back and forth to the end user’s computer. Therefore, we can’t just serialize the TodoEnt objects. As a result, we are going to be converting back and forth between TodoDTO objects and TodoEnt objects a lot, so we’ll write a couple of extension methods that are going to make this utilitarian chore simple.

  1. Open the App_StartModels.cs file.
  2. Make a copy of the exiting TodoDTO class (without the attributes), paste the copy in, and rename it TodoEnt. Add an autoimplemented property of type int named UserId. Finally, add an [AutoIncrement] attribute to the Id field. The code should look like the following.
    public class TodoEnt
    {
    	[AutoIncrement]
    	public long Id { get; set; }
    	public string Content { get; set; }
    	public int Order { get; set; }
    	public bool Done { get; set; }
    	public int UserId { get; set; }
    }
  3. Add an extension method named ToDTO to the existing class named ModelExtensions. It should look like the following.
    public static TodoDTO ToDTO(this TodoEnt todoEnt)
    {
    	return new TodoDTO()
    	{
    		Id = todoEnt.Id,
    		Content = todoEnt.Content,
    		Done = todoEnt.Done,
    		Order = todoEnt.Order
    	};
    }
  4. Add another extension method, in the ModelExtensions class, to convert back from a TodoDTO to a TodoEnt, as follows. Notice this method takes an extra parameter, which we’ll use to populate the userId on the entity object.
    public static TodoEnt ToEntity(this TodoDTO todo, int userId)
    {
    	return new TodoEnt()
    	{
    		Id = todo.Id,
    		Content = todo.Content,
    		Done = todo.Done,
    		Order = todo.Order,
    		UserId = userId
    	};
    }
  5. Make sure your code compiles cleanly. The code should run, but we’re not yet taking advantage of any of the code we have just written.

Update the Repository to Track Todos by UserId


The existing repository does not track Todos by UserId. We will update the code to accomodate this requirement, giving our system the capability to become multi-user!

  1. Open the file App_StartRepositories.cs.
  2. Rename the existing TodoRepository class to TodoMemRepository using the Refactor->Rename functionality we’ve used previously.
  3. Change the exising method descriptions in the ITodoRepository interface to take UserId as the first parameter. The code should end up looking something like the following.
    public interface ITodoRepository
    {
    	void DeleteByIds(int userId, params long[] ids);
    	List<TodoDTO> GetAll(int userId);
    	List<TodoDTO> GetByIds(int userId, long[] ids);
    	TodoDTO Store(int userId, TodoDTO todo);
    }
  4. Next, we’ll need to start fixing up our repository. Let’s change the datatype for our list of todos. At the top of the code file, change List<TodoDTO> to List<TodoEnt> as shown.
    List<TodoEnt> todos = new List<TodoEnt>();
  5. We’ll need to fix up all the existing methods in the TodoMemRepository to accomodate the new parameter. Here’s the code for the method GetByIds.
    public List<TodoDTO>GetByIds(int userId, long[] ids)
    {
    	return todos
    		.Where(t => t.UserId == userId && ids.Contains(t.Id))
    		.ToList()
    		.ConvertAll(t => t.ToDTO());
    }
  6. Here’s the GetAll method.
    public List<TodoDTO> GetAll(int userId)
    {
    	return todos
    		.Where(x => x.UserId == userId)
    		.ToList()
    		.ConvertAll(t => t.ToDTO());
    }
  7. The Store method.
    public TodoDTO Store(int userId, TodoDTO todo)
    {
    	var existing = todos.FirstOrDefault(x => x.UserId == userId && x.Id == todo.Id);
    	if (existing == null)
    	{
    		var newId = todos.Count > 0 ? todos.Max(x => x.Id) + 1 : 1;
    		todo.Id = newId;
    		todos.Add(todo.ToEntity(userId));
    	}
    	else
    	{
    		existing.PopulateWith(todo.ToEntity(userId));
    	}
    	return todo;
    }
  8. The DeleteByIds method.
    public void DeleteByIds(int userId, params long[] ids)
    {
    	todos.RemoveAll(t => t.UserId == userId && ids.Contains(t.Id));
    }
  9. Note: At this point you will NOT be able to successfully compile your code. We’ll fix that in the next section.

Implement Basic Authentication


Well, we’ve certainly built enough infrastructure code! We are finally going to enforce Basic HTTP authentication on our service. We’ll demonstrate how ServiceStack can track sessions, and how to get the UserId from the session. We’ll also demonstrate how the browser can prompt for a username and password, and we’ll show how we can implement the username and password in the client side JavaScript code.

  1. Open the file App_StartWebServiceExamples.cs.
  2. Put the [Authenticate] attribute on the TodosService class definition.
  3. Add the following field definition at the top of the TodosService class.
    private int userId;
  4. Now that the code in the TodosService class is running as an authenticated user, there is going to be information in the session object which contains the user’s Id. We’ll grab that and feed it to each of the method calls when we invoke our repository methods. Add the following as the first line of code in each of the methods in the TodosService class.
    userId = int.Parse(this.GetSession().UserAuthId);
  5. Next, whenever any method is called from the Repository property, pass the userId as the first parameter. This should eliminate all of our errors and allow our code to compile. For example, in the Get method, instead of calling Repository.GetAll(), call Repository.GetAll(userId). Instead of Repository.GetByIds(request.Ids), call Repository.GetByIds(userId, request.Ids), and so on.
  6. Make sure the project compiles cleanly.
  7. Run the project. After the Todos screen comes up, the browser should prompt you for a username and password. This is basic authentication in action! If you type in either username/password combination from your web.config file, the project should run correctly and store Todos. If Todos are not being stored, it may be because you’re not using a correct username/password combination.
  8. Next, we’re going to eliminate the dialog box that pops up when the browser opens. To do that, open the default.htm file. Just above the line that starts window.Todo = …, around line 22, we’ll add the following line of code.
    $.ajaxSetup({ username: "Admin", password: "IAmAdmin" });
  9. Run the project again. It should run without error and without any dialog boxes appearing prompting you for a username and password. What the line of code above does is pass those settings be default for every ajax call made by jQuery. It is not the best way of handling usernames and passwords on the client side, but this is one of those extension points that I noted in the introduction to the article. Take some of our courses to learn how to eliminate the hardcoded information.

Add a Repository for Storing Todos in the Database

The existing repository, as mentioned earlier in the article, simply stores Todos in a List. That’s fine for demonstration purposes, but not for persistence purposes. We need to write a repository that will store and retrieve Todo information from the database. It needs to conform to the ITodoRepository interface that we created previously.

  1. Open the file App_StartWebServiceExamples.cs.
  2. Add the following using statements to the top of the file.
    using ServiceStack.OrmLite;
    using System.Data;
  3. Rename the existing TodoRepository class to TodoMemRepository using the Refactor->Rename functionality we’ve used previously.
  4. Create a new class named TodoDbRepository that implements ITodoRepository. Have the system implicitly implement the interface (Right-Click TodoDbRepository: ITodoRepository, Select Implement Interface -> Implement Interface).
  5. We now have several broken methods in our new class. Before we can fix them up, we need to way to access a connection to our database. Add a private autoimplemented property named DbFactory, of type IDbConnectionFactory, to our class. Then populate it with a constructor, as shown. This code will also “automagically” create a new table instance if the table doesn’t exist in the database.
    protected IDbConnectionFactory DbFactory { get; set; }
    
    protected IDbConnection Db
    {
    	get { return DbFactory.Open(); }
    }
    
    public TodoDbRepository(IDbConnectionFactory factory)
    {
    	DbFactory = factory;
    	Db.CreateTableIfNotExists<TodoEnt>();
    }
  6. Replace the existing code in the GetAll method with the following.
    return Db
    	.Select<TodoEnt>(t => t.UserId == userId)
    	.ConvertAll(t => t.ToDTO());
  7. Here’s the code for GetByIds.
    return Db
    	.Select<TodoEnt>(t => t.UserId == userId && Sql.In(t.Id, ids))
    	.ConvertAll(t => t.ToDTO());
  8. The Store method looks like the following.
    TodoEnt todoEnt = todo.ToEntity(userId);
    if (todoEnt.Id < 1)
    {
    	Db.Insert<TodoEnt>(todoEnt);
    	todo.Id = Db.GetLastInsertId();
    }
    else
    {
    	Db.Update<TodoEnt>(todoEnt);
    }
    return todo;
  9. Last method, the DeleteByIds code.
    if (ids != null)
    {
    	foreach (long id in ids)
    	{
    		Db.Delete<TodoEnt>(t => t.UserId == userId && t.Id == id);
    	}
    }
  10. To get our code base to switch back and forth between the two repositories, we need to make two changes. First, at the top of our TodosService class (in App_StartWebServiceExamples.cs), instead of hardcoding the type TodoMemRepository, we set the datatype on the Repository variable to ITodoRepository. The line of code in question should look like the following.
    public ITodoRepository Repository { get; set; }  //Injected by IOC
  11. Next, open the App_StartAppHost.cs file. At the very bottom of the Configure method is a line that registers the TodoMemRepository. Replace that existing line of code with the following two lines.
    //container.Register<ITodoRepository>(c => new TodoMemRepository());
    container.Register<ITodoRepository>(c => new TodoDbRepository(c.Resolve<IDbConnectionFactory>()));
    
  12. Now, when you run the code, the todos will be stored in the database. Go ahead and run the project, adding and removing some todos. Stop and restart the project, and the todos should still appear. Stop the project, edit the default.htm file to register as the other user, and run the code again. You should be able to switch back and forth between the two users and see all of their todos preserved between invocations.

Conclusion


We now have a much more functional Todo application. Service offerings need to be able to track information by user and persist that information in a database. Both of those goals have been accomplished in this article.

As noted in the article’s introduction, consider this a starting point. We offer a number of courses which will help you learn how to build on this foundation, including creating client applications via HTML5 for Visual Studio 2010 (MS10953) and Visual Studio 2012 (MS20480). We also offer courses showing how to set up an architecture on the server for serving web pages (MS10267, MS10264) and data (MS10263). Come and learn from us here at LRS Education Services!