diff --git a/src/Microsoft.Identity.Web/MicrosoftIdentityOptions.cs b/src/Microsoft.Identity.Web/MicrosoftIdentityOptions.cs index a8bd60e35..1a18008e4 100644 --- a/src/Microsoft.Identity.Web/MicrosoftIdentityOptions.cs +++ b/src/Microsoft.Identity.Web/MicrosoftIdentityOptions.cs @@ -50,6 +50,12 @@ public class MicrosoftIdentityOptions : OpenIdConnectOptions /// public string PostLogoutRedirectUri { get; set; } + /// + /// Gets or sets TokenAcquisition as a Singleton. There are scenarios, like using the Graph SDK, + /// which require TokenAcquisition to be a Singleton. + /// + public bool SingletonTokenAcquisition { get; set; } = false; + /// /// Gets or sets the edit profile user flow name for B2C, e.g. b2c_1_edit_profile. /// diff --git a/src/Microsoft.Identity.Web/Resource/RegisterValidAudience.cs b/src/Microsoft.Identity.Web/Resource/RegisterValidAudience.cs index e4beaddeb..0c6c34667 100644 --- a/src/Microsoft.Identity.Web/Resource/RegisterValidAudience.cs +++ b/src/Microsoft.Identity.Web/Resource/RegisterValidAudience.cs @@ -41,7 +41,7 @@ public void RegisterAudienceValidation( /// the default App ID URI generated by the portal is api://{clientID} /// - However, the audience (aud) of the token acquired to access this Web API is different depending /// on the "accepted access token version" for the Web API: - /// - if accepted token version is 1.0, the audience provided in the token + /// - if accepted token version is 1.0, the audience provided in the token /// by the Microsoft identity platform (formerly Azure AD v2.0) endpoint is: api://{ClientID} /// - if the accepted token version is 2.0, the audience provided by Azure AD v2.0 in the token /// is {CliendID} @@ -52,10 +52,10 @@ public void RegisterAudienceValidation( /// considers that this is the default App ID URI as explained abovce. When developer provide the /// "Audience" member, its available in the TokenValidationParameter.ValidAudience. /// - /// audiences in the security token - /// Security token from which to validate the audiences - /// Token validation parameters - /// true is the token is valid, and false, otherwise + /// audiences in the security token. + /// Security token from which to validate the audiences. + /// Token validation parameters. + /// true is the token is valid, and false, otherwise. internal /*for test only*/ bool ValidateAudience( IEnumerable audiences, SecurityToken securityToken, diff --git a/src/Microsoft.Identity.Web/ServiceCollectionExtensions.cs b/src/Microsoft.Identity.Web/ServiceCollectionExtensions.cs index d513c8cea..ac25e68eb 100644 --- a/src/Microsoft.Identity.Web/ServiceCollectionExtensions.cs +++ b/src/Microsoft.Identity.Web/ServiceCollectionExtensions.cs @@ -14,6 +14,7 @@ public static class ServiceCollectionExtensions /// Add the token acquisition service. /// /// Service collection. + /// /// the service collection. /// /// This method is typically called from the Startup.ConfigureServices(IServiceCollection services) @@ -28,11 +29,21 @@ public static class ServiceCollectionExtensions /// ; /// /// - public static IServiceCollection AddTokenAcquisition(this IServiceCollection services) + public static IServiceCollection AddTokenAcquisition( + this IServiceCollection services, + bool isTokenAcquisitionSingleton = false) { // Token acquisition service services.AddHttpContextAccessor(); - services.AddScoped(); + if (!isTokenAcquisitionSingleton) + { + services.AddScoped(); + } + else + { + services.AddSingleton(); + } + return services; } } diff --git a/src/Microsoft.Identity.Web/WebApiServiceCollectionExtensions.cs b/src/Microsoft.Identity.Web/WebApiServiceCollectionExtensions.cs index a31b405cc..8a1289f8e 100644 --- a/src/Microsoft.Identity.Web/WebApiServiceCollectionExtensions.cs +++ b/src/Microsoft.Identity.Web/WebApiServiceCollectionExtensions.cs @@ -116,11 +116,15 @@ public static IServiceCollection AddProtectedWebApiCallsProtectedWebApi( Action configureMicrosoftIdentityOptions, string jwtBearerScheme = JwtBearerDefaults.AuthenticationScheme) { - services.AddTokenAcquisition(); - services.AddHttpContextAccessor(); services.Configure(configureConfidentialClientApplicationOptions); services.Configure(configureMicrosoftIdentityOptions); + var microsoftIdentityOptions = new MicrosoftIdentityOptions(); + configureMicrosoftIdentityOptions(microsoftIdentityOptions); + + services.AddTokenAcquisition(microsoftIdentityOptions.SingletonTokenAcquisition); + services.AddHttpContextAccessor(); + services.Configure(jwtBearerScheme, options => { options.Events ??= new JwtBearerEvents(); diff --git a/src/Microsoft.Identity.Web/WebAppServiceCollectionExtensions.cs b/src/Microsoft.Identity.Web/WebAppServiceCollectionExtensions.cs index 6e981d2f9..0ab3abf80 100644 --- a/src/Microsoft.Identity.Web/WebAppServiceCollectionExtensions.cs +++ b/src/Microsoft.Identity.Web/WebAppServiceCollectionExtensions.cs @@ -169,7 +169,11 @@ public static IServiceCollection AddWebAppCallsProtectedWebApi( services.Configure(configureConfidentialClientApplicationOptions); services.AddHttpContextAccessor(); - services.AddTokenAcquisition(); + + var microsoftIdentityOptions = new MicrosoftIdentityOptions(); + configureMicrosoftIdentityOptions(microsoftIdentityOptions); + + services.AddTokenAcquisition(microsoftIdentityOptions.SingletonTokenAcquisition); services.Configure(openIdConnectScheme, options => { diff --git a/tests/Microsoft.Identity.Web.Test.Common/TestHelpers/MsalTestTokenCacheProvider.cs b/tests/Microsoft.Identity.Web.Test.Common/TestHelpers/MsalTestTokenCacheProvider.cs index 15925e41d..e6114c55c 100644 --- a/tests/Microsoft.Identity.Web.Test.Common/TestHelpers/MsalTestTokenCacheProvider.cs +++ b/tests/Microsoft.Identity.Web.Test.Common/TestHelpers/MsalTestTokenCacheProvider.cs @@ -20,7 +20,7 @@ public MsalTestTokenCacheProvider( : base(microsoftIdentityOptions, httpContextAccessor) { MemoryCache = memoryCache; - _cacheOptions = cacheOptions.Value; + _cacheOptions = cacheOptions?.Value; } public IMemoryCache MemoryCache { get; } diff --git a/tests/Microsoft.Identity.Web.Test.Integration/AcquireTokenForAppIntegrationTests.cs b/tests/Microsoft.Identity.Web.Test.Integration/AcquireTokenForAppIntegrationTests.cs index 9fed5c26b..ec7469d93 100644 --- a/tests/Microsoft.Identity.Web.Test.Integration/AcquireTokenForAppIntegrationTests.cs +++ b/tests/Microsoft.Identity.Web.Test.Integration/AcquireTokenForAppIntegrationTests.cs @@ -170,7 +170,7 @@ private void InitializeTokenAcquisitionObjects() logger); } - private IHttpContextAccessor CreateMockHttpContextAccessor() + private static IHttpContextAccessor CreateMockHttpContextAccessor() { var mockHttpContextAccessor = Substitute.For(); mockHttpContextAccessor.HttpContext = new DefaultHttpContext(); @@ -184,6 +184,7 @@ private IHttpContextAccessor CreateMockHttpContextAccessor() private void BuildTheRequiredServices() { var services = new ServiceCollection(); + services.AddTokenAcquisition(); services.AddTransient( _provider => Options.Create(new MicrosoftIdentityOptions diff --git a/tests/Microsoft.Identity.Web.Test/WebApiExtensionsTests.cs b/tests/Microsoft.Identity.Web.Test/WebApiExtensionsTests.cs index 83f4c5d71..5b390e0bb 100644 --- a/tests/Microsoft.Identity.Web.Test/WebApiExtensionsTests.cs +++ b/tests/Microsoft.Identity.Web.Test/WebApiExtensionsTests.cs @@ -342,7 +342,7 @@ public async Task AddProtectedWebApiCallsProtectedWebApi_WithConfigName() provider.GetRequiredService>().Create(string.Empty); provider.GetRequiredService>().Create(string.Empty); - config.Received(2).GetSection(_configSectionName); + config.Received(3).GetSection(_configSectionName); await AddProtectedWebApiCallsProtectedWebApi_TestCommon(services, provider, tokenValidatedFuncMock).ConfigureAwait(false); } diff --git a/tests/Microsoft.Identity.Web.Test/WebAppExtensionsTests.cs b/tests/Microsoft.Identity.Web.Test/WebAppExtensionsTests.cs index 991bfe63e..1169d1625 100644 --- a/tests/Microsoft.Identity.Web.Test/WebAppExtensionsTests.cs +++ b/tests/Microsoft.Identity.Web.Test/WebAppExtensionsTests.cs @@ -275,7 +275,7 @@ public async Task AddWebAppCallsProtectedWebApi_WithConfigNameParameters() provider.GetRequiredService>().Create(string.Empty); provider.GetRequiredService>().Create(string.Empty); - configMock.Received(2).GetSection(_configSectionName); + configMock.Received(3).GetSection(_configSectionName); var oidcOptions = provider.GetRequiredService>().Create(_oidcScheme);