Protecting a Self-Hosted API with Microsoft.Owin.Security.ActiveDirectory
As you might have sensed, OWIN is slated to occupy a prominent role in our next wave of claims-based identity software (more details soon (I hope)).
One question I am getting with increasing frequency is “just how lightweight is this OWIN thing? Is it going to impose heavy requirements that will prevent me from doing anything with it unless I use the latest and greatest?”.
The ones among you already familiar with it are already smiling, I am sure… OWIN was designed exactly to decouple apps and frameworks from the underlying layers, and while the Microsoft OWIN Components do have some system requirements (e.g. you can’t go lower that .NET 4.5) those are not more restrictive than other constraints we already have in place (the JWT handler requires .NET 4.5 anyway).
To give you a concrete feeling of the above, I am going to show you how you can set up a super simple Web API in a console app and how you can easily secure with with Windows Azure AD with the exact same code you use when programming against IIS Express/full IIS. In fact, I could just reference existing tutorials (like this one from the excellent Kanchan) and just add the authentication bits, but in my experience some denormalization often helps to understand the topic better. It will take a bit of extra work on my part, but it’s all good
Here there’s what we are going to do:
- We’ll create a self-hosted Web API, following almost verbatim Kanchan’s tutorial
- We will add the middleware for validating JWT tokens from AAD
- We’ll create a simple client to poke our miniAPI
To emphasize that this works also with the N-1 version of VS/.NET, I’ll use VS2012.
Create a Minimal Self-Hosted API
Let’s start by creating a console application. I called mine SelfHostingFunAPI, but I am sure you’ll be able to come up with something more tasteful.
Once you have the project available, head to the solution explorer, right-click on its entry and choose “Manage NuGet Packages”. We are going to add the necessaire for hosting Web API on an arbitrary process. Select the Stable Only feed, and search for Microsoft.AspNet.WebApi.OwinSelfHost.
Pick the entry that looks like the one highlighted above, and hit install. Accept all the various licenses (if you so choose! ) and hit “close”.
Very good, we now have all the assemblies we need to self-host our API. Let’s start adding some beef to the project by adding a controller for our API. Create a new class (solution explorer->right-click->Add->Class) and call it ValuesController.
Beware: from this moment on names count! MVC and OWIN are convention-driven, and if classes are not named as expected stuff simply does not happen.
Implement the class with something to the effect of the code below:
using System.Collections.Generic; using System.Web.Http; namespace SelfHostingFunAPI { public class ValuesController : ApiController { public IEnumerable<string> Get() { return new string[] { "value1", "value2" }; } } }
Yes, that is a shortened version of the classic default controller in Web API. Make sure that the class is public.
That done, let’s add the Startup class for hosting our Web API in OWIN. Create Startup.cs as above (remember what I said about names! ) and paste in there the following code:
using Owin; using System.Web.Http; namespace SelfHostingFunAPI { public class Startup { public void Configuration(IAppBuilder appBuilder) { // Configure Web API for self-host. HttpConfiguration config = new HttpConfiguration(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); appBuilder.UseWebApi(config); } } }
Once again, there’s nothing mysterious here. The builder class sets up the default route which leads to our API controller, then adds to the middleware pipeline the Web API hoster.
Lastly, we need to host the OWIN pipeline itself. Head to Program.cs, and write something along the lines of the following:
using Microsoft.Owin.Hosting; using System; using System.Net.Http; namespace SelfHostingFunAPI { class Program { static void Main(string[] args) { // Start OWIN host var server = WebApp.Start<Startup>(url: "http://localhost:9000/"); Console.ForegroundColor = ConsoleColor.Blue; Console.WriteLine("Web API listening at http://localhost:9000/"); // Test call HttpClient client = new HttpClient(); var response = client.GetAsync("http://localhost:9000/api/values").Result; Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine(response.Content.ReadAsStringAsync().Result); Console.ReadLine(); } } }
The call to WebApp.Start initializes a new server, which listens at the specified address. The rest of the method calls the Web API to double check that we did everything correctly. Hit F5 and see what happens:
Perhaps that’s not exactly pyrotechnics, but we do have confirmation that our self-hosted Web API is functional. Hurrah!
Before moving on to the next task, let’s eliminate the test call from Main: we’ll be using an actual client moving forward. Change Main’s body with the following.
static void Main(string[] args) { // Start OWIN host var server = WebApp.Start<Startup>(url: "http://localhost:9000/"); Console.ForegroundColor = ConsoleColor.Blue; Console.WriteLine("Web API listening at http://localhost:9000/"); Console.WriteLine("Press ENTER to terminate"); Console.ReadLine(); }
Secure the API with Windows Azure AD
Here comes the raison d’être of the entire post. Say that we want to restrict access to our minimal, no-IIS API only to users from a certain Windows Azure AD tenant. The good news is – we reuse exactly the same OWIN middleware that VS2013 adds in the new Web API templates protected by organizational accounts. What’s more, we reuse it using the exact same code. In fact, here I will assume that you went through the tutorial in this MSDN Magazine article (yes, denormalization has limits ) and that you can reuse in the self hosted case the API and client entries you created in the directory back then.
That’s super-easy. Let’s start by adding the NuGet which wraps the JWT handler and handles token validation for WIndows Azure AD in OWIN. Open the “Manage NuGet Packages” dialog, and search for Microsoft.Owin.Security.ActiveDirectory.
Install the highlighted.
That done, we have to add the right middleware in the pipeline. Open the solution you created following the MSDN magazine article, and head to the Startup.Auth.cs file. Copy the call to UseWindowsAzureActiveDirectoryBearerAuthentication (I was in vacation in Fiji when the name was paicked up, true story ) and paste it in your Startup.cs file in the self hosted API project, as follows:
using Microsoft.Owin.Security.ActiveDirectory; using Owin; using System.Web.Http; namespace SelfHostingFunAPI { public class Startup { public void Configuration(IAppBuilder appBuilder) { // Configure Web API for self-host. HttpConfiguration config = new HttpConfiguration(); config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); appBuilder.UseWindowsAzureActiveDirectoryBearerAuthentication( new WindowsAzureActiveDirectoryBearerAuthenticationOptions { Audience = "https://contoso7.onmicrosoft.com/RichAPI", Tenant = "contoso7.onmicrosoft.com" }); appBuilder.UseWebApi(config); } } }
The new code is in bold. You’ll notice that instead of relying on the config settings (as I did when targeting full ASP.NET/IIS) here I pasted directly the values, but of course you can keep those wherever you deem most appropriate.
Beware: the order with which you add the middleware is significant. Thanks Hongye for catching that!
Now our pipeline includes the right middleware: if we receive a JWT, we’ll validate it and if it checks out we’ll pass the corresponding ClaimsPrincipal to the API body. Very good, but not good enough. Let’s modify the controller class to mandate that all callers must present a valid token from the tenant of choice:
using System; using System.Collections.Generic; using System.Security.Claims; using System.Web.Http; namespace SelfHostingFunAPI { [Authorize] public class ValuesController : ApiController { public IEnumerable<string> Get() { Console.WriteLine("==>I have been called by {0}", ClaimsPrincipal.Current.FindFirst(ClaimTypes.Upn)); return new string[] { "value1", "value2" }; } } }
As before, the new code is in bold. The key element here is the [Authorize] attribute, which adds the constraint to all of the methods in the class.
I feel a bit dirty in adding a writeline in the body of web API, but it will be useful to show on the service console that we’ve been called by the intended identity. The UPN claim is among the ones that Windows Azure AD sends in JWTs.
Believe it or not, that’s all we had to do to secure the Web API: we just had to add the code in bold
Now, let’s take care of the client.
Create a Client App and Test the API
If you want to do things quickly: you can simply take the WPF client created for the MSDN magazine tutorial, point it to the URL of the self-hosted service () and use it ‘as in’ – and you’ll be seeing things in action.
That said. Given that we used a nice & rugged console, I thought it would be more tough of me to show a client also implemented as a console if you want to try it yourself, create a console app and use something like the following as main:
[STAThread] static void Main(string[] args) { HttpClient client = new HttpClient(); Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("Client ready."); Console.WriteLine("Press any key to invoke the service"); Console.WriteLine("Press ESC to terminate"); ConsoleKeyInfo cki; AuthenticationContext _ac =
new AuthenticationContext("https://login.windows.net/contoso7.onmicrosoft.com"); AuthenticationResult _arr = null; do { cki = Console.ReadKey(true); // get the access token _arr = _ac.AcquireToken("https://contoso7.onmicrosoft.com/RichAPI", "be182811-9d0b-45b2-9ffa-52ede2a12230", new Uri("http://whatevah")); // invoke the web API string result = string.Empty; HttpClient httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _arr.AccessToken); HttpResponseMessage response = httpClient.GetAsync("http://localhost:9000/api/Values").Result; // display the result if (response.IsSuccessStatusCode) { result = response.Content.ReadAsStringAsync().Result; Console.WriteLine("==> Successfully invoked the service"); Console.WriteLine(result); } } while (cki.Key != ConsoleKey.Escape); }
This is a very classic ADAL client. Getting the token requires the usual 2 lines of code (the settings are straight from the client registered for calling the API in the MSDN Magazine , the rest is showing some text and performing the actual REST call.
Want to see this moving? Start both projects and trigger a call.
Sign in as any user in your tenant, and you’ll get to something like the screen below:
Ta-dah!
Wrap
What did we see? In a nutshell:
- You don’t need VS2013 and associated tools to use OWIN, VS2012 works just fine
- OWIN can be used in absolutely minimal apps – including cases in which there’s no web server
- As long as the OWIN pipeline is there, the authentication middleware is happy – and does its job without requiring any special treatment
I don’t know about you, but I find this very promising – looking forward to have more and more of our claims identity goodness available in similar fashion!