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);