public async Task <AuthenticationToken> GetAuthResultAsync(string resource, string authority) { try { // Validate resource, since it gets sent as a command line argument to Azure CLI ValidationHelper.ValidateResource(resource); // Execute Azure CLI to get token var response = await _processManager.ExecuteAsync(new Process { StartInfo = GetProcessStartInfo(resource) }).ConfigureAwait(false); // Parse the response var tokenResponse = TokenResponse.Parse(response); var accessToken = tokenResponse.AccessToken2; var token = AccessToken.Parse(accessToken); PrincipalUsed.IsAuthenticated = true; if (token != null) { // Set principal used based on the claims in the access token. PrincipalUsed.UserPrincipalName = !string.IsNullOrEmpty(token.Upn) ? token.Upn : token.Email; PrincipalUsed.TenantId = token.TenantId; } var authResult = Models.AppAuthenticationResult.Create(tokenResponse, TokenResponse.DateFormat.DateTimeString); var authenticationToken = new AuthenticationToken { AccessToken = authResult.AccessToken, TokenType = authResult.TokenType, Resource = authResult.Resource, ExpiresOn = tokenResponse.ExpiresOn, ExpiresIn = token.ExpiryTime.ToString(), ExtExpiresIn = token.ExpiryTime.ToString(), RefreshToken = tokenResponse.AccessToken2 }; return(authenticationToken); } catch (Exception exp) { throw new Exception( $"{nameof(AzureCliAccessTokenProvider)} not able to obtain the token for {ConnectionString} , {resource} , {authority}, {exp.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); }
public async Task <AuthenticationToken> GetAuthResultAsync(string resource, string authority) { try { // Validate resource, since it gets sent as a command line argument to Visual Studio token provider. ValidationHelper.ValidateResource(resource); _visualStudioTokenProviderFile = _visualStudioTokenProviderFile ?? GetTokenProviderFile(); // Get process start infos based on Visual Studio token providers var processStartInfos = GetProcessStartInfos(_visualStudioTokenProviderFile, resource, UriHelper.GetTenantByAuthority(authority)); // To hold reason why token could not be acquired per token provider tried. var exceptionDictionary = new Dictionary <string, string>(); foreach (var startInfo in processStartInfos) { try { // For each of them, try to get token var response = await _processManager .ExecuteAsync(new Process { StartInfo = startInfo }) .ConfigureAwait(false); var tokenResponse = TokenResponse.Parse(response); var accessToken = AccessToken.Parse(tokenResponse.AccessToken); PrincipalUsed.IsAuthenticated = true; if (accessToken != null) { // Set principal used based on the claims in the access token. PrincipalUsed.UserPrincipalName = !string.IsNullOrEmpty(accessToken.Upn) ? accessToken.Upn : accessToken.Email; PrincipalUsed.TenantId = accessToken.TenantId; } var authResult = Models.AppAuthenticationResult.Create(tokenResponse, TokenResponse.DateFormat.DateTimeString); var authenticationToken = new AuthenticationToken { AccessToken = authResult.AccessToken, TokenType = authResult.TokenType, Resource = authResult.Resource, ExpiresOn = tokenResponse.ExpiresOn, ExpiresIn = accessToken.ExpiryTime.ToString(), ExtExpiresIn = accessToken.ExpiryTime.ToString(), RefreshToken = tokenResponse.AccessToken, }; return(authenticationToken); } catch (Exception exp) { // If token cannot be acquired using a token provider, try the next one exceptionDictionary[Path.GetFileName(startInfo.FileName)] = exp.Message; } } // Could not acquire access token, throw exception var message = string.Empty; // Include exception details for each token provider that was tried foreach (var key in exceptionDictionary.Keys) { message += Environment.NewLine + $"Exception for Visual Studio token provider {key} : {exceptionDictionary[key]} "; } // Throw exception if none of the token providers worked throw new Exception(message); } catch (Exception exp) { throw new Exception( $"{nameof(VisualStudioAccessTokenProvider)} not able to obtain the token for {ConnectionString} , {resource} , {authority}, {exp.Message}"); } }