Securing REST API using Azure Active Directory


Scenario:  you have a web & mobile front-end, both using a REST API as a back-end.  You want to secure that back-end with authentication / authorization.  How do you do that in Azure?

image

There are obviously a bunch of ways to do that.  In this post, I’ll discuss the recommended approach:  using Azure Active Directory.  We’ll build an Hello World solution in .NET implementing it.

This is actually quite well documented in the Azure documentation so in this post I’ll just try to complete the documentation with practical points.

You can read about Azure API apps here and follow a good Getting started tutorial here.  This walks you through building a three tier app, exposes you to the powerful Swagger integration (you can read about Swagger in this post) and the configurations around Cross-Origin Resource Sharing (CORS).

You can then go about and read authentication / authorization of API apps here.

I will not cover API Management in this post but once you expose your API publically, i.e. not just to your own services, it’s a good idea to consider Azure API Management to manage it, if only to throttle usage to guarantee a good quality of service to everyone.

Overview of the solution

So we’re going to use Azure Active Directory (or Azure AD or AAD) as the identity provider for our solution.

image

Now keep in mind there are many variations on this architecture.  There could be a “Web API” within the Web app for AJAX calls.  There could be an Azure Mobile App API (used by the mobile device only), itself using the API app.  Many other variations.

Here we’ll focus on the architecture pictured in the diagram above.

Notice that the API app is accessed by two classes of identities:

  • Service Principal:  the Web app accesses the API as itself, not as the end-user
  • End-User Principal:  the mobile app accesses the API as the end-user

This creates an asymmetry within the API app.  For instance, authorization might be a bit funky since the API would trust the service principal to implement authorization rules on its end while it would need to implement it for the end-user.

This is one of the reason why some people might put another API between the mobile app & the back-end API.

In this post, I’ll focus on a Service Principal accessing an API app.

If you are unfamiliar with AAD applications, have a look at Azure Active Directory Application and if you’re unfamiliar with AAD Service Principals, read Using Azure Active Directory Service Principal.

Let’s build it!

Ok, let’s go and create a solution.  Let’s call it ApiAuthDemo.

Under that solution, let’s create a ASP.NET application.  I’m using VS 2015 so it might look a little different on other versions of the project.  I choose the ASP.NET Web Application template under the Cloud template folder.

image

I call the project AboutMeApi.  I then choose Azure API App sub-template.

image

I then create a straight Console App.  This will be the client accessing the API for the demo.

image

I call it AboutMeConsole.

So in the end, you should have the following structure in Visual Studio:

image

About Me API

Let’s flesh out the API.  And when I say flesh out, I mean let’s change 500 bytes of code.  This is a demo after all!

Under Models folder, create a class PersonaModel with the following code:


namespace AboutMeApi.Models
{
    public class PersonaModel
    {
        public string Description { get; internal set; }
        public string Name { get; set; }
        public string[] Claims { get; internal set; }
    }
}

Under the Controllers folder rename ValuesController by PersonaController.  This should hopefully rename the underlying class.  Let’s keep the following code:

using AboutMeApi.Models;
using System.Linq;
using System.Security.Claims;
using System.Web.Http;

namespace AboutMeApi.Controllers
{
    public class PersonaController : ApiController
    {
        // GET api/values
        public PersonaModel Get()
        {
            var identity = (ClaimsIdentity)User.Identity;
            var claims = from i in identity.Claims
                         select i.Type + " | " + i.Value;

            return new PersonaModel
            {
                Description = "Description of the invoking user",
                Name = identity.Name,
                Claims = claims.ToArray()
            };
        }
    }
}

As you can see, what this API does is take the current user information and return it to the caller.

Then under App_Start folder, in SwaggerConfig, uncomment the following:

// ***** Uncomment the following to enable the swagger UI *****
})
.EnableSwaggerUi(c =>
{

Now we can start the web app to check out our API.  Start it with F5.  You should land on the root of your web site and get a Forbidden 403 error.  That’s because there is no content in the web site, it’s an API.

Go to the Swagger path:  http://localhost:18008/swagger/.  Actually, my web app is installed on port 18008, yours will be different as it is randomly assigned.

This gives you the beautiful Swagger UI.  You see the Persona API, expend it.  You’ll see the GET operation ; click it.  Then Try it out.  No parameters are required and you should have the following in return payload:

{ "Description": "Description of the invoking user", "Name": "", "Claims": [] }

That is because you aren’t authenticated so the current user is empty.  No problem, we’ll get there.

Configuring Service Principal in AAD

Before we build the client test console, we’ll need to be able to authenticate.

For that, we’ll create a Service Principal in AAD.  This will be the identity of the service calling our API ; in our case, the Console Application.

As explained in Azure Active Directory Application, an AAD application is a Service Principal which is the equivalent of a “Service User” in on premise AD, except it is a first class citizen.

Here I will refer you to the excellent article of Tom FitzMacken:  Create Active Directory application and service principal using portal to create your Service Principal in your AAD with the following properties:

Name Value
Name AboutMeConsoleClient
Sign-on URL https://console.Aboutme.com
App ID URI https://console.Aboutme.com

You’ll need to create a key and remember its value.  You should also copy the client ID of your Service principal.

Create API Application in AAD

This step could be done automatically when deploying your API app in Azure, but I like to see what’s going under the cover…  a little.

So let’s create another app with the following properties:

Name Value
Name AboutMeApi
Sign-on URL https://api.Aboutme.com
App ID URI https://api.Aboutme.com

We will change some of those properties when we deploy the API to Azure but it’s ok for now.

You do not need to create a key for that application as we will not login to AAD with it but use it only as an application for our Service Principal to log against.  You need to capture the client-ID of the app though.

Console App

Now we can build our console app.

First, let’s add a REST API client of the API we just created in the Console app.

Right-click on the Console project, select Add, follow the sub-menu to REST API Client…

You should see the following dialog.

image

Since Swagger defines the meta data of your API, it is possible to construct a client for it from that meta data.  This is what this tool does.

Select an existing swagger metadata file & click Browse.  Now you need to tell the system where the swagger file is.  If you go back to the Swagger web page, at the top of the screen you’ll see

image

So http://localhost:18008/swagger/docs/v1 is where your swagger metadata file is.  Again, the port number will likely be different on your PC.

Change the namespace for AboutMeConsole.AboutMeApi before hitting OK.

Now you can go in the Program file and paste the following code:

using AboutMeConsole.AboutMeApi;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System;
using System.Net.Http.Headers;
using System.Threading.Tasks;

namespace AboutMeConsole
{
    class Program
    {
        static void Main(string[] args)
        {
            DoJobAsync().Wait();
        }

        private async static Task DoJobAsync()
        {
            var authenticationResult = await AuthenticateAsync();
            var client = new AboutMeApiClient
            {
            };

            client.HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
                "Bearer",
                authenticationResult.AccessToken);

            var persona = await client.Persona.GetAsync();
        }

        private async static Task<AuthenticationResult> AuthenticateAsync()
        {
            var authority = "https://login.microsoftonline.com/<your AAD name here>.onmicrosoft.com";
            //  Invoked App ID
            var resource = "<The client ID of AboutMeApi app>";
            var clientID = "<The client ID of AboutMeConsoleClient app>";
            var clientSecret = "<The secret key of AboutMeConsoleClient app>";
            var clientCredential = new ClientCredential(clientID, clientSecret);
            var context = new AuthenticationContext(authority, false);
            var authenticationResult = await context.AcquireTokenAsync(
                resource,
                clientCredential);

            return authenticationResult;
        }
    }
}

You should have a few classes your system doesn’t recognize.  You need to import the NuGet package Microsoft.IdentityModel.Clients.ActiveDirectory to get those classes.

You can then go to the AuthenticateAsync method and replace the placeholder, e.g. <your AAD name here>, by the values you’ve collected.

Now let me explain a bit what the code does here.

First the style:  async.  This is a console app, so async is pretty useless since the main thread is always running, but I like to get everyone in the habit of using await / async for all their code.  So in the main, I do a call to an async method (DoJobAsync) and I wait (block) on it.  This is the only non-async code.  The rest you could copy paste in service code.

So, in DoJobAsync we first fetch a token from AAD, then we create an API client.  On that client we add the access token in the request headers.

image

We have the flow described above where the console app goes to AAD, authenticates itself as the console app, in the context of the API app (remember, authentication in AAD always is in the context of a target app).  It receives a bearer token that it passes to the API when invoking it.

Now if you try the console app, you’ll see that the persona object returned by the API is basically empty.  Why is that?

That’s because there is no code in the API app to crack open the token and find the identity of the caller.  We could add ADAL code there and that might be a sensible thing to do in a development environment, but I won’t cover this in this post.

Instead, I’ll leverage the authentication / authorization feature of Azure App Service.

Deploying the API in Azure

Let’s publish the API in Azure.  Put it under your favorite App Service Plan & Resource Group.

Try to call the API App name itself AboutMeApi.  But since it needs to be globally unique (being mapped to a DNS), you’ll likely need to append some numbers at the end.  I called mine AboutMeApi2016.

You can then go in the API app in the portal and select the Authentication / Authorization feature.

image

This feature is pretty handy.  It basically runs the authentication code outside of your app.  A gateway, in front of your app, intercept the bearer token cracks it open, does the cryptography to check out everything is valid and reemit http-headers your app can readily consume.  Isn’t that fantastic?  If not, what is, I’m asking you.

I definitely suggest you read the documentation around service authentication to understand the different options you have.

For us, let’s just switch the authentication to ON, leave the default to “Log in with Azure Active Directory”, then configure the AAD.  For that, select the Express Management mode and then select your app.  If you’re like me and have multiple AAD in your subscription and the AAD you’re using isn’t the default one, select Advanced.

In client ID, paste the client ID of the API app we created earlier.  In Issuer URL, you need to paste https://sts.windows.net/ and append the tenant id of your AAD.  To find that out, go to your AAD configuration, select “View Endpoints”

image

Then you should see the ID where I’ve put the orange box.

image

You can save your API App configuration in the portal.

Console App pointing to Azure App

Now let’s go back to our Console app and change the following code in DoJobAsync:

            var client = new AboutMeApiClient
            {
                BaseUri = new Uri("https://aboutmeapi2016.azurewebsites.net")
            };

Of course, you need to put your own unique URL there but…  and I can’t stress that enough, you need to put HTTPS in your url there.  If you don’t, the authorization feature is going to give you an unauthorized and if you’re lucky like me, you’ll need 3 hours to find out why.  Hint:  it is the letter ‘s’ missing at the end of ‘http’

Now, let’s run the console app.  You’ll see the persona object gets filled with information, including the list of claims given by AAD.

An interesting fact is that your Service Principal doesn’t have a name and this is why the name property is still blank.  Your application is identified by an app-id.

Token Cache

I went extremely quickly through the code but an interesting fact is that the object AuthenticationContext contains a property TokenCache which is a global cache in your app-domain.  This means that the next time you call it for an authentication, it won’t go to AAD to fetch a token.

This is gold for performance and scalability of course.

It is clever enough to figure out when the cached token are expiring and will then go again to AAD.

Conclusion

Ok, this wasn’t trivial I know.  Lots of GUIDs and configuration right-left & center.

But the important items are:

  • You can access an API App using a Service Principal ; this is what you’ll typically do for an API accessed as a “back-end” from another service
  • You can have different Service Principal accessing the same API and you could give different authorization to different ones ; authorization would be done in the code, either declaratively (Authorize attribute) or imperatively
  • Azure App Service can take care of the Authentication / Authorization for you

An issue you’ll have in your dev setup, i.e. your own PC while you develop your solution, is that you won’t have the App Service Authentication gateway in front of your service.

You could fix that in different ways.  You could ignore the authentication in dev, knowing it’s taken care of in production.  A bit risky, but you know you could have a flag making sure it’s there in production.  You could put ADAL code in your app and take care of the authentication yourself.  Or you could have a gateway in dev that essentially does the same thing than the one in Azure.  You simply don’t deploy it when you go to Azure.

Advertisements

One thought on “Securing REST API using Azure Active Directory

  1. Pingback: Azure Active Directory Labs Series – Protect Web App | Vincent-Philippe Lauzon's blog

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s