public async Task DiscoveryTestFirstSuccess() { // Mock process manager is being asked to act like Azure CLI was able to get the token. MockProcessManager mockProcessManager = new MockProcessManager(MockProcessManager.MockProcessManagerRequestType.Success); AzureCliAccessTokenProvider azureCliAccessTokenProvider = new AzureCliAccessTokenProvider(mockProcessManager); // Mock MSI is being asked to act like MSI was able to get token. MockMsi mockMsi = new MockMsi(MockMsi.MsiTestType.MsiAppServicesSuccess); HttpClient httpClient = new HttpClient(mockMsi); MsiAccessTokenProvider msiAccessTokenProvider = new MsiAccessTokenProvider(httpClient); // AzureServiceTokenProvider is being asked to use two providers, and return token from the first that succeeds. var providers = new List <NonInteractiveAzureServiceTokenProviderBase> { azureCliAccessTokenProvider, msiAccessTokenProvider }; AzureServiceTokenProvider azureServiceTokenProvider = new AzureServiceTokenProvider(providers); var token = await azureServiceTokenProvider.GetAccessTokenAsync(Constants.GraphResourceId, Constants.TenantId); // Mock process manager will be hit first, and so the hit count should be 1. Assert.Equal(1, mockProcessManager.HitCount); // Msi handler will not be hit, since AzureCliAccessTokenProvider was able to return token. So this hit count should be 0. Assert.Equal(0, mockMsi.HitCount); Validator.ValidateToken(token, azureServiceTokenProvider.PrincipalUsed, Constants.UserType, Constants.TenantId); }
public async Task DiscoveryTestFirstFail() { // Mock process manager is being asked to act like Azure CLI was NOT able to get the token. MockProcessManager mockProcessManager = new MockProcessManager(MockProcessManager.MockProcessManagerRequestType.ProcessNotFound); AzureCliAccessTokenProvider azureCliAccessTokenProvider = new AzureCliAccessTokenProvider(mockProcessManager); // Mock MSI is being asked to act like MSI was able to get token. MockMsi mockMsi = new MockMsi(MockMsi.MsiTestType.MsiAzureVmSuccess); HttpClient httpClient = new HttpClient(mockMsi); MsiAccessTokenProvider msiAccessTokenProvider = new MsiAccessTokenProvider(httpClient); // AzureServiceTokenProvider is being asked to use two providers, and return token from the first that succeeds. var providers = new List <NonInteractiveAzureServiceTokenProviderBase> { azureCliAccessTokenProvider, msiAccessTokenProvider }; AzureServiceTokenProvider azureServiceTokenProvider = new AzureServiceTokenProvider(providers); var token = await azureServiceTokenProvider.GetAccessTokenAsync(Constants.GraphResourceId, Constants.TenantId); // Mock process manager will be hit first, and so hit count will be 1. Assert.Equal(1, mockProcessManager.HitCount); // AzureCliAccessTokenProvider will fail, and so Msi handler will be hit next. So hit count is 2 here (1 for probe request, 1 for token request). Assert.Equal(2, mockMsi.HitCount); // MsiAccessTokenProvider should succeed, and we should get a valid token. Validator.ValidateToken(token, azureServiceTokenProvider.PrincipalUsed, Constants.AppType, Constants.TenantId, Constants.TestAppId); }
public void DiscoveryTestBothFail() { // Mock process manager is being asked to act like Azure CLI was NOT able to get the token. MockProcessManager mockProcessManager = new MockProcessManager(MockProcessManager.MockProcessManagerRequestType.ProcessNotFound); AzureCliAccessTokenProvider azureCliAccessTokenProvider = new AzureCliAccessTokenProvider(mockProcessManager); // Mock MSI is being asked to act like MSI was able to get token. MockMsi mockMsi = new MockMsi(MockMsi.MsiTestType.MsiAppServicesFailure); HttpClient httpClient = new HttpClient(mockMsi); MsiAccessTokenProvider msiAccessTokenProvider = new MsiAccessTokenProvider(httpClient); // AzureServiceTokenProvider is being asked to use two providers, and and both should fail to get token. var providers = new List <NonInteractiveAzureServiceTokenProviderBase> { azureCliAccessTokenProvider, msiAccessTokenProvider }; AzureServiceTokenProvider azureServiceTokenProvider = new AzureServiceTokenProvider(providers); var exception = Assert.ThrowsAsync <AzureServiceTokenProviderException>(() => azureServiceTokenProvider.GetAccessTokenAsync(Constants.GraphResourceId, Constants.TenantId)); Assert.Contains(Constants.NoMethodWorkedToGetTokenError, exception.Result.Message); // Mock process manager will fail, and so hit count will be 1. Assert.Equal(1, mockProcessManager.HitCount); // AzureCliAccessTokenProvider will fail, and so Msi handler will be hit next. So hit count is 1 here. Assert.Equal(1, mockMsi.HitCount); }
public async Task GetAppAuthResultForceRefreshTest() { // Create two instances of AzureServiceTokenProvider based on AzureCliAccessTokenProvider. MockProcessManager mockProcessManager = new MockProcessManager(MockProcessManager.MockProcessManagerRequestType.Success); AzureCliAccessTokenProvider azureCliAccessTokenProvider = new AzureCliAccessTokenProvider(mockProcessManager); AzureServiceTokenProvider azureServiceTokenProvider = new AzureServiceTokenProvider(azureCliAccessTokenProvider); AzureServiceTokenProvider azureServiceTokenProvider1 = new AzureServiceTokenProvider(azureCliAccessTokenProvider); // Part 1: Verify force refresh of sequential requests will request new tokens. await azureServiceTokenProvider.GetAccessTokenAsync(Constants.KeyVaultResourceId); Assert.Equal(1, mockProcessManager.HitCount); await azureServiceTokenProvider.GetAccessTokenAsync(Constants.KeyVaultResourceId); Assert.Equal(1, mockProcessManager.HitCount); // cache hit. await azureServiceTokenProvider.GetAccessTokenAsync(Constants.KeyVaultResourceId, forceRefresh : true); Assert.Equal(2, mockProcessManager.HitCount); // force refresh hit. await azureServiceTokenProvider1.GetAccessTokenAsync(Constants.KeyVaultResourceId); Assert.Equal(2, mockProcessManager.HitCount); // cache hit. await azureServiceTokenProvider1.GetAccessTokenAsync(Constants.KeyVaultResourceId, forceRefresh : true); Assert.Equal(3, mockProcessManager.HitCount); // force refresh hit. // Part 2: Verify parallel force-refreshes re-use the same fetched token. // TODO: current system does not allow for controlling this flow, test would be non-deterministic and intermitently fail. }
public async Task DiscoveryTestFirstFail() { // Mock process manager is being asked to act like Azure CLI was NOT able to get the token. MockProcessManager mockProcessManager = new MockProcessManager(MockProcessManager.MockProcessManagerRequestType.ProcessNotFound); AzureCliAccessTokenProvider azureCliAccessTokenProvider = new AzureCliAccessTokenProvider(mockProcessManager); // Mock MSI is being asked to act like MSI was able to get token. MockMsi mockMsi = new MockMsi(MockMsi.MsiTestType.MsiAppServicesSuccess); MsiAccessTokenProvider msiAccessTokenProvider = new MsiAccessTokenProvider(mockMsi); // set env vars so MsiAccessTokenProvider assumes App Service environment and not VM environment Environment.SetEnvironmentVariable(Constants.MsiAppServiceEndpointEnv, Constants.MsiEndpoint); Environment.SetEnvironmentVariable(Constants.MsiAppServiceHeaderEnv, Constants.ClientSecret); // AzureServiceTokenProvider is being asked to use two providers, and return token from the first that succeeds. var providers = new List <NonInteractiveAzureServiceTokenProviderBase> { azureCliAccessTokenProvider, msiAccessTokenProvider }; AzureServiceTokenProvider azureServiceTokenProvider = new AzureServiceTokenProvider(providers); var token = await azureServiceTokenProvider.GetAccessTokenAsync(Constants.GraphResourceId, Constants.TenantId); // Mock process manager will be hit first, and so hit count will be 1. Assert.Equal(1, mockProcessManager.HitCount); // AzureCliAccessTokenProvider will fail, and so Msi handler will be hit next. So hit count is 1 here. Assert.Equal(1, mockMsi.HitCount); // MsiAccessTokenProvider should succeed, and we should get a valid token. Validator.ValidateToken(token, azureServiceTokenProvider.PrincipalUsed, Constants.AppType, Constants.TenantId, Constants.TestAppId); }
public async Task KeyVaultCertificateSecretIdentifierSuccessTest(bool includeTenantId) { X509Certificate2 cert = new X509Certificate2(Convert.FromBase64String(Constants.TestCert), string.Empty); MockProcessManager mockProcessManager = new MockProcessManager(MockProcessManager.MockProcessManagerRequestType.Success); AzureCliAccessTokenProvider azureCliAccessTokenProvider = new AzureCliAccessTokenProvider(mockProcessManager); // Create KeyVaultClient with MockKeyVault to mock successful calls to KeyVault MockKeyVault mockKeyVault = new MockKeyVault(MockKeyVault.KeyVaultClientTestType.CertificateSecretIdentifierSuccess); HttpClient httpClient = new HttpClient(mockKeyVault); KeyVaultClient keyVaultClient = new KeyVaultClient(httpClient, azureCliAccessTokenProvider); // MockAuthenticationContext is being asked to act like client cert auth suceeded. MockAuthenticationContext mockAuthenticationContext = new MockAuthenticationContext(MockAuthenticationContext.MockAuthenticationContextTestType.AcquireTokenAsyncClientCertificateSuccess); string tenantIdParam = includeTenantId ? Constants.TenantId : null; // Create ClientCertificateAzureServiceTokenProvider instance with a subject name ClientCertificateAzureServiceTokenProvider provider = new ClientCertificateAzureServiceTokenProvider(Constants.TestAppId, Constants.TestKeyVaultCertificateSecretIdentifier, CertificateIdentifierType.KeyVaultCertificateSecretIdentifier, null, Constants.AzureAdInstance, tenantIdParam, 0, mockAuthenticationContext, keyVaultClient); // Get the token. This will test that ClientCertificateAzureServiceTokenProvider could fetch the cert from CurrentUser store based on subject name in the connection string. var authResult = await provider.GetAuthResultAsync(Constants.ArmResourceId, string.Empty).ConfigureAwait(false); Validator.ValidateToken(authResult.AccessToken, provider.PrincipalUsed, Constants.AppType, Constants.TenantId, Constants.TestAppId, cert.Thumbprint, expiresOn: authResult.ExpiresOn); }
public async Task ResourceInvalidCharsTest() { MockProcessManager mockProcessManager = new MockProcessManager(MockProcessManager.MockProcessManagerRequestType.Success); AzureCliAccessTokenProvider azureCliAccessTokenProvider = new AzureCliAccessTokenProvider(mockProcessManager); var exception = await Assert.ThrowsAsync <AzureServiceTokenProviderException>(() => Task.Run(() => azureCliAccessTokenProvider.GetAuthResultAsync("https://test^", Constants.TenantId))); Assert.Contains(Constants.NotInExpectedFormatError, exception.Message); }
public async Task CliNotFoundTest() { MockProcessManager mockProcessManager = new MockProcessManager(MockProcessManager.MockProcessManagerRequestType.ProcessNotFound); AzureCliAccessTokenProvider azureCliAccessTokenProvider = new AzureCliAccessTokenProvider(mockProcessManager); var exception = await Assert.ThrowsAsync <AzureServiceTokenProviderException>(() => Task.Run(() => azureCliAccessTokenProvider.GetAuthResultAsync(Constants.KeyVaultResourceId, Constants.TenantId))); Assert.Contains(Constants.ProgramNotFoundError, exception.Message); }
public async Task FailedToGetTokenUsingAzureCliTest() { MockProcessManager mockProcessManager = new MockProcessManager(MockProcessManager.MockProcessManagerRequestType.Failure); AzureCliAccessTokenProvider azureCliAccessTokenProvider = new AzureCliAccessTokenProvider(mockProcessManager); var exception = await Assert.ThrowsAsync <AzureServiceTokenProviderException>(() => Task.Run(() => azureCliAccessTokenProvider.GetAuthResultAsync(Constants.KeyVaultResourceId, Constants.TenantId))); Assert.Contains(Constants.FailedToGetTokenError, exception.Message); Assert.Contains(Constants.DeveloperToolError, exception.Message); }
public async Task ResourceInvalidCharsTest() { var mockProcessManager = new MockProcessManager(MockProcessManager.MockProcessManagerRequestType.Success); // VisualStudioAccessTokenProvider has in internal only constructor to allow for unit testing. var visualStudioAccessTokenProvider = new VisualStudioAccessTokenProvider(mockProcessManager, _visualStudioTokenProviderFile); var exception = await Assert.ThrowsAsync <AzureServiceTokenProviderException>(() => Task.Run(() => visualStudioAccessTokenProvider.GetAuthResultAsync("https://test^", Constants.TenantId))); Assert.Contains(Constants.NotInExpectedFormatError, exception.Message); }
public async Task GetTokenUsingAzureCliTest() { // Mock the progress manager. This emulates running an actual process e.g. az account get-access-token MockProcessManager mockProcessManager = new MockProcessManager(MockProcessManager.MockProcessManagerRequestType.Success); // AzureCliAccessTokenProvider has in internal only constructor to allow for unit testing. AzureCliAccessTokenProvider azureCliAccessTokenProvider = new AzureCliAccessTokenProvider(mockProcessManager); // Get token and validate it var authResult = await azureCliAccessTokenProvider.GetAuthResultAsync(Constants.KeyVaultResourceId, Constants.TenantId).ConfigureAwait(false); Validator.ValidateToken(authResult.AccessToken, azureCliAccessTokenProvider.PrincipalUsed, Constants.UserType, Constants.TenantId); }
public async Task GetTokenTest() { // Mock the progress manager. This emulates running an actual process to get token from Visual Studio key chain. var mockProcessManager = new MockProcessManager(MockProcessManager.MockProcessManagerRequestType.VisualStudioSuccess); // VisualStudioAccessTokenProvider has in internal only constructor to allow for unit testing. var visualStudioAccessTokenProvider = new VisualStudioAccessTokenProvider(mockProcessManager, _visualStudioTokenProviderFile); // Get token and validate it var authResult = await visualStudioAccessTokenProvider.GetAuthResultAsync(Constants.KeyVaultResourceId, Constants.TenantId).ConfigureAwait(false); Validator.ValidateToken(authResult.AccessToken, visualStudioAccessTokenProvider.PrincipalUsed, Constants.UserType, Constants.TenantId, expiresOn: authResult.ExpiresOn); }
public async Task InvalidKeyVaultSecretType() { MockProcessManager mockProcessManager = new MockProcessManager(MockProcessManager.MockProcessManagerRequestType.Success); AzureCliAccessTokenProvider azureCliAccessTokenProvider = new AzureCliAccessTokenProvider(mockProcessManager); // use a secret identifier, but return secret bundle for password/secret and not certificate MockKeyVault mockKeyVault = new MockKeyVault(TestType.PasswordSecretIdentifierSuccess); HttpClient httpClient = new HttpClient(mockKeyVault); KeyVaultClient keyVaultClient = new KeyVaultClient(httpClient, azureCliAccessTokenProvider); var exception = await Assert.ThrowsAnyAsync <Exception>(() => Task.Run(() => keyVaultClient.GetCertificateAsync(Constants.TestKeyVaultCertificateSecretIdentifier))); Assert.Contains(KeyVaultClient.SecretBundleInvalidContentTypeError, exception.Message); }
public async Task SecretNotFoundTest() { MockProcessManager mockProcessManager = new MockProcessManager(MockProcessManager.MockProcessManagerRequestType.Success); AzureCliAccessTokenProvider azureCliAccessTokenProvider = new AzureCliAccessTokenProvider(mockProcessManager); MockKeyVault mockKeyVault = new MockKeyVault(TestType.SecretNotFound); HttpClient httpClient = new HttpClient(mockKeyVault); KeyVaultClient keyVaultClient = new KeyVaultClient(httpClient, azureCliAccessTokenProvider); var exception = await Assert.ThrowsAnyAsync <Exception>(() => Task.Run(() => keyVaultClient.GetCertificateAsync(Constants.TestKeyVaultCertificateSecretIdentifier))); Assert.Contains(KeyVaultClient.KeyVaultResponseError, exception.Message); Assert.Contains(MockKeyVault.SecretNotFoundErrorMessage, exception.Message); }
public async Task FailedToGetToken() { var mockProcessManager = new MockProcessManager(MockProcessManager.MockProcessManagerRequestType.Failure); // VisualStudioAccessTokenProvider has in internal only constructor to allow for unit testing. var visualStudioAccessTokenProvider = new VisualStudioAccessTokenProvider(mockProcessManager, _visualStudioTokenProviderFile); var exception = await Assert.ThrowsAsync <AzureServiceTokenProviderException>(() => Task.Run(() => visualStudioAccessTokenProvider.GetAuthResultAsync(Constants.KeyVaultResourceId, Constants.TenantId))); Assert.Contains(AzureServiceTokenProviderException.GenericErrorMessage, exception.Message); Assert.Contains(Constants.DeveloperToolError, exception.Message); Assert.Contains(Constants.KeyVaultResourceId, exception.Message); Assert.Contains(VisualStudioAccessTokenProvider.TokenProviderGenericError, exception.Message); }
public async Task ResourceNullOrEmptyWhenGettingTokenTest() { // Mock ProcessManager is being asked to act like Azure CLI is not installed. MockProcessManager mockProcessManager = new MockProcessManager(MockProcessManager.MockProcessManagerRequestType.ProcessNotFound); AzureCliAccessTokenProvider azureCliAccessTokenProvider = new AzureCliAccessTokenProvider(mockProcessManager); AzureServiceTokenProvider azureServiceTokenProvider = new AzureServiceTokenProvider(azureCliAccessTokenProvider); var exception = await Assert.ThrowsAsync <ArgumentNullException>(() => Task.Run(() => azureServiceTokenProvider.GetAccessTokenAsync(null, Constants.TenantId))); Assert.Contains(Constants.CannotBeNullError, exception.ToString()); exception = await Assert.ThrowsAsync <ArgumentNullException>(() => Task.Run(() => azureServiceTokenProvider.GetAccessTokenAsync(string.Empty, Constants.TenantId))); Assert.Contains(Constants.CannotBeNullError, exception.ToString()); }
public async Task KeyVaultTokenProviderErrorTest() { MockProcessManager mockProcessManager = new MockProcessManager(MockProcessManager.MockProcessManagerRequestType.Failure); AzureCliAccessTokenProvider azureCliAccessTokenProvider = new AzureCliAccessTokenProvider(mockProcessManager); MockKeyVault mockKeyVault = new MockKeyVault(TestType.CertificateSecretIdentifierSuccess); HttpClient httpClient = new HttpClient(mockKeyVault); KeyVaultClient keyVaultClient = new KeyVaultClient(httpClient, azureCliAccessTokenProvider); var exception = await Assert.ThrowsAnyAsync <Exception>(() => Task.Run(() => keyVaultClient.GetCertificateAsync(Constants.TestKeyVaultCertificateSecretIdentifier))); Assert.Contains(AzureServiceTokenProviderException.GenericErrorMessage, exception.Message); Assert.Contains(KeyVaultClient.KeyVaultAccessTokenRetrievalError, exception.Message); Assert.Contains(string.Format(KeyVaultClient.TokenProviderErrorsFormat, 1), exception.Message); Assert.Contains(Constants.DeveloperToolError, exception.Message); }
public async Task GetTokenExceptionTest() { // Mock ProcessManager is being asked to act like Azure CLI is not installed. MockProcessManager mockProcessManager = new MockProcessManager(MockProcessManager.MockProcessManagerRequestType.ProcessNotFound); AzureCliAccessTokenProvider azureCliAccessTokenProvider = new AzureCliAccessTokenProvider(mockProcessManager); AzureServiceTokenProvider azureServiceTokenProvider = new AzureServiceTokenProvider(azureCliAccessTokenProvider); await Assert.ThrowsAsync <AzureServiceTokenProviderException>(() => Task.Run(() => azureServiceTokenProvider.GetAccessTokenAsync(Constants.GraphResourceId, Constants.TenantId))); // Hit count should be 1, since we called Get Token once. Assert.Equal(1, mockProcessManager.HitCount); var exception = await Assert.ThrowsAsync <AzureServiceTokenProviderException>(() => Task.Run(() => azureServiceTokenProvider.GetAccessTokenAsync(Constants.GraphResourceId, Constants.TenantId))); // Hit count is 2, since token could not be fetched, and so was not in cache. // This tells us that tokens are only cached if they are aquired. Assert.Equal(2, mockProcessManager.HitCount); // Exception should contain the resource and tenant Assert.Contains(Constants.GraphResourceId, exception.ToString()); Assert.Contains(Constants.TenantId, exception.ToString()); }
public void DiscoveryTestBothFail() { // Mock process manager is being asked to act like Azure CLI was NOT able to get the token. MockProcessManager mockProcessManager = new MockProcessManager(MockProcessManager.MockProcessManagerRequestType.ProcessNotFound); AzureCliAccessTokenProvider azureCliAccessTokenProvider = new AzureCliAccessTokenProvider(mockProcessManager); // Mock MSI is being asked to act like MSI was NOT able to get token. MockMsi mockMsi = new MockMsi(MockMsi.MsiTestType.MsiAppServicesFailure); HttpClient httpClient = new HttpClient(mockMsi); MsiAccessTokenProvider msiAccessTokenProvider = new MsiAccessTokenProvider(httpClient); // set env vars so MsiAccessTokenProvider assumes App Service environment and not VM environment Environment.SetEnvironmentVariable(Constants.MsiAppServiceEndpointEnv, Constants.MsiEndpoint); Environment.SetEnvironmentVariable(Constants.MsiAppServiceSecretEnv, Constants.ClientSecret); // use test hook to expedite test MsiRetryHelper.WaitBeforeRetry = false; // AzureServiceTokenProvider is being asked to use two providers, and and both should fail to get token. var providers = new List <NonInteractiveAzureServiceTokenProviderBase> { azureCliAccessTokenProvider, msiAccessTokenProvider }; AzureServiceTokenProvider azureServiceTokenProvider = new AzureServiceTokenProvider(providers); var exception = Assert.ThrowsAsync <AzureServiceTokenProviderException>(() => azureServiceTokenProvider.GetAccessTokenAsync(Constants.GraphResourceId, Constants.TenantId)); Assert.Contains(Constants.NoMethodWorkedToGetTokenError, exception.Result.Message); // Mock process manager will fail, and so hit count will be 1. Assert.Equal(1, mockProcessManager.HitCount); // AzureCliAccessTokenProvider will fail, and so MSI handler will be hit next. MSI retry count is 5 so hit count is 5 here. Assert.Equal(5, mockMsi.HitCount); // Clean up environment variables Environment.SetEnvironmentVariable(Constants.MsiAppServiceEndpointEnv, null); Environment.SetEnvironmentVariable(Constants.MsiAppServiceSecretEnv, null); }
public async Task KeyVaultCertificateNotFoundTest() { MockAuthenticationContext mockAuthenticationContext = new MockAuthenticationContext(MockAuthenticationContext.MockAuthenticationContextTestType.AcquireTokenAsyncClientCertificateSuccess); MockProcessManager mockProcessManager = new MockProcessManager(MockProcessManager.MockProcessManagerRequestType.Success); AzureCliAccessTokenProvider azureCliAccessTokenProvider = new AzureCliAccessTokenProvider(mockProcessManager); MockKeyVault mockKeyVault = new MockKeyVault(MockKeyVault.KeyVaultClientTestType.SecretNotFound); HttpClient httpClient = new HttpClient(mockKeyVault); KeyVaultClient keyVaultClient = new KeyVaultClient(httpClient, azureCliAccessTokenProvider); string SecretIdentifier = "https://testbedkeyvault.vault.azure.net/secrets/secret/"; ClientCertificateAzureServiceTokenProvider provider = new ClientCertificateAzureServiceTokenProvider(Constants.TestAppId, SecretIdentifier, CertificateIdentifierType.KeyVaultSecretIdentifier, Constants.CurrentUserStore, Constants.TenantId, Constants.AzureAdInstance, mockAuthenticationContext, keyVaultClient); var exception = await Assert.ThrowsAsync <AzureServiceTokenProviderException>(() => Task.Run(() => provider.GetAuthResultAsync(Constants.ArmResourceId, Constants.TenantId))); Assert.Contains(Constants.ArmResourceId, exception.Message); Assert.Contains(Constants.TenantId, exception.Message); Assert.Contains(AzureServiceTokenProviderException.KeyVaultCertificateRetrievalError, exception.Message); Assert.Contains(KeyVaultClient.KeyVaultResponseError, exception.Message); Assert.Contains(MockKeyVault.SecretNotFoundErrorMessage, exception.Message); }
public async Task GetAppAuthResultCacheTest() { // Create two instances of AzureServiceTokenProvider based on AzureCliAccessTokenProvider. MockProcessManager mockProcessManager = new MockProcessManager(MockProcessManager.MockProcessManagerRequestType.Success); AzureCliAccessTokenProvider azureCliAccessTokenProvider = new AzureCliAccessTokenProvider(mockProcessManager); AzureServiceTokenProvider azureServiceTokenProvider = new AzureServiceTokenProvider(azureCliAccessTokenProvider); AzureServiceTokenProvider azureServiceTokenProvider1 = new AzureServiceTokenProvider(azureCliAccessTokenProvider); List <Task> tasks = new List <Task>(); // ManualResetEvent will enable testing of SemaphoreSlim used in AzureServiceTokenProvider. ManualResetEvent manualResetEvent = new ManualResetEvent(false); // Use AzureServiceTokenProviders to get tokens in parallel. for (int i = 0; i < 5; i++) { Task task = Task.Run(async delegate { // This will prevent the next line from running, until manualResetEvent.Set() is called. // This will ensure all GetAccessTokenAsync calls are made at once. manualResetEvent.WaitOne(); await azureServiceTokenProvider.GetAccessTokenAsync(Constants.KeyVaultResourceId); }); tasks.Add(task); Task task1 = Task.Run(async delegate { manualResetEvent.WaitOne(); // This is using the other instance of AzureServiceTokenProvider. await azureServiceTokenProvider1.GetAccessTokenAsync(Constants.KeyVaultResourceId); }); tasks.Add(task1); } // This will cause GetAccessTokenAsync calls to be made concurrently. manualResetEvent.Set(); await Task.WhenAll(tasks); // Even though multiple calls are made to get token concurrently, using two different instances, the process manager should only be called once. // This test tells us that the cache is working as intended. Assert.Equal(1, mockProcessManager.HitCount); // Get the token again. This will test if the cache call before semaphore use is working as intended. for (int i = 0; i < 5; i++) { tasks.Add(azureServiceTokenProvider.GetAccessTokenAsync(Constants.KeyVaultResourceId)); } await Task.WhenAll(tasks); // The hit count should still be 1, since the token should be fetched from cache. Assert.Equal(1, mockProcessManager.HitCount); // Update the cache entry, to simulate token expiration. This updated token will expire in just less than 5 minutes. // In a real scenario, the token will expire after some time. // AppAuthResultCache should not return this, since it is about to expire. var tokenResponse = TokenResponse.Parse(TokenHelper.GetUserTokenResponse(5 * 60 - 2)); var authResult = AppAuthenticationResult.Create(tokenResponse, TokenResponse.DateFormat.DateTimeString); AppAuthResultCache.AddOrUpdate("ConnectionString:;Authority:;Resource:https://vault.azure.net/", new Tuple <AppAuthenticationResult, Principal>(authResult, null)); // Get the token again. for (int i = 0; i < 5; i++) { tasks.Add(azureServiceTokenProvider.GetAccessTokenAsync(Constants.KeyVaultResourceId)); } await Task.WhenAll(tasks); // Hit count should be 2 now, since new token should have been aquired. Assert.Equal(2, mockProcessManager.HitCount); }