benjamin perkins, (aka @csharpguitar)

How (I) connected a console application to a Web API protected by an Azure Active Directory

The code can be downloaded from here or on GitHub.

I wrote this post here where I discussed how I configured Azure Active Directory into one of my Azure App Service Web Apps. One of the main differences between that post and what I am trying to do here was realized when I received this exception:

AADSTS90014: The request body must contain the following parameter: ‘client_secret or client_assertion’.

What this ultimately lead is my understanding that when I configure/create my Azure Active Directory application during the configuration the, shown in Figure 4, is that is creates a ‘confidential’ application. I am not part of the Active Directory team so I can only speculate that it means that only certain types of applications can access this application, possibly only applications running on the Azure Platform. To get the access to work using this client application I needed to create a ‘public’ application. Check out the section called ‘Configure a native client application’ here which IMO is the way in which you create a ‘public’ Azure Active Directory application.

Here are the steps I took to get my console application using an HttpClient to call a Web API hosted on the Azure platform as an App Service Web App:

Sweet success! This wasn’t easy, but should it be? This was a real challenge for me, it took a lot of time and trail to get it to work and I wasn’t able to find an end-to-end set of instructions for getting it to work, maybe because this is still in PREVIEW so it has and likely will change…let’s get started.

Check out my other post on a similar subject: How (I) configured Azure Active Directory into my ASP.NET MVC OWIN web application

Create the Web API and publish it to the Web App

I created a simple Web API using Visual Studio 2015 as seen in Figure 1.

authenticate a Web API using Azure Active Directory

Figure 1, authenticate a Web API using Azure Active Directory

I added a controller named MgmtController.cs with a method called GetMgmt(string id, string value) that simply returns the DateTime and the parameters that are sent to it. See code snippet 1 that shows the code.

namespace mgmt.Controllers
{
    public class MgmtController : ApiController
    {
        [HttpGet]
        [Route("api/GetMgmt/{id}/{value}")]
        public IHttpActionResult GetMgmt(string id, string value)
        {
            try
            {
                return Ok(DateTime.Now.ToString() + $" id is '{id}' and the value is '{value}'");
            }
            catch (Exception ex)
            {
                return Ok(ex.Message.ToString());
            }
        }
    }
}
Code snippet 1, an example Web API used to authenticate against an Azure Active Directory Application running on a Azure Web App

Create the Console application and test the Web API is working – without authentication

Next I created a console application, as shown in Figure 2.

C# console application for consuming Web API protected by Azure Active Directory running on an Azure App Service Web App

Figure 2, C# console application for consuming Web API protected by Azure Active Directory running on an Azure App Service Web App

The code that called the Web API is shown in code snippet 2.

static async Task CallWebApiUnprotectedAsync(string webApiUrl)
{
 try
 {
   HttpClient client = new HttpClient();
   Uri requestURI = new Uri(webApiUrl);
   WriteLine($"Reading values from '{requestURI}'.");
   HttpResponseMessage httpResponse = await client.GetAsync(requestURI);
   WriteLine($"HTTP Status Code: '{httpResponse.StatusCode.ToString()}'");
   WriteLine($"HTTP Response: '{httpResponse.ToString()}'");
   string responseString = await httpResponse.Content.ReadAsStringAsync();
   var json = JsonConvert.DeserializeObject(responseString);
   WriteLine($"JSON Response: {json}");
 }
 catch(Exception ex)
 {
   WriteLine("Exception in CallWebApiUnprotectedAsync(): " + ex.Message);
 }            
}
Code snippet 2, an example Console application calling a Web API running on an Azure Web App

Running the code I get the output shown in Figure 3.

calling a Web API hosted on an Azure Web App from a console application

Figure 3, calling a Web API hosted on an Azure Web App from a console application

Configure Authentication/Authorization for the Web App Now that I was certain my Web API worked and can be called, unprotected from my console application I moved onto the enablement of Azure Active Directory. I had already created an Azure Active Directory as you can see in Figure 4 in the Current Active Directory value of ‘Benjamin Perkins’ on the Azure Active Directory Settings blade. The actual Azure Active Directory is benjaminPerkins.onmicrosoft.com,

So, from the beginning, I published the App Service Web App to a the platform, it is called ‘mgmt’. I logged into the Azure Management portal here and and navigated to the Web App. Then I clicked on the Authentication / Authorization settings link which opened the Authentication / Authorization blade. By default it is Off, so click the On button, since I wanted to enable Azure Active Directory, I selected it from the drop-down, then I click on Azure Active Directory from the list of Authentication Providers which opened the blade I referred to initially ‘Azure Active Directory Settings. Click on Express, unless you want to do some Advanced stuff, leave the Create New Ad App selected and give the App an name, in my case ‘mgmtADApp’. Like mentioned originally, this creates a CONFIDENTIAL application.

enabled / configure Azure Active Directory authentication for an Azure App Service Web App

Figure 4, enabled / configure Azure Active Directory authentication for an Azure App Service Web App

Click OK, then Save the configuration. You can then navigate to the Azure Active Directory feature in the Azure Management Portal here and see that the App is registered and has an application type equal to Web App / API. As seen in Figure 5.

register a CONFIDENTIAL Azure Active Directory Application

Figure 5, register a CONFIDENTIAL Azure Active Directory Application

What I did next was access the Web App using a browser, just to make sure that I could, indeed login and it was successful. As I was already logged into the Azure Management portal with the identity that is allowed AAD access to my Web App, I was not prompted to provide my credentials. I noticed during the login that I get redirected, authenticated and redirected back to the my site. I copied the URL while it was waiting on the authentication and found some valuable information within it.

The above 4 bullet points are the values you need to create an instance of Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext, and those were not easy nor simple to find….I found out…

Call the protected Web API, test and fail miserably…

Before we get to the solution, I want to share some exceptions I worked through.

The page cannot be displayed because an internal server error has occurred

Figure 6, The page cannot be displayed because an internal server error has occurred

After finally figuring out that I needed a PUBLIC NATIVE CLIENT APPLICATION I got a 401.71, IIS status 401 sub status 71, HTTP Status Code: ‘Unauthorized’, HTTP Response: ‘StatusCode: 401, ReasonPhrase: ‘Unauthorized’. I saw the 401 in the output of my console application, as shown in Figure 7, but I, like so often, the real detail comes with the sub status code which I found be enabling Failed Request Tracing, Figure 8 and reviewing the XML file, Figure 9.

received a 401.71 IIS unauthorized HTTP response from my Azure Active Directory Application running in an App Service from a client

Figure 7, received a 401.71 IIS unauthorized HTTP response from my Azure Active Directory Application running in an App Service from a client

enable FREB logs to troubleshoot 401.71 Azure Active Directory Application authentication issue

Figure 8, enable FREB logs to troubleshoot 401.71 Azure Active Directory Application authentication issue

Finding the sub status code took me further as there were some other articles with tips and thoughts about why it was happening and how to resolve. None provided the complete solution, but they moved me forward.

FREB trace with the 401.71 issue when calling an AAD application from a console application

Figure 9, FREB trace with the 401.71 issue when calling an AAD application from a console application

So, finally, here are the steps I took to code the console application that calls a Web API hosted on Azure as an App Service Web App protected by Azure Active Directory:

Add the Microsoft.IdentityModel.Clients.ActiveDirectory NuGet Package

Right-click the console application project and select Manage NuGet Packages… as shown in Figure 10.

add a NuGet Package library to a console application in Visual Studio

Figure 10, add a NuGet Package library to a console application in Visual Studio

Search for Microsoft.IdentityModel.Clients.ActiveDirectory and install it, you should find something similar to Figure 11.

install the NuGet package Microsoft.IdentityModel.Clients.ActiveDirectory  401.71 Azure Active Directory Web API

Figure 11, install the NuGet package Microsoft.IdentityModel.Clients.ActiveDirectory 401.71 Azure Active Directory Web API

Write the code find out the values for AUTHORITY, RESOURCE, CLIENTID, REDIRECTURI and PARAMETERS

The code in Code Snippet 3 is how I called the API.

Points:

static async Task CallWebApiProtectedAsync(string webApiUrl)
{
 parameters = new PlatformParameters(PromptBehavior.Always);
 //string authority = "https://login.microsoftonline.com/b29343ba-***/oauth2/authorize"; // Authorization endpoint
 string authority = "https://login.windows.net/b29343ba-***/oauth2/token"; //token endpoint
 string resource = "4f2e934d***";  //Web API Client Id (mgmtADApp)
 string clientId = "a2b01fd9-***"; //Client Id (mgmgADAppClient)
 string redirectUri = "https://mgmt.azurewebsites.net/.auth/login/done";
 try
 {
  AuthenticationContext authContext = new AuthenticationContext(authority);
  var token = await authContext.AcquireTokenAsync(resource, clientId, new Uri(redirectUri), parameters);
  var authHeader = token.CreateAuthorizationHeader();
  HttpClient client = new HttpClient();
  client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue(token.AccessTokenType, token.AccessToken);
  Uri requestURI = new Uri(webApiUrl);
  WriteLine($"Reading values from '{requestURI}'.");
  HttpResponseMessage httpResponse = await client.GetAsync(requestURI);
  WriteLine($"HTTP Status Code: '{httpResponse.StatusCode.ToString()}'");
  WriteLine($"HTTP Response: '{httpResponse.ToString()}'");
  string responseString = await httpResponse.Content.ReadAsStringAsync();
  var json = JsonConvert.DeserializeObject(responseString);
  WriteLine($"JSON Response: {json}");
 }
 catch (Exception ex)
 {
  WriteLine("Exception in CallWebApiUnprotectedAsync(): " + ex.Message);
 }
}
Code Snippet 3, calling a Web API on Azure Web App protected by Azure Active Directory using a console

Navigate to Figure 12 by selecting the Azure Active Directory feature then click the App Registrations then Endpoints.

finding the right endpoint for Azure Active Directory authentication

Figure 12, finding the right endpoint for Azure Active Directory authentication

NOTE to self –> find out what these other endpoints do and are and how to use them and why to use them versus the others…

I read through this book “Modern authentication with Azure Active Directory for Web applications” by Vittorio Bertocci which does a super job at describing the Endpoint types:

Create a Native Client Application in my Azure Active Directory

The instructions are pretty straight forward here, so won’t go too deep. Focus on the section “(Optional) Configure a native client application”.

Much of the configuration of the Azure Active Directory is still in the AUX portal here, no problem with that. Open the Active Directory link and you’ll see something similar to that shown in Figure 13.

adding an native client application to azure active directory

Figure 13, adding an native client application to azure active directory

Notice also after clicking on the APPLICATIONS link that the mgmtADApp which was created in Figure 4 is present.

Click the ADD+ button at the bottom of the page similar to that in Figure 14.

add an Azure Active Directory Application

Figure 14, add an Azure Active Directory Application

Then follow the wizard that which consists of 3 pages, Figure 15, Figure 16 and Figure 17.

Add an application my organization is developing

Figure 15, Add an application my organization is developing

Create a NATIVE CLIENT APPLICATION, Web API, Web App, Active Directory

Figure 16, Create a NATIVE CLIENT APPLICATION, Web API, Web App, Active Directory

REDIRECT URI for an Azure Active Directory protected Azure App Services Web App

Figure 17, REDIRECT URI for an Azure Active Directory protected Azure App Services Web App

This is the setting I reference to earlier in point 4 of the Write the code section, this value and the one in the code need to match 100%.

Once created, you can look in the new and AUX portal to see the mgmtADAppClient, like in Figure 12.

Set the permissions, this one kind of got me at first. I thought I needed to add the application I just created to the app I just created, which means I wanted to add the app to itself. It turns out I needed to add the mgmgADApp, CONFIDENTIAL application to the NATIVE PUBLIC applciation I just created, I saw Figure 18 and 19.

X when adding a permission to an Azure Active Directory Application

Figure 18, X when adding a permission to an Azure Active Directory Application

I would expect the X shown in Figure 18 because I wouldn’t want, need to grant a permission to itself.

the X when adding a permission to an Azure Active Directory Application is gone

Figure 19, the X when adding a permission to an Azure Active Directory Application is gone

Then, back on the CONFIGURE page for my mgmgADAppClient application I need to grant the Access mgmtAdApp to this NATIVE PUBLIC Azure Active Directory application, shown in Figure 20. Save the configuration, notice the CLIENT ID use it in the code.

add permission to access Web API, Web App, Client / Console App, Azure Active Directory

Figure 20, add permission to access Web API, Web App, Client / Console App, Azure Active Directory

You can also see the permissions in the new portal, as shown in Figure 21.

check the Azure Active Directory permissions

Figure 21, check the Azure Active Directory permissions

Then i run the code and, holy jeebers, 200!!!

Success

Figure 22, Success

I love this stuff, the satisfaction I get when it works is …..