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}");
            }
        }
Пример #2
0
        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);
        }
Пример #3
0
        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}");
            }
        }