diff --git a/src/Microsoft.Identity.Web/DownstreamWebApiSupport/MicrosoftIdentityAuthenticationMessageHandlerHttpClientBuilderExtensions.cs b/src/Microsoft.Identity.Web/DownstreamWebApiSupport/MicrosoftIdentityAuthenticationMessageHandlerHttpClientBuilderExtensions.cs index 94f3cb794..c43ce5435 100644 --- a/src/Microsoft.Identity.Web/DownstreamWebApiSupport/MicrosoftIdentityAuthenticationMessageHandlerHttpClientBuilderExtensions.cs +++ b/src/Microsoft.Identity.Web/DownstreamWebApiSupport/MicrosoftIdentityAuthenticationMessageHandlerHttpClientBuilderExtensions.cs @@ -15,16 +15,32 @@ namespace Microsoft.Identity.Web /// public static class MicrosoftIdentityAuthenticationMessageHandlerHttpClientBuilderExtensions { + private const string ObseleteMessage = "This method which accepts a service name is deprecated. Use the overload that uses the HttpClient name as the service name instead."; + /// /// Adds a named Microsoft Identity user authentication message handler related to a specific configuration section. /// /// Builder. - /// Name of the configuration for the service. /// Configuration. /// The builder for chaining. #if NET6_0_OR_GREATER && !NET8_0_OR_GREATER [RequiresUnreferencedCode("Calls Microsoft.Extensions.DependencyInjection.OptionsConfigurationServiceCollectionExtensions.Configure(IServiceCollection, String, IConfiguration).")] #endif + public static IHttpClientBuilder AddMicrosoftIdentityUserAuthenticationHandler( + this IHttpClientBuilder builder, + IConfiguration configuration) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + return builder.AddMicrosoftIdentityUserAuthenticationHandler(builder.Name, configuration); + } + + /// + /// Name of the configuration for the service. + [Obsolete(ObseleteMessage)] public static IHttpClientBuilder AddMicrosoftIdentityUserAuthenticationHandler( this IHttpClientBuilder builder, string serviceName, @@ -42,9 +58,23 @@ public static IHttpClientBuilder AddMicrosoftIdentityUserAuthenticationHandler( /// Adds a named Microsoft Identity user authentication message handler initialized with delegates. /// /// Builder. - /// Name of the configuration for the service. /// Action to configure the options. /// The builder for chaining. + public static IHttpClientBuilder AddMicrosoftIdentityUserAuthenticationHandler( + this IHttpClientBuilder builder, + Action configureOptions) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + return builder.AddMicrosoftIdentityUserAuthenticationHandler(builder.Name, configureOptions); + } + + /// + /// Name of the configuration for the service. + [Obsolete(ObseleteMessage)] public static IHttpClientBuilder AddMicrosoftIdentityUserAuthenticationHandler( this IHttpClientBuilder builder, string serviceName, @@ -62,12 +92,26 @@ public static IHttpClientBuilder AddMicrosoftIdentityUserAuthenticationHandler( /// Adds a named Microsoft Identity application authentication message handler related to a specific configuration section. /// /// Builder. - /// Name of the configuration for the service. /// Configuration. /// The builder for chaining. #if NET6_0_OR_GREATER && !NET8_0_OR_GREATER [RequiresUnreferencedCode("Calls Microsoft.Extensions.DependencyInjection.OptionsConfigurationServiceCollectionExtensions.Configure(IServiceCollection, String, IConfiguration).")] #endif + public static IHttpClientBuilder AddMicrosoftIdentityAppAuthenticationHandler( + this IHttpClientBuilder builder, + IConfiguration configuration) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + return builder.AddMicrosoftIdentityAppAuthenticationHandler(builder.Name, configuration); + } + + /// + /// Name of the configuration for the service. + [Obsolete(ObseleteMessage)] public static IHttpClientBuilder AddMicrosoftIdentityAppAuthenticationHandler( this IHttpClientBuilder builder, string serviceName, @@ -85,9 +129,23 @@ public static IHttpClientBuilder AddMicrosoftIdentityAppAuthenticationHandler( /// Adds a named Microsoft Identity application authentication message handler initialized with delegates. /// /// Builder. - /// Name of the configuration for the service. /// Action to configure the options. /// The builder for chaining. + public static IHttpClientBuilder AddMicrosoftIdentityAppAuthenticationHandler( + this IHttpClientBuilder builder, + Action configureOptions) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + return builder.AddMicrosoftIdentityAppAuthenticationHandler(builder.Name, configureOptions); + } + + /// + /// Name of the configuration for the service. + [Obsolete(ObseleteMessage)] public static IHttpClientBuilder AddMicrosoftIdentityAppAuthenticationHandler( this IHttpClientBuilder builder, string serviceName, diff --git a/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml b/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml index 738222e8b..ab5d8f7d9 100644 --- a/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml +++ b/src/Microsoft.Identity.Web/Microsoft.Identity.Web.xml @@ -875,42 +875,54 @@ Extension for IHttpClientBuilder for startup initialization of Microsoft Identity authentication handlers. - + Adds a named Microsoft Identity user authentication message handler related to a specific configuration section. Builder. - Name of the configuration for the service. Configuration. The builder for chaining. - + + + Name of the configuration for the service. + + Adds a named Microsoft Identity user authentication message handler initialized with delegates. Builder. - Name of the configuration for the service. Action to configure the options. The builder for chaining. - + + + Name of the configuration for the service. + + Adds a named Microsoft Identity application authentication message handler related to a specific configuration section. Builder. - Name of the configuration for the service. Configuration. The builder for chaining. - + + + Name of the configuration for the service. + + Adds a named Microsoft Identity application authentication message handler initialized with delegates. Builder. - Name of the configuration for the service. Action to configure the options. The builder for chaining. + + + Name of the configuration for the service. + Adds the common configuration for message handlers. diff --git a/tests/Microsoft.Identity.Web.Test/HttpClientBuilderExtensionsTests.cs b/tests/Microsoft.Identity.Web.Test/HttpClientBuilderExtensionsTests.cs index 511acb540..cdc5a9f65 100644 --- a/tests/Microsoft.Identity.Web.Test/HttpClientBuilderExtensionsTests.cs +++ b/tests/Microsoft.Identity.Web.Test/HttpClientBuilderExtensionsTests.cs @@ -16,17 +16,18 @@ namespace Microsoft.Identity.Web.Test public class HttpClientBuilderExtensionsTests { private const string HttpClientName = "test-client"; - private const string ServiceName = "test-service"; private readonly IConfigurationSection _configSection; public HttpClientBuilderExtensionsTests() { - _configSection = GetConfigSection(ServiceName); + _configSection = GetConfigSection(); } - private IConfigurationSection GetConfigSection(string key) + private IConfigurationSection GetConfigSection() { + var key = "test-service"; + var builder = new ConfigurationBuilder(); builder.AddInMemoryCollection( new Dictionary() @@ -62,6 +63,13 @@ public DelegatingHandler CreateUserHandler(string? serviceName) } } + protected internal class TypedClient + { + public TypedClient(HttpClient client) + { + } + } + [Theory] [InlineData(false)] [InlineData(true)] @@ -74,12 +82,12 @@ public void AddMicrosoftIdentityAuthenticationHandler_WithConfiguration(bool use if (useApp) { services.AddHttpClient(HttpClientName) - .AddMicrosoftIdentityAppAuthenticationHandler(ServiceName, _configSection); + .AddMicrosoftIdentityAppAuthenticationHandler(_configSection); } else { services.AddHttpClient(HttpClientName) - .AddMicrosoftIdentityUserAuthenticationHandler(ServiceName, _configSection); + .AddMicrosoftIdentityUserAuthenticationHandler(_configSection); } // assert @@ -88,11 +96,11 @@ public void AddMicrosoftIdentityAuthenticationHandler_WithConfiguration(bool use var provider = services.BuildServiceProvider(); var options = provider.GetRequiredService>(); - Assert.Equal(TestConstants.Scopes, options.Get(ServiceName).Scopes); - Assert.Equal(TestConstants.TenantIdAsGuid, options.Get(ServiceName).Tenant); - Assert.Equal(TestConstants.B2CSignUpSignInUserFlow, options.Get(ServiceName).UserFlow); - Assert.False(options.Get(ServiceName).IsProofOfPossessionRequest); - Assert.Equal(JwtBearerDefaults.AuthenticationScheme, options.Get(ServiceName).AuthenticationScheme); + Assert.Equal(TestConstants.Scopes, options.Get(HttpClientName).Scopes); + Assert.Equal(TestConstants.TenantIdAsGuid, options.Get(HttpClientName).Tenant); + Assert.Equal(TestConstants.B2CSignUpSignInUserFlow, options.Get(HttpClientName).UserFlow); + Assert.False(options.Get(HttpClientName).IsProofOfPossessionRequest); + Assert.Equal(JwtBearerDefaults.AuthenticationScheme, options.Get(HttpClientName).AuthenticationScheme); } [Theory] @@ -115,12 +123,54 @@ public void AddMicrosoftIdentityAuthenticationHandler_WithOptions(bool useApp) if (useApp) { services.AddHttpClient(HttpClientName) - .AddMicrosoftIdentityAppAuthenticationHandler(ServiceName, configureOptions); + .AddMicrosoftIdentityAppAuthenticationHandler(configureOptions); } else { services.AddHttpClient(HttpClientName) - .AddMicrosoftIdentityUserAuthenticationHandler(ServiceName, configureOptions); + .AddMicrosoftIdentityUserAuthenticationHandler(configureOptions); + } + + // assert + Assert.Contains(services, s => s.ServiceType == typeof(IConfigureOptions)); + + var provider = services.BuildServiceProvider(); + var options = provider.GetRequiredService>(); + + Assert.Equal(TestConstants.GraphScopes, options.Get(HttpClientName).Scopes); + Assert.Equal(TestConstants.TenantIdAsGuid, options.Get(HttpClientName).Tenant); + Assert.Equal(TestConstants.B2CResetPasswordUserFlow, options.Get(HttpClientName).UserFlow); + Assert.True(options.Get(HttpClientName).IsProofOfPossessionRequest); + Assert.Equal(JwtBearerDefaults.AuthenticationScheme, options.Get(HttpClientName).AuthenticationScheme); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void AddMicrosoftIdentityAuthenticationHandler_WithTypedHttpClient(bool useApp) + { + // arrange + var clientName = typeof(TypedClient).Name; + var services = new ServiceCollection(); + Action configureOptions = options => + { + options.Scopes = TestConstants.GraphScopes; + options.Tenant = TestConstants.TenantIdAsGuid; + options.UserFlow = TestConstants.B2CResetPasswordUserFlow; + options.IsProofOfPossessionRequest = true; + options.AuthenticationScheme = JwtBearerDefaults.AuthenticationScheme; + }; + + // act + if (useApp) + { + services.AddHttpClient() + .AddMicrosoftIdentityAppAuthenticationHandler(configureOptions); + } + else + { + services.AddHttpClient() + .AddMicrosoftIdentityUserAuthenticationHandler(configureOptions); } // assert @@ -129,11 +179,11 @@ public void AddMicrosoftIdentityAuthenticationHandler_WithOptions(bool useApp) var provider = services.BuildServiceProvider(); var options = provider.GetRequiredService>(); - Assert.Equal(TestConstants.GraphScopes, options.Get(ServiceName).Scopes); - Assert.Equal(TestConstants.TenantIdAsGuid, options.Get(ServiceName).Tenant); - Assert.Equal(TestConstants.B2CResetPasswordUserFlow, options.Get(ServiceName).UserFlow); - Assert.True(options.Get(ServiceName).IsProofOfPossessionRequest); - Assert.Equal(JwtBearerDefaults.AuthenticationScheme, options.Get(ServiceName).AuthenticationScheme); + Assert.Equal(TestConstants.GraphScopes, options.Get(clientName).Scopes); + Assert.Equal(TestConstants.TenantIdAsGuid, options.Get(clientName).Tenant); + Assert.Equal(TestConstants.B2CResetPasswordUserFlow, options.Get(clientName).UserFlow); + Assert.True(options.Get(clientName).IsProofOfPossessionRequest); + Assert.Equal(JwtBearerDefaults.AuthenticationScheme, options.Get(clientName).AuthenticationScheme); } [Theory] @@ -158,12 +208,12 @@ public void AddMicrosoftIdentityAuthenticationHandler_WithCustomFactory(bool use if (useApp) { services.AddHttpClient(HttpClientName) - .AddMicrosoftIdentityAppAuthenticationHandler(ServiceName, configureOptions); + .AddMicrosoftIdentityAppAuthenticationHandler(configureOptions); } else { services.AddHttpClient(HttpClientName) - .AddMicrosoftIdentityUserAuthenticationHandler(ServiceName, configureOptions); + .AddMicrosoftIdentityUserAuthenticationHandler(configureOptions); } // assert