In this post we are going to talk a little bit about OWIN. What is it, how can we use it with ASP.NET Web API to expose API endpoints, as well as how to secure those endpoints.
The application uses OWIN to self-host the Web API as well as ASP.NET Identity as underlying membership mechanism. Users can fetch public data from /api/people endpoint, as well as secured privatedata from /api/user endpoint. The latter one is to demonstratesecurity in Web API.
Brief discussion on OWIN
OWIN stands for Open Web Interface for .NET. It definesa standard communication interface between .NET web servers and web applications, proving a specification to work with. This specification can be found here.
It enables implementation offunctionality without having to take dependency on the hosting framework, with this abstraction being specified in many other web platforms such Ruby, Node.js or Python.
OWIN specificationdefines parts for:
- Host
- Server
- Middleware
- Application
Host is a process. It can be a windows application, a windows service, anything that is responsible of providing a runtime platform for incoming requests.
Into the host, is the server, which accepts HTTP requests and sends responses using the OWIN runtime. Server and Application communicate through the OWIN pipeline, which makes thispossible by the use of a number of middlewares that are registered on it.
Lastly, Application is implemented in a framework such as ASP.NET Web API or NancyFx andgenerates a response back to the client, whichtravels through the pipeline, passing all intermediate middleware, to the server and from server to the client.
OWIN Katana is the Microsoft implementation of OWIN specification. The package Microsoft.Owin contains the Katana implementation.
This isthe exact implementationI am going to demonstrate in the next lines.
So, next up, the Katana implementation is going to be used to self-host an ASP.NET Web API as well as some middleware to secure API endpoints.
Setup
I usedVisual Studio and madea new Console Application. The application structure is like so:
People.SelfHostedApi
|--App_Start
|----IdentityConfig.cs
|----WebApiRouteConfig.cs
|--Controllers
|----PeopleController.cs
|----UserController.cs
|--Database
|----ApplicationDbContext.cs
|--Infrastructure
|----Formatters
|------BrowserFormatter.cs
|--Migrations
|----Configuration.cs
|--Models
|----Person.cs
|--Security
|----CustomAuthorizationServerProvider.cs
|--App.config
|--Program.cs
|--Startup.cs
Packages needed
Throughnuget package manager and I installed the following packages:
- Microsoft.AspNet.WebApi.OwinSelfHost
- Microsoft.Owin.Security.OAuth
- Microsoft.AspNet.Identity.Owin
- Microsoft.AspNet.Identity.EntityFramework
With all these, the setup is done.It's time to proceed to code.
Hosting the API
Ineed a way tohost theapplication. Then I remember the parts of the OWIN specification from before. Regarding thehosting,I needto build the process that is running the application. This will be a Windows Console Application. In the Program.cs, in Main method theAPI is kicked-off by using the WebApp
class, calling the Start
method, which takes the URL of the application andan entry point of the OWIN configuration (Startup class).
class Program
{
static void Main(string[] args)
{
const string url = "http://localhost:3001";
using (WebApp.Start<Startup>(url))
{
Console.WriteLine("Application deployed and hosted in {0}", url);
Console.WriteLine("Press any key to terminate...");
Console.ReadLine();
}
}
}
I am wrapping the call of the Start
method in a using statement, as this is a disposable entity and I call the Console.ReadLine
method inside its body in order to keep the application open.
Nothing else to see here, let's move to the juicy bits of the Startup class.
public class Startup
{
public static void Configuration(IAppBuilder app)
{
var configuration = new HttpConfiguration();
WebApiRouteConfig.Register(configuration);
app.UseWebApi(configuration);
}
}
A class like this, that implements the OWIN Katana must have a static void methodwith the nameConfiguration, and take a single parameter of type IAppBuilder
.
See the UseWebApi
extension method? These kind of methods are nothing more than extensions to the IAppBuilder.Use
method, which is used to add middleware into the OWIN pipeline. In fact, OWIN is all about the pipeline. The components that are registered into itknow nothing about each other, but they manage to work together, by walking down the road in the pipeline until the final component is reached. Then, up they go back to the client, climbing the pipeline once again, bottom-up.
First thing first, the Server part is now implemented. The OWIN Katana acts as a server, as it can accept HTTPrequests and send responses, even without using the Web API middleware, you just need to add middleware to handle requests and responses.
Where is the Application though? For this part, most critical is to create a new HttpConfiguration
instance. HttpConfiguration controls whatever hasto do with routing, serializers, formatters, etc. It is a dependency for the Web API middleware,UseWebApi
extension method, which is putting the last piece in the OWIN puzzle, the Application part.
With all these in place,we have an application that can receive HTTP traffic, process the requests, and reply back to the client with appropriate HTTP responses.
Concluding with the Web API configuration, I register the routes for Web API to handle by calling theWebApiRouteConfig.Register
, pretty much standard routine, no need to go there.
Le'tscreate a simple ApiController
and expose some endpoints to see this in action.
This is a simple controller, that returns a list of people or finds a person by id.
public class PeopleController : ApiController
{
private readonly List<Person> _people;
public PeopleController()
{
_people = new List<Person>
{
new Person { Age = 15, Id = 1, Name = "John Doe"},
new Person { Age = 22, Id = 2, Name = "Jane Doe"}
};
}
public IHttpActionResult Get()
{
return Ok(_people);
}
public IHttpActionResult Get(int id)
{
var person = _people.Find(p => p.Id == id);
return Ok(person);
}
}
Running the application, I test it by running a Postman instance, hitting the GET and GET/{id} endpoints.
Listing 1-2. GET /api/people/2
That's a fairly simple example using an in-memory collection of people. If we wanted to fetch those data from a different kind of store (database, XML file,CSV file, whatever, it doesn't matter) we'd only have to implement a store and use its methods.
Securing endpoints and using ASP.NET Identity
In order to make this more interesting, I am going to add another controller which will fetch the currentauthenticated user details andreturn them back to the client. Also, people data will be persisted in a database, which needs to be accessed in order to fetch them, so that said, I'm going tomake use of the ASP.NET Identity featuresfor the features described.
I just create a class Person
to represent theentity.
public class Person
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
And a simpledatabase context.
public class ApplicationDbContext: IdentityDbContext<IdentityUser>
{
public ApplicationDbContext(): base("DefaultConnection", throwIfV1Schema: false) { }
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
public DbSet<Person> People { get; set; }
}
Nothing fancy. Just a quick note on IdentityDbContext
. It is located at Microsoft.AspNet.Identity.EntityFramework assembly and it an implementation of the DbContext
class which gives you access to the underlying context (database). This class manipulates the Entity Framework model internally so it sets proper tables, proper relationships to manage users, roles, claims, etc. For creating your own store you need toderive from that class.
With Entity Framework Code First Migrations Itheninitialize thestore with the schema and possible seed values (I have already done that, refer to the code on Github for more).
Finally I added a custom UserManager
in order to initialize the custom store, which uses the ApplicationDbContext
I made earlier, again Iwon't go through that now.
I need to go to some modifications now in existing code, as well as bring in life the UserController. Let's go first with the controllers, then to Startup class and finally to a simple custom authorization server provider implementation.
UserController
[Authorize]
public class UserController : ApiController
{
private ApplicationUserManager _userManager;
public ApplicationUserManager UserManager =>
_userManager ?? (_userManager = Request.GetOwinContext().GetUserManager<ApplicationUserManager>());
public IHttpActionResult Get()
{
var id = User.Identity.GetUserId();
var user = UserManager.FindById(id);
return Ok(user);
}
}
In this controller, I have only one simple GET method which returns all details about a logged-in user, for demonstration purposes.
The controller is decorated with theAuthorizeAttribute
to enforce Authentication.
The ApplicationUserManageris the one I created earlier. In order to get it, I get the OwinContext associated with theHttpRequestMessage
, using the GetUserManager
extension method of IOwinContext, to get the ApplicationUserManager
instance, registered to OwinContext (see below). Using C# 6 Expression Bodied property for UserManager property.
If a user is authenticated, the /api/user endpointis accessible.Depending on the claims registered on the current context, the IPrincipal.User is populated, along with the IIdentity.Identity. These properties belong to the System.Security namespace. TheMicrosoft.AspNet.Identity
package implements some extension methods on them, more specifically on IIdentity
interface, which are very useful, methods likeGetUserId
andGetUserName
which fetch the user id or username for the current authenticated context. Having the userId or username we can query the AspNet.Users
table through the UserManager
instance and fetch the associated user.
PeopleController
public class PeopleController : ApiController
{
private readonly ApplicationDbContext _context;
public PeopleController()
{
_context = new ApplicationDbContext();
}
public IHttpActionResult Get()
{
var people = _context.People.ToList();
return Ok(people);
}
public IHttpActionResult Get(int id)
{
var person = _context.People.Find(id);
return Ok(person);
}
}
The highlighted lines in code above are the changes in the PeopleController
for fetching data from the database. Nothing fancy here either, I just need a reference to ApplicationDbContext in order to communicate with the underlying store to fetch people data.
Note that this is not the recommended way tointroduce the ApplicationDbContext in PeopleController. Thisleads to coupling and violates the 'D' of SOLID. Also,this code is not testable and we are going to tackle this subject in futureposts. To make thissolution more elegant, the dependency should be passed through the constructor, in an abstract format, as an interface, using a DI container.
Startup.Configuration
public class Startup
{
public static void Configuration(IAppBuilder app)
{
app.CreatePerOwinContext<ApplicationDbContext>(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
var configuration = new HttpConfiguration();
WebApiRouteConfig.Register(configuration);
app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(30),
Provider = new CustomAuthorizationServerProvider()
});
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
app.UseWebApi(configuration);
}
}
Iadded additional code to Configuration method, essentially using more middleware forthe OWIN pipeline. These middleware as stated before, doesn'treallyknow about each other, they are just registered in the pipeline, doing work on incoming requests, then delegating the process to the next in the pipeline andfinally they individually handle the response back to the client.
UseOAuthAuthorizationServer
just creates an authorization server andknows nothing about how you implement yourapplication, it doesn't care about your endpoints, it doesn't care about your routes, it doesn't care about content. All it cares is to create,manage, invalidate tokens, as well as authenticating requests.
OAuthBearerAuthentication
doesn't care about the authorization server or the Web API, it cares only about tokens in your request header.
Yet, all these work wonderfully together. That's the magic with OWIN components. They plug-in to the OWIN pipeline, they do their thing andthen they pass control to the next one in the pipeline.
How about these weirdCreatePerOwinContext<T>
you might ask, what is their purpose? This is an extension on IAppBuilder
which registers a callback to instantiate a type of T on OWIN context, which is availableonce per request. In the example above, I register the database context (ApplicationDbContext
which derives from IdentiyDbContext
) so I can have access to the database instance if I wanted to, as well as to theApplicationUserManager
, which is essentially an implementation of UserManager<T>
, an entity that provides enough abstractions for a developer to manage the AspNet.Users
table, of ASP.NET Identity.
Note thatwe can customize the authorization server to our hearts content, by providing details on the token endpoint, use of HTTPS, expiration of the issued token, custom implementation ofan authorization provider and much more.For more info relate to MSDN documentation.
I have added a custom implementation of an authorization server provider, which is fairly simple, it just validates a token based on the username and password provided by an HTTP POST request at /token endpoint.
CustomAuthorizationServerProvider
public class CustomAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
return Task.FromResult(0);
}
public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add(new KeyValuePair<string, string[]>("Access-Control-Allow-Origin", new[] { "*" }));
var userManager = context.OwinContext.GetUserManager<ApplicationUserManager>();
var user = userManager.Find(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "Username and password do not match.");
return Task.FromResult(0);
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, user.UserName));
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id));
context.Validated(identity);
return Task.FromResult(0);
}
}
Please note this code is for demonstration purposes only and should not be used in production. If you really want to implement OAuth in production you need to fully comply with the protocol and implement your logic on authorization server. In coming posts I am going to cover that a little bit more.
I just override two methods ofOAuthAuthorizationServerProvider
base class. TheValidateClientAuthentication
method validatesthat the origin of the request is a valid "client_id". We don't really care to implement theOAuth specification here, so essentially, every incoming request is validated.
FromGrantResourceOwnerCredentials
method metadata:
Called when a request to the Token endpoint arrives with a "grant_type" of "password".
This occurs when the user has provided name and password credentials directly
into the client application's user interface, and the client application is using
those to acquire an "access_token" and optional "refresh_token". If the web application
supports the resource owner credentials grant type it must validate the context.Username
and context.Password as appropriate. To issue an access token the context.Validated
must be called with a new ticket containing the claims about the resource owner
which should be associated with the access token. The application should take
appropriate measures to ensure that the endpoint isn’t abused by malicious callers.
The default behavior is to reject this grant type. See also http://tools.ietf.org/html/rfc6749#section-4.3.2
Asthe method states, if we use a grant_type of password, then this method is called. In the code above, Iget the Username and Password from the context and validate them against the database, on AspNet.Users
. Ifa match is found, I create a newClaimsIdentity
instance, adding claims about the username and the userId. These will be later available through the IIdentity.Identity.GetUserId
orIIdentity.Identity.GetUserName
extension methods.
Claims are statements about an entity, in previous example this entity is the user itself. ClaimsIdentity is an implementation of IIdentity, which holds an arbitary number of Claims, which is exactly what I did before, adding the userId and the name in ClaimsIdentity collection.
By calling theOAuthGrantResourceOwnerCredentialsContext.Validated()
method, the user entity is authenticated and this information is available in the application context. Now subsequent requests will be authenticated.
If user is notfound, I return an "invalid_grant"
error back to the client.
Please also note that OAuthAuthorizationServerProvider
methods are all asynchronous, so each returns a Task
.
Test drive
Let's run the application now, first try the /api/people endpoints.
Listing 1-4. GET /api/people/2
Nice, data are fetched from thedatabase for the PeopleController.
Let's see what's up with the UserController,the AuthorizeAttribute takes place there, so we expect to see a 401 Unauthorized response, if we do not provide any token information on HTTP headers.
Listing 1-5. GET /api/user (401)
We are completely locked out of the /api/user endpoint. The only way to have access is to get a Bearer token and provide it in the subsequent requests in the HTTP Headers. Tokens can be acquiredfrom the /token endpoint,whichis aPOST action. By providing a payload ofx-www-form-urlencoded, passing the grant_type, username and password for the account (grant_type=password&username=name&password=pass
), a user essentially gets a Bearer token to use.
Now it's time to get back to/api/user and call the endpoint,providing an Authorization header, with theBearer token I justgot previously. If the token is correct, then access will be granted forthis resource. Remember, a token is invalid after 30 minutes, so you need to go through the same process again to issue another one, unless you set a RefreshTokenProvider.
Listing 1-7. GET /api/user (200)
Summary
In this post webriefly touched the surface of OWIN Katana implementation. Use cases where presented aboutASP.NET Web API, and self-hosting in OWIN. Also, there was a brief discussion on securing endpoints with a simple token authorization server implementation.
In next posts we will go throughunit testing the OWIN pipeline, individual controllers, integration testing and documentation. We will explore different, more concrete security configurations, as well as deployment on Azure.
Codecan be found here.
Next post: Fixing the design of this application and writing unit tests
Comments powered by Disqus.