Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Identity.Abstractions;

namespace Microsoft.Identity.Web
Expand All @@ -22,9 +23,23 @@ internal partial class DownstreamApi : IDownstreamApi
where TOutput : class
{
DownstreamApiOptions effectiveOptions = MergeOptions(serviceName, downstreamApiOptionsOverride, HttpMethod.Get);
HttpResponseMessage response = await CallApiInternalAsync(serviceName, effectiveOptions, false, null, user, cancellationToken).ConfigureAwait(false);

return await DeserializeOutput<TOutput>(response, effectiveOptions).ConfigureAwait(false);

try
{
HttpResponseMessage response = await CallApiInternalAsync(serviceName, effectiveOptions, false, null, user, cancellationToken).ConfigureAwait(false);
return await DeserializeOutput<TOutput>(response, effectiveOptions).ConfigureAwait(false);
}
catch(Exception ex) when (
ex is InvalidOperationException
|| ex is HttpRequestException)
{
Logger.HttpRequestError(
_logger,
serviceName!,
effectiveOptions.BaseUrl!,
effectiveOptions.RelativePath!, ex);
throw;
}
}

/// <inheritdoc/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections;
using Microsoft.Extensions.Logging;

namespace Microsoft.Identity.Web
{
/// <summary>
/// LoggerMessage class for DownstreamApi
/// </summary>
internal partial class DownstreamApi
{
internal static class Logger
{
private static readonly Action<ILogger, string, string, string, Exception?> s_httpRequestError =
LoggerMessage.Define<string, string, string>(
LogLevel.Debug,
DownstreamApiLoggingEventId.HttpRequestError,
"[MsIdWeb] An error occurred during HTTP Request. " +
"ServiceName: {serviceName}, " +
"BaseUrl: {BaseUrl}, " +
"RelativePath: {RelativePath} ");

private static readonly Action<ILogger, Exception?> s_unauthenticatedApiCall =
LoggerMessage.Define(
LogLevel.Information,
DownstreamApiLoggingEventId.UnauthenticatedApiCall,
"[MsIdWeb] An unauthenticated call was made to the Api with null Scopes");


/// <summary>
/// Logger for handling options exceptions in DownstreamApi
/// </summary>
/// <param name="logger">ILogger</param>
/// <param name="ServiceName">Name of Api receiving request</param>
/// <param name="BaseUrl">Base url from appsettings.</param>
/// <param name="RelativePath">Relative path from appsettings.</param>
/// <param name="ex">Exception</param>
public static void HttpRequestError(
ILogger logger,
string ServiceName,
string BaseUrl,
string RelativePath,
Exception? ex) => s_httpRequestError(logger, ServiceName, BaseUrl, RelativePath, ex);

/// <summary>
/// Logger for unauthenticated internal Api call in DownstreamApi
/// </summary>
/// <param name="logger">Logger</param>
/// <param name="ex">Exception</param>
public static void UnauthenticatedApiCall(
ILogger logger,
Exception? ex) => s_unauthenticatedApiCall(logger, ex);
}
}
}
11 changes: 10 additions & 1 deletion src/Microsoft.Identity.Web.DownstreamApi/DownstreamApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Identity.Abstractions;

Expand All @@ -23,21 +24,25 @@ internal partial class DownstreamApi : IDownstreamApi
private readonly IOptionsMonitor<DownstreamApiOptions> _namedDownstreamApiOptions;
private const string ScopesNotConfiguredInConfigurationOrViaDelegate = "IDW10107: Scopes need to be passed-in either by configuration or by the delegate overriding it. ";
private const string Authorization = "Authorization";
protected readonly ILogger<DownstreamApi> _logger;

/// <summary>
/// Constructor.
/// </summary>
/// <param name="authorizationHeaderProvider">Authorization header provider.</param>
/// <param name="namedDownstreamApiOptions">Named options provider.</param>
/// <param name="httpClientFactory">HTTP client factory.</param>
/// <param name="logger">Logger.</param>
public DownstreamApi(
IAuthorizationHeaderProvider authorizationHeaderProvider,
IOptionsMonitor<DownstreamApiOptions> namedDownstreamApiOptions,
IHttpClientFactory httpClientFactory)
IHttpClientFactory httpClientFactory,
ILogger<DownstreamApi> logger)
{
_authorizationHeaderProvider = authorizationHeaderProvider;
_namedDownstreamApiOptions = namedDownstreamApiOptions;
_httpClientFactory = httpClientFactory;
_logger = logger;
}

/// <inheritdoc/>
Expand Down Expand Up @@ -306,6 +311,10 @@ await _authorizationHeaderProvider.CreateAuthorizationHeaderForUserAsync(
cancellationToken).ConfigureAwait(false);
httpRequestMessage.Headers.Add(Authorization, authorizationHeader);
}
else
{
Logger.UnauthenticatedApiCall(_logger, null);
}
// Opportunity to change the request message
effectiveOptions.CustomizeHttpRequestMessage?.Invoke(httpRequestMessage);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Microsoft.Extensions.Logging;

namespace Microsoft.Identity.Web
{
internal static class DownstreamApiLoggingEventId
{
#pragma warning disable IDE1006 // Naming styles
// DownstreamApi EventIds 100+
public static readonly EventId HttpRequestError = new EventId(100, "HttpRequestError");
public static readonly EventId UnauthenticatedApiCall = new EventId(101, "UnauthenticatedApiCall");
#pragma warning restore IDE1006 // Naming styles
}
}