Be Sociable, Share!

Tweet



Email

WhatsApp



Recently I’ve been asked by many blog readers on how to secure ASP.NET Web API 2 using Azure Active Directory, in other words we want to outsource the authentication part from the Web API to Microsoft Azure Active Directory (AD). We have already seen how the authentication can be done with local database accounts, and social identity providers, so in this tutorial we’ll try something different and we’ll obtain the bearer access tokens from external authority which is our Azure Active Directory (AD).

Microsoft Azure Active Directory (AD) is PaaS service available to every Azure subscription, this service is used to store information about users and organizational structure. We’ll use this service as our Authority service which will be responsible to secure our Resource (Web API) and issue access tokens and refresh tokens using OAuth 2 Code flow grant.

The resource (Web API) should be consumed by a Client, so the client will be requesting the data from the resource (Web API), but in order for this request to be accepted by the resource, the client must send a valid access token obtained from the Authority service (Azure AD) with each request. Do not worry if this is not clear now, I’ll describe this thoroughly while we’re implementing this tutorial.

What we’ll build in this tutorial?

As you noticed in the previous posts, I’m not big fan of the built in templates in Visual Studio 2013, those templates mix MVC controllers along with Web API controllers and bring confusion to the developers, so in this post I’ve decided to build simple ASP.NET Web API secured by Azure AD using Owin middle-ware components which we’ll add manually using Nuget, then I’ll build simple client (desktop forms application) which will consume this Web API.

The Source code for this tutorial is available on GitHub.

Building the Back-end Resource (Web API)

Step 1: Creating the Web API Project

In this tutorial I’m using Visual Studio 2013 and .Net framework 4.5, to get started create an empty solution and name it “WebApiAzureActiveDirectory”, then add new empty ASP.NET Web application named “WebApiAzureAD.Api”, the selected template for the project will be “Empty” template with no core dependencies, check the image below:

Step 2: Install the needed NuGet Packages

This project is empty so we need to install the NuGet packages needed to setup our Owin server and configure ASP.NET Web API 2 to be hosted within an Owin server, so open NuGet Package Manager Console and install the below packages:

Install-Package Microsoft.AspNet.WebApi Install-Package Microsoft.AspNet.WebApi.Owin Install-Package Microsoft.Owin.Host.SystemWeb Install-Package Microsoft.Owin.Security.ActiveDirectory 1 2 3 4 Install-Package Microsoft . AspNet . WebApi Install-Package Microsoft . AspNet . WebApi . Owin Install-Package Microsoft . Owin . Host . SystemWeb Install-Package Microsoft . Owin . Security . ActiveDirectory

The use for the first three packages have been discussed on this post, the package “Install-Package Microsoft.Owin.Security.ActiveDirectory” is responsible to configure our Owin middle-ware server to use Microsoft Azure Active Directory to offload the authentication process to it. We’ll see how we’ll do this in the coming steps.

Step 3: Register the Web API into Azure Active Directory

Now we need to jump to Azure Management Portal in order to register our Web API (Resource) as an application in our Azure Active Directory (Authority) so this authority will accept issuing tokens for our Web API, to do so and after your successful login to Azure Management Portal, click on “Active Directory” in the left hand navigation menu, choose your active directory tenant you want to register your Web API with, then select the “Applications” tab, then click on the add icon at bottom of the page. Once the modal window shows as the image below select “Add an application my organization is developing”.

Then a wizard of 2 steps will show up asking you to select the type of the app you want to add, in our case we are currently adding a Web API so select “Web Application and/or Web API”, then provide a name for the application, in my case I’ll call it “WebApiAzureAD”, then click next.

In the second step as the image below we need to fill two things, the Sign-On URL which is usually will be your base Url for your Web API, so in my case it will be “http://localhost:55577”, and the second field APP ID URI will usually be filled with a URI that Azure AD can use for this app, it usually take the form of “http://<your_AD_tenant_name>/<your_app_friendly_name>” so we will replace this with the correct values for my app and will be filed as “http://taiseerjoudeharamex.onmicrosoft.com/WebApiAzureAD” then click OK.

Important Note:

On production environment, all the communication should be done over HTTPS only, the access token we’ll transmit from the client to the API should be transmitted over HTTPS only.

To get your AD tenant name, you can navigate to to your active directory and click on the “Domains” tab.

Step 4: Expose our Web API to other applications

After our Web API has been added to Azure Active Directory apps, we need to do one more thing here which is exposing our permission scopes to client apps developers who will build clients to consume our Web API. To do so we need to change our Web API configuring using the application manifest. Basically the application manifest is a JSON file that represents our application identity configuration.

So as the image below and after you navigate to the app we’ve just added click on “Manage Manifest” icon at the bottom of the page, then click on “Download Manifest”.

Open the downloaded JSON application manifest file and replace the “appPermissions” node with the below JSON snippet. This snippet is just an example of how to expose a permission scope known as user impersonation, do not forget to generate new GUID for the “permessionid” value. You can read more about Web API configuration here.

"appPermissions": [ { "claimValue": "user_impersonation", "description": "Allow the application full access to the service on behalf of the signed-in user", "directAccessGrantTypes": [], "displayName": "Have full access to the service", "impersonationAccessGrantTypes": [ { "impersonated": "User", "impersonator": "Application" } ], "isDisabled": false, "origin": "Application", "permissionId": "856aba87-e34d-4857-9cb1-cc4ac92a35f8", "resourceScopeType": "Personal", "userConsentDescription": "Allow the application full access to the service on your behalf", "userConsentDisplayName": "Have full access to the service" } ] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 "appPermissions" : [ { "claimValue" : "user_impersonation" , "description" : "Allow the application full access to the service on behalf of the signed-in user" , "directAccessGrantTypes" : [ ] , "displayName" : "Have full access to the service" , "impersonationAccessGrantTypes" : [ { "impersonated" : "User" , "impersonator" : "Application" } ] , "isDisabled" : false , "origin" : "Application" , "permissionId" : "856aba87-e34d-4857-9cb1-cc4ac92a35f8" , "resourceScopeType" : "Personal" , "userConsentDescription" : "Allow the application full access to the service on your behalf" , "userConsentDisplayName" : "Have full access to the service" } ]

After you replaced the “appPermission” node, save the application manifest file locally then upload it again to your app using the “Upload Manifest” feature, now we have added our Web API as an application to the Azure Active Directory, so lets go back to visual studio and do the concrete implementation for the Web API.

Step 5: Add Owin “Startup” Class

Now back to our visual studio, we need to build the API components because we didn’t use a ready made template, this way is cleaner and you understand the need and use for each component you install in your solution, so add new class named “Startup”. It will contain the code below:

using Microsoft.Owin; using Microsoft.Owin.Security.ActiveDirectory; using Owin; using System; using System.Collections.Generic; using System.Configuration; using System.Linq; using System.Web; using System.Web.Http; [assembly: OwinStartup(typeof(WebApiAzureAD.Api.Startup))] namespace WebApiAzureAD.Api { public partial class Startup { public void Configuration(IAppBuilder app) { HttpConfiguration config = new HttpConfiguration(); ConfigureAuth(app); WebApiConfig.Register(config); app.UseWebApi(config); } private void ConfigureAuth(IAppBuilder app) { app.UseWindowsAzureActiveDirectoryBearerAuthentication( new WindowsAzureActiveDirectoryBearerAuthenticationOptions { Audience = ConfigurationManager.AppSettings["Audience"], Tenant = ConfigurationManager.AppSettings["Tenant"] }); } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 using Microsoft . Owin ; using Microsoft . Owin . Security . ActiveDirectory ; using Owin ; using System ; using System . Collections . Generic ; using System . Configuration ; using System . Linq ; using System . Web ; using System . Web . Http ; [ assembly : OwinStartup ( typeof ( WebApiAzureAD . Api . Startup ) ) ] namespace WebApiAzureAD . Api { public partial class Startup { public void Configuration ( IAppBuilder app ) { HttpConfiguration config = new HttpConfiguration ( ) ; ConfigureAuth ( app ) ; WebApiConfig . Register ( config ) ; app . UseWebApi ( config ) ; } private void ConfigureAuth ( IAppBuilder app ) { app . UseWindowsAzureActiveDirectoryBearerAuthentication ( new WindowsAzureActiveDirectoryBearerAuthenticationOptions { Audience = ConfigurationManager . AppSettings [ "Audience" ] , Tenant = ConfigurationManager . AppSettings [ "Tenant" ] } ) ; } } }

What we’ve done here is simple, and you can check my other posts to understand what the need for “Startup” class and how it works.

What worth explaining here is what the private method “ConfigureAuth” responsible for, so the implementation inside this method basically telling our Web API that the authentication middle ware which will be used is going to be “Windows Azure Active Directory Bearer Tokens” for the specified Active Directory “Tenant” and “Audience” (APP ID URI). Now any Api Controller added to this Web Api and decorated with [Authorize] attribute will only understand bearer tokens issued from this specified Active Directory Tenant and Application, any other form of tokens will be rejected and HTTP status code 401 will be returned.

It is a good practice to store the values for your Audience, Tenant, Secrets, etc… in a configuration file and not to hard-code them, so open the web.config file and add 2 new “appSettings” as the snippet below:

<appSettings> <add key="Tenant" value="taiseerjoudeharamex.onmicrosoft.com" /> <add key="Audience" value="http://taiseerjoudeharamex.onmicrosoft.com/WebApiAzureAD" /> </appSettings> 1 2 3 4 <appSettings> <add key = "Tenant" value = "taiseerjoudeharamex.onmicrosoft.com" /> <add key = "Audience" value = "http://taiseerjoudeharamex.onmicrosoft.com/WebApiAzureAD" /> </appSettings>

Before moving to the next step, do not forget to add the class “WebApiConfig.cs” under folder “App_Start” which contains the code below:

public static class WebApiConfig { public static void Register(HttpConfiguration config) { // Web API configuration and services // Web API routes config.MapHttpAttributeRoutes(); var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First(); jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 public static class WebApiConfig { public static void Register ( HttpConfiguration config ) { // Web API configuration and services // Web API routes config . MapHttpAttributeRoutes ( ) ; var jsonFormatter = config . Formatters . OfType < JsonMediaTypeFormatter > ( ) . First ( ) ; jsonFormatter . SerializerSettings . ContractResolver = new CamelCasePropertyNamesContractResolver ( ) ; } }

Step 6: Add a Secure OrdersController

Now we want to add a secure controller to serve our Orders. What I mean by a “secure” controller that its a controller attribute with [Authorize] attribute and can be accessed only when the request contains a valid access token issued by our Azure AD tenant for this app only. To keep things simple we’ll return static data. So add new controller named “OrdersController” and paste the code below:

[Authorize] [RoutePrefix("api/orders")] public class OrdersController : ApiController { [Route("")] public IHttpActionResult Get() { var isAuth = User.Identity.IsAuthenticated; var userName = User.Identity.Name; return Ok(Order.CreateOrders()); } } #region Helpers public class Order { public int OrderID { get; set; } public string CustomerName { get; set; } public string ShipperCity { get; set; } public Boolean IsShipped { get; set; } public static List<Order> CreateOrders() { List<Order> OrderList = new List<Order> { new Order {OrderID = 10248, CustomerName = "Taiseer Joudeh", ShipperCity = "Amman", IsShipped = true }, new Order {OrderID = 10249, CustomerName = "Ahmad Hasan", ShipperCity = "Dubai", IsShipped = false}, new Order {OrderID = 10250,CustomerName = "Tamer Yaser", ShipperCity = "Jeddah", IsShipped = false }, new Order {OrderID = 10251,CustomerName = "Lina Majed", ShipperCity = "Abu Dhabi", IsShipped = false}, new Order {OrderID = 10252,CustomerName = "Yasmeen Rami", ShipperCity = "Kuwait", IsShipped = true} }; return OrderList; } } #endregion 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 [ Authorize ] [ RoutePrefix ( "api/orders" ) ] public class OrdersController : ApiController { [ Route ( "" ) ] public IHttpActionResult Get ( ) { var isAuth = User . Identity . IsAuthenticated ; var userName = User . Identity . Name ; return Ok ( Order . CreateOrders ( ) ) ; } } #region Helpers public class Order { public int OrderID { get ; set ; } public string CustomerName { get ; set ; } public string ShipperCity { get ; set ; } public Boolean IsShipped { get ; set ; } public static List < Order > CreateOrders ( ) { List < Order > OrderList = new List < Order > { new Order { OrderID = 10248 , CustomerName = "Taiseer Joudeh" , ShipperCity = "Amman" , IsShipped = true } , new Order { OrderID = 10249 , CustomerName = "Ahmad Hasan" , ShipperCity = "Dubai" , IsShipped = false } , new Order { OrderID = 10250 , CustomerName = "Tamer Yaser" , ShipperCity = "Jeddah" , IsShipped = false } , new Order { OrderID = 10251 , CustomerName = "Lina Majed" , ShipperCity = "Abu Dhabi" , IsShipped = false } , new Order { OrderID = 10252 , CustomerName = "Yasmeen Rami" , ShipperCity = "Kuwait" , IsShipped = true } } ; return OrderList ; } } #endregion

Till this step we’ve built our API and configured the authentication part to be outsourced to Azure Active Directory (AD), now it is the time to build a client which will be responsible to consume this back-end API and talk to our Azure AD tenant to obtain access tokens.

Building the Client Application

As I stated before, we’ll build very simple desktop application to consume the back-end API, but before digging into the code, let’s add this application to our Azure AD tenant and configure the permission for the client to allow accessing the back-end API.

Step 7: Register the Client Application into Azure Active Directory

To do so navigate to Azure Management Portal again and add new application as we did on step 3. But this time and for this application will select “Native Client Application”, give the application friendly name, in my case I’ll name it “ClientAppAzureAD” and for the redirect URI I’ll enter “http://WebApiAzureADClient”. You can think for this URI as the endpoint which will be used to return the authorization code from Azure AD which will be used later to exchange this authorization code with an access token. No need to worry your self about this, because all of this will be handled for us auto-magically when we use and talk about Azure Active Directory Authentication Library (ADAL) toolkit.

Step 8: Configure the Client Application Permissions

This step is very important, we need to configure the client app and specify which registered application it can access, in our case we need to give the client application permission to access our back-end API (WebApiAzureAD), the delegated permissions selected will be “Have full access to the service”, and if you notice this permission list is coming from the modified “appPermission” node in the JSON application manifest file we’ve modified in step 4. After you do this click Save.

Step 9: Adding the client application project

Time to get back to visual studio to build the client application, You can use any type of client application you prefer (console app, WPF, windows forms app, etc..) in my case I’ll use windows form application, so I’ll add to our solution a new project of type “Windows Forms Application” named “WebApiAzureAD.Client”.

Once the project is added to the solution, we need to add some new keys to the “app.config” file which we’ll use to communicate with our Azure AD tenant, so open app.config file and paste the the snippet below:

<appSettings> <add key="ida:AADInstance" value="https://login.windows.net/{0}" /> <add key="ida:Tenant" value="taiseerjoudeharamex.onmicrosoft.com" /> <add key="ida:ClientId" value="1c92b0cc-6d13-497d-87da-bef413c9f26f" /> <add key="ida:RedirectUri" value="http://WebApiAzureADClient" /> <add key="ApiResourceId" value="http://taiseerjoudeharamex.onmicrosoft.com/WebApiAzureAD" /> <add key="ApiBaseAddress" value="http://localhost:55577/" /> </appSettings> 1 2 3 4 5 6 7 8 9 10 11 <appSettings> <add key = "ida:AADInstance" value = "https://login.windows.net/{0}" /> <add key = "ida:Tenant" value = "taiseerjoudeharamex.onmicrosoft.com" /> <add key = "ida:ClientId" value = "1c92b0cc-6d13-497d-87da-bef413c9f26f" /> <add key = "ida:RedirectUri" value = "http://WebApiAzureADClient" /> <add key = "ApiResourceId" value = "http://taiseerjoudeharamex.onmicrosoft.com/WebApiAzureAD" /> <add key = "ApiBaseAddress" value = "http://localhost:55577/" /> </appSettings>

So the value for “ClientId” key is coming from the “Client Id” value for the client we defined in Azure AD, and the same applies for the key “RedirectUri”.

For the value for “ApiResourceId” and “ApiBaseAddress” both values are coming from the back-end API application we already registered with Azure AD.

Step 10: Install the needed Nuget Packages for the client application

We need to install the below Nuget packages in order to be able to call the back-end API and our Azure AD tenant to obtain tokens, so open package manager console and install the below:

PM> Install-Package Microsoft.Net.Http PM> Install-Package Microsoft.IdentityModel.Clients.ActiveDirectory 1 2 PM > Install - Package Microsoft . Net . Http PM > Install - Package Microsoft . IdentityModel . Clients . ActiveDirectory

The first package installed is responsible to HttpClient for sending requests over HTTP, as well as HttpRequestMessage and HttpResponseMessage for processing HTTP messages.

The second package installed represents Azure AD Authentication Library (ADAL) which is used to enable a .NET client application to authenticate users against Azure AD and obtain access tokens to call back-end Web API.

Step 11: Obtain the token and call the back-end API

Now it is the time to implement the logic in the client application which is responsible to obtain the access token from our Azure AD tenant, then use this access token to access the secured API end point.

To do so, add new button on the form and open “Form1.cs” and paste the code below:

public partial class Form1 : Form { public Form1() { InitializeComponent(); } private static string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"]; private static string tenant = ConfigurationManager.AppSettings["ida:Tenant"]; private static string clientId = ConfigurationManager.AppSettings["ida:ClientId"]; Uri redirectUri = new Uri(ConfigurationManager.AppSettings["ida:RedirectUri"]); private static string authority = String.Format(aadInstance, tenant); private static string apiResourceId = ConfigurationManager.AppSettings["ApiResourceId"]; private static string apiBaseAddress = ConfigurationManager.AppSettings["ApiBaseAddress"]; private AuthenticationContext authContext = null; private async void button1_Click(object sender, EventArgs e) { authContext = new AuthenticationContext(authority); AuthenticationResult authResult = authContext.AcquireToken(apiResourceId, clientId, redirectUri); HttpClient client = new HttpClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authResult.AccessToken); HttpResponseMessage response = await client.GetAsync(apiBaseAddress + "api/orders"); string responseString = await response.Content.ReadAsStringAsync(); MessageBox.Show(responseString); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public partial class Form1 : Form { public Form1 ( ) { InitializeComponent ( ) ; } private static string aadInstance = ConfigurationManager . AppSettings [ "ida:AADInstance" ] ; private static string tenant = ConfigurationManager . AppSettings [ "ida:Tenant" ] ; private static string clientId = ConfigurationManager . AppSettings [ "ida:ClientId" ] ; Uri redirectUri = new Uri ( ConfigurationManager . AppSettings [ "ida:RedirectUri" ] ) ; private static string authority = String . Format ( aadInstance , tenant ) ; private static string apiResourceId = ConfigurationManager . AppSettings [ "ApiResourceId" ] ; private static string apiBaseAddress = ConfigurationManager . AppSettings [ "ApiBaseAddress" ] ; private AuthenticationContext authContext = null ; private async void button1_Click ( object sender , EventArgs e ) { authContext = new AuthenticationContext ( authority ) ; AuthenticationResult authResult = authContext . AcquireToken ( apiResourceId , clientId , redirectUri ) ; HttpClient client = new HttpClient ( ) ; client . DefaultRequestHeaders . Authorization = new AuthenticationHeaderValue ( "Bearer" , authResult . AccessToken ) ; HttpResponseMessage response = await client . GetAsync ( apiBaseAddress + "api/orders" ) ; string responseString = await response . Content . ReadAsStringAsync ( ) ; MessageBox . Show ( responseString ) ; } }

What we’ve implemented now is the below:

We’ve read bunch of settings which will be used to inform the client application what is the Uri/name for Azure AD tenant that it should call, the client id we obtained after registering the client application in Azure AD. As well we need to read the App Id Uri (ApiResourceId) which tells the client which Web API it should call to get the data from.

We’ve created an instance of the “AuthenticationContext” class, this instance will represent the authority that our client will work with. In our case the authority will be our Azure AD tenant represented by the Uri https://login.windows.net/taiseerjoudeharamex.onmicrosoft.com.

Now we’ll call the method “AcquireToken” which will be responsible to do internally the heavy lifting for us and the communication with our authority to obtain an access token. To do so and as we are building client application we need to pass three parameters which they are: a. The resource which the token will be sent to, b. The client id. c. The redirect uri for this client.

Now you will ask your self where the end user using this system will enter his AD credentials? The nice thing here that the AuthenticationContext class which is part of ADAL will take care of showing the authentication dialog in a popup and do the right communication with the correct end point where the end user will be able to provide his AD credentials, there is no need to write any extra single line of code to do this, thanks for the nice abstraction provided by ADAL. We’ll see this in action once we test the application.

After the user provides his valid AD credentials, a token is obtained and returned as property in the “AuthenticationResult” response along with other properties.

Now we need to do an ordinary HTTP GET method to our secure end point (/api/orders) and we’ve to pass this obtained access token in the authorization header using a bearer scheme. If everything goes correctly we’ll receive HTTP status code 200 along with the secured orders data.

Step 12: Testing the solution

We are ready to test the application, jump to your solution, right click on Web Api project “WebApiAzureAD.Api” select “Debug” then “Start New Instance”, then jump to your desktop application and do the same, start new instance of the EXE, click on the button and you will see the ADAL authentication dialog popups as the image blow.

Fill the credentials for an AD user registered in our tenant and click sign in, if the credentials provided is correct you will receive an access token as the image below, this access token will be sent int the authorization header for the GET request and you will receive your orders data.

Now if you tried to click on the button again without terminating the EXE (client app) you will notice that the ADAL authorization popup will not show up again and you get the token directly without providing any credentials, thanks to the built-in token cache which keeps track of the tokens.

If you closed the application and reopen it, then the ADAL authorization pop up will show up again, maybe this is not so convenience for end users. So to over come this issue you can use strategy called “token caching” which allows you to persist the obtained tokens and store them securely on a local protected file using DPAPI protection, this is better way to do it because you will not hit the authority server if you closed your client application and you still have a valid access token when you open the client again. You can read more about this implementation here.

Conclusion

Securing your ASP.NET Web API 2 by depending on Azure AD is something easy, if you are building an API for the enterprise which will be used by different applications, you can easily get up and running in no time.

As well the ADAL toolkit is providing us with great level of abstraction over the OAuth 2.0 specifications which makes developers life easy when building the clients, they will focus on business logic and the authentication/autherization part is will be handled by ADAL.

That’s it for now, I hope this tutorial will help you securing your ASP.NET Web API 2 using Azure AD, if you have any question or you have suggestions please drop me a comment.

The Source code for this tutorial is available on GitHub.

Follow me on Twitter @tjoudeh

Resources