diff --git a/Directory.Build.props b/Directory.Build.props index e6b33147e..98601fabd 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -87,7 +87,7 @@ 4.36.0 4.57.0-preview 3.1.3 - 5.3.0 + 6.0.0 9.0.0-preview.4.24266.19 9.0.0-preview.4.24267.6 diff --git a/benchmark/TokenAcquisitionBenchmark.cs b/benchmark/TokenAcquisitionBenchmark.cs index ec657628b..5c369c304 100644 --- a/benchmark/TokenAcquisitionBenchmark.cs +++ b/benchmark/TokenAcquisitionBenchmark.cs @@ -5,8 +5,6 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Identity.Abstractions; using Microsoft.Identity.Web; -using BenchmarkDotNet.Diagnostics.Windows; -using BenchmarkDotNet.Diagnostics.Windows.Configs; namespace Benchmarks { @@ -40,7 +38,7 @@ public async Task CreateAuthorizationHeader() { // Get the authorization request creator service IAuthorizationHeaderProvider authorizationHeaderProvider = s_serviceProvider!.GetRequiredService(); - await authorizationHeaderProvider.CreateAuthorizationHeaderForAppAsync("https://graph.microsoft.com/.default"); + await authorizationHeaderProvider.CreateAuthorizationHeaderForAppAsync("https://graph.microsoft.com/.default").ConfigureAwait(false); } [Benchmark] diff --git a/src/Microsoft.Identity.Web.DownstreamApi/DownstreamApi.cs b/src/Microsoft.Identity.Web.DownstreamApi/DownstreamApi.cs index c4cfe0c73..5551a33b3 100644 --- a/src/Microsoft.Identity.Web.DownstreamApi/DownstreamApi.cs +++ b/src/Microsoft.Identity.Web.DownstreamApi/DownstreamApi.cs @@ -224,11 +224,11 @@ public Task CallApiForAppAsync( 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 { @@ -304,15 +304,15 @@ internal async Task CallApiInternalAsync( CancellationToken cancellationToken = default) { // Downstream API URI - string apiUrl = effectiveOptions.GetApiUrl(); - + 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); - + await UpdateRequestAsync(httpRequestMessage, content, effectiveOptions, appToken, user, cancellationToken); + using HttpClient client = string.IsNullOrEmpty(serviceName) ? _httpClientFactory.CreateClient() : _httpClientFactory.CreateClient(serviceName); // Send the HTTP message @@ -351,26 +351,24 @@ internal async Task UpdateRequestAsync( httpRequestMessage.Content = content; } + 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()) { - string authorizationHeader = appToken ? - await _authorizationHeaderProvider.CreateAuthorizationHeaderForAppAsync( - effectiveOptions.Scopes.FirstOrDefault()!, - effectiveOptions, - cancellationToken).ConfigureAwait(false) : - await _authorizationHeaderProvider.CreateAuthorizationHeaderForUserAsync( - effectiveOptions.Scopes, - effectiveOptions, - user, - cancellationToken).ConfigureAwait(false); + string authorizationHeader = await _authorizationHeaderProvider.CreateAuthorizationHeaderAsync( + effectiveOptions.Scopes, + effectiveOptions, + user, + cancellationToken).ConfigureAwait(false); + httpRequestMessage.Headers.Add(Authorization, authorizationHeader); - } + } else { Logger.UnauthenticatedApiCall(_logger, null); - } + } if (!string.IsNullOrEmpty(effectiveOptions.AcceptHeader)) { httpRequestMessage.Headers.Accept.ParseAdd(effectiveOptions.AcceptHeader); diff --git a/src/Microsoft.Identity.Web.GraphServiceClient/GraphAuthenticationProvider.cs b/src/Microsoft.Identity.Web.GraphServiceClient/GraphAuthenticationProvider.cs index 7c63678e1..a02f96c31 100644 --- a/src/Microsoft.Identity.Web.GraphServiceClient/GraphAuthenticationProvider.cs +++ b/src/Microsoft.Identity.Web.GraphServiceClient/GraphAuthenticationProvider.cs @@ -24,9 +24,10 @@ internal class GraphAuthenticationProvider : IAuthenticationProvider readonly IAuthorizationHeaderProvider _authorizationHeaderProvider; readonly GraphServiceClientOptions _defaultAuthenticationOptions; private readonly string[] _graphUris = ["graph.microsoft.com", "graph.microsoft.us", "dod-graph.microsoft.us", "graph.microsoft.de", "microsoftgraph.chinacloudapi.cn", "canary.graph.microsoft.com", "graph.microsoft-ppe.com"]; + readonly IEnumerable _defaultGraphScope = ["https://graph.microsoft.com/.default"]; /// - /// Constructor from the authorization header provider. + /// Constructor for the authorization header provider. /// /// /// @@ -86,21 +87,12 @@ public async Task AuthenticateRequestAsync( // Add the authorization header if (allowedHostsValidator.IsUrlHostValid(request.URI) && !request.Headers.ContainsKey(AuthorizationHeaderKey)) { - string authorizationHeader; - if (authorizationHeaderProviderOptions!.RequestAppToken) - { - authorizationHeader = await _authorizationHeaderProvider.CreateAuthorizationHeaderForAppAsync("https://graph.microsoft.com/.default", + string authorizationHeader = await _authorizationHeaderProvider.CreateAuthorizationHeaderAsync( + authorizationHeaderProviderOptions!.RequestAppToken ? _defaultGraphScope : scopes!, authorizationHeaderProviderOptions, - cancellationToken); - } - else - { - authorizationHeader = await _authorizationHeaderProvider.CreateAuthorizationHeaderForUserAsync( - scopes!, - authorizationHeaderProviderOptions, - claimsPrincipal: user, - cancellationToken); - } + user, + cancellationToken).ConfigureAwait(false); + request.Headers.Add(AuthorizationHeaderKey, authorizationHeader); } } diff --git a/src/Microsoft.Identity.Web.MicrosoftGraph/TokenAcquisitionAuthenticationProvider.cs b/src/Microsoft.Identity.Web.MicrosoftGraph/TokenAcquisitionAuthenticationProvider.cs index 4dc92904b..bcf700bbd 100644 --- a/src/Microsoft.Identity.Web.MicrosoftGraph/TokenAcquisitionAuthenticationProvider.cs +++ b/src/Microsoft.Identity.Web.MicrosoftGraph/TokenAcquisitionAuthenticationProvider.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System; +using System.Collections.Generic; using System.Net.Http; using System.Security.Claims; using System.Threading.Tasks; @@ -16,13 +17,14 @@ namespace Microsoft.Identity.Web internal class TokenAcquisitionAuthenticationProvider : IAuthenticationProvider { public TokenAcquisitionAuthenticationProvider(IAuthorizationHeaderProvider authorizationHeaderProvider, TokenAcquisitionAuthenticationProviderOption options) - { + { _authorizationHeaderProvider = authorizationHeaderProvider; _initialOptions = options; } private readonly IAuthorizationHeaderProvider _authorizationHeaderProvider; private readonly TokenAcquisitionAuthenticationProviderOption _initialOptions; + private readonly IEnumerable _defaultGraphScope = ["https://graph.microsoft.com/.default"]; /// /// Adds an authorization header to an HttpRequestMessage. @@ -55,26 +57,17 @@ public async Task AuthenticateRequestAsync(HttpRequestMessage request) DownstreamApiOptions? downstreamOptions = new DownstreamApiOptions() { BaseUrl = "https://graph.microsoft.com", Scopes = scopes }; downstreamOptions.AcquireTokenOptions.AuthenticationOptionsName = scheme; downstreamOptions.AcquireTokenOptions.Tenant = tenant; + downstreamOptions.RequestAppToken = appOnly; if (msalAuthProviderOption?.AuthorizationHeaderProviderOptions != null) { msalAuthProviderOption.AuthorizationHeaderProviderOptions(downstreamOptions); } - string authorizationHeader; - if (appOnly) - { - authorizationHeader = await _authorizationHeaderProvider.CreateAuthorizationHeaderForAppAsync( - Constants.DefaultGraphScope, - downstreamOptions).ConfigureAwait(false); - } - else - { - authorizationHeader = await _authorizationHeaderProvider.CreateAuthorizationHeaderForUserAsync( - scopes!, + string authorizationHeader = await _authorizationHeaderProvider.CreateAuthorizationHeaderAsync( + appOnly ? _defaultGraphScope : scopes!, downstreamOptions, - claimsPrincipal: user).ConfigureAwait(false); - } + user).ConfigureAwait(false); // add or replace authorization header if (request.Headers.Contains(Constants.Authorization)) diff --git a/src/Microsoft.Identity.Web.TokenAcquisition/BaseAuthorizationHeaderProvider.cs b/src/Microsoft.Identity.Web.TokenAcquisition/BaseAuthorizationHeaderProvider.cs index 151ef8f84..947a44770 100644 --- a/src/Microsoft.Identity.Web.TokenAcquisition/BaseAuthorizationHeaderProvider.cs +++ b/src/Microsoft.Identity.Web.TokenAcquisition/BaseAuthorizationHeaderProvider.cs @@ -3,9 +3,7 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Security.Claims; -using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; @@ -29,21 +27,35 @@ public BaseAuthorizationHeaderProvider(IServiceProvider serviceProvider) // in the public API as it's going to be deprecated in future versions of IdWeb. Here this // is an implementation detail. var _tokenAcquisition = serviceProvider.GetRequiredService(); - implementation = new DefaultAuthorizationHeaderProvider(_tokenAcquisition); + _headerProvider = new DefaultAuthorizationHeaderProvider(_tokenAcquisition); } - private IAuthorizationHeaderProvider implementation; + private readonly IAuthorizationHeaderProvider _headerProvider; /// public virtual Task CreateAuthorizationHeaderForUserAsync(IEnumerable scopes, AuthorizationHeaderProviderOptions? authorizationHeaderProviderOptions = null, ClaimsPrincipal? claimsPrincipal = null, CancellationToken cancellationToken = default) { - return implementation.CreateAuthorizationHeaderForUserAsync(scopes, authorizationHeaderProviderOptions, claimsPrincipal, cancellationToken); + return _headerProvider.CreateAuthorizationHeaderForUserAsync(scopes, authorizationHeaderProviderOptions, claimsPrincipal, cancellationToken); } /// public virtual Task CreateAuthorizationHeaderForAppAsync(string scopes, AuthorizationHeaderProviderOptions? downstreamApiOptions = null, CancellationToken cancellationToken = default) { - return implementation.CreateAuthorizationHeaderForAppAsync(scopes, downstreamApiOptions, cancellationToken); + return _headerProvider.CreateAuthorizationHeaderForAppAsync(scopes, downstreamApiOptions, cancellationToken); + } + + /// + public virtual Task CreateAuthorizationHeaderAsync( + IEnumerable scopes, + AuthorizationHeaderProviderOptions? authorizationHeaderProviderOptions = null, + ClaimsPrincipal? claimsPrincipal = null, + CancellationToken cancellationToken = default) + { + return _headerProvider.CreateAuthorizationHeaderAsync( + scopes, + authorizationHeaderProviderOptions, + claimsPrincipal, + cancellationToken); } } } diff --git a/src/Microsoft.Identity.Web.TokenAcquisition/DefaultAuthorizationHeaderProvider.cs b/src/Microsoft.Identity.Web.TokenAcquisition/DefaultAuthorizationHeaderProvider.cs index a630a859f..d8fa44f12 100644 --- a/src/Microsoft.Identity.Web.TokenAcquisition/DefaultAuthorizationHeaderProvider.cs +++ b/src/Microsoft.Identity.Web.TokenAcquisition/DefaultAuthorizationHeaderProvider.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Security.Claims; using System.Threading; using System.Threading.Tasks; @@ -50,6 +51,46 @@ public async Task CreateAuthorizationHeaderForAppAsync( return result.CreateAuthorizationHeader(); } + /// + public async Task CreateAuthorizationHeaderAsync( + IEnumerable scopes, + AuthorizationHeaderProviderOptions? downstreamApiOptions = null, + ClaimsPrincipal? claimsPrincipal = null, + CancellationToken cancellationToken = default) + { + Client.AuthenticationResult result; + + // Previously, with the API name we were able to distinguish between app and user token acquisition + // This context is missing in the new API, so can we enforce that downstreamApiOptions.RequestAppToken + // needs to be set to true to acquire a token for the app. We cannot rely on ClaimsPrincipal as it can be null for user token acquisition. + // DevEx Before: + // await authorizationHeaderProvider.CreateAuthorizationHeaderForAppAsync("https://graph.microsoft.com/.default").ConfigureAwait(false); + // DevEx with the new API: + // await authorizationHeaderProvider.CreateAuthorizationHeaderAsync( + // new [] { "https://graph.microsoft.com/.default" }, + // new AuthorizationHeaderProviderOptions { RequestAppToken = true }).ConfigureAwait(false); + if (downstreamApiOptions != null && downstreamApiOptions.RequestAppToken) + { + result = await _tokenAcquisition.GetAuthenticationResultForAppAsync( + scopes.FirstOrDefault()!, + downstreamApiOptions?.AcquireTokenOptions.AuthenticationOptionsName, + downstreamApiOptions?.AcquireTokenOptions.Tenant, + CreateTokenAcquisitionOptionsFromApiOptions(downstreamApiOptions, cancellationToken)).ConfigureAwait(false); + return result.CreateAuthorizationHeader(); + } + else + { + result = await _tokenAcquisition.GetAuthenticationResultForUserAsync( + scopes, + downstreamApiOptions?.AcquireTokenOptions.AuthenticationOptionsName, + downstreamApiOptions?.AcquireTokenOptions.Tenant, + downstreamApiOptions?.AcquireTokenOptions.UserFlow, + claimsPrincipal, + CreateTokenAcquisitionOptionsFromApiOptions(downstreamApiOptions, cancellationToken)).ConfigureAwait(false); + return result.CreateAuthorizationHeader(); + } + } + private static TokenAcquisitionOptions CreateTokenAcquisitionOptionsFromApiOptions( AuthorizationHeaderProviderOptions? downstreamApiOptions, CancellationToken cancellationToken) diff --git a/tests/E2E Tests/TokenAcquirerTests/TokenAcquirer.cs b/tests/E2E Tests/TokenAcquirerTests/TokenAcquirer.cs index 361afed5f..51de4a4cb 100644 --- a/tests/E2E Tests/TokenAcquirerTests/TokenAcquirer.cs +++ b/tests/E2E Tests/TokenAcquirerTests/TokenAcquirer.cs @@ -4,12 +4,10 @@ using System; using System.Collections.Concurrent; using System.Linq; -using System.Net.Http; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Threading; using System.Threading.Tasks; -using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Microsoft.Graph; @@ -427,6 +425,9 @@ public async Task AcquireTokenWithManagedIdentity_UserAssigned() // Assert: Make sure we got a token Assert.False(string.IsNullOrEmpty(result)); + + result = await api.CreateAuthorizationHeaderAsync([scope], GetAuthHeaderOptions_ManagedId(baseUrl, clientId)); + Assert.False(string.IsNullOrEmpty(result)); } private static AuthorizationHeaderProviderOptions GetAuthHeaderOptions_ManagedId(string baseUrl, string? userAssignedClientId = null) diff --git a/tests/Microsoft.Identity.Web.Test/BaseAuthorizationHeaderProviderTest.cs b/tests/Microsoft.Identity.Web.Test/BaseAuthorizationHeaderProviderTest.cs index 5dee70fa5..e6637eeab 100644 --- a/tests/Microsoft.Identity.Web.Test/BaseAuthorizationHeaderProviderTest.cs +++ b/tests/Microsoft.Identity.Web.Test/BaseAuthorizationHeaderProviderTest.cs @@ -31,7 +31,7 @@ public CustomAuthorizationHeaderProvider(IServiceProvider serviceProvider) : bas public override Task CreateAuthorizationHeaderForAppAsync(string scopes, AuthorizationHeaderProviderOptions? downstreamApiOptions = null, CancellationToken cancellationToken = default) { if (downstreamApiOptions?.ProtocolScheme == "Custom") - return Task.FromResult("Custom"); + return Task.FromResult("CustomHeaderForApp"); else return base.CreateAuthorizationHeaderForAppAsync(scopes, downstreamApiOptions, cancellationToken); } @@ -39,10 +39,20 @@ public override Task CreateAuthorizationHeaderForAppAsync(string scopes, public override Task CreateAuthorizationHeaderForUserAsync(IEnumerable scopes, AuthorizationHeaderProviderOptions? authorizationHeaderProviderOptions = null, ClaimsPrincipal? claimsPrincipal = null, CancellationToken cancellationToken = default) { if (authorizationHeaderProviderOptions?.ProtocolScheme == "Custom") - return Task.FromResult("Custom"); + return Task.FromResult("CustomHeaderForUser"); else return base.CreateAuthorizationHeaderForUserAsync(scopes, authorizationHeaderProviderOptions, claimsPrincipal, cancellationToken); } + + public override Task CreateAuthorizationHeaderAsync(IEnumerable scopes, AuthorizationHeaderProviderOptions? authorizationHeaderProviderOptions = null, ClaimsPrincipal? claimsPrincipal = null, CancellationToken cancellationToken = default) + { + if (claimsPrincipal == null && authorizationHeaderProviderOptions?.ProtocolScheme == "Custom") + return Task.FromResult("CustomHeaderForApp"); + else if (claimsPrincipal != null && authorizationHeaderProviderOptions?.ProtocolScheme == "Custom") + return Task.FromResult("CustomHeaderForUser"); + else + return base.CreateAuthorizationHeaderAsync(scopes, authorizationHeaderProviderOptions, claimsPrincipal, cancellationToken); + } } // Mock for ITokenAcquisition @@ -106,13 +116,31 @@ public async Task TestBaseAuthorizationHeaderProvider() var serviceProvider = tokenAcquirerFactory.Build(); IAuthorizationHeaderProvider authorizationHeaderProvider = serviceProvider.GetRequiredService(); + + // test acquiring a header on behalf of a user string result = await authorizationHeaderProvider.CreateAuthorizationHeaderForUserAsync(["scope"], - new AuthorizationHeaderProviderOptions { ProtocolScheme = "Custom" }, null, CancellationToken.None); - Assert.Equal("Custom", result); + new AuthorizationHeaderProviderOptions { ProtocolScheme = "Custom" }, new ClaimsPrincipal(), CancellationToken.None); + Assert.Equal("CustomHeaderForUser", result); result = await authorizationHeaderProvider.CreateAuthorizationHeaderForUserAsync(["scope"], - new AuthorizationHeaderProviderOptions { }, null, CancellationToken.None); + new AuthorizationHeaderProviderOptions { }, new ClaimsPrincipal(), CancellationToken.None); Assert.Equal("Bearer eXY", result); + + result = await authorizationHeaderProvider.CreateAuthorizationHeaderAsync(["scope"], + new AuthorizationHeaderProviderOptions { ProtocolScheme = "Custom" }, new ClaimsPrincipal(), CancellationToken.None); + Assert.Equal("CustomHeaderForUser", result); + + TokenAcquirerFactory.ResetDefaultInstance(); // Test only + + // test acquiring a header on behalf of an app + result = await authorizationHeaderProvider.CreateAuthorizationHeaderForAppAsync("scope", + new AuthorizationHeaderProviderOptions { ProtocolScheme = "Custom" }, CancellationToken.None); + Assert.Equal("CustomHeaderForApp", result); + + result = await authorizationHeaderProvider.CreateAuthorizationHeaderAsync(["scope"], + new AuthorizationHeaderProviderOptions { ProtocolScheme = "Custom" }, null, CancellationToken.None); + Assert.Equal("CustomHeaderForApp", result); + TokenAcquirerFactory.ResetDefaultInstance(); // Test only } } diff --git a/tests/Microsoft.Identity.Web.Test/DownstreamWebApiSupport/CaeTests.cs b/tests/Microsoft.Identity.Web.Test/DownstreamWebApiSupport/CaeTests.cs index 8c853cbf9..9a64640ae 100644 --- a/tests/Microsoft.Identity.Web.Test/DownstreamWebApiSupport/CaeTests.cs +++ b/tests/Microsoft.Identity.Web.Test/DownstreamWebApiSupport/CaeTests.cs @@ -45,9 +45,9 @@ public async Task DownstreamApi_GetForApp_Retries401WithClaimsResponseOnce() var appVal = await _downstreamApi.GetForAppAsync("GraphApp"); - await _authorizationHeaderProvider.ReceivedWithAnyArgs(2).CreateAuthorizationHeaderForAppAsync(string.Empty); - await _authorizationHeaderProvider.Received().CreateAuthorizationHeaderForAppAsync( - Arg.Any(), Arg.Is(o => o.AcquireTokenOptions.Claims!.Equals(ParsedClaims, StringComparison.Ordinal))); + await _authorizationHeaderProvider.ReceivedWithAnyArgs(2).CreateAuthorizationHeaderAsync(Enumerable.Empty()); + await _authorizationHeaderProvider.Received().CreateAuthorizationHeaderAsync( + Arg.Any>(), Arg.Is(o => o.AcquireTokenOptions.Claims!.Equals(ParsedClaims, StringComparison.Ordinal))); } [Fact] @@ -61,8 +61,8 @@ public async Task DownstreamApi_GetForUser_Retries401WithClaimsResponseOnce() var userVal = await _downstreamApi.GetForUserAsync("GraphUser"); - await _authorizationHeaderProvider.ReceivedWithAnyArgs(2).CreateAuthorizationHeaderForUserAsync(Enumerable.Empty()); - await _authorizationHeaderProvider.Received().CreateAuthorizationHeaderForUserAsync( + await _authorizationHeaderProvider.ReceivedWithAnyArgs(2).CreateAuthorizationHeaderAsync(Enumerable.Empty()); + await _authorizationHeaderProvider.Received().CreateAuthorizationHeaderAsync( Arg.Any>(), Arg.Is(o => o.AcquireTokenOptions.Claims!.Equals(ParsedClaims, StringComparison.Ordinal))); } @@ -77,9 +77,9 @@ public async Task DownstreamApi_GetForApp_DoesntRetrySucessfullResponse() var appVal = await _downstreamApi.GetForAppAsync("GraphApp"); - await _authorizationHeaderProvider.ReceivedWithAnyArgs(1).CreateAuthorizationHeaderForAppAsync(string.Empty); - await _authorizationHeaderProvider.Received().CreateAuthorizationHeaderForAppAsync( - Arg.Any(), Arg.Is(o => o.AcquireTokenOptions.Claims == null)); + await _authorizationHeaderProvider.ReceivedWithAnyArgs(1).CreateAuthorizationHeaderAsync(Enumerable.Empty()); + await _authorizationHeaderProvider.Received().CreateAuthorizationHeaderAsync( + Arg.Any>(), Arg.Is(o => o.AcquireTokenOptions.Claims == null)); } [Fact] @@ -93,8 +93,8 @@ public async Task DownstreamApi_GetForUser_DoesntRetrySucessfullResponse() var userVal = await _downstreamApi.GetForUserAsync("GraphUser"); - await _authorizationHeaderProvider.ReceivedWithAnyArgs(1).CreateAuthorizationHeaderForUserAsync(Enumerable.Empty()); - await _authorizationHeaderProvider.Received().CreateAuthorizationHeaderForUserAsync( + await _authorizationHeaderProvider.ReceivedWithAnyArgs(1).CreateAuthorizationHeaderAsync(Enumerable.Empty()); + await _authorizationHeaderProvider.Received().CreateAuthorizationHeaderAsync( Arg.Any>(), Arg.Is(o => o.AcquireTokenOptions.Claims == null)); } @@ -109,9 +109,9 @@ public async Task DownstreamApi_GetForApp_DoesntRetry401WithoutClaimsResponse() await Assert.ThrowsAsync(() => _downstreamApi.GetForAppAsync("GraphApp")); - await _authorizationHeaderProvider.ReceivedWithAnyArgs(1).CreateAuthorizationHeaderForAppAsync(string.Empty); - await _authorizationHeaderProvider.Received().CreateAuthorizationHeaderForAppAsync( - Arg.Any(), Arg.Is(o => o.AcquireTokenOptions.Claims == null)); + await _authorizationHeaderProvider.ReceivedWithAnyArgs(1).CreateAuthorizationHeaderAsync(Enumerable.Empty()); + await _authorizationHeaderProvider.Received().CreateAuthorizationHeaderAsync( + Arg.Any>(), Arg.Is(o => o.AcquireTokenOptions.Claims == null)); } [Fact] @@ -125,8 +125,8 @@ public async Task DownstreamApi_GetForUser_DoesntRetry401WithoutClaimsResponse() await Assert.ThrowsAsync(() => _downstreamApi.GetForUserAsync("GraphUser")); - await _authorizationHeaderProvider.ReceivedWithAnyArgs(1).CreateAuthorizationHeaderForUserAsync(Enumerable.Empty()); - await _authorizationHeaderProvider.Received().CreateAuthorizationHeaderForUserAsync( + await _authorizationHeaderProvider.ReceivedWithAnyArgs(1).CreateAuthorizationHeaderAsync(Enumerable.Empty()); + await _authorizationHeaderProvider.Received().CreateAuthorizationHeaderAsync( Arg.Any>(), Arg.Is(o => o.AcquireTokenOptions.Claims == null)); } @@ -135,6 +135,7 @@ private void BuildRequiredServices(string serviceName, Action(); _authorizationHeaderProvider.CreateAuthorizationHeaderForAppAsync(string.Empty).ReturnsForAnyArgs("Bearer eyJhY2Nlc3NfdG9rZW4iOg=="); _authorizationHeaderProvider.CreateAuthorizationHeaderForUserAsync(Enumerable.Empty()).ReturnsForAnyArgs("Bearer eyJhY2Nlc3NfdG9rZW4iOg=="); + _authorizationHeaderProvider.CreateAuthorizationHeaderAsync(Enumerable.Empty()).ReturnsForAnyArgs("Bearer eyJhY2Nlc3NfdG9rZW4iOg=="); var httpMessageHandler = new QueueHttpMessageHandler(); diff --git a/tests/Microsoft.Identity.Web.Test/DownstreamWebApiSupport/DownstreamApiTests.cs b/tests/Microsoft.Identity.Web.Test/DownstreamWebApiSupport/DownstreamApiTests.cs index 3788ece82..dc4818bae 100644 --- a/tests/Microsoft.Identity.Web.Test/DownstreamWebApiSupport/DownstreamApiTests.cs +++ b/tests/Microsoft.Identity.Web.Test/DownstreamWebApiSupport/DownstreamApiTests.cs @@ -309,6 +309,11 @@ public Task CreateAuthorizationHeaderForUserAsync(IEnumerable sc { return Task.FromResult("Bearer ey"); } + + public Task CreateAuthorizationHeaderAsync(IEnumerable scopes, AuthorizationHeaderProviderOptions? authorizationHeaderProviderOptions = null, ClaimsPrincipal? claimsPrincipal = null, CancellationToken cancellationToken = default) + { + return Task.FromResult("Bearer ey"); + } } }