This 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.
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.
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:
public interface IUser<out TKey>
{
TKey Id
{
get;
}
string UserName
{
get;
set;
}
}
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:
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);
}
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.
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:
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; }
}
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
.
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.
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;
using Microsoft.Owin;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.AspNet.Identity.EntityFramework;
namespace MinimalOwinWebApiSelfHost.Models
{
public class ApplicationUser : IdentityUser
{
public ApplicationUser() { }
public ApplicationUser(string email) : base(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.
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:
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.
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;
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();
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;
using System.Data.Entity;
using System.Security.Claims;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
namespace MinimalOwinWebApiSelfHost.Models
{
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("MyDatabase") { }
static ApplicationDbContext()
{
Database.SetInitializer(
new ApplicationDbInitializer());
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
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();
var john = new ApplicationUser
{
Email = "john@example.com",
UserName = "john@example.com"
};
var jimi = new ApplicationUser
{
Email = "jimi@Example.com",
UserName = "jimi@example.com"
};
var manager = new UserManager<ApplicationUser>(
new UserStore<ApplicationUser>(context));
var result1 = await manager.CreateAsync(john, "JohnsPassword");
var result2 = await manager.CreateAsync(jimi, "JimisPassword");
await manager.AddClaimAsync(john.Id,
new Claim(ClaimTypes.Name, "john@example.com"));
await manager.AddClaimAsync(john.Id,
new Claim(ClaimTypes.Role, "Admin"));
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.
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)
{
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),
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.
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;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.OAuth;
using System.Security.Claims;
using MinimalOwinWebApiSelfHost.Models;
using Microsoft.AspNet.Identity.Owin;
namespace MinimalOwinWebApiSelfHost.OAuthServerProvider
{
public class ApplicationOAuthServerProvider
: OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(
OAuthValidateClientAuthenticationContext context)
{
await Task.FromResult(context.Validated());
}
public override async Task GrantResourceOwnerCredentials(
OAuthGrantResourceOwnerCredentialsContext context)
{
var manager = context.OwinContext.GetUserManager<ApplicationUserManager>();
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;
}
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.
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
{
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.
If we spin up our Web Api, all should look well:
Running the Web Api Application - All is Well:
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:
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:
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:
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.
John on Google CodeProject