Skip to content

Instantly share code, notes, and snippets.

@fabiomaulo
Created October 8, 2017 19:52
Show Gist options
  • Save fabiomaulo/1a3d6a29ce27a3c566c405225ddbf714 to your computer and use it in GitHub Desktop.
Save fabiomaulo/1a3d6a29ce27a3c566c405225ddbf714 to your computer and use it in GitHub Desktop.
HttpClient handler with Auth using AAD
/// <summary>
/// AuthHandler for AAD
/// </summary>
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;
}
}
@fabiomaulo
Copy link
Author

fabiomaulo commented Oct 8, 2017

	public class ApiHttpClient
	{
		/// <summary>
		/// Create a new <see cref="HttpClient"/> with the <see cref="ActiveDirectoryAuthHandler"/> in the pipeline.
		/// </summary>
		/// <param name="aadInstance">The AAD-Instance (ex: https://login.microsoftonline.com/hunabku.onmicrosoft.com).</param>
		/// <param name="clientAppId">The App ID of this client.</param>
		/// <param name="clientAppKey">The App Key of this client.</param>
		/// <param name="apiAppIdUri">The App ID of the API</param>
		/// <param name="apiServerUrl">The API base address (ex: http://myapp.azurewebsites.net)</param>
		/// <returns>A new instance of <see cref="HttpClient"/>. </returns>
		public static HttpClient Default(string aadInstance
			, string clientAppId
			, string clientAppKey
			, string apiAppIdUri
			, string apiServerUrl = null)
		{
			var client = new HttpClient(new ActiveDirectoryAuthHandler(
				new AuthenticationContext(aadInstance)
				, new ClientCredential(clientAppId, clientAppKey)
				, apiAppIdUri));
			if (!string.IsNullOrWhiteSpace(apiServerUrl))
			{
				client.BaseAddress = new Uri(apiServerUrl);
			}

			return client;
		}

#if !NETSTANDARD
		/// <summary>
		/// Create a new <see cref="HttpClient"/> with the <see cref="ActiveDirectoryAuthHandler"/> and the <see cref="System.Net.Http.WebRequestHandler"/> in the pipeline.
		/// </summary>
		/// <param name="aadInstance">The AAD-Instance (ex: https://login.microsoftonline.com/hunabku.onmicrosoft.com).</param>
		/// <param name="clientAppId">The App ID of this client.</param>
		/// <param name="clientAppKey">The App Key of this client.</param>
		/// <param name="apiAppIdUri">The App ID of the API</param>
		/// <param name="apiServerUrl">The API base address (ex: http://myapp.azurewebsites.net)</param>
		/// <returns>A new instance of <see cref="HttpClient"/>. </returns>
		public static HttpClient WithNetCache(string aadInstance
			, string clientAppId
			, string clientAppKey
			, string apiAppIdUri
			, string apiServerUrl = null)
		{
			var client = new HttpClient(new ActiveDirectoryAuthHandler(
				new AuthenticationContext(aadInstance)
				, new ClientCredential(clientAppId, clientAppKey)
				, apiAppIdUri
				, new System.Net.Http.WebRequestHandler()
				{
					CachePolicy = new System.Net.Cache.HttpRequestCachePolicy(System.Net.Cache.HttpRequestCacheLevel.Default)
				}));
			if (!string.IsNullOrWhiteSpace(apiServerUrl))
			{
				client.BaseAddress = new Uri(apiServerUrl);
			}

			return client;
		}
#endif
	}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment