Try fast search NHibernate

16 April 2017

API-APP server-to-server through Azure Active Directory

In the previous post you see how register your API app in the AAD and how register a client app to then access using RESTful AAD api. The work to do to access your API is not so much using any langue, btw is even more easy using .NET.

The POC

Creates a new Console project and then add Microsoft.IdentityModel.Clients.ActiveDirectory package.

Client01

Now add this class to your project

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

namespace ClientFor1MApi
{
  public class ActiveDirectoryAuthHandler : DelegatingHandler
  {
    private const int maxAuthRetry = 3;
    private readonly AuthenticationContext authContext;
    private readonly ClientCredential clientCredential;
    private readonly string appIdUri;

    public ActiveDirectoryAuthHandler(AuthenticationContext authContext
      , ClientCredential clientCredential
      , string appIdUri
      , HttpMessageHandler innerHandler) : base(innerHandler)
    {
      this.authContext = authContext ?? throw new ArgumentNullException(nameof(authContext));
      this.clientCredential = clientCredential ?? throw new ArgumentNullException(nameof(clientCredential));
      this.appIdUri = appIdUri ?? throw new ArgumentNullException(nameof(appIdUri));
    }

    public ActiveDirectoryAuthHandler(AuthenticationContext authContext
      , ClientCredential clientCredential
      , string appIdUri) : base(new HttpClientHandler())
    {
      this.authContext = authContext ?? throw new ArgumentNullException(nameof(authContext));
      this.clientCredential = clientCredential ?? throw new ArgumentNullException(nameof(clientCredential));
      this.appIdUri = appIdUri ?? throw new ArgumentNullException(nameof(appIdUri));
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
      var authResult = await GetAuth();
      if (authResult != null)
      {
        request.Headers.Authorization = new AuthenticationHeaderValue(authResult.AccessTokenType, authResult.AccessToken);
      }
      return await base.SendAsync(request, cancellationToken);
    }

    private async Task<AuthenticationResult> GetAuth()
    {
      int retryCount = 0;
      bool mustRetry;

      do
      {
        mustRetry = false;
        try
        {
          // ADAL includes an in memory cache, so this call will only send a message to the server if the cached token is expired.
          return await authContext.AcquireTokenAsync(appIdUri, clientCredential);
        }
        catch (AdalException ex) when (ex.ErrorCode == "temporarily_unavailable")
        {
          mustRetry = true;
          retryCount++;
          await Task.Delay(3000);
        }
      } while ((mustRetry == true) && (retryCount < maxAuthRetry));

      return null;
    }
  }
}

We will use the implementation of HttpMessageHandler for the pipeline of the instance of HttpClient we will use to access to our API.

The HtppMessageHandler needs three parameters: AuthenticationContext, ClientCredential and appIDUri. To create each one we needs some information we have seen in the previous post and to be consistent I will reuse the same images.

The AuthenticationContext

To create an instance of the AuthenticationContext we need only the URL of the AAD where our API-app and our client are registered. It should look something like this:

Client02

The ClientCredential

To create an instance of ClientCredential we need the “Application ID” and the “Key” of our client registered un our AAD:

the Application ID is

AadPanel15

the key is

AadPanel14

The appIDUri

The App ID URI is the value we give to our API-app registered in our AAD

AadPanel02

The usage of ActiveDirectoryAuthHandler

  public class Program
  {
    static void Main(string[] args)
    {
      var aadInstance = "https://login.microsoftonline.com/yourAaadDomain.onmicrosoft.com";
      var clientAppId = "1e852e38-2ef9-439b-b696-dccaa811562c";
      var clientAppKey = "vRSDlEOAFrYi59zoVQyuamJv0ZPRegZTd87ugngD8aU=";
      var apiAppIdUri = "https://The1MApi.azurewebsites.net";

      var httpClient = new HttpClient(new ActiveDirectoryAuthHandler(
        new AuthenticationContext(aadInstance)
        , new ClientCredential(clientAppId, clientAppKey)
        , apiAppIdUri
        ))
      {
        BaseAddress= new Uri("https://The1MApi.azurewebsites.net")
      };

      var _ = httpClient.PostAsync("api/values"
        , new FormUrlEncodedContent(new Dictionary<string, string> { { "value", "a new value from client" } })).Result;

      var response = httpClient.GetAsync("api/values").Result;

      Console.Write(response.Content.ReadAsStringAsync().Result);

      Console.ReadLine();
    }
  }

Just remember that the instance of the HttpClient should be a singleton with the same lifecycle of your application.

No comments:

Post a Comment