diff --git a/.github/workflows/aot-check.yml b/.github/workflows/aot-check.yml
index 2e81c1969..b0427b167 100644
--- a/.github/workflows/aot-check.yml
+++ b/.github/workflows/aot-check.yml
@@ -29,5 +29,5 @@ jobs:
- name: Runs powershell script
id: aot-powershell
- run: build\test-aot.ps1 'net8.0'
+ run: build\test-aot.ps1 'net9.0'
diff --git a/Directory.Build.props b/Directory.Build.props
index b6448fd88..ae43d78fd 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -2,7 +2,7 @@
- 3.8.5
+ 3.9.1
$(MicrosoftIdentityWebVersion)
@@ -95,7 +95,7 @@
4.6.0
4.36.0
4.57.0-preview
- 9.0.0
+ 9.1.0
8.0.5
diff --git a/build/test-aot.ps1 b/build/test-aot.ps1
index 99e02bb5f..faee0c9a1 100644
--- a/build/test-aot.ps1
+++ b/build/test-aot.ps1
@@ -16,7 +16,7 @@ foreach ($line in $($publishOutput -split "`r`n"))
}
Write-Host "Actual warning count is: ", $actualWarningCount
-$expectedWarningCount = 65
+$expectedWarningCount = 50
if ($LastExitCode -ne 0)
{
diff --git a/src/Microsoft.Identity.Web.DownstreamApi/DownstreamApi.HttpMethods.cs b/src/Microsoft.Identity.Web.DownstreamApi/DownstreamApi.HttpMethods.cs
index 84022a689..19d8863e1 100644
--- a/src/Microsoft.Identity.Web.DownstreamApi/DownstreamApi.HttpMethods.cs
+++ b/src/Microsoft.Identity.Web.DownstreamApi/DownstreamApi.HttpMethods.cs
@@ -19,6 +19,11 @@ namespace Microsoft.Identity.Web
internal partial class DownstreamApi : IDownstreamApi
{
///
+
+#if NET8_0_OR_GREATER
+ [RequiresUnreferencedCode("Calls JsonSerializer.Serialize")]
+ [RequiresDynamicCode("Calls JsonSerializer.Serialize")]
+#endif
public async Task GetForUserAsync(
string? serviceName,
Action? downstreamApiOptionsOverride = null,
@@ -46,6 +51,11 @@ ex is InvalidOperationException
}
///
+
+#if NET8_0_OR_GREATER
+ [RequiresUnreferencedCode("Calls JsonSerializer.Serialize")]
+ [RequiresDynamicCode("Calls JsonSerializer.Serialize")]
+#endif
public async Task GetForUserAsync(
string? serviceName,
TInput input,
@@ -82,6 +92,11 @@ ex is InvalidOperationException
}
///
+
+#if NET8_0_OR_GREATER
+ [RequiresUnreferencedCode("Calls JsonSerializer.Serialize")]
+ [RequiresDynamicCode("Calls JsonSerializer.Serialize")]
+#endif
public async Task GetForAppAsync(
string? serviceName,
Action? downstreamApiOptionsOverride = null,
@@ -108,6 +123,11 @@ ex is InvalidOperationException
}
///
+
+#if NET8_0_OR_GREATER
+ [RequiresUnreferencedCode("Calls JsonSerializer.Serialize")]
+ [RequiresDynamicCode("Calls JsonSerializer.Serialize")]
+#endif
public async Task GetForAppAsync(
string? serviceName,
TInput input,
@@ -143,6 +163,11 @@ ex is InvalidOperationException
}
///
+
+#if NET8_0_OR_GREATER
+ [RequiresUnreferencedCode("Calls JsonSerializer.Serialize")]
+ [RequiresDynamicCode("Calls JsonSerializer.Serialize")]
+#endif
public async Task PostForUserAsync(
string? serviceName,
TInput input,
@@ -177,6 +202,11 @@ ex is InvalidOperationException
}
///
+
+#if NET8_0_OR_GREATER
+ [RequiresUnreferencedCode("Calls JsonSerializer.Serialize")]
+ [RequiresDynamicCode("Calls JsonSerializer.Serialize")]
+#endif
public async Task PostForUserAsync(
string? serviceName,
TInput input,
@@ -213,6 +243,11 @@ ex is InvalidOperationException
}
///
+
+#if NET8_0_OR_GREATER
+ [RequiresUnreferencedCode("Calls JsonSerializer.Serialize")]
+ [RequiresDynamicCode("Calls JsonSerializer.Serialize")]
+#endif
public async Task PostForAppAsync(
string? serviceName,
TInput input,
@@ -246,6 +281,11 @@ ex is InvalidOperationException
}
///
+
+#if NET8_0_OR_GREATER
+ [RequiresUnreferencedCode("Calls JsonSerializer.Serialize")]
+ [RequiresDynamicCode("Calls JsonSerializer.Serialize")]
+#endif
public async Task PostForAppAsync(
string? serviceName,
TInput input,
@@ -281,6 +321,11 @@ ex is InvalidOperationException
}
///
+
+#if NET8_0_OR_GREATER
+ [RequiresUnreferencedCode("Calls JsonSerializer.Serialize")]
+ [RequiresDynamicCode("Calls JsonSerializer.Serialize")]
+#endif
public async Task PutForUserAsync(
string? serviceName,
TInput input,
@@ -315,6 +360,11 @@ ex is InvalidOperationException
}
///
+
+#if NET8_0_OR_GREATER
+ [RequiresUnreferencedCode("Calls JsonSerializer.Serialize")]
+ [RequiresDynamicCode("Calls JsonSerializer.Serialize")]
+#endif
public async Task PutForUserAsync(
string? serviceName,
TInput input,
@@ -351,6 +401,11 @@ ex is InvalidOperationException
}
///
+
+#if NET8_0_OR_GREATER
+ [RequiresUnreferencedCode("Calls JsonSerializer.Serialize")]
+ [RequiresDynamicCode("Calls JsonSerializer.Serialize")]
+#endif
public async Task PutForAppAsync(
string? serviceName,
TInput input,
@@ -384,6 +439,11 @@ ex is InvalidOperationException
}
///
+
+#if NET8_0_OR_GREATER
+ [RequiresUnreferencedCode("Calls JsonSerializer.Serialize")]
+ [RequiresDynamicCode("Calls JsonSerializer.Serialize")]
+#endif
public async Task PutForAppAsync(
string? serviceName,
TInput input,
@@ -421,6 +481,11 @@ ex is InvalidOperationException
#if !NETFRAMEWORK && !NETSTANDARD2_0
///
+
+#if NET8_0_OR_GREATER
+ [RequiresUnreferencedCode("Calls JsonSerializer.Serialize")]
+ [RequiresDynamicCode("Calls JsonSerializer.Serialize")]
+#endif
public async Task PatchForUserAsync(
string? serviceName,
TInput input,
@@ -455,6 +520,11 @@ ex is InvalidOperationException
}
///
+
+#if NET8_0_OR_GREATER
+ [RequiresUnreferencedCode("Calls JsonSerializer.Serialize")]
+ [RequiresDynamicCode("Calls JsonSerializer.Serialize")]
+#endif
public async Task PatchForUserAsync(
string? serviceName,
TInput input,
@@ -491,6 +561,11 @@ ex is InvalidOperationException
}
///
+
+#if NET8_0_OR_GREATER
+ [RequiresUnreferencedCode("Calls JsonSerializer.Serialize")]
+ [RequiresDynamicCode("Calls JsonSerializer.Serialize")]
+#endif
public async Task PatchForAppAsync(
string? serviceName,
TInput input,
@@ -524,6 +599,11 @@ ex is InvalidOperationException
}
///
+
+#if NET8_0_OR_GREATER
+ [RequiresUnreferencedCode("Calls JsonSerializer.Serialize")]
+ [RequiresDynamicCode("Calls JsonSerializer.Serialize")]
+#endif
public async Task PatchForAppAsync(
string? serviceName,
TInput input,
@@ -561,6 +641,11 @@ ex is InvalidOperationException
#endif // !NETFRAMEWORK && !NETSTANDARD2_0
///
+
+#if NET8_0_OR_GREATER
+ [RequiresUnreferencedCode("Calls JsonSerializer.Serialize")]
+ [RequiresDynamicCode("Calls JsonSerializer.Serialize")]
+#endif
public async Task DeleteForUserAsync(
string? serviceName,
TInput input,
@@ -595,6 +680,11 @@ ex is InvalidOperationException
}
///
+
+#if NET8_0_OR_GREATER
+ [RequiresUnreferencedCode("Calls JsonSerializer.Serialize")]
+ [RequiresDynamicCode("Calls JsonSerializer.Serialize")]
+#endif
public async Task DeleteForUserAsync(
string? serviceName,
TInput input,
@@ -631,6 +721,11 @@ ex is InvalidOperationException
}
///
+
+#if NET8_0_OR_GREATER
+ [RequiresUnreferencedCode("Calls JsonSerializer.Serialize")]
+ [RequiresDynamicCode("Calls JsonSerializer.Serialize")]
+#endif
public async Task DeleteForAppAsync(
string? serviceName,
TInput input,
@@ -664,6 +759,11 @@ ex is InvalidOperationException
}
///
+
+#if NET8_0_OR_GREATER
+ [RequiresUnreferencedCode("Calls JsonSerializer.Serialize")]
+ [RequiresDynamicCode("Calls JsonSerializer.Serialize")]
+#endif
public async Task DeleteForAppAsync(
string? serviceName,
TInput input,
@@ -701,6 +801,7 @@ ex is InvalidOperationException
#if NET8_0_OR_GREATER
///
+
public async Task GetForUserAsync(
string? serviceName,
JsonTypeInfo outputJsonTypeInfo,
@@ -729,6 +830,7 @@ ex is InvalidOperationException
}
///
+
public async Task GetForUserAsync(
string? serviceName,
TInput input,
@@ -767,6 +869,7 @@ ex is InvalidOperationException
}
///
+
public async Task GetForAppAsync(
string? serviceName,
JsonTypeInfo outputJsonTypeInfo,
@@ -794,6 +897,7 @@ ex is InvalidOperationException
}
///
+
public async Task GetForAppAsync(
string? serviceName,
TInput input,
@@ -831,6 +935,7 @@ ex is InvalidOperationException
}
///
+
public async Task PostForUserAsync(
string? serviceName,
TInput input,
@@ -866,6 +971,7 @@ ex is InvalidOperationException
}
///
+
public async Task PostForUserAsync(
string? serviceName,
TInput input,
@@ -904,6 +1010,7 @@ ex is InvalidOperationException
}
///
+
public async Task PostForAppAsync(
string? serviceName,
TInput input,
@@ -938,6 +1045,7 @@ ex is InvalidOperationException
}
///
+
public async Task PostForAppAsync(
string? serviceName,
TInput input,
@@ -975,6 +1083,7 @@ ex is InvalidOperationException
}
///
+
public async Task PutForUserAsync(
string? serviceName,
TInput input,
@@ -1010,6 +1119,7 @@ ex is InvalidOperationException
}
///
+
public async Task PutForUserAsync(
string? serviceName,
TInput input,
@@ -1048,6 +1158,7 @@ ex is InvalidOperationException
}
///
+
public async Task PutForAppAsync(
string? serviceName,
TInput input,
@@ -1082,6 +1193,7 @@ ex is InvalidOperationException
}
///
+
public async Task PutForAppAsync(
string? serviceName,
TInput input,
@@ -1119,6 +1231,7 @@ ex is InvalidOperationException
}
///
+
public async Task PatchForUserAsync(
string? serviceName,
TInput input,
@@ -1154,6 +1267,7 @@ ex is InvalidOperationException
}
///
+
public async Task PatchForUserAsync(
string? serviceName,
TInput input,
@@ -1192,6 +1306,7 @@ ex is InvalidOperationException
}
///
+
public async Task PatchForAppAsync(
string? serviceName,
TInput input,
@@ -1226,6 +1341,7 @@ ex is InvalidOperationException
}
///
+
public async Task PatchForAppAsync(
string? serviceName,
TInput input,
@@ -1263,6 +1379,7 @@ ex is InvalidOperationException
}
///
+
public async Task DeleteForUserAsync(
string? serviceName,
TInput input,
@@ -1298,6 +1415,7 @@ ex is InvalidOperationException
}
///
+
public async Task DeleteForUserAsync(
string? serviceName,
TInput input,
@@ -1336,6 +1454,7 @@ ex is InvalidOperationException
}
///
+
public async Task DeleteForAppAsync(
string? serviceName,
TInput input,
@@ -1370,6 +1489,7 @@ ex is InvalidOperationException
}
///
+
public async Task DeleteForAppAsync(
string? serviceName,
TInput input,
diff --git a/src/Microsoft.Identity.Web.DownstreamApi/DownstreamApi.HttpMethods.tt b/src/Microsoft.Identity.Web.DownstreamApi/DownstreamApi.HttpMethods.tt
index c57431c99..44ff93ac3 100644
--- a/src/Microsoft.Identity.Web.DownstreamApi/DownstreamApi.HttpMethods.tt
+++ b/src/Microsoft.Identity.Web.DownstreamApi/DownstreamApi.HttpMethods.tt
@@ -70,15 +70,22 @@ namespace Microsoft.Identity.Web
firstMethod = false;
#>
///
+
+<# if ((hasInput || hasOutput) && (framework != "net8" && framework != "net9")){ #>
+#if NET8_0_OR_GREATER
+ [RequiresUnreferencedCode("Calls JsonSerializer.Serialize")]
+ [RequiresDynamicCode("Calls JsonSerializer.Serialize")]
+#endif
+<# } #>
public async <#= returnType #> <#= httpMethod #>For<#= token #>Async<#= template #>(
string? serviceName,
<# if (hasInput){ #>
TInput input,
<# } #>
-<# if (hasInput && framework == "net8"){ #>
+<# if (hasInput && (framework == "net8" || framework == "net9")){ #>
JsonTypeInfo inputJsonTypeInfo,
<# } #>
-<# if (hasOutput && framework == "net8"){ #>
+<# if (hasOutput && (framework == "net8" || framework == "net9")){ #>
JsonTypeInfo outputJsonTypeInfo,
<# } #>
Action? downstreamApiOptionsOverride = null,
diff --git a/src/Microsoft.Identity.Web.DownstreamApi/DownstreamApi.cs b/src/Microsoft.Identity.Web.DownstreamApi/DownstreamApi.cs
index eb498f990..a1bd51c4e 100644
--- a/src/Microsoft.Identity.Web.DownstreamApi/DownstreamApi.cs
+++ b/src/Microsoft.Identity.Web.DownstreamApi/DownstreamApi.cs
@@ -1,177 +1,194 @@
-// Copyright (c) Microsoft Corporation. All rights reserved.
-// Licensed under the MIT License.
-
-using System;
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+using System;
using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Net.Http;
-using System.Runtime.CompilerServices;
-using System.Security.Claims;
-using System.Text;
-using System.Text.Json;
-using System.Text.Json.Serialization.Metadata;
-using System.Threading;
-using System.Threading.Tasks;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
-using Microsoft.Identity.Abstractions;
-using Microsoft.Identity.Client;
-
-namespace Microsoft.Identity.Web
-{
- ///
- internal partial class DownstreamApi : IDownstreamApi
- {
- private readonly IAuthorizationHeaderProvider _authorizationHeaderProvider;
- private readonly IHttpClientFactory _httpClientFactory;
- private readonly IOptionsMonitor _namedDownstreamApiOptions;
- private const string Authorization = "Authorization";
- protected readonly ILogger _logger;
- private const string AuthSchemeDstsSamlBearer = "http://schemas.microsoft.com/dsts/saml2-bearer";
-
- ///
- /// Constructor.
- ///
- /// Authorization header provider.
- /// Named options provider.
- /// HTTP client factory.
- /// Logger.
- public DownstreamApi(
- IAuthorizationHeaderProvider authorizationHeaderProvider,
- IOptionsMonitor namedDownstreamApiOptions,
- IHttpClientFactory httpClientFactory,
- ILogger logger)
- {
- _authorizationHeaderProvider = authorizationHeaderProvider;
- _namedDownstreamApiOptions = namedDownstreamApiOptions;
- _httpClientFactory = httpClientFactory;
- _logger = logger;
- }
-
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Task CallApiAsync(
- string? serviceName,
- Action? downstreamApiOptionsOverride = null,
- ClaimsPrincipal? user = null,
- HttpContent? content = null,
- CancellationToken cancellationToken = default)
- {
- DownstreamApiOptions effectiveOptions = MergeOptions(serviceName, downstreamApiOptionsOverride);
- return CallApiInternalAsync(serviceName, effectiveOptions, effectiveOptions.RequestAppToken, content,
- user, cancellationToken);
- }
-
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Task CallApiAsync(
- DownstreamApiOptions downstreamApiOptions,
- ClaimsPrincipal? user = null,
- HttpContent? content = null,
- CancellationToken cancellationToken = default)
- {
- return CallApiInternalAsync(null, downstreamApiOptions, downstreamApiOptions.RequestAppToken, content,
- user, cancellationToken);
- }
-
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Task CallApiForUserAsync(
- string? serviceName,
- Action? downstreamApiOptionsOverride = null,
- ClaimsPrincipal? user = null,
- HttpContent? content = null,
- CancellationToken cancellationToken = default)
- {
- DownstreamApiOptions effectiveOptions = MergeOptions(serviceName, downstreamApiOptionsOverride);
- return CallApiInternalAsync(serviceName, effectiveOptions, false, content, user, cancellationToken);
- }
-
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Task CallApiForAppAsync(
- string? serviceName,
- Action? downstreamApiOptionsOverride = null,
- HttpContent? content = null,
- CancellationToken cancellationToken = default)
- {
- DownstreamApiOptions effectiveOptions = MergeOptions(serviceName, downstreamApiOptionsOverride);
- return CallApiInternalAsync(serviceName, effectiveOptions, true, content, null, cancellationToken);
- }
-
- ///
- public async Task CallApiForUserAsync(
- string? serviceName,
- TInput input,
- Action? downstreamApiOptionsOverride = null,
- ClaimsPrincipal? user = null,
- CancellationToken cancellationToken = default) where TOutput : class
- {
- DownstreamApiOptions effectiveOptions = MergeOptions(serviceName, downstreamApiOptionsOverride);
- HttpContent? effectiveInput = SerializeInput(input, effectiveOptions);
-
- HttpResponseMessage response = await CallApiInternalAsync(serviceName, effectiveOptions, false,
- effectiveInput, user, cancellationToken).ConfigureAwait(false);
-
- // Only dispose the HttpContent if was created here, not provided by the caller.
- if (input is not HttpContent)
- {
- effectiveInput?.Dispose();
- }
-
- return await DeserializeOutputAsync(response, effectiveOptions).ConfigureAwait(false);
- }
-
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public async Task CallApiForAppAsync(
- string? serviceName,
- TInput input,
- Action? downstreamApiOptionsOverride = null,
- CancellationToken cancellationToken = default) where TOutput : class
- {
- DownstreamApiOptions effectiveOptions = MergeOptions(serviceName, downstreamApiOptionsOverride);
- HttpContent? effectiveInput = SerializeInput(input, effectiveOptions);
- HttpResponseMessage response = await CallApiInternalAsync(serviceName, effectiveOptions, true,
- effectiveInput, null, cancellationToken).ConfigureAwait(false);
-
- // Only dispose the HttpContent if was created here, not provided by the caller.
- if (input is not HttpContent)
- {
- effectiveInput?.Dispose();
- }
-
- return await DeserializeOutputAsync(response, effectiveOptions).ConfigureAwait(false);
- }
-
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public async Task CallApiForAppAsync(string serviceName,
- Action? downstreamApiOptionsOverride = null,
- CancellationToken cancellationToken = default) where TOutput : class
- {
- DownstreamApiOptions effectiveOptions = MergeOptions(serviceName, downstreamApiOptionsOverride);
- HttpResponseMessage response = await CallApiInternalAsync(serviceName, effectiveOptions, true,
- null, null, cancellationToken).ConfigureAwait(false);
-
- return await DeserializeOutputAsync(response, effectiveOptions).ConfigureAwait(false);
- }
-
- ///
- public async Task CallApiForUserAsync(
- string? serviceName,
- Action? downstreamApiOptionsOverride = null,
- ClaimsPrincipal? user = null,
- CancellationToken cancellationToken = default) where TOutput : class
- {
- DownstreamApiOptions effectiveOptions = MergeOptions(serviceName, downstreamApiOptionsOverride);
- HttpResponseMessage response = await CallApiInternalAsync(serviceName, effectiveOptions, false,
- null, user, cancellationToken).ConfigureAwait(false);
- return await DeserializeOutputAsync(response, effectiveOptions).ConfigureAwait(false);
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Linq;
+using System.Net.Http;
+using System.Runtime.CompilerServices;
+using System.Security.Claims;
+using System.Text;
+using System.Text.Json;
+using System.Text.Json.Serialization.Metadata;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using Microsoft.Identity.Abstractions;
+using Microsoft.Identity.Client;
+
+namespace Microsoft.Identity.Web
+{
+ ///
+ internal partial class DownstreamApi : IDownstreamApi
+ {
+ private readonly IAuthorizationHeaderProvider _authorizationHeaderProvider;
+ private readonly IHttpClientFactory _httpClientFactory;
+ private readonly IOptionsMonitor _namedDownstreamApiOptions;
+ private const string Authorization = "Authorization";
+ protected readonly ILogger _logger;
+ private const string AuthSchemeDstsSamlBearer = "http://schemas.microsoft.com/dsts/saml2-bearer";
+
+ ///
+ /// Constructor.
+ ///
+ /// Authorization header provider.
+ /// Named options provider.
+ /// HTTP client factory.
+ /// Logger.
+ public DownstreamApi(
+ IAuthorizationHeaderProvider authorizationHeaderProvider,
+ IOptionsMonitor namedDownstreamApiOptions,
+ IHttpClientFactory httpClientFactory,
+ ILogger logger)
+ {
+ _authorizationHeaderProvider = authorizationHeaderProvider;
+ _namedDownstreamApiOptions = namedDownstreamApiOptions;
+ _httpClientFactory = httpClientFactory;
+ _logger = logger;
+ }
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Task CallApiAsync(
+ string? serviceName,
+ Action? downstreamApiOptionsOverride = null,
+ ClaimsPrincipal? user = null,
+ HttpContent? content = null,
+ CancellationToken cancellationToken = default)
+ {
+ DownstreamApiOptions effectiveOptions = MergeOptions(serviceName, downstreamApiOptionsOverride);
+ return CallApiInternalAsync(serviceName, effectiveOptions, effectiveOptions.RequestAppToken, content,
+ user, cancellationToken);
+ }
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Task CallApiAsync(
+ DownstreamApiOptions downstreamApiOptions,
+ ClaimsPrincipal? user = null,
+ HttpContent? content = null,
+ CancellationToken cancellationToken = default)
+ {
+ return CallApiInternalAsync(null, downstreamApiOptions, downstreamApiOptions.RequestAppToken, content,
+ user, cancellationToken);
+ }
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Task CallApiForUserAsync(
+ string? serviceName,
+ Action? downstreamApiOptionsOverride = null,
+ ClaimsPrincipal? user = null,
+ HttpContent? content = null,
+ CancellationToken cancellationToken = default)
+ {
+ DownstreamApiOptions effectiveOptions = MergeOptions(serviceName, downstreamApiOptionsOverride);
+ return CallApiInternalAsync(serviceName, effectiveOptions, false, content, user, cancellationToken);
+ }
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Task CallApiForAppAsync(
+ string? serviceName,
+ Action? downstreamApiOptionsOverride = null,
+ HttpContent? content = null,
+ CancellationToken cancellationToken = default)
+ {
+ DownstreamApiOptions effectiveOptions = MergeOptions(serviceName, downstreamApiOptionsOverride);
+ return CallApiInternalAsync(serviceName, effectiveOptions, true, content, null, cancellationToken);
}
-#if NET8_0_OR_GREATER
+ ///
+#if NET8_0_OR_GREATER
+ [RequiresUnreferencedCode("Calls JsonSerializer.Serialize")]
+ [RequiresDynamicCode("Calls JsonSerializer.Serialize")]
+#endif
+ public async Task CallApiForUserAsync(
+ string? serviceName,
+ TInput input,
+ Action? downstreamApiOptionsOverride = null,
+ ClaimsPrincipal? user = null,
+ CancellationToken cancellationToken = default) where TOutput : class
+ {
+ DownstreamApiOptions effectiveOptions = MergeOptions(serviceName, downstreamApiOptionsOverride);
+ HttpContent? effectiveInput = SerializeInput(input, effectiveOptions);
+
+ HttpResponseMessage response = await CallApiInternalAsync(serviceName, effectiveOptions, false,
+ effectiveInput, user, cancellationToken).ConfigureAwait(false);
+
+ // Only dispose the HttpContent if was created here, not provided by the caller.
+ if (input is not HttpContent)
+ {
+ effectiveInput?.Dispose();
+ }
+
+ return await DeserializeOutputAsync(response, effectiveOptions).ConfigureAwait(false);
+ }
+
+ ///
+#if NET8_0_OR_GREATER
+ [RequiresUnreferencedCode("Calls JsonSerializer.Serialize")]
+ [RequiresDynamicCode("Calls JsonSerializer.Serialize")]
+#endif
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public async Task CallApiForAppAsync(
+ string? serviceName,
+ TInput input,
+ Action? downstreamApiOptionsOverride = null,
+ CancellationToken cancellationToken = default) where TOutput : class
+ {
+ DownstreamApiOptions effectiveOptions = MergeOptions(serviceName, downstreamApiOptionsOverride);
+ HttpContent? effectiveInput = SerializeInput(input, effectiveOptions);
+ HttpResponseMessage response = await CallApiInternalAsync(serviceName, effectiveOptions, true,
+ effectiveInput, null, cancellationToken).ConfigureAwait(false);
+
+ // Only dispose the HttpContent if was created here, not provided by the caller.
+ if (input is not HttpContent)
+ {
+ effectiveInput?.Dispose();
+ }
+
+ return await DeserializeOutputAsync(response, effectiveOptions).ConfigureAwait(false);
+ }
+
+ ///
+#if NET8_0_OR_GREATER
+ [RequiresUnreferencedCode("Calls JsonSerializer.Serialize")]
+ [RequiresDynamicCode("Calls JsonSerializer.Serialize")]
+#endif
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public async Task CallApiForAppAsync(string serviceName,
+ Action? downstreamApiOptionsOverride = null,
+ CancellationToken cancellationToken = default) where TOutput : class
+ {
+ DownstreamApiOptions effectiveOptions = MergeOptions(serviceName, downstreamApiOptionsOverride);
+ HttpResponseMessage response = await CallApiInternalAsync(serviceName, effectiveOptions, true,
+ null, null, cancellationToken).ConfigureAwait(false);
+
+ return await DeserializeOutputAsync(response, effectiveOptions).ConfigureAwait(false);
+ }
+
+ ///
+#if NET8_0_OR_GREATER
+ [RequiresUnreferencedCode("Calls JsonSerializer.Serialize")]
+ [RequiresDynamicCode("Calls JsonSerializer.Serialize")]
+#endif
+ public async Task CallApiForUserAsync(
+ string? serviceName,
+ Action? downstreamApiOptionsOverride = null,
+ ClaimsPrincipal? user = null,
+ CancellationToken cancellationToken = default) where TOutput : class
+ {
+ DownstreamApiOptions effectiveOptions = MergeOptions(serviceName, downstreamApiOptionsOverride);
+ HttpResponseMessage response = await CallApiInternalAsync(serviceName, effectiveOptions, false,
+ null, user, cancellationToken).ConfigureAwait(false);
+ return await DeserializeOutputAsync(response, effectiveOptions).ConfigureAwait(false);
+ }
+
+#if NET8_0_OR_GREATER
///
public async Task CallApiForUserAsync(
string? serviceName,
@@ -183,19 +200,19 @@ public Task CallApiForAppAsync(
CancellationToken cancellationToken = default)
where TOutput : class
{
- DownstreamApiOptions effectiveOptions = MergeOptions(serviceName, downstreamApiOptionsOverride);
- HttpContent? effectiveInput = SerializeInput(input, effectiveOptions, inputJsonTypeInfo);
-
- HttpResponseMessage response = await CallApiInternalAsync(serviceName, effectiveOptions, false,
- effectiveInput, user, cancellationToken).ConfigureAwait(false);
-
- // Only dispose the HttpContent if was created here, not provided by the caller.
- if (input is not HttpContent)
- {
- effectiveInput?.Dispose();
- }
-
- return await DeserializeOutputAsync(response, effectiveOptions, outputJsonTypeInfo).ConfigureAwait(false);
+ DownstreamApiOptions effectiveOptions = MergeOptions(serviceName, downstreamApiOptionsOverride);
+ HttpContent? effectiveInput = SerializeInput(input, effectiveOptions, inputJsonTypeInfo);
+
+ HttpResponseMessage response = await CallApiInternalAsync(serviceName, effectiveOptions, false,
+ effectiveInput, user, cancellationToken).ConfigureAwait(false);
+
+ // Only dispose the HttpContent if was created here, not provided by the caller.
+ if (input is not HttpContent)
+ {
+ effectiveInput?.Dispose();
+ }
+
+ return await DeserializeOutputAsync(response, effectiveOptions, outputJsonTypeInfo).ConfigureAwait(false);
}
///
@@ -207,9 +224,9 @@ public Task CallApiForAppAsync(
CancellationToken cancellationToken = default)
where TOutput : class
{
- DownstreamApiOptions effectiveOptions = MergeOptions(serviceName, downstreamApiOptionsOverride);
- HttpResponseMessage response = await CallApiInternalAsync(serviceName, effectiveOptions, false,
- null, user, cancellationToken).ConfigureAwait(false);
+ DownstreamApiOptions effectiveOptions = MergeOptions(serviceName, downstreamApiOptionsOverride);
+ HttpResponseMessage response = await CallApiInternalAsync(serviceName, effectiveOptions, false,
+ null, user, cancellationToken).ConfigureAwait(false);
return await DeserializeOutputAsync(response, effectiveOptions, outputJsonTypeInfo).ConfigureAwait(false);
}
@@ -223,17 +240,17 @@ public Task CallApiForAppAsync(
CancellationToken cancellationToken = default)
where TOutput : class
{
- DownstreamApiOptions effectiveOptions = MergeOptions(serviceName, downstreamApiOptionsOverride);
- HttpContent? effectiveInput = SerializeInput(input, effectiveOptions, inputJsonTypeInfo);
- HttpResponseMessage response = await CallApiInternalAsync(serviceName, effectiveOptions, true,
- effectiveInput, null, cancellationToken).ConfigureAwait(false);
-
- // Only dispose the HttpContent if was created here, not provided by the caller.
- if (input is not HttpContent)
- {
- effectiveInput?.Dispose();
- }
-
+ DownstreamApiOptions effectiveOptions = MergeOptions(serviceName, downstreamApiOptionsOverride);
+ HttpContent? effectiveInput = SerializeInput(input, effectiveOptions, inputJsonTypeInfo);
+ HttpResponseMessage response = await CallApiInternalAsync(serviceName, effectiveOptions, true,
+ effectiveInput, null, cancellationToken).ConfigureAwait(false);
+
+ // Only dispose the HttpContent if was created here, not provided by the caller.
+ if (input is not HttpContent)
+ {
+ effectiveInput?.Dispose();
+ }
+
return await DeserializeOutputAsync(response, effectiveOptions, outputJsonTypeInfo).ConfigureAwait(false);
}
@@ -245,235 +262,263 @@ public Task CallApiForAppAsync(
CancellationToken cancellationToken = default)
where TOutput : class
{
- DownstreamApiOptions effectiveOptions = MergeOptions(serviceName, downstreamApiOptionsOverride);
- HttpResponseMessage response = await CallApiInternalAsync(serviceName, effectiveOptions, true,
- null, null, cancellationToken).ConfigureAwait(false);
-
+ DownstreamApiOptions effectiveOptions = MergeOptions(serviceName, downstreamApiOptionsOverride);
+ HttpResponseMessage response = await CallApiInternalAsync(serviceName, effectiveOptions, true,
+ null, null, cancellationToken).ConfigureAwait(false);
+
return await DeserializeOutputAsync(response, effectiveOptions, outputJsonTypeInfo).ConfigureAwait(false);
}
- internal static HttpContent? SerializeInput(TInput input, DownstreamApiOptions effectiveOptions, JsonTypeInfo inputJsonTypeInfo)
- {
- return SerializeInputImpl(input, effectiveOptions, inputJsonTypeInfo);
+ internal static HttpContent? SerializeInput(TInput input, DownstreamApiOptions effectiveOptions, JsonTypeInfo inputJsonTypeInfo)
+ {
+ HttpContent? httpContent;
+
+ if (effectiveOptions.Serializer != null)
+ {
+ httpContent = effectiveOptions.Serializer(input);
+ }
+ else
+ {
+ // if the input is already an HttpContent, it's used as is, and should already contain a ContentType.
+ httpContent = input switch
+ {
+ HttpContent content => content,
+ string str when !string.IsNullOrEmpty(effectiveOptions.ContentType) && effectiveOptions.ContentType.StartsWith("text", StringComparison.OrdinalIgnoreCase) => new StringContent(str),
+ string str => new StringContent(JsonSerializer.Serialize(str, inputJsonTypeInfo),
+ Encoding.UTF8,
+ "application/json"),
+ byte[] bytes => new ByteArrayContent(bytes),
+ Stream stream => new StreamContent(stream),
+ null => null,
+ _ => new StringContent(
+ JsonSerializer.Serialize(input, inputJsonTypeInfo),
+ Encoding.UTF8,
+ "application/json"),
+ };
+ }
+ return httpContent;
}
- internal static async Task DeserializeOutputAsync(HttpResponseMessage response, DownstreamApiOptions effectiveOptions, JsonTypeInfo outputJsonTypeInfo)
- where TOutput : class
+ internal static async Task DeserializeOutputAsync(HttpResponseMessage response, DownstreamApiOptions effectiveOptions, JsonTypeInfo outputJsonTypeInfo)
+ where TOutput : class
{
return await DeserializeOutputImplAsync(response, effectiveOptions, outputJsonTypeInfo);
}
-#endif
+#endif
- ///
- /// Merge the options from configuration and override from caller.
- ///
- /// Named configuration.
- /// Delegate to override the configuration.
+ ///
+ /// Merge the options from configuration and override from caller.
+ ///
+ /// Named configuration.
+ /// Delegate to override the configuration.
internal /* for tests */ DownstreamApiOptions MergeOptions(
- string? optionsInstanceName,
- Action? calledApiOptionsOverride)
- {
- // Gets the options from configuration (or default value)
- DownstreamApiOptions options;
- if (optionsInstanceName != null)
- {
- options = _namedDownstreamApiOptions.Get(optionsInstanceName);
- }
- else
- {
- options = _namedDownstreamApiOptions.CurrentValue;
- }
-
- DownstreamApiOptions clonedOptions = new DownstreamApiOptions(options);
- calledApiOptionsOverride?.Invoke(clonedOptions);
- return clonedOptions;
- }
-
- ///
- /// Merge the options from configuration and override from caller.
- ///
- /// Named configuration.
- /// Delegate to override the configuration.
- /// Http method overriding the configuration options.
+ string? optionsInstanceName,
+ Action? calledApiOptionsOverride)
+ {
+ // Gets the options from configuration (or default value)
+ DownstreamApiOptions options;
+ if (optionsInstanceName != null)
+ {
+ options = _namedDownstreamApiOptions.Get(optionsInstanceName);
+ }
+ else
+ {
+ options = _namedDownstreamApiOptions.CurrentValue;
+ }
+
+ DownstreamApiOptions clonedOptions = new DownstreamApiOptions(options);
+ calledApiOptionsOverride?.Invoke(clonedOptions);
+ return clonedOptions;
+ }
+
+ ///
+ /// Merge the options from configuration and override from caller.
+ ///
+ /// Named configuration.
+ /// Delegate to override the configuration.
+ /// Http method overriding the configuration options.
internal /* for tests */ DownstreamApiOptions MergeOptions(
- string? optionsInstanceName,
- Action? calledApiOptionsOverride, HttpMethod httpMethod)
- {
- // Gets the options from configuration (or default value)
- DownstreamApiOptions options;
- if (optionsInstanceName != null)
- {
- options = _namedDownstreamApiOptions.Get(optionsInstanceName);
- }
- else
- {
- options = _namedDownstreamApiOptions.CurrentValue;
- }
-
- DownstreamApiOptionsReadOnlyHttpMethod clonedOptions = new DownstreamApiOptionsReadOnlyHttpMethod(options, httpMethod.ToString());
- calledApiOptionsOverride?.Invoke(clonedOptions);
- return clonedOptions;
+ string? optionsInstanceName,
+ Action? calledApiOptionsOverride, HttpMethod httpMethod)
+ {
+ // Gets the options from configuration (or default value)
+ DownstreamApiOptions options;
+ if (optionsInstanceName != null)
+ {
+ options = _namedDownstreamApiOptions.Get(optionsInstanceName);
+ }
+ else
+ {
+ options = _namedDownstreamApiOptions.CurrentValue;
+ }
+
+ DownstreamApiOptionsReadOnlyHttpMethod clonedOptions = new DownstreamApiOptionsReadOnlyHttpMethod(options, httpMethod.ToString());
+ calledApiOptionsOverride?.Invoke(clonedOptions);
+ return clonedOptions;
}
- internal static HttpContent? SerializeInput(TInput input, DownstreamApiOptions effectiveOptions)
- {
- return SerializeInputImpl(input, effectiveOptions, null);
- }
-
- private static HttpContent? SerializeInputImpl(TInput input, DownstreamApiOptions effectiveOptions, JsonTypeInfo? inputJsonTypeInfo = null)
- {
+#if NET7_0_OR_GREATER
+ [RequiresUnreferencedCode("Calls JsonSerializer.Serialize")]
+ [RequiresDynamicCode("Calls JsonSerializer.Serialize")]
+#endif
+ internal static HttpContent? SerializeInput(TInput input, DownstreamApiOptions effectiveOptions)
+ {
HttpContent? httpContent;
if (effectiveOptions.Serializer != null)
- {
+ {
httpContent = effectiveOptions.Serializer(input);
- }
- else
- {
+ }
+ else
+ {
// if the input is already an HttpContent, it's used as is, and should already contain a ContentType.
httpContent = input switch
{
HttpContent content => content,
string str when !string.IsNullOrEmpty(effectiveOptions.ContentType) && effectiveOptions.ContentType.StartsWith("text", StringComparison.OrdinalIgnoreCase) => new StringContent(str),
string str => new StringContent(
- inputJsonTypeInfo == null ? JsonSerializer.Serialize(str) : JsonSerializer.Serialize(str, inputJsonTypeInfo),
+ JsonSerializer.Serialize(str),
Encoding.UTF8,
"application/json"),
byte[] bytes => new ByteArrayContent(bytes),
Stream stream => new StreamContent(stream),
null => null,
_ => new StringContent(
- inputJsonTypeInfo == null ? JsonSerializer.Serialize(input) : JsonSerializer.Serialize(input, inputJsonTypeInfo),
+ JsonSerializer.Serialize(input),
Encoding.UTF8,
"application/json"),
};
- }
- return httpContent;
- }
-
- internal static async Task DeserializeOutputAsync(HttpResponseMessage response, DownstreamApiOptions effectiveOptions)
+ }
+ return httpContent;
+ }
+
+#if NET7_0_OR_GREATER
+ [RequiresUnreferencedCode("Calls JsonSerializer.Serialize")]
+ [RequiresDynamicCode("Calls JsonSerializer.Serialize")]
+#endif
+ internal static async Task DeserializeOutputAsync(HttpResponseMessage response, DownstreamApiOptions effectiveOptions)
where TOutput : class
{
- try
- {
- response.EnsureSuccessStatusCode();
- }
- catch
- {
- string error = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
-
-#if NET5_0_OR_GREATER
- throw new HttpRequestException($"{(int)response.StatusCode} {response.StatusCode} {error}", null, response.StatusCode);
-#else
- throw new HttpRequestException($"{(int)response.StatusCode} {response.StatusCode} {error}");
-#endif
- }
-
- HttpContent content = response.Content;
-
- if (content == null)
- {
- return default;
- }
-
- string? mediaType = content.Headers.ContentType?.MediaType;
-
- if (effectiveOptions.Deserializer != null)
- {
- return effectiveOptions.Deserializer(content) as TOutput;
- }
+ try
+ {
+ response.EnsureSuccessStatusCode();
+ }
+ catch
+ {
+ string error = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
+
+#if NET5_0_OR_GREATER
+ throw new HttpRequestException($"{(int)response.StatusCode} {response.StatusCode} {error}", null, response.StatusCode);
+#else
+ throw new HttpRequestException($"{(int)response.StatusCode} {response.StatusCode} {error}");
+#endif
+ }
+
+ HttpContent content = response.Content;
+
+ if (content == null)
+ {
+ return default;
+ }
+
+ string? mediaType = content.Headers.ContentType?.MediaType;
+
+ if (effectiveOptions.Deserializer != null)
+ {
+ return effectiveOptions.Deserializer(content) as TOutput;
+ }
else if (typeof(TOutput).IsAssignableFrom(typeof(HttpContent)))
{
return content as TOutput;
- }
- else
- {
- string stringContent = await content.ReadAsStringAsync();
- if (mediaType == "application/json")
- {
- return JsonSerializer.Deserialize(stringContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
- }
- if (mediaType != null && !mediaType.StartsWith("text/", StringComparison.OrdinalIgnoreCase))
- {
- // Handle other content types here
- throw new NotSupportedException("Content type not supported. Provide your own deserializer. ");
- }
- return stringContent as TOutput;
}
- }
-
- private static async Task DeserializeOutputImplAsync(HttpResponseMessage response, DownstreamApiOptions effectiveOptions, JsonTypeInfo outputJsonTypeInfo)
- where TOutput : class
- {
- try
- {
- response.EnsureSuccessStatusCode();
- }
- catch
- {
- string error = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
-
-#if NET5_0_OR_GREATER
- throw new HttpRequestException($"{(int)response.StatusCode} {response.StatusCode} {error}", null, response.StatusCode);
-#else
- throw new HttpRequestException($"{(int)response.StatusCode} {response.StatusCode} {error}");
-#endif
- }
-
- HttpContent content = response.Content;
-
- if (content == null)
- {
- return default;
- }
-
- string? mediaType = content.Headers.ContentType?.MediaType;
-
- if (effectiveOptions.Deserializer != null)
- {
- return effectiveOptions.Deserializer(content) as TOutput;
- }
+ else
+ {
+ string stringContent = await content.ReadAsStringAsync();
+ if (mediaType == "application/json")
+ {
+ return JsonSerializer.Deserialize(stringContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
+ }
+ if (mediaType != null && !mediaType.StartsWith("text/", StringComparison.OrdinalIgnoreCase))
+ {
+ // Handle other content types here
+ throw new NotSupportedException("Content type not supported. Provide your own deserializer. ");
+ }
+ return stringContent as TOutput;
+ }
+ }
+
+ private static async Task DeserializeOutputImplAsync(HttpResponseMessage response, DownstreamApiOptions effectiveOptions, JsonTypeInfo outputJsonTypeInfo)
+ where TOutput : class
+ {
+ try
+ {
+ response.EnsureSuccessStatusCode();
+ }
+ catch
+ {
+ string error = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
+
+#if NET5_0_OR_GREATER
+ throw new HttpRequestException($"{(int)response.StatusCode} {response.StatusCode} {error}", null, response.StatusCode);
+#else
+ throw new HttpRequestException($"{(int)response.StatusCode} {response.StatusCode} {error}");
+#endif
+ }
+
+ HttpContent content = response.Content;
+
+ if (content == null)
+ {
+ return default;
+ }
+
+ string? mediaType = content.Headers.ContentType?.MediaType;
+
+ if (effectiveOptions.Deserializer != null)
+ {
+ return effectiveOptions.Deserializer(content) as TOutput;
+ }
else if (typeof(TOutput).IsAssignableFrom(typeof(HttpContent)))
{
return content as TOutput;
- }
- else
- {
- string stringContent = await content.ReadAsStringAsync();
- if (mediaType == "application/json")
- {
- return JsonSerializer.Deserialize(stringContent, outputJsonTypeInfo);
- }
- if (mediaType != null && !mediaType.StartsWith("text/", StringComparison.OrdinalIgnoreCase))
- {
- // Handle other content types here
- throw new NotSupportedException("Content type not supported. Provide your own deserializer. ");
- }
- return stringContent as TOutput;
- }
- }
-
+ }
+ else
+ {
+ string stringContent = await content.ReadAsStringAsync();
+ if (mediaType == "application/json")
+ {
+ return JsonSerializer.Deserialize(stringContent, outputJsonTypeInfo);
+ }
+ if (mediaType != null && !mediaType.StartsWith("text/", StringComparison.OrdinalIgnoreCase))
+ {
+ // Handle other content types here
+ throw new NotSupportedException("Content type not supported. Provide your own deserializer. ");
+ }
+ return stringContent as TOutput;
+ }
+ }
+
internal /* for tests */ async Task CallApiInternalAsync(
- string? serviceName,
- DownstreamApiOptions effectiveOptions,
- bool appToken,
- HttpContent? content = null,
- ClaimsPrincipal? user = null,
- CancellationToken cancellationToken = default)
- {
- // Downstream API URI
- string apiUrl = effectiveOptions.GetApiUrl();
-
+ string? serviceName,
+ DownstreamApiOptions effectiveOptions,
+ bool appToken,
+ HttpContent? content = null,
+ ClaimsPrincipal? user = null,
+ CancellationToken cancellationToken = default)
+ {
+ // Downstream API URI
+ string apiUrl = effectiveOptions.GetApiUrl();
+
// Create an HTTP request message
using HttpRequestMessage httpRequestMessage = new(
- new HttpMethod(effectiveOptions.HttpMethod),
- apiUrl);
-
- await UpdateRequestAsync(httpRequestMessage, content, effectiveOptions, appToken, user, cancellationToken);
-
+ new HttpMethod(effectiveOptions.HttpMethod),
+ apiUrl);
+
+ await UpdateRequestAsync(httpRequestMessage, content, effectiveOptions, appToken, user, cancellationToken);
+
using HttpClient client = string.IsNullOrEmpty(serviceName) ? _httpClientFactory.CreateClient() : _httpClientFactory.CreateClient(serviceName);
-
- // Send the HTTP message
+
+ // Send the HTTP message
var downstreamApiResult = await client.SendAsync(httpRequestMessage, cancellationToken).ConfigureAwait(false);
// Retry only if the resource sent 401 Unauthorized with WWW-Authenticate header and claims
@@ -495,7 +540,7 @@ public Task CallApiForAppAsync(
return downstreamApiResult;
}
-
+
internal /* internal for test */ async Task UpdateRequestAsync(
HttpRequestMessage httpRequestMessage,
HttpContent? content,
@@ -513,10 +558,10 @@ public Task CallApiForAppAsync(
effectiveOptions.RequestAppToken = appToken;
- // Obtention of the authorization header (except when calling an anonymous endpoint
- // which is done by not specifying any scopes
- if (effectiveOptions.Scopes != null && effectiveOptions.Scopes.Any())
- {
+ // Obtention of the authorization header (except when calling an anonymous endpoint
+ // which is done by not specifying any scopes
+ if (effectiveOptions.Scopes != null && effectiveOptions.Scopes.Any())
+ {
string authorizationHeader = await _authorizationHeaderProvider.CreateAuthorizationHeaderAsync(
effectiveOptions.Scopes,
effectiveOptions,
@@ -532,18 +577,18 @@ public Task CallApiForAppAsync(
{
httpRequestMessage.Headers.Add(Authorization, authorizationHeader);
}
- }
- else
- {
- Logger.UnauthenticatedApiCall(_logger, null);
- }
- if (!string.IsNullOrEmpty(effectiveOptions.AcceptHeader))
+ }
+ else
+ {
+ Logger.UnauthenticatedApiCall(_logger, null);
+ }
+ if (!string.IsNullOrEmpty(effectiveOptions.AcceptHeader))
{
httpRequestMessage.Headers.Accept.ParseAdd(effectiveOptions.AcceptHeader);
- }
- // Opportunity to change the request message
+ }
+ // Opportunity to change the request message
effectiveOptions.CustomizeHttpRequestMessage?.Invoke(httpRequestMessage);
- }
+ }
internal /* for test */ static Dictionary CallerSDKDetails { get; } = new()
{
@@ -565,5 +610,5 @@ private static void AddCallerSDKTelemetry(DownstreamApiOptions effectiveOptions)
CallerSDKDetails["caller-sdk-ver"];
}
}
- }
-}
+ }
+}
diff --git a/src/Microsoft.Identity.Web.DownstreamApi/DownstreamApiExtensions.cs b/src/Microsoft.Identity.Web.DownstreamApi/DownstreamApiExtensions.cs
index 54b3b47b0..1856962b9 100644
--- a/src/Microsoft.Identity.Web.DownstreamApi/DownstreamApiExtensions.cs
+++ b/src/Microsoft.Identity.Web.DownstreamApi/DownstreamApiExtensions.cs
@@ -30,7 +30,9 @@ public static IServiceCollection AddDownstreamApi(
{
_ = Throws.IfNull(services);
- services.Configure(serviceName, configuration);
+ // Help the compiler figure out the type so that the code generator generates
+ // the binding code
+ services.Configure(serviceName, configuration.GetSection(string.Empty));
RegisterDownstreamApi(services);
return services;
}
@@ -50,7 +52,7 @@ public static IServiceCollection AddDownstreamApi(
{
_ = Throws.IfNull(services);
- services.Configure(serviceName, configureOptions);
+ services.Configure(serviceName, configureOptions);
RegisterDownstreamApi(services);
return services;
@@ -69,15 +71,14 @@ public static IServiceCollection AddDownstreamApis(
{
_ = Throws.IfNull(services);
- Dictionary options = new();
- configurationSection.Bind(options);
+ // Help the compiler figure out the type so that the code generator generates
+ // the binding code
+ Dictionary options =configurationSection.Get>()
+ ?? new Dictionary(StringComparer.OrdinalIgnoreCase);
foreach (var optionsForService in options.Keys)
{
- // lambda expression is needed as a workaround for IL2026 and IL3050 so the ConfigBinder Source Generator works
- // https://github.com/dotnet/aspire/blob/2ed738cb524f7ce82490f0da33a1ea3e194011e8/src/Components/Aspire.Azure.Messaging.ServiceBus/AspireServiceBusExtensions.cs#L105
- IConfigurationSection optionsForServiceSection = configurationSection.GetSection(optionsForService);
- services.Configure(optionsForService, bindOptions => optionsForServiceSection.Bind(bindOptions));
+ services.Configure(optionsForService, configurationSection.GetSection(optionsForService));
}
RegisterDownstreamApi(services);
return services;
diff --git a/src/Microsoft.Identity.Web.DownstreamApi/Microsoft.Identity.Web.DownstreamApi.csproj b/src/Microsoft.Identity.Web.DownstreamApi/Microsoft.Identity.Web.DownstreamApi.csproj
index ba139f474..2ae4be127 100644
--- a/src/Microsoft.Identity.Web.DownstreamApi/Microsoft.Identity.Web.DownstreamApi.csproj
+++ b/src/Microsoft.Identity.Web.DownstreamApi/Microsoft.Identity.Web.DownstreamApi.csproj
@@ -1,4 +1,4 @@
-
+
Microsoft Identity Web downstream API
Microsoft Identity Web downstream API
@@ -6,8 +6,9 @@
{A123BD94-812D-40EC-9576-1A7AB5C59913}
README.md
-
+
true
+ true
diff --git a/src/Microsoft.Identity.Web.MicrosoftGraph/Microsoft.Identity.Web.MicrosoftGraph.csproj b/src/Microsoft.Identity.Web.MicrosoftGraph/Microsoft.Identity.Web.MicrosoftGraph.csproj
index fbca3319c..f32827cd6 100644
--- a/src/Microsoft.Identity.Web.MicrosoftGraph/Microsoft.Identity.Web.MicrosoftGraph.csproj
+++ b/src/Microsoft.Identity.Web.MicrosoftGraph/Microsoft.Identity.Web.MicrosoftGraph.csproj
@@ -11,6 +11,11 @@
{E4BC2331-6822-45C3-9702-D76DD0556532}
README.md
+
+ true
+ true
+
+
True
diff --git a/src/Microsoft.Identity.Web.MicrosoftGraphBeta/Microsoft.Identity.Web.MicrosoftGraphBeta.csproj b/src/Microsoft.Identity.Web.MicrosoftGraphBeta/Microsoft.Identity.Web.MicrosoftGraphBeta.csproj
index 89cc55f31..d88d67cec 100644
--- a/src/Microsoft.Identity.Web.MicrosoftGraphBeta/Microsoft.Identity.Web.MicrosoftGraphBeta.csproj
+++ b/src/Microsoft.Identity.Web.MicrosoftGraphBeta/Microsoft.Identity.Web.MicrosoftGraphBeta.csproj
@@ -12,6 +12,11 @@
$(NoWarn);NU5104
+
+ true
+ true
+
+
diff --git a/src/Microsoft.Identity.Web.TokenAcquisition/ClientInfo.cs b/src/Microsoft.Identity.Web.TokenAcquisition/ClientInfo.cs
index 72c52e76e..47dc1fd00 100644
--- a/src/Microsoft.Identity.Web.TokenAcquisition/ClientInfo.cs
+++ b/src/Microsoft.Identity.Web.TokenAcquisition/ClientInfo.cs
@@ -17,9 +17,6 @@ internal class ClientInfo
[JsonPropertyName(ClaimConstants.UniqueTenantIdentifier)]
public string? UniqueTenantIdentifier { get; set; } = null;
-#if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("Calls Microsoft.Identity.Web.ClientInfo.DeserializeFromJson(byte[]).")]
-#endif
public static ClientInfo? CreateFromJson(string? clientInfo)
{
if (string.IsNullOrEmpty(clientInfo))
@@ -31,22 +28,30 @@ internal class ClientInfo
return bytes != null ? DeserializeFromJson(bytes) : null;
}
-#if NET6_0_OR_GREATER
- [RequiresUnreferencedCode("Calls System.Text.Json.JsonSerializer.Deserialize(ReadOnlySpan, JsonSerializerOptions).")]
-#endif
internal static ClientInfo? DeserializeFromJson(byte[]? jsonByteArray)
{
if (jsonByteArray == null || jsonByteArray.Length == 0)
{
return default;
}
-
- var options = new JsonSerializerOptions
+#if NET6_0_OR_GREATER
+ return JsonSerializer.Deserialize(jsonByteArray, ClientInfoJsonContext.Default.ClientInfo);
+#else
+ var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
};
return JsonSerializer.Deserialize(jsonByteArray, options);
+#endif
}
}
+
+#if NET6_0_OR_GREATER
+ [JsonSerializable(typeof(ClientInfo))]
+ [JsonSourceGenerationOptions(PropertyNameCaseInsensitive = true)]
+ partial class ClientInfoJsonContext : JsonSerializerContext
+ {
+ }
+#endif
}
diff --git a/src/Microsoft.Identity.Web.TokenAcquisition/Microsoft.Identity.Web.TokenAcquisition.csproj b/src/Microsoft.Identity.Web.TokenAcquisition/Microsoft.Identity.Web.TokenAcquisition.csproj
index 4a2fcf8d0..34322a4d9 100644
--- a/src/Microsoft.Identity.Web.TokenAcquisition/Microsoft.Identity.Web.TokenAcquisition.csproj
+++ b/src/Microsoft.Identity.Web.TokenAcquisition/Microsoft.Identity.Web.TokenAcquisition.csproj
@@ -8,6 +8,11 @@
README.md
+
+
+
diff --git a/src/Microsoft.Identity.Web.TokenAcquisition/PublicAPI/net6.0/InternalAPI.Unshipped.txt b/src/Microsoft.Identity.Web.TokenAcquisition/PublicAPI/net6.0/InternalAPI.Unshipped.txt
index 5dfd019d9..2c5e8d3a5 100644
--- a/src/Microsoft.Identity.Web.TokenAcquisition/PublicAPI/net6.0/InternalAPI.Unshipped.txt
+++ b/src/Microsoft.Identity.Web.TokenAcquisition/PublicAPI/net6.0/InternalAPI.Unshipped.txt
@@ -1 +1,2 @@
-const Microsoft.Identity.Web.IDWebErrorMessage.ExceptionAcquiringTokenForConfidentialClient = "IDW10501: Exception acquiring token for a confidential client: " -> string!
\ No newline at end of file
+const Microsoft.Identity.Web.IDWebErrorMessage.ExceptionAcquiringTokenForConfidentialClient = "IDW10501: Exception acquiring token for a confidential client: " -> string!
+Microsoft.Identity.Web.ClientInfoJsonContext
\ No newline at end of file
diff --git a/src/Microsoft.Identity.Web.TokenAcquisition/PublicAPI/net7.0/InternalAPI.Unshipped.txt b/src/Microsoft.Identity.Web.TokenAcquisition/PublicAPI/net7.0/InternalAPI.Unshipped.txt
index 5dfd019d9..2c5e8d3a5 100644
--- a/src/Microsoft.Identity.Web.TokenAcquisition/PublicAPI/net7.0/InternalAPI.Unshipped.txt
+++ b/src/Microsoft.Identity.Web.TokenAcquisition/PublicAPI/net7.0/InternalAPI.Unshipped.txt
@@ -1 +1,2 @@
-const Microsoft.Identity.Web.IDWebErrorMessage.ExceptionAcquiringTokenForConfidentialClient = "IDW10501: Exception acquiring token for a confidential client: " -> string!
\ No newline at end of file
+const Microsoft.Identity.Web.IDWebErrorMessage.ExceptionAcquiringTokenForConfidentialClient = "IDW10501: Exception acquiring token for a confidential client: " -> string!
+Microsoft.Identity.Web.ClientInfoJsonContext
\ No newline at end of file
diff --git a/src/Microsoft.Identity.Web.TokenAcquisition/PublicAPI/net8.0/InternalAPI.Unshipped.txt b/src/Microsoft.Identity.Web.TokenAcquisition/PublicAPI/net8.0/InternalAPI.Unshipped.txt
index 5dfd019d9..2c5e8d3a5 100644
--- a/src/Microsoft.Identity.Web.TokenAcquisition/PublicAPI/net8.0/InternalAPI.Unshipped.txt
+++ b/src/Microsoft.Identity.Web.TokenAcquisition/PublicAPI/net8.0/InternalAPI.Unshipped.txt
@@ -1 +1,2 @@
-const Microsoft.Identity.Web.IDWebErrorMessage.ExceptionAcquiringTokenForConfidentialClient = "IDW10501: Exception acquiring token for a confidential client: " -> string!
\ No newline at end of file
+const Microsoft.Identity.Web.IDWebErrorMessage.ExceptionAcquiringTokenForConfidentialClient = "IDW10501: Exception acquiring token for a confidential client: " -> string!
+Microsoft.Identity.Web.ClientInfoJsonContext
\ No newline at end of file
diff --git a/src/Microsoft.Identity.Web.TokenAcquisition/PublicAPI/net9.0/InternalAPI.Unshipped.txt b/src/Microsoft.Identity.Web.TokenAcquisition/PublicAPI/net9.0/InternalAPI.Unshipped.txt
index 5dfd019d9..2c5e8d3a5 100644
--- a/src/Microsoft.Identity.Web.TokenAcquisition/PublicAPI/net9.0/InternalAPI.Unshipped.txt
+++ b/src/Microsoft.Identity.Web.TokenAcquisition/PublicAPI/net9.0/InternalAPI.Unshipped.txt
@@ -1 +1,2 @@
-const Microsoft.Identity.Web.IDWebErrorMessage.ExceptionAcquiringTokenForConfidentialClient = "IDW10501: Exception acquiring token for a confidential client: " -> string!
\ No newline at end of file
+const Microsoft.Identity.Web.IDWebErrorMessage.ExceptionAcquiringTokenForConfidentialClient = "IDW10501: Exception acquiring token for a confidential client: " -> string!
+Microsoft.Identity.Web.ClientInfoJsonContext
\ No newline at end of file
diff --git a/src/Microsoft.Identity.Web.TokenAcquisition/TokenAcquirerFactory.cs b/src/Microsoft.Identity.Web.TokenAcquisition/TokenAcquirerFactory.cs
index 6e9ede235..d07119d64 100644
--- a/src/Microsoft.Identity.Web.TokenAcquisition/TokenAcquirerFactory.cs
+++ b/src/Microsoft.Identity.Web.TokenAcquisition/TokenAcquirerFactory.cs
@@ -247,8 +247,7 @@ private IConfiguration ReadConfiguration()
/// Returns the base path for configuration files
protected virtual string DefineConfiguration(IConfigurationBuilder builder)
{
- Assembly assembly = Assembly.GetEntryAssembly() ?? Assembly.GetExecutingAssembly();
- return Path.GetDirectoryName(assembly!.Location)!;
+ return Path.GetDirectoryName(AppContext.BaseDirectory)!;
}
ITokenAcquirerFactory implementation;
diff --git a/src/Microsoft.Identity.Web.TokenAcquisition/WebApiBuilders.cs b/src/Microsoft.Identity.Web.TokenAcquisition/WebApiBuilders.cs
index f51ce2901..152970ddc 100644
--- a/src/Microsoft.Identity.Web.TokenAcquisition/WebApiBuilders.cs
+++ b/src/Microsoft.Identity.Web.TokenAcquisition/WebApiBuilders.cs
@@ -37,7 +37,8 @@ public static MicrosoftIdentityAppCallsWebApiAuthenticationBuilder EnableTokenAc
{
if (configuration != null)
{
- services.Configure(authenticationScheme, configuration);
+ // TODO: This never was right. And the configureConfidentialClientApplicationOptions delegate is not used
+ // services.Configure(authenticationScheme, configuration);
services.Configure(authenticationScheme, options
=>
{ configuration.Bind(options); });
diff --git a/tests/DevApps/aspnet-mvc/OwinWebApi/Web.config b/tests/DevApps/aspnet-mvc/OwinWebApi/Web.config
index 4c9a36187..84916fe17 100644
--- a/tests/DevApps/aspnet-mvc/OwinWebApi/Web.config
+++ b/tests/DevApps/aspnet-mvc/OwinWebApi/Web.config
@@ -58,7 +58,7 @@
-
+
@@ -74,7 +74,7 @@
-
+
@@ -82,23 +82,23 @@
-
+
-
+
-
+
-
+
-
+
diff --git a/tests/DevApps/aspnet-mvc/OwinWebApp/Web.config b/tests/DevApps/aspnet-mvc/OwinWebApp/Web.config
index 3c287d045..dd9bee28b 100644
--- a/tests/DevApps/aspnet-mvc/OwinWebApp/Web.config
+++ b/tests/DevApps/aspnet-mvc/OwinWebApp/Web.config
@@ -59,11 +59,11 @@
-
+
-
+
@@ -75,7 +75,7 @@
-
+
@@ -83,23 +83,23 @@
-
+
-
+
-
+
-
+
-
+
diff --git a/tests/Microsoft.Identity.Web.AotCompatibility.TestApp/Microsoft.Identity.Web.AotCompatibility.TestApp.csproj b/tests/Microsoft.Identity.Web.AotCompatibility.TestApp/Microsoft.Identity.Web.AotCompatibility.TestApp.csproj
index d35fa212d..f87105e11 100644
--- a/tests/Microsoft.Identity.Web.AotCompatibility.TestApp/Microsoft.Identity.Web.AotCompatibility.TestApp.csproj
+++ b/tests/Microsoft.Identity.Web.AotCompatibility.TestApp/Microsoft.Identity.Web.AotCompatibility.TestApp.csproj
@@ -1,7 +1,7 @@
- net8.0
+ net9.0
Exe
true
true