A More Useful Port of the Chinook Database to Postgresql

Posted on April 5 2015 12:26 PM by jatten in Database, Postgres, CodeProject   ||   Comments (0)

chinookI use Chinook Database as the sample database for a lot of things, primarily because the same data set is available for most of the major database platforms, so it's easy to port/compare etc. Chinook Database offers up a set of tables with enough sample data to satisfy most testing/demo needs, in a general enough schema that it is easy to get something up and running quickly.

However, the Chinook database as made available at the Chinook site maintains the same object naming conventions (proper-cased table and column names) across database platforms, and this I DON'T like. Also, the Postgres version of Chinook available as part of the Chinook package uses integer primary keys, but not auto-incrementing integers (this corresponds to the serial data type in Postgres).

Image by Ingrid Talar  |  Some Rights Reserved

Because I use Chinook fairly often with Postgres, I decided to make a few mods to the Chinook Postgres script to fix these things, and provide a more native PG experience when using this otherwise handy sample database.

Use Postgres-Idiomatic SQL

Postgresql ("Postgres" or "pg") has its roots in the Unix world. Database object names are case-sensitive, and in fact the convention is to use lower-case names, and where needed, separate with underscores. It is possible to use proper-cased object names in Postges by escaping them with double-quotes. However, this makes for some atrocious-looking SQL.

A sample excerpted from the Chinook script Postgres perfectly illustrates this:

Excerpt from Standard Postgres Chinook Script:
CREATE TABLE "Album"
(
    "AlbumId" INT NOT NULL,
    "Title" VARCHAR(160) NOT NULL,
    "ArtistId" INT NOT NULL,
    CONSTRAINT "PK_Album" PRIMARY KEY  ("AlbumId")
);

 

The above forces the Album table to be created with proper-cased object names (the table name itself, as well as the columns). However, from this point forward you will need to use the double-quote escapes in any SQL you push in which requires the use of object names explicitly.

At a minimum, you would need to do THIS to pull all the records from the Album table:

Select all the Records from the Chinook DB Album Table:
SELECT * FROM "Album"

 

Note the quotes around the table name. If you don’t use those, you’ll get an error indicating that the table album doesn’t exist. This is because Postgres will automatically down-case incoming SQL before processing it.

Now let’s imagine you wanted to set some criteria on that SELECT statement:

Select some specific Records from the Chinook DB Album Table:
SELECT * FROM "Album" WHERE "ArtistId" = 2

 

Again with the double-quote escape sequence. Things continue to go downhill from there. Imagine a simple JOIN between two tables with a minimal set of columns returned:

Select Records from the Chinook DB Album Table using a JOIN to Artists:
SELECT 
  ar."Name", 
  al."Title"
FROM 
  "Album" AS al, 
  "Artist" AS ar
WHERE 
  al."ArtistId" = ar."ArtistId";

 

Yeah. Now things start to look ugly.

Composing SQL against this (and even worse, code which incorporates said SQL) gets painful fast.

Use Serial Integer Primary Keys

The maintainers of Chinook Database for some reason chose not to implement auto-incrementing integers for table primary keys in the Postgres version. Whatever the reason, I want auto-incrementing integer keys on the tables in my Chinook database.

Making this happen took a little doing, since the sample data itself establishes table relationships based on existing primary key data. How to solve this?

The Postgres serial type utilizes sequences to maintain the current value of a particular serial column. We can specify the starting value of a sequence, and the amount by which it is incremented, using the Postgres CREATE SEQUENCE command:

Create Sequence Example:
CREATE SEQUENCE "albums_id_seq"
 INCREMENT 1
 MINVALUE 1
 MAXVALUE 2147483647
 START 347
 CACHE 1;
SELECT setval('"public"."albums_id_seq"', 347, true);

 

But wait, John – you pluralized the name of the albums table there. Also, what’s with that setting the START value at 347?

Good question. We’ll get to the pluralized naming convention – I changed that for the entire Chinook script. As for the initial value, the Chinook sample data set contains an initial set of 347 album records. Therefore, we want to set an initial value for our table PK sequence at 347, so that the next ID provided will be 348. 

Down-Casing Object Names, and Simplifying

I went through the Chinook script for Postgres, and modified the object names for all of the tables and columns, down-casing everything, and adding underscores as needed. I also pluralized the table names themselves. With that in mind, as well as the fact that we want to use a serial data type for our primary key columns, the create statement for our albums table might now look more like this:

Modified Create SQL for Chinook Album Table:
CREATE TABLE albums
(
    id int DEFAULT nextval('albums_id_seq'::regclass) NOT NULL,
    title VARCHAR(160) NOT NULL,
    artist_id INT NOT NULL,
    CONSTRAINT pk_albums PRIMARY KEY  (id)
);

 

Notice we have pluralized the table name, and also used a straight-forward id as the name for the primary key column, doing away with the problematic AlbumId (we could have used album_id, but why?). We set the default value for the id using the Postgres nextval() function, which will pull the next value based on the sequence we created unless a value is provided.

Get the Modified Chinook Script from Github

You can find the full, modified version of the Chinook database script at my Github repo. There are two versions, each on a separate branch.

Branch Master contains the full Chinook script, with down-cased/underscore-separated object names. However, table names have not been pluralized, and table primary key columns maintain the underscored version of the original column name. For example, AlbumID becomes simply album_id.

Branch pg_names contains the same database, but with table names pluralized, and table PK column names reduced to simply id. In other words, the table Album in the original Chinook DB is now albums, and the PK for albums is simply id.

See Something Amiss?

I am not a DBA, and it is possible I did something stupid in here. I like to think I know enough database to do those things I need to do as a developer. However, if you see something I did that might be accomplished better another way, please do sound off in the comments, shoot me a PR on Github, or shoot me an email at the address in the “About the Author” section.

 

Posted on April 5 2015 12:26 PM by jatten     

Comments (0)

ASP.NET Web API: Understanding OWIN/Katana Authentication/Authorization Part III: Adding Identity

Posted on February 15 2015 06:19 PM by jatten in ASP.NET MVC, ASP.Net, C#, CodeProject   ||   Comments (7)

eyball-500This is the third post in a series in which we have built up a minimal, self-hosted, OWIN-based Web Api application essentially from scratch. Our objective has been to develop a better understanding of how the various components fir together and interact in an OWIN-based environment, and to do so without creating any dependencies on IIS or the heavy weight System.Web.dll.

Up to this point, we have created a basic Web Api, and implemented our own authentication/authorization, using basic OWIN authorization, and our own set of models. What we have built so far represents a bare-bones model of how authentication and authorization work in an OWIN-based application. We have taken some architectural shortcuts in the name of maintaining a simple, easy to understand structure as again, our goal so far has emphasized concept over details.

Image by alles-schlumpf  |  Some Rights Reserved

Previous posts, in order:

In this post, we are going pull in the ASP.NET Identity framework, and ideally, we will again achieve a better understanding of how Identity fits in to a general Web Api application, and we will allow Identity to perform some of the heavy lifting for us when it comes to difficult, hard-to-get-right-even-for-the-experts details such as crypto and security details.

Source Code for Examples

We are building up a project over a series of posts here. In order that the source for each post make sense, I am setting up branches that illustrate the concepts for each post:

On Github, the branches of the Web Api repo so far look like this:

  • Branch: Master - Always the most current, includes all changes
  • Branch: auth-db - The code we build up in the course of the previous post, adding a persistence layer for our authentication system.
  • Branch: auth-identity - the code we will build out in the course of this post. We will start where we left off in the previous post, and modify to bring in a minimal implementation using Identity Framework.

The code for the API client application is in a different repo, and the branches look like this:

  • Branch: Master - Always the most current, includes all changes
  • Branch: owin-auth - Added async methods, and token-based authentication calls to the Web Api application. The code for the client application remains unchanged, except when we need to switch out user credentials.

In the previous article, we created create the classes MyUser and MyUserClaim classes in order to implement our authorization and authentication mechanism in the OWIN/Katana environment, and which also became our code-first models for database persistence via Entity Framework. We also created the MyUserStore class, which contained the methods necessary to save and retreive user authentication data from our backing store.

We had assembled these very rudimentary classes into a functioning authentication and authorization framework of sorts, using them in our application to perform the basic functions needed to properly authenticate a user, and establish a minimal authorization mechanism based upon the role claims possessed by each user.

In this post, we are going to use ASP.NET Identity instead, and replace our crude, homebuilt mechanism with a fully-functioning, if basic, auth system.

Core Identity Framework

To understand how Identity will fit into our application, and the OWIN/Katana environment, it is useful to examine the structure of Identity framework itself.

The actual core Identity library is Microsoft.AspNet.Identity.Core. This library defines a host of interfaces upon which the function of Identity is based, and a smaller number of concrete implementation classes which are expressed in terms of these interfaces. We actually already have this library in our project, because we pulled in the Microsoft.AspNet.Identity.Owin library previously via Nuget. We haven't used any of the identity components to this point, but we made use of some items from dependent libraries included in that Nuget package, such as Microsoft.Owin.Security and Microsoft.Owin.OAuth

In general, the models which might need to be consumed in an application are expressed as interfaces, and the internals of the framework provide implementation for those interfaces. It is up to the application, and/or any other frameworks pulled in, to provide the concrete implementations for these model interfaces.

For example, the core Identity framework provides us with a pair of interfaces to represent a User:

Two Versions of the IUser Interface:
// Interface with generic type argument for the Key:
public interface IUser<out TKey>
{
    TKey Id
    {
        get;
    }
 
    string UserName
    {
        get;
        set;
    }
}
// Interface derived from IUser<Tkey> Specifying string Key:
public interface IUser : IUser<string> { }

 

In a similar manner, most of the interfaces defined in the core Identity framework to represent persistence models are expressed in a manner which allows us to specify the type for the key to be used.

Additionally, many of the Identity interfaces are dependent on other interfaces in the framework. In these cases, the interface is expressed in terms of a generic type argument representing the concrete implementation of the dependency. For example, core Identity framework provides two interfaces to represent a UserStore:

Two Versions of the IUserStore Interface:
// Interface with generic type arguments for User, and the User Key:
public interface IUserStore<TUser, in TKey> : IDisposable
where TUser : class, IUser<TKey>
{
    Task CreateAsync(TUser user);
    Task DeleteAsync(TUser user);
    Task<TUser> FindByIdAsync(TKey userId);
    Task<TUser> FindByNameAsync(string userName);
    Task UpdateAsync(TUser user);
}
// Interface expressing IUserStore in terms of generic User type, 
// and specifying a string User Key
public interface IUserStore<TUser> : IUserStore<TUser, string>, IDisposable
where TUser : class, IUser<string>
{
}

 

If you explore the Microsoft.AspNet.Identity.Core library using a free tool such as Telerik's fine Just Decompile, you can explore the various interfaces and concrete classes available, and develop an understanding of how they relate. However, a quick look using even the VS Object Browser will reveal that there are no concrete implementations of the basic model interfaces we need in order to implement Identity in our application. For this, we either need to roll our own, or pull in another library which provides ready-to-use implementations.

Since we are already using Entity Framework, in our case this is easy.

Identity and Entity Framework

The Microsoft.AspNet.Identity.EntityFramework library provides concrete implementation classes needed to use Identity from an application using Entity Framework. In this library we find some model classes we can use as-is, or which we can extend and customize as needed. For example, the IdentityUser class provides a concrete implementation for IUser<Tkey> :

The Base IdentityUser Class:
// Base implements IUser<TKey> and is expressed with generic type arguments
// for other model types required by Identity Framework:
public class IdentityUser<TKey, TLogin, TRole, TClaim> : IUser<TKey>
    where TLogin : IdentityUserLogin<TKey>
    where TRole : IdentityUserRole<TKey>
    where TClaim : IdentityUserClaim<TKey>
{
    public IdentityUser()
    {
        this.Claims = new List<TClaim>();
        this.Roles = new List<TRole>();
        this.Logins = new List<TLogin>();
    }
 
    public virtual TKey Id { get; set; }
    public virtual string UserName { get; set; }
    public virtual string Email { get; set; }
    public virtual bool EmailConfirmed { get; set; }
    public virtual string PhoneNumber { get; set; }
    public virtual bool PhoneNumberConfirmed { get; set; }
 
    public virtual string SecurityStamp { get; set; }
    public virtual bool TwoFactorEnabled { get; set; }
    public virtual string PasswordHash { get; set; }
 
    public virtual int AccessFailedCount { get; set; }
    public virtual bool LockoutEnabled { get; set; }
    public virtual DateTime? LockoutEndDateUtc { get; set; }
 
    public ICollection<TLogin> Logins { get; set; }
    public ICollection<TRole> Roles { get; set; }
    public ICollection<TClaim> Claims { get; set; }
}
 
 
// Alternate implementation derives from Generic implementation, 
// and expresses Generic model types in terms of classes defined 
// within Identity.EntityFrameowrk Library: 
public class IdentityUser : IdentityUser<string, IdentityUserLogin, 
    IdentityUserRole, IdentityUserClaim>, IUser, IUser<string>
{
    public IdentityUser()
    {
        this.Id = Guid.NewGuid().ToString();
    }
 
    public IdentityUser(string userName) : this()
    {
        this.UserName = userName;
    }
}

 

We can see in the above example that there is a lot of functionality ready to go in the basic IdentityUser implementation. Also, note that the various generic type arguments provided in the base implementation are again provided such that we might extend our Identity model classes with custom implementations, for example, if we wanted to use integer keys instead of strings.

The Identity.EntityFramework library provides similar implementations for the other interfaces defined in Identity.Core. In addition, as we might expect, Identity.EntityFramework also includes the IdentityDbContext class, which knits together the various model classes, ready for use in a EF/Code-First application.

What is important here is understanding that the Identity.Core library provides the interfaces we need, and implements the interactions between those interfaces. It is up to us to provide the implementation for those interfaces, either by rolling our own, or by using a library specific to our persistence model such as Identity.EntityFramework.

Add Identity.EntityFramework Library Via Nuget

Since we are using Entity Framework in our minimal OWIN Web Api project, we will pull in the Identity.EntityFramework package we discussed above to take advantage of the ready-made Identity implementation afforded by the Identity team.

NOTE: This post assumes you are working with ASP.NET < 5.0, and Identity 2.1. As of this publication date, the newest package for this library is a pre-release of Identity 3.0 which targets ASP.NET 5. Unless you are working with ASP.NET 5.0 ("vNext") you want to make sure you are pulling in version 2.1

Add the Microsoft.AspNet.Identity.EntityFramework Nuget Package:
PM> Install-Package Microsoft.AspNet.Identity.EntityFramework -Version 2.1.0

 

We already have the Identity.Core library in our example project, because it was included when we pulled in the Identity.Owin Nuget package in Part I of this series.

Add Identity to the Self-Hosted Web Api - Models and Stores

To get started, we will be picking up where we left off in the previous post. Recall that we had added an AuthModels.cs file, and coded up a MyUser class, a MyUserClaim class, a MyPasswordHasher class, and a MyUserStore class.

We can get rid of all that now, delete the AuthModels.cs file.

In order to add the (mostly) ready-to-use Identity framework to our project, let's add a new code file named IdentityModels.cs, and add the following code:

Add Identity Models:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
// Add using statements:
using Microsoft.Owin;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.AspNet.Identity.EntityFramework;
 
namespace MinimalOwinWebApiSelfHost.Models
{
    public class ApplicationUser : IdentityUser
    {
        // A default Constructor:
        public ApplicationUser() { }
        
        public ApplicationUser(string email) : base(email)
        {
            // Use the email for both user name AND email:
            UserName = email;
        }
    }
 
 
    public class ApplicationUserManager 
        : UserManager<ApplicationUser>
    {
        public ApplicationUserManager(IUserStore<ApplicationUser> store) 
            : base(store) { }
 
 
        public static ApplicationUserManager Create(
            IdentityFactoryOptions<ApplicationUserManager> options,
            IOwinContext context)
        {
            return new ApplicationUserManager(
                new UserStore<ApplicationUser>(
                    context.Get<ApplicationDbContext>()));
        }
    }
}

 

Now, let's take a good hard look at what's going on in the above code.

First, we have added an ApplicationUser class, which derives from IdentityUser. IdentityUser is a concrete implementation if the Identity.Core IUser interface, and is provided by the Identity.EntityFramework library we discussed previously. We aren't doing much in this derived class at this point, but we will be adding to it later.

Next, we have the ApplicationUserManager class, which derives from UserManager. Unlike IdentityUser, the UserManager class is a part of the Identity.Core library. In other words, any application using the Identity framework can expect to have access to UserManager, or a derivation similar to what we have done above.

Notice that our ApplicationUserManager expects an argument of IUserStore as a constructor parameter. What's this?

We'll take a short detour to discuss UserManager and UserStore, and the purpose behind each.

UserManager and UserStore - What's the Difference?

The core Identity framework defines the UserManager class to implement various functionality required to, well, manage user information according to business rules and configuration established either by Identity framework itself, or as part of configuration options set up during development.

These framework "rules" and configuration items are independent of the specific persistence store used to save and retrieve the user identity data. In other words, the UserManager understands and works with Identity model objects, without concern for the concrete database underlying the application.

Examples of the sorts of functionality afforded by UserManager include methods such as:

  • CreateAsync(TUser user, string Password)
  • AddToRoleAsync(TKey userId, string role)
  • AddClaimAsync(Tkey userId, Claim claim)

The UserManager class is defined in terms of various interfaces which represent abstractions over database-specific implementation models. As an example, the basic UserManager is defined with a generic type argument for the IUser implementation, which must be specified. The UserManager class also requires a constructor argument which implements the IUserStore interface.

A concrete implementation of IUserStore represents the underlying persistence layer of the application. In other words, a UserStore implementation knows how to "talk" to a specific data store (such as MongoDb, SQL Server, RavenDb, Etc.).

In our code, we are using the default UserStore implementation provided by EntityFramework (which is, itself, an abstraction over our SQL CE or SQL Server database…).

The idea behind defining a UserManager base class in Identity.Core in terms of an interface IUserStore is to achieve a clean separation between the business rules governing how authentication and authorization occurs between Identity model objects, and the details of the application's specific backing store.

Within out application proper, when we use Identity we should generally be using the UserManager (or a derived version of it) the work directly with our identity model objects. We rarely have need to consume an instance of UserStore directly, except for the purpose of injecting it as a constructor argument to UserManager.

UserManager, UserStore, and Identity:

usermanager-userstore

Also notice in our code, we have added a static method, Create(). We'll discuss this momentarily as well. But first, we will update the code for our database context.

Update the DbContext to Inherit from IdentityDbContext

When we finished the last article in this series, we had updated our ApplicationDbContext code file to look like this:

The Previous DbContext:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
// Add using:
using System.Data.Entity;
using System.Security.Claims;
 
namespace MinimalOwinWebApiSelfHost.Models
{
    public class ApplicationDbContext : DbContext
    {
        public ApplicationDbContext()
            : base("MyDatabase")
        {
 
        }
 
        static ApplicationDbContext()
        {
            Database.SetInitializer(new ApplicationDbInitializer());
        }
 
        public IDbSet<Company> Companies { get; set; }
        public IDbSet<MyUser> Users { get; set; }
        public IDbSet<MyUserClaim> Claims { get; set; }
    }
 
 
    public class ApplicationDbInitializer 
        : DropCreateDatabaseAlways<ApplicationDbContext>
    {
        protected async override void Seed(ApplicationDbContext context)
        {
            context.Companies.Add(new Company { Name = "Microsoft" });
            context.Companies.Add(new Company { Name = "Apple" });
            context.Companies.Add(new Company { Name = "Google" });
            context.SaveChanges();
 
            // Set up two initial users with different role claims:
            var john = new MyUser { Email = "john@example.com" };
            var jimi = new MyUser { Email = "jimi@Example.com" };
 
            john.Claims.Add(new MyUserClaim 
            { 
                ClaimType = ClaimTypes.Name, 
                UserId = john.Id, 
                ClaimValue = john.Email 
            });
            john.Claims.Add(new MyUserClaim 
            { 
                ClaimType = ClaimTypes.Role, 
                UserId = john.Id, 
                ClaimValue = "Admin" 
            });
 
            jimi.Claims.Add(new MyUserClaim 
            { 
                ClaimType = ClaimTypes.Name, 
                UserId = jimi.Id, 
                ClaimValue = jimi.Email 
            });
            jimi.Claims.Add(new MyUserClaim 
            { 
                ClaimType = ClaimTypes.Role, 
                UserId = john.Id, 
                ClaimValue = "User" 
            });
 
            var store = new MyUserStore(context);
            await store.AddUserAsync(john, "JohnsPassword");
            await store.AddUserAsync(jimi, "JimisPassword");
        }
    }
}

 

In the same code file, we had defined both our ApplicationDbContext, and a database initializer.

Now that we have access to the Identity.Core and Identity.EntityFramework libraries, we can simplify this somewhat.

First of all, Identity.EntityFramework provides us with IdentityDbContext, which, as its name implies, is an Identity-specific implementation of the DbContext. There is a little more going on under the hood than we will cover here, but essentially, IdentityDbContext provides us with a base class from which we can inherit, and which provides a DbContext implementation which works with the base Identity.EntityFramework models we will be working with.

First, we are going to modify the code for our ApplicationDbContext above to derive from IdentityDbContext. Then, we will update the ApplicationDbInitializer to work with our new DbContext and Identity models.

Modify DbContext to Derive from IdentityDbContext:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
// Add using:
using System.Data.Entity;
using System.Security.Claims;
 
// Add THESE to use Identity and Entity Framework:
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
 
namespace MinimalOwinWebApiSelfHost.Models
{
    // Derive from IdentityDbContext:
    public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {
        public ApplicationDbContext()
            : base("MyDatabase") { }
 
 
        static ApplicationDbContext()
        {
            Database.SetInitializer(
                new ApplicationDbInitializer());
        }
 
 
        // Add a static Create() method:
        public static ApplicationDbContext Create()
        {
            return new ApplicationDbContext();
        }
 
 
        // We still need a DbSet for our Companies 
        // (and any other domain objects):
        public IDbSet<Company> Companies { get; set; }
    }
 
 
    public class ApplicationDbInitializer 
        : DropCreateDatabaseAlways<ApplicationDbContext>
    {
        protected async override void Seed(ApplicationDbContext context)
        {
            context.Companies.Add(new Company { Name = "Microsoft" });
            context.Companies.Add(new Company { Name = "Apple" });
            context.Companies.Add(new Company { Name = "Google" });
            context.SaveChanges();
 
            // Set up two initial users with different role claims:
            var john = new ApplicationUser 
            { 
                Email = "john@example.com", 
                UserName = "john@example.com" 
            };
            var jimi = new ApplicationUser 
            { 
                Email = "jimi@Example.com", 
                UserName = "jimi@example.com" 
            };
 
            // Introducing...the UserManager:
            var manager = new UserManager<ApplicationUser>(
                new UserStore<ApplicationUser>(context));
 
            var result1 = await manager.CreateAsync(john, "JohnsPassword");
            var result2 = await manager.CreateAsync(jimi, "JimisPassword");
 
            // Add claims for user #1:
            await manager.AddClaimAsync(john.Id, 
                new Claim(ClaimTypes.Name, "john@example.com"));
 
            await manager.AddClaimAsync(john.Id, 
                new Claim(ClaimTypes.Role, "Admin"));
 
            // Add claims for User #2:
            await manager.AddClaimAsync(jimi.Id, 
                new Claim(ClaimTypes.Name, "jimi@example.com"));
 
            await manager.AddClaimAsync(jimi.Id, 
                new Claim(ClaimTypes.Role, "User"));
        }
    }
}

 

In the above code, notice that we have now implemented our ApplicationDbContext by deriving from IdentityDbContext<ApplicationUser> . In so doing, we have specified a DbContext implementation which will be ready to use, and already work with our ApplicationUser class.

We have also updated our ApplicationDbInitializer, making use of the handy interface afforded by our ApplicationUserManager to easily add our test users, and related user claims.

Pay close attention here. Remember previously, how we went ahead and create a mock password hasher, and all that nonsense about implementing a proper crypto mechanism for hashing passwords? here, Identity has taken care of all that for us. No mocking needed. Just the way we like it, we have left the details of the crypto to folks who know what they are doing. Remember, crypto is hard, even for the experts, and it is a solved problem.

Now, before we go much further, we are going to take another short detour. Notice how, like we did with ApplicationUserManager, we also defined a static Create() method on our ApplicationDbContext? We're going to take a look at why we did that now.

Context per Request and CreatePerOwinContext

From the standpoint of our data model, we want to make sure that we are always working with the same instance of our database context, and hence, with the same set of objects. For example, if two separate instances of ApplicationDbContext (or, similarly, UserStore or UserManager) are created while processing the same HTTP request, it is possible we could introduce changes to two instances of the same  user data.

We want to make sure that when we retreive a user object, it will always refer to the same instance of that user's data within the context of a single HTTP request.

The Microsoft.AspNet.Identity.Owin library provides an extension method by which we can ensure a single instance of an object is created per OwinContext. The CreatePerOwinContext() method allows us to pass in a generic type argument, and a function reference which returns an instance of the desired object. We set this up during the Owin Configuration() method. Then, when the OwinContext object is created for each incoming HTTP request, a discreet instance of the desired object will be created per OWIN context.

For a deeper look at this concept, see Per request lifetime management for UserManager class in ASP.NET Identity

In our application, we want to make sure that during request processing, we are only ever working against the same instance of ApplicationDbContext, as well as ApplicationUserManager. We achieve this by adding those state Create() methods on ApplicationDbContext and ApplicationUserManager respectively, and then passing references to them to CreatePerOwinContext() during the Owin Configuration() method.

We will want to add the following code to our ConfigureAuth() method in our Startup class:

Create ApplicationDbContext and ApplicationUserManager per Owin Context:
private void ConfigureAuth(IAppBuilder app)
{
    // Create per OWIN Context:
    app.CreatePerOwinContext<ApplicationDbContext>(ApplicationDbContext.Create);
    app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
 
    var OAuthOptions = new OAuthAuthorizationServerOptions
    {
        TokenEndpointPath = new PathString("/Token"),
        Provider = new ApplicationOAuthServerProvider(),
        AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
 
        // Only do this for demo!!
        AllowInsecureHttp = true
    };
    app.UseOAuthAuthorizationServer(OAuthOptions);
    app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());

 

Recall from our previous explorations of OWIN and Katana that when we pass function references like this, the function itself is not executed at this point in our code. Instead, a reference to the function is added to the Owin Environment Dictionary, which will then be Invoked each time a new Owin context is created in response to an incoming HTTP request.

We can see some of the logic to all this if we take a closer look at the static Create() method we defined on our ApplicationUserManager class:

The Create() Method from ApplicationUserManager:
public static ApplicationUserManager Create(
    IdentityFactoryOptions<ApplicationUserManager> options,
    IOwinContext context)
{
    return new ApplicationUserManager(
        new UserStore<ApplicationUser>(
            context.Get<ApplicationDbContext>()));
}

 

Notice how even here, we are initializing an instance of UserStore and passing in a reference to ApplicationDbContext by retrieving it from the OwinContext instance? This way we ensure that even here, we are using the single DbContext object instance created specifically for each request.

Update OAuth Server Provider for Identity

Now that we have implemented a very basic Identity model, we can modify our ApplicationOauthServerProvider. In the call to GrantResourceOwnerCredentials(), we can avail ourselves of our ApplicationUserManager and Identity models to simplify the grant process.

Add Microsoft.AspNet.Identity.Owin to the using statements at the top of the file, and then replace the existing code as follows:

Update the code for GrantResourceOwnerCredentials for Identity:
using System.Threading.Tasks;
 
// Add Usings:
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.OAuth;
using System.Security.Claims;
using MinimalOwinWebApiSelfHost.Models;
 
// Add to use Identity:
using Microsoft.AspNet.Identity.Owin;
 
namespace MinimalOwinWebApiSelfHost.OAuthServerProvider
{
    public class ApplicationOAuthServerProvider 
        : OAuthAuthorizationServerProvider
    {
        public override async Task ValidateClientAuthentication(
            OAuthValidateClientAuthenticationContext context)
        {
            // This call is required...
            // but we're not using client authentication, so validate and move on...
            await Task.FromResult(context.Validated());
        }
 
 
        public override async Task GrantResourceOwnerCredentials(
            OAuthGrantResourceOwnerCredentialsContext context)
        {
            // ** Use extension method to get a reference 
            // to the user manager from the Owin Context:
            var manager = context.OwinContext.GetUserManager<ApplicationUserManager>();
 
            // UserManager allows us to retrieve use with name/password combo:
            var user = await manager.FindAsync(context.UserName, context.Password);
            if (user == null)
            {
                context.SetError(
                    "invalid_grant", "The user name or password is incorrect.");
                context.Rejected();
                return;
            }
 
            // Add claims associated with this user to the ClaimsIdentity object:
            var identity = new ClaimsIdentity(context.Options.AuthenticationType);
            foreach (var userClaim in user.Claims)
            {
                identity.AddClaim(new Claim(userClaim.ClaimType, userClaim.ClaimValue));
            }
 
            context.Validated(identity);
        }
    }
}

 

Once again, see in the above how we made sure to grab a reference to the ApplicationUserManager instance from the context object? The context object helpfully provides a GetUserManager() extension method.

Also note how the ApplicationUserManager makes it very convenient to retrieve a user object with a user name and password. If the password fails to match, null will be returned, and the invalid grant error returned.

Update the CompaniesController to Use the DbContext per Request

We don't HAVE to do this, but we can. We might update our CompaniesController to take advantage of the Context-per-Request strategy afforded by Identity here.

Make sure to add Microsoft.AspNet.Identity.Owin to the using statements at the top of the CompaniesController file.

Update CompaniesController to Use Context per Request:
public class CompaniesController : ApiController
{
    // Ditch THIS:
    //ApplicationDbContext dbContext = new ApplicationDbContext();
 
    // Replace with something like THIS:
    ApplicationDbContext dbContext
    {
        get
        {
            return Request.GetOwinContext().Get<ApplicationDbContext>();
        }
    }
 
    ... All the rest of the controller code....
 
}

 

With that, we can give our new and improved application a test run, using the same Api Client application from our previous post.

Running the Application with Identity in Place

If we spin up our Web Api, all should look well:

Running the Web Api Application - All is Well:

run-web-api-application

Next, if we run the API Client application, making sure we use our valid user credentials (they should match the credentials for the Admin user in the Seed() method), everything should work as before:

Running the API Client Application:

run-api-client-application-with-identity

Similarly, if we modify the code in our client application and pass an invalid password, we get an invalid grant error:

Invalid Password Submitted by Client Returns Invalid Grant Error:

run-api-client-application-with-identity-invalid-grant

And, if we change the client credentials to those of the user in the plain old User role (which does NOT have authorization to access our CompaniesController), we receive a 401/Unauthorized error:

API Client with Insufficient Authorization:

run-api-client-application-with-identity-unauthorized

Summing it Up

In the course of the last four articles, we have hopefully developed a better idea of how the pieces fit together in an OWIN-based Web Api application. The lines between the various frameworks become blurry as an application grows, and my objective was to break things down in a manner that would bring some clarity to what happens where, and why.

The structure of our example application is crude, and if you were to take this a few steps further, you would definitely want to change some things, do some refactoring, and implement a great deal more exception and error handling.

Similarly, our console-based API Client application is sufficient only to the task of exercising our Web Api for demonstration purposes.

From here, we could go a long ways further in developing a claims-based authorization model. As it stands, our little sample application does use claims, but relies on the in-build ability of [Authorize] attribute to perform a role-based authorization check. We will explore claims more extensively in an upcoming post.

As always, feedback is most welcome, especially if you noticed me doing something stupid, or find outright errors in the code. Pull Requests are welcome against the sample repo, so long as they address improvements or bug fixes. For obvious reasons, I want to keep the code samples in sync with the articles.

Additional Resources and Items of Interest

 

Posted on February 15 2015 06:19 PM by jatten     

Comments (7)

ASP.NET Web Api: Understanding OWIN/Katana Authentication/Authorization Part II: Models and Persistence

Posted on January 25 2015 09:23 AM by jatten in ASP.Net, ASP.NET MVC, C#   ||   Comments (6)

authorized-personnel-only-320In the previous post in this series we learned how the most basic authentication and authorization elements fit together in an OWIN-based Web Api application. We have seen how to authenticate a user using an Authentication Server embedded within our application, and how to add an elementary claim to use with the [Authorize] attribute.

To this point, we have been avoiding using the ready-built Identity framework, and instead we have been focusing on understanding how these pieces interrelate. We will continue this approach (for now) here, by adding some concrete authorization models to our application, and a persistence layer to store important user data.

Image by clement127  |  Some Rights Reserved

Once again, we will be doing most of this "from scratch," in a pretty minimal fashion. I want to explore the relationships between project components without too many distractions. So we're not attempting to design the optimal auth system here or demonstrate the latest best practices. But hopefully we will come away with a better understanding of how a fully developed authentication/authorization system such as Identity works in the context of our application. Understanding THAT gives empowers us to utilize tools like Identity more effectively.

From the Ground Up

In this series of posts we started with concepts, and are building slowly build from there.

  • Part I (last post) - We will examine the basic OAuth Resource Owner Flow model for authentication, and assemble to most basic components we need to implement authentication using this model. We will not be concerning ourselves with the cryptographic requirements of properly hashing passwords, or persisting user information to a database. We will also not be using Identity, instead implementing security using the basic components available in the Microsoft.Owin libraries.
  • Part II (this post) - We will mock up some basic classes needed to model our user data, and a persistence model to see how storage of user data and other elements works at a fundamental level.
  • Part III - We will replace our mock objects with Identity 2.0 components to provide the crypto and security features (because rolling your own crypto is not a good idea).

Source Code for Examples

We are building up a project over a series of posts here. In order that the source for each post make sense, I am setting up branches that illustrate the concepts for each post:

On Github, the branches of the Web Api repo so far look like this:

The code for the API client application is in a different repo, and the branches look like this:

  • Branch: Master - Always the most current, includes all changes
  • Branch: owin-auth - Added async methods, and token-based authentication calls to the Web Api application. This is where we left the code in the last post.

Adding Auth Models to the Minimal Web Api

We'll be starting from where we left off in the last post. Recall that We had set up a basic embedded authorization server in our application which would process HTTP POST requests made by a client to the token endpoint, validate the user credentials/password received, and return an access token. From there, the client could submit the access token with subsequent requests to authenticate, and access whichever resources are available for the given identity and/or role.

If we review our existing code for the ApplicationOAuthServerProvider, we see in the GrantOwnerResourceCredentials() method that we are performing a mock credentials check. In order to keep things simple, we just checked to see if the password submitted matched the string literal "password" and moved on:

The Existing GrantOwnerResourceCredentials Method:
public override async Task GrantResourceOwnerCredentials(
    OAuthGrantResourceOwnerCredentialsContext context)
{
    // DEMO ONLY: Pretend we are doing some sort of REAL checking here:
    if (context.Password != "password")
    {
        context.SetError(
            "invalid_grant", "The user name or password is incorrect.");
        context.Rejected();
        return;
    }
    // Create or retrieve a ClaimsIdentity to represent the 
    // Authenticated user:
    ClaimsIdentity identity = 
        new ClaimsIdentity(context.Options.AuthenticationType);
    identity.AddClaim(new Claim("user_name", context.UserName));
    identity.AddClaim(new Claim(ClaimTypes.Role, "Admin"));
  
    // Identity info will ultimatly be encoded into an Access Token
    // as a result of this call:
    context.Validated(identity);
}

 

In reality, we would most likely check to see if there was a user in our backing store which matched whatever credentials were submitted, and then also check to see if the password submitted was valid. But not by checking against a plain text representation from our backing store!

In order to flesh out this method, we need to model our authorization objects, and we need to persist some user data in our database.

First, let's add some basic models. Add a new code file to the Models folder in the project, and then add the following code:

The AuthModels.cs Code File:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
 
// Add usings:
using System.Data.Entity;
using System.ComponentModel.DataAnnotations;
using System.Security.Claims;
 
namespace MinimalOwinWebApiSelfHost.Models
{
    public class MyUser
    {
        public MyUser()
        {
            Id = Guid.NewGuid().ToString();
            Claims = new List<MyUserClaim>();
        }
 
        [Key]
        public string Id { get; set; }
        public string Email { get; set; }
        public string PasswordHash { get; set; }
        public ICollection<MyUserClaim> Claims { get; set; }
    }
 
 
    public class MyUserClaim
    {
        public MyUserClaim()
        {
            Id = Guid.NewGuid().ToString();
        }
        [Key]
        public string Id { get; set; }
        public string UserId { get; set; }
        public string ClaimType { get; set; }
        public string ClaimValue { get; set; }
    }
 
 
    public class MyPasswordHasher
    {
        public string CreateHash(string password)
        {
            // FOR DEMO ONLY! Use a standard method or 
            // crypto library to do this for real:
            char[] chars = password.ToArray();
            char[] hash = chars.Reverse().ToArray();
            return new string(hash);
        }
    }
}

 

Above, we see a few basic models. We expect to have a user representation, and we do, in the form of the MyUser class. While you may have been expecting to see a MyRole class, we have instead opted to carry on with the claims implementation we were using in our original project. Therefore, we have added a MyUserClaim class instead. We'll discuss this further shortly.

Finally, we have that odd-looking MyPasswordHasher class. As you may have guessed from the comment in the code, we are really only going to mock a proper hashing mechanism here. As before, we're going to keep things simple for our example. In reality, one would apply a proven crypto library to this task, and proven, tried and true methods for properly hashing a password. Or, of course, use a library for such things, like Identity.

Adding The Models to the ApplicationDbContext

Now that we have our auth-related entity models, we can add them to the existing ApplicationDbContext so that they can be modeled in the database, and we can access the data they represent from the context.

Recall that we set this particular example application up to use a local, file-based database (SQL CE) however, everything we are doing here would work just fine with SQL Server as well.

Add the Auth-Related Models to the ApplicationDbContext:
public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext()
        : base("MyDatabase")
    {
    }
 
 
    static ApplicationDbContext()
    {
        Database.SetInitializer(new ApplicationDbInitializer());
    }
 
 
    public IDbSet<Company> Companies { get; set; }
    public IDbSet<MyUser> Users { get; set; }
    public IDbSet<MyUserClaim> Claims { get; set; }
}

 

Tying the Models Together - The User Store

For our simple model set, and to keep concept straightforward, we are going to implement a simple MyUserStore class, and add sufficient functionality to get our application working and no more.

Add the following class (I added this to the AuthModels.cs file, but you can add it in its own if you want):

The UserStore Class:
public class MyUserStore
{
    ApplicationDbContext _db;
    public MyUserStore(ApplicationDbContext context)
    {
        _db = context;
    }
 
 
    public async Task AddUserAsync(MyUser user, string password)
    {
        if (await UserExists(user))
        {
            throw new Exception(
                "A user with that Email address already exists");
        }
        var hasher = new MyPasswordHasher();
        user.PasswordHash = hasher.CreateHash(password).ToString();
        _db.Users.Add(user);
        await _db.SaveChangesAsync();
    }
 
 
    public async Task<MyUser> FindByEmailAsync(string email)
    {
        var user = _db.Users
            .Include(c => c.Claims)
            .FirstOrDefaultAsync(u => u.Email == email);
        return await _db.Users
            .FirstOrDefaultAsync(u => u.Email == email);
    }
 
 
    public async Task<MyUser> FindByIdAsync(string userId)
    {
        return await _db.Users
            .FirstOrDefaultAsync(u => u.Id == userId);
    }
 
 
    public async Task<bool> UserExists(MyUser user)
    {
        return await _db.Users
            .AnyAsync(u => u.Id == user.Id || u.Email == user.Email);
    }
 
 
    public async Task AddClaimAsync(string UserId, MyUserClaim claim)
    {
        var user = await FindByIdAsync(UserId);
        if(user == null)
        {
            throw new Exception("User does not exist");
        }
        user.Claims.Add(claim);
        await _db.SaveChangesAsync();
    }
 
 
    public bool PasswordIsValid(MyUser user, string password)
    {
        var hasher = new MyPasswordHasher();
        var hash = hasher.CreateHash(password);
        return hash.Equals(user.PasswordHash);
    }
}

 

In the code above, we have assembled a few basic methods to deal with persisting and retrieving User information. Note in the AddUserAsync() method, we perform some minimal validation (make sure a user with the same email address does not already exist). Also, see that we use our super-secret, super-secure MyPasswordHasher to hash, salt, re-hash, etc. our user password, and then we persist the hashed value (NEVER the clear-text password). In other words, at no point are we saving the user-submitted clear-text password to disk, anywhere.

Similarly, we provide a simple PasswordIsValid() method which again uses the MyPasswordHasher class to compare the hash of the password submitted with that of a user record (which for now, would be submitted as an argument after being previously retrieved elsewhere in our code).

The MyUserStore class provides simplistic examples of how one might implement some of this. There is minimal validation and exception handling here. This class works well for our example, and to demonstrate the concepts we are dealing with, but is not likely how you would do this in a production application.

Initialize the Database with User Data

Now all we really need to do is update our ApplicationDbInitializer to seed the database with some initial user data. Recall, we had already set this up (in the same code file as the ApplicationDbContext) to seed our Company table with some starting data. Update the code as follows. You will also need to add System.Security.Claims to the using statements at the top of your code file:

Update ApplicationDbInitializer to Seed Application with Initial User Data:
public class ApplicationDbInitializer 
    : DropCreateDatabaseAlways<ApplicationDbContext>
{
    protected async override void Seed(ApplicationDbContext context)
    {
        context.Companies.Add(new Company { Name = "Microsoft" });
        context.Companies.Add(new Company { Name = "Apple" });
        context.Companies.Add(new Company { Name = "Google" });
        context.SaveChanges();
 
        // Set up two initial users with different role claims:
        var john = new MyUser { Email = "john@example.com" };
        var jimi = new MyUser { Email = "jimi@Example.com" };
 
        john.Claims.Add(new MyUserClaim 
        { 
                ClaimType = ClaimTypes.Name, 
                UserId = john.Id, 
                ClaimValue = john.Email 
        });
        john.Claims.Add(new MyUserClaim 
        { 
                ClaimType = ClaimTypes.Role, 
                UserId = john.Id, 
                ClaimValue = "Admin" 
        });
 
        jimi.Claims.Add(new MyUserClaim 
        { 
            ClaimType = ClaimTypes.Name, 
            serId = jimi.Id, 
            ClaimValue = jimi.Email 
        });
        jimi.Claims.Add(new MyUserClaim 
        { 
            ClaimType = ClaimTypes.Role, 
            UserId = john.Id, 
            ClaimValue = "User" 
        });
 
        var store = new MyUserStore(context);
        await store.AddUserAsync(john, "JohnsPassword");
        await store.AddUserAsync(jimi, "JimisPassword");
    }
}

 

As we see above, we have taken advantage of the methods exposed on our new MyUserStore class to add two users, along with appropriate claims, to the database.

Also recall we are deriving our initializer from DropDatabaseCreateAlways so that the database will be re-created and re-seeded each time we run the application.

Find the User and Authenticate the Token Request

All that's left to do now is update our GrantResourceOwnerCredentials() method to avail itself of our new user entities and data to perform its function.

Validate and Authenticate a User in GrantResourceOwnerCredentials() Method:
public override async Task GrantResourceOwnerCredentials(
    OAuthGrantResourceOwnerCredentialsContext context)
{
    // Retrieve user from database:
    var store = new MyUserStore(new ApplicationDbContext());
    var user = await store.FindByEmailAsync(context.UserName);
 
    // Validate user/password:
    if(user == null || !store.PasswordIsValid(user, context.Password))
    {
        context.SetError(
            "invalid_grant", "The user name or password is incorrect.");
        context.Rejected();
        return;
    }
 
    var identity = new ClaimsIdentity(context.Options.AuthenticationType);
    foreach(var userClaim in user.Claims)
    {
        identity.AddClaim(new Claim(userClaim.ClaimType, userClaim.ClaimValue));
    }
     
    context.Validated(identity);
}

 

Here, we retrieve a user record from our store (if there is a record for the user credentials in the request), and then we create a new ClaimsIdentity for that user, much the same as before. This time, however, we also have a record of the various claims for this user, and we add those as well.

In this case, we really only have the user's name, and the role(s) our application recognizes for the user, but we could implement a more complex claims model if we needed. For now, we will stick with user name and roles, because the default authorization scheme, using the [Authorize] attribute, is pre-configured to work with user names and roles. We will look at customizing this in a later post.

The Api Client Application

We can leave our Api Client application pretty much as-is at the moment. If you don't have the client application set up, you can pull down the source for the project from the Github repo. Make sure to checkout the branch owin-auth (not master!).

Recall that we has set up our application to request a token from our Api, and then make some Api calls to the CompaniesController:

Abbreviated Client Code Showing the Token Request:
static async Task Run()
{
    // Create an http client provider:
    string hostUriString = "http://localhost:8080";
    var provider = new apiClientProvider(hostUriString);
    string _accessToken;
    Dictionary<string, string> _tokenDictionary;
    try
    {
        // Pass in the credentials and retrieve a token dictionary:
        _tokenDictionary = await provider.GetTokenDictionary(
            "john@example.com", "JohnsPassword");
        _accessToken = _tokenDictionary["access_token"];
 
        // Write the contents of the dictionary:
        foreach (var kvp in _tokenDictionary)
        {
            Console.WriteLine("{0}: {1}", kvp.Key, kvp.Value);
            Console.WriteLine("");
        }
 
        // Create a company client instance:
        var baseUri = new Uri(hostUriString);
        var companyClient = new CompanyClient(baseUri, _accessToken);
 
        // ... a bunch of code calling to API and writing to console...
    }
    catch (AggregateException ex)
    {
        // If it's an aggregate exception, an async error occurred:
        Console.WriteLine(ex.InnerExceptions[0].Message);
        Console.WriteLine("Press the Enter key to Exit...");
        Console.ReadLine();
        return;
    }
    catch (Exception ex)
    {
        // Something else happened:
        Console.WriteLine(ex.Message);
        Console.WriteLine("Press the Enter key to Exit...");
        Console.ReadLine();
        return;
    }
}

 

The only thing we have changed in the above code is the password we are passing in with the token request - we have changed it to match the password for the user record we created in our Seed() method.

Running the Application with an Authenticated User

If we run our Web Api application, and then run the client, everything should work swimmingly. The Web Api application spins up the same as it always has, and the client output should look familiar:

Console Output from Client Application:

client-with-authneticated-user

Everything looks the same as it did when we wrapped up the previous post, because we haven't changed anything the affects how the client application does its job. We've only changed the internals of our Web Api so that the embedded authorization server now knows how to retrieve user data from our database in order to authenticate a user, and perform a basic authorization check against the roles available to that user.

Let's see what happens when things go wrong.

Improper Authentication - Invalid Credentials

First, let's see what happens if we try to request a token with the wrong password. In the client application, change the password we are using in our token request to something other than "JohnsPassword":

Using Incorrect Password for Client Token Request:
// Pass in the credentials and retrieve a token dictionary:
_tokenDictionary = await provider.GetTokenDictionary(
    "john@example.com", "SomePassword");
_accessToken = _tokenDictionary["access_token"];

 

If we run the client again, we see all is not well:

Running the Client with Invalid Credentials:

client-with-invalid-password

In this case, were get back an "Invalid Grant" because the client could not properly authenticate with the credentials provided.

On the other hand, things look a little different is we request a token for a user which can be authenticated, but who is not authorized access to the resource requested.

Insufficient Authorization

Recall that in our Web Api application, we protected the CompaniesController resource using the [Authorize] attribute, and we restricted access to users in the role "Admin":

The CompaniesController is Protected Using [Authorize]:
[Authorize(Roles="Admin")]
public class CompaniesController : ApiController
{
    // ... blah blah Controller Methods etc...
    
}

 

Also recall that we seeded two users in our database. The user "jimi" does not have a claim for the "Admin" role, but instead claims the "User" role. Let's change the code in our client application to request an access token for "jimi" instead, and then see what happens.

Change Client Token Request for Alternate User:
// Pass in the credentials and retrieve a token dictionary:
_tokenDictionary = await provider.GetTokenDictionary(
    "jimi@example.com", "JimisPassword");
_accessToken = _tokenDictionary["access_token"];

 

Running the client application now produces a slightly different result:

Running the Client with Valid Credentials but Insufficient Authorization:

client-with-insufficient-authorization

Unlike previously, we did not receive an invalid grant error, because the user credentials were properly authenticated. However, the user does not possess the proper Role claim in our system to access the protected resource.

In reality, the default implementation of [Authorize] limits our ability to leverage claims to the fullest extent. [Authorize] recognizes claims for user names, and roles. What if we want more granular control over our application permissions?

We're not going to go into that in this post. However, keep this in mind, as leveraging Claims, and customizing authentication using claims instead of simple roles can become important for more complex application which require fine-grained control of permissions.

What Next?

In this post we created a "quick and dirty" implementation which performs some very basic authentication and authorization for our application.

In the real world, we would definitely tend to some critical details, such as proper crypto for hashing passwords. We would also probably want to beef up our design by applying some common patterns of abstraction. Notice, we have coded everything here directly to the implementation class. Also, we have rather tightly coupled our logical processing to our persistence model.

Lastly, we have put in place only the most rudimentary validation and exception handling.

We could go down a long road exploring how to better separate our persistence mechanism from our authentication logic, and more effectively handling exceptions and errors. However, those details are often application-specific, and/or require a long, long post.

Instead, we could now take everything we have learned, and pull in some ready-made components which already provide all of this, and more.

If the work we have done so far has been beginning to look a little familiar, that is no accident.

In the next post, we will implement our own authentication and authorization using the Identity 2.1 Framework.

NEXT:  Understanding OWIN/Katana Authentication/Authorization Part III: Adding Identity

Additional Resources and Items of Interest

Articles by others I have found invaluable:

 

Posted on January 25 2015 09:23 AM by jatten     

Comments (6)

About the author

My name is John Atten, and my "handle" on many of my online accounts is xivSolutions. I am Fascinated by all things technology and software development. I work mostly with C#, JavaScript/Node, and databases of many flavors. Actively learning always. I dig web development. I am always looking for new information, and value your feedback (especially where I got something wrong!). You can email me at:

johnatten at typecastexception dot com

Web Hosting by