Skip to content

Commit 922beec

Browse files
Register IAuthorizationHeaderProvider2 in DI (#3927)
* Register IAuthorizationHeaderProvider2 in DI DefaultAuthorizationHeaderProvider implements both IAuthorizationHeaderProvider and IAuthorizationHeaderProvider2, but only v1 was registered - so resolving v2 directly returned nothing (internal callers only worked because they cast). Register v2 as the same instance (mirroring how ITokenAcquisitionInternal is exposed off ITokenAcquisition) in both the singleton and scoped paths, and remove it on the lifetime-mismatch re-registration. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add GetAuthorizationHeaderProvider2() to OWIN controllers (#3928) * Register IAuthorizationHeaderProvider2 in DI DefaultAuthorizationHeaderProvider implements both IAuthorizationHeaderProvider and IAuthorizationHeaderProvider2, but only v1 was registered - so resolving v2 directly returned nothing (internal callers only worked because they cast). Register v2 as the same instance (mirroring how ITokenAcquisitionInternal is exposed off ITokenAcquisition) in both the singleton and scoped paths, and remove it on the lifetime-mismatch re-registration. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Add GetAuthorizationHeaderProvider2() to OWIN controllers OWIN controllers already get a one-liner for the v1 IAuthorizationHeaderProvider; this adds the same convenience for IAuthorizationHeaderProvider2 on both ApiController and ControllerBase, resolving it from the OWIN TokenAcquirerFactory service provider. Depends on IAuthorizationHeaderProvider2 being registered in DI. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent a4588a9 commit 922beec

5 files changed

Lines changed: 94 additions & 1 deletion

File tree

src/Microsoft.Identity.Web.OWIN/ApiControllerExtensions.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,20 @@ public static IAuthorizationHeaderProvider GetAuthorizationHeaderProvider(this A
4242
return headerProvider;
4343
}
4444

45+
/// <summary>
46+
/// Get the authorization header provider, exposing the richer <see cref="IAuthorizationHeaderProvider2"/> surface.
47+
/// </summary>
48+
/// <returns></returns>
49+
public static IAuthorizationHeaderProvider2 GetAuthorizationHeaderProvider2(this ApiController _)
50+
{
51+
IAuthorizationHeaderProvider2? headerProvider = TokenAcquirerFactory.GetDefaultInstance().ServiceProvider?.GetService(typeof(IAuthorizationHeaderProvider2)) as IAuthorizationHeaderProvider2;
52+
if (headerProvider == null)
53+
{
54+
throw new ConfigurationErrorsException("Cannot find IAuthorizationHeaderProvider2. Did you create an OwinTokenAcquirerFactory in Startup_Auth.cs?. See https://aka.ms/ms-id-web/owin. ");
55+
}
56+
return headerProvider;
57+
}
58+
4559
/// <summary>
4660
/// Get the downstream API service from an ApiController.
4761
/// </summary>

src/Microsoft.Identity.Web.OWIN/ControllerBaseExtensions.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,20 @@ public static IAuthorizationHeaderProvider GetAuthorizationHeaderProvider(this C
4242
return headerProvider;
4343
}
4444

45+
/// <summary>
46+
/// Get the authorization header provider, exposing the richer <see cref="IAuthorizationHeaderProvider2"/> surface.
47+
/// </summary>
48+
/// <returns></returns>
49+
public static IAuthorizationHeaderProvider2 GetAuthorizationHeaderProvider2(this ControllerBase _)
50+
{
51+
IAuthorizationHeaderProvider2? headerProvider = TokenAcquirerFactory.GetDefaultInstance().ServiceProvider?.GetService(typeof(IAuthorizationHeaderProvider2)) as IAuthorizationHeaderProvider2;
52+
if (headerProvider == null)
53+
{
54+
throw new ConfigurationErrorsException("Cannot find IAuthorizationHeaderProvider2. Did you create an OwinTokenAcquirerFactory in Startup_Auth.cs?. See https://aka.ms/ms-id-web/owin. ");
55+
}
56+
return headerProvider;
57+
}
58+
4559
/// <summary>
4660
/// Get the downstream API service from an ApiController.
4761
/// </summary>
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
#nullable enable
2+
static Microsoft.Identity.Web.ApiControllerExtensions.GetAuthorizationHeaderProvider2(this System.Web.Http.ApiController! _) -> Microsoft.Identity.Abstractions.IAuthorizationHeaderProvider2!
3+
static Microsoft.Identity.Web.ControllerBaseExtensions.GetAuthorizationHeaderProvider2(this System.Web.Mvc.ControllerBase! _) -> Microsoft.Identity.Abstractions.IAuthorizationHeaderProvider2!

src/Microsoft.Identity.Web.TokenAcquisition/ServiceCollectionExtensions.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ public static IServiceCollection AddTokenAcquisition(
7575
ServiceDescriptor? ccaProviderService = services.FirstOrDefault(s => s.ServiceType == typeof(IConfidentialClientApplicationProvider));
7676
ServiceDescriptor? tokenAcquisitionhost = services.FirstOrDefault(s => s.ServiceType == typeof(ITokenAcquisitionHost));
7777
ServiceDescriptor? authenticationHeaderCreator = services.FirstOrDefault(s => s.ServiceType == typeof(IAuthorizationHeaderProvider));
78+
ServiceDescriptor? authenticationHeaderCreator2 = services.FirstOrDefault(s => s.ServiceType == typeof(IAuthorizationHeaderProvider2));
7879
ServiceDescriptor? tokenAcquirerFactory = services.FirstOrDefault(s => s.ServiceType == typeof(ITokenAcquirerFactory));
7980
ServiceDescriptor? authSchemeInfoProvider = services.FirstOrDefault(s => s.ServiceType == typeof(Abstractions.IAuthenticationSchemeInformationProvider));
8081
ServiceDescriptor? credentialsProviderService = services.FirstOrDefault(s => s.ServiceType == typeof(ICredentialsProvider));
@@ -91,6 +92,11 @@ public static IServiceCollection AddTokenAcquisition(
9192
services.Remove(authenticationHeaderCreator);
9293
services.Remove(authSchemeInfoProvider);
9394

95+
if (authenticationHeaderCreator2 != null)
96+
{
97+
services.Remove(authenticationHeaderCreator2);
98+
}
99+
94100
if (ccaProviderService != null)
95101
{
96102
services.Remove(ccaProviderService);
@@ -148,6 +154,8 @@ public static IServiceCollection AddTokenAcquisition(
148154
services.AddSingleton<Abstractions.IAuthenticationSchemeInformationProvider>(sp =>
149155
sp.GetRequiredService<ITokenAcquisitionHost>());
150156
services.AddSingleton<IAuthorizationHeaderProvider, DefaultAuthorizationHeaderProvider>();
157+
// Expose the same instance as IAuthorizationHeaderProvider2 so it can be resolved directly.
158+
services.AddSingleton(s => (IAuthorizationHeaderProvider2)s.GetRequiredService<IAuthorizationHeaderProvider>());
151159
if (!HasImplementationType(services, typeof(CredentialsProvider)))
152160
{
153161
services.TryAddSingleton<ICredentialsProvider, CredentialsProvider>();
@@ -183,6 +191,8 @@ public static IServiceCollection AddTokenAcquisition(
183191
services.AddScoped<Abstractions.IAuthenticationSchemeInformationProvider>(sp =>
184192
sp.GetRequiredService<ITokenAcquisitionHost>());
185193
services.AddScoped<IAuthorizationHeaderProvider, DefaultAuthorizationHeaderProvider>();
194+
// Expose the same instance as IAuthorizationHeaderProvider2 so it can be resolved directly.
195+
services.AddScoped(s => (IAuthorizationHeaderProvider2)s.GetRequiredService<IAuthorizationHeaderProvider>());
186196
if (!HasImplementationType(services, typeof(CredentialsProvider)))
187197
{
188198
services.TryAddScoped<ICredentialsProvider, CredentialsProvider>();

tests/Microsoft.Identity.Web.Test/ServiceCollectionExtensionsTests.cs

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,14 @@ public void AddTokenAcquisition_Sdk_AddsWithCorrectLifetime()
7474
Assert.Null(actual.ImplementationFactory);
7575
},
7676
actual =>
77+
{
78+
Assert.Equal(ServiceLifetime.Scoped, actual.Lifetime);
79+
Assert.Equal(typeof(IAuthorizationHeaderProvider2), actual.ServiceType);
80+
Assert.Null(actual.ImplementationType);
81+
Assert.Null(actual.ImplementationInstance);
82+
Assert.NotNull(actual.ImplementationFactory);
83+
},
84+
actual =>
7785
{
7886
Assert.Equal(typeof(ICredentialsLoader), actual.ServiceType);
7987
Assert.Equal(typeof(DefaultCertificateLoader), actual.ImplementationType);
@@ -155,7 +163,7 @@ public void AddTokenAcquisition_Sdk_SupportsKeyedServices()
155163
services.AddTokenAcquisition();
156164

157165
// Verify the number of services added by AddTokenAcquisition (ignoring the service we added here).
158-
Assert.Equal(14, services.Count(t => t.ServiceType != typeof(ServiceCollectionExtensionsTests)));
166+
Assert.Equal(15, services.Count(t => t.ServiceType != typeof(ServiceCollectionExtensionsTests)));
159167
}
160168
#endif
161169

@@ -210,6 +218,43 @@ public void AddTokenAcquisition_ResolvesIConfidentialClientApplicationProviderTo
210218
Assert.IsAssignableFrom<TokenAcquisition>(confidentialClientApplicationProvider);
211219
}
212220

221+
[Fact]
222+
public void AddTokenAcquisition_RegistersIAuthorizationHeaderProvider2()
223+
{
224+
// Arrange
225+
var services = new ServiceCollection();
226+
227+
// Act
228+
services.AddTokenAcquisition();
229+
230+
// Assert
231+
ServiceDescriptor serviceDescriptor = Assert.Single(services, s => s.ServiceType == typeof(IAuthorizationHeaderProvider2));
232+
Assert.Equal(ServiceLifetime.Scoped, serviceDescriptor.Lifetime);
233+
Assert.Null(serviceDescriptor.ImplementationType);
234+
Assert.Null(serviceDescriptor.ImplementationInstance);
235+
Assert.NotNull(serviceDescriptor.ImplementationFactory);
236+
}
237+
238+
[Fact]
239+
public void AddTokenAcquisition_ResolvesIAuthorizationHeaderProvider2ToSameInstanceAsV1()
240+
{
241+
// Arrange
242+
var services = new ServiceCollection();
243+
services.AddTokenAcquisition();
244+
services.AddInMemoryTokenCaches();
245+
services.AddHttpClient();
246+
using ServiceProvider serviceProvider = services.BuildServiceProvider();
247+
using IServiceScope serviceScope = serviceProvider.CreateScope();
248+
249+
// Act
250+
var headerProvider = serviceScope.ServiceProvider.GetRequiredService<IAuthorizationHeaderProvider>();
251+
var headerProvider2 = serviceScope.ServiceProvider.GetRequiredService<IAuthorizationHeaderProvider2>();
252+
253+
// Assert
254+
Assert.Same(headerProvider, headerProvider2);
255+
Assert.IsType<DefaultAuthorizationHeaderProvider>(headerProvider2);
256+
}
257+
213258
[Fact]
214259
public void AddHttpContextAccessor_ThrowsWithoutServices()
215260
{
@@ -277,6 +322,14 @@ public void AddTokenAcquisitionCalledTwice_RegistersTokenAcquisitionOnlyAsSingle
277322
Assert.Null(actual.ImplementationFactory);
278323
},
279324
actual =>
325+
{
326+
Assert.Equal(ServiceLifetime.Singleton, actual.Lifetime);
327+
Assert.Equal(typeof(IAuthorizationHeaderProvider2), actual.ServiceType);
328+
Assert.Null(actual.ImplementationType);
329+
Assert.Null(actual.ImplementationInstance);
330+
Assert.NotNull(actual.ImplementationFactory);
331+
},
332+
actual =>
280333
{
281334
Assert.Equal(typeof(ICredentialsLoader), actual.ServiceType);
282335
Assert.Equal(typeof(DefaultCertificateLoader), actual.ImplementationType);

0 commit comments

Comments
 (0)