Beispiel #1
0
        /// <summary>
        /// Used to get authentication result for Integrated Windows Authentication scenario, where the token may already be in ADAL cache.
        /// </summary>
        /// <param name="authority"></param>
        /// <param name="resource"></param>
        /// <param name="clientId"></param>
        /// <returns></returns>
        public async Task <AppAuthenticationResult> AcquireTokenSilentAsync(string authority, string resource, string clientId)
        {
            AuthenticationContext authenticationContext = new AuthenticationContext(authority);
            var authResult = await authenticationContext.AcquireTokenSilentAsync(resource, clientId).ConfigureAwait(false);

            return(AppAuthenticationResult.Create(authResult));
        }
        public override async Task <AppAuthenticationResult> GetAuthResultAsync(string resource, string authority,
                                                                                CancellationToken cancellationToken = default(CancellationToken))
        {
            try
            {
                // Validate resource, since it gets sent as a command line argument to Azure CLI
                ValidationHelper.ValidateResource(resource);

                // Execute Azure CLI to get token
                string response = await _processManager.ExecuteAsync(new Process { StartInfo = GetProcessStartInfo(resource) }, cancellationToken).ConfigureAwait(false);

                // Parse the response
                TokenResponse tokenResponse = TokenResponse.Parse(response);

                var accessToken = tokenResponse.AccessToken2;

                AccessToken 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;
                }

                return(AppAuthenticationResult.Create(tokenResponse));
            }
            catch (Exception exp)
            {
                throw new AzureServiceTokenProviderException(ConnectionString, resource, authority,
                                                             $"{AzureServiceTokenProviderException.AzureCliUsed} {AzureServiceTokenProviderException.GenericErrorMessage} {exp.Message}");
            }
        }
Beispiel #3
0
        /// <summary>
        /// Used to get authentication for client credentials flow using a client certificate.
        /// </summary>
        /// <param name="authority"></param>
        /// <param name="resource"></param>
        /// <param name="clientCertificate"></param>
        /// <returns></returns>
        public async Task <AppAuthenticationResult> AcquireTokenAsync(string authority, string resource, IClientAssertionCertificate clientCertificate)
        {
            var authenticationContext = GetAuthenticationContext(authority);
            var authResult            = await authenticationContext.AcquireTokenAsync(resource, clientCertificate, true).ConfigureAwait(false);

            return(AppAuthenticationResult.Create(authResult));
        }
Beispiel #4
0
        /// <summary>
        /// Used to get authentication result for Integrated Windows Authentication scenario.
        /// </summary>
        /// <param name="authority"></param>
        /// <param name="resource"></param>
        /// <param name="clientId"></param>
        /// <param name="userCredential"></param>
        /// <returns></returns>
        public async Task <AppAuthenticationResult> AcquireTokenAsync(string authority, string resource, string clientId, UserCredential userCredential)
        {
            var authenticationContext = GetAuthenticationContext(authority);
            var authResult            = await authenticationContext.AcquireTokenAsync(resource, clientId, userCredential).ConfigureAwait(false);

            return(AppAuthenticationResult.Create(authResult));
        }
Beispiel #5
0
        internal static AppAuthenticationResult Create(TokenResponse response, CultureInfo datetimeCulture = null)
        {
            if (response == null)
            {
                throw new ArgumentNullException(nameof(response));
            }

            string         expiresOnString = response.ExpiresOn ?? response.ExpiresOn2;
            DateTimeOffset expiresOn       = DateTimeOffset.MinValue;

            double seconds;

            if (double.TryParse(expiresOnString, out seconds))
            {
                expiresOn = AppAuthentication.AccessToken.UnixTimeEpoch.AddSeconds(seconds);
            }
            else if (!DateTimeOffset.TryParse(expiresOnString, datetimeCulture, DateTimeStyles.None, out expiresOn))
            {
                throw new ArgumentException("ExpiresOn in token response could not be parsed");
            }

            var result = new AppAuthenticationResult()
            {
                AccessToken = response.AccessToken ?? response.AccessToken2,
                ExpiresOn   = expiresOn,
                Resource    = response.Resource,
                TokenType   = response.TokenType ?? response.TokenType2
            };

            return(result);
        }
        internal static AppAuthenticationResult Create(TokenResponse response, TokenResponse.DateFormat dateFormat)
        {
            if (response == null)
            {
                throw new ArgumentNullException(nameof(response));
            }

            string         expiresOnString = response.ExpiresOn ?? response.ExpiresOn2;
            DateTimeOffset expiresOn       = DateTimeOffset.MinValue;

            switch (dateFormat)
            {
            case TokenResponse.DateFormat.DateTimeString:
                expiresOn = DateTimeOffset.Parse(expiresOnString);
                break;

            case TokenResponse.DateFormat.Unix:
                double seconds = double.Parse(expiresOnString);
                expiresOn = AppAuthentication.AccessToken.UnixTimeEpoch.AddSeconds(seconds);
                break;
            }

            var result = new AppAuthenticationResult()
            {
                AccessToken = response.AccessToken ?? response.AccessToken2,
                ExpiresOn   = expiresOn,
                Resource    = response.Resource,
                TokenType   = response.TokenType ?? response.TokenType2
            };

            return(result);
        }
Beispiel #7
0
        internal static AppAuthenticationResult Create(AuthenticationResult authResult)
        {
            if (authResult == null)
            {
                throw new ArgumentNullException(nameof(authResult));
            }

            var result = new AppAuthenticationResult()
            {
                AccessToken = authResult.AccessToken,
                ExpiresOn   = authResult.ExpiresOn
            };

            return(result);
        }
Beispiel #8
0
        // For unit testing
        internal static AppAuthenticationResult Create(string accessToken)
        {
            if (string.IsNullOrEmpty(accessToken))
            {
                throw new ArgumentNullException(nameof(accessToken));
            }

            var tokenObj = AppAuthentication.AccessToken.Parse(accessToken);

            var result = new AppAuthenticationResult
            {
                AccessToken = accessToken,
                ExpiresOn   = AppAuthentication.AccessToken.UnixTimeEpoch.AddSeconds(tokenObj.ExpiryTime)
            };

            return(result);
        }
Beispiel #9
0
        public override async Task <AppAuthenticationResult> GetAuthResultAsync(string resource, string authority,
                                                                                CancellationToken cancellationToken = default(CancellationToken))
        {
            // Use the httpClient specified in the constructor. If it was not specified in the constructor, use the default httpClient.
            HttpClient httpClient = _httpClient ?? DefaultHttpClient;

            try
            {
                // Check if App Services MSI is available. If both these environment variables are set, then it is.
                string msiEndpoint = Environment.GetEnvironmentVariable("MSI_ENDPOINT");
                string msiSecret   = Environment.GetEnvironmentVariable("MSI_SECRET");
                var    isAppServicesMsiAvailable = !string.IsNullOrWhiteSpace(msiEndpoint) && !string.IsNullOrWhiteSpace(msiSecret);

                // if App Service MSI is not available then Azure VM IMDS must be available, verify with a probe request
                if (!isAppServicesMsiAvailable)
                {
                    using (var internalTokenSource = new CancellationTokenSource())
                        using (var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(internalTokenSource.Token, cancellationToken))
                        {
                            HttpRequestMessage imdsProbeRequest = new HttpRequestMessage(HttpMethod.Get, AzureVmImdsEndpoint);

                            try
                            {
                                internalTokenSource.CancelAfter(AzureVmImdsTimeout);
                                await httpClient.SendAsync(imdsProbeRequest, linkedTokenSource.Token).ConfigureAwait(false);
                            }
                            catch (OperationCanceledException)
                            {
                                // request to IMDS timed out (internal cancellation token cancelled), neither Azure VM IMDS nor App Services MSI are available
                                if (internalTokenSource.Token.IsCancellationRequested)
                                {
                                    throw new HttpRequestException();
                                }

                                throw;
                            }
                        }
                }

                // If managed identity is specified, include client ID parameter in request
                string clientIdParameterName = isAppServicesMsiAvailable ? "clientid" : "client_id";
                string clientIdParameter     = _managedIdentityClientId != default(string)
                    ? $"&{clientIdParameterName}={_managedIdentityClientId}"
                    : string.Empty;

                // Craft request as per the MSI protocol
                var requestUrl = isAppServicesMsiAvailable
                    ? $"{msiEndpoint}?resource={resource}{clientIdParameter}&api-version=2017-09-01"
                    : $"{AzureVmImdsEndpoint}?resource={resource}{clientIdParameter}&api-version=2018-02-01";

                Func <HttpRequestMessage> getRequestMessage = () =>
                {
                    HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrl);

                    if (isAppServicesMsiAvailable)
                    {
                        request.Headers.Add("Secret", msiSecret);
                    }
                    else
                    {
                        request.Headers.Add("Metadata", "true");
                    }

                    return(request);
                };

                HttpResponseMessage response = await httpClient.SendAsyncWithRetry(getRequestMessage, cancellationToken).ConfigureAwait(false);

                // If the response is successful, it should have JSON response with an access_token field
                if (response.IsSuccessStatusCode)
                {
                    PrincipalUsed.IsAuthenticated = true;

                    string jsonResponse = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

                    // Parse the JSON response
                    TokenResponse tokenResponse = TokenResponse.Parse(jsonResponse);

                    AccessToken token = AccessToken.Parse(tokenResponse.AccessToken);

                    // If token is null, then there has been a parsing issue, which means the access token format has changed
                    if (token != null)
                    {
                        PrincipalUsed.AppId    = token.AppId;
                        PrincipalUsed.TenantId = token.TenantId;
                    }

                    TokenResponse.DateFormat expectedDateFormat = isAppServicesMsiAvailable
                        ? TokenResponse.DateFormat.DateTimeString
                        : TokenResponse.DateFormat.Unix;

                    return(AppAuthenticationResult.Create(tokenResponse, expectedDateFormat));
                }

                string exceptionText = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

                throw new Exception($"MSI ResponseCode: {response.StatusCode}, Response: {exceptionText}");
            }
            catch (HttpRequestException)
            {
                throw new AzureServiceTokenProviderException(ConnectionString, resource, authority,
                                                             $"{AzureServiceTokenProviderException.ManagedServiceIdentityUsed} {AzureServiceTokenProviderException.RetryFailure} {AzureServiceTokenProviderException.MsiEndpointNotListening}");
            }
            catch (Exception exp)
            {
                throw new AzureServiceTokenProviderException(ConnectionString, resource, authority,
                                                             $"{AzureServiceTokenProviderException.ManagedServiceIdentityUsed} {AzureServiceTokenProviderException.GenericErrorMessage} {exp.Message}");
            }
        }
        public override async Task <AppAuthenticationResult> GetAuthResultAsync(string resource, string authority,
                                                                                CancellationToken cancellationToken = default(CancellationToken))
        {
            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.
                Dictionary <string, string> exceptionDictionary = new Dictionary <string, string>();

                foreach (var startInfo in processStartInfos)
                {
                    try
                    {
                        // For each of them, try to get token
                        string response = await _processManager
                                          .ExecuteAsync(new Process { StartInfo = startInfo }, cancellationToken)
                                          .ConfigureAwait(false);

                        TokenResponse tokenResponse = TokenResponse.Parse(response);

                        AccessToken token = AccessToken.Parse(tokenResponse.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;
                        }

                        return(AppAuthenticationResult.Create(tokenResponse));
                    }
                    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
                string message = string.Empty;

                // Include exception details for each token provider that was tried
                foreach (string 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 AzureServiceTokenProviderException(ConnectionString, resource, authority,
                                                             $"{AzureServiceTokenProviderException.VisualStudioUsed} {AzureServiceTokenProviderException.GenericErrorMessage} {exp.Message}");
            }
        }
        public override async Task <AppAuthenticationResult> GetAuthResultAsync(string resource, string authority,
                                                                                CancellationToken cancellationToken = default)
        {
            MsiEnvironment?msiEnvironment = null;

            try
            {
                // Check if App Services MSI or Service Fabric MSI are available. Both use different set of shared env vars.
                var msiEndpoint = Environment.GetEnvironmentVariable("IDENTITY_ENDPOINT");
                var msiHeader   = Environment.GetEnvironmentVariable("IDENTITY_HEADER");
                _serviceFabricMsiThumbprint = Environment.GetEnvironmentVariable("IDENTITY_SERVER_THUMBPRINT"); // only in Service Fabric, needed to create HttpClient
                var serviceFabricApiVersion = Environment.GetEnvironmentVariable("IDENTITY_API_VERSION");       // only in Service Fabric

                var endpointAndHeaderAvailable       = !string.IsNullOrWhiteSpace(msiEndpoint) && !string.IsNullOrWhiteSpace(msiHeader);
                var thumbprintAndApiVersionAvailable = !string.IsNullOrWhiteSpace(_serviceFabricMsiThumbprint) && !string.IsNullOrWhiteSpace(serviceFabricApiVersion);

                if (endpointAndHeaderAvailable)
                {
                    msiEnvironment = thumbprintAndApiVersionAvailable
                        ? MsiEnvironment.ServiceFabric
                        : MsiEnvironment.AppServices;
                }

                // if App Service and Service Fabric MSI is not available then Azure VM IMDS may be available, test with a probe request
                if (msiEnvironment == null)
                {
                    using (var internalTokenSource = new CancellationTokenSource())
                        using (var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(internalTokenSource.Token, cancellationToken))
                        {
                            string             probeRequestUrl  = $"{ImdsEndpoint}{ImdsInstanceRoute}?api-version={ImdsInstanceApiVersion}";
                            HttpRequestMessage imdsProbeRequest = new HttpRequestMessage(HttpMethod.Get, probeRequestUrl);
                            imdsProbeRequest.Headers.Add(AzureVMImdsHeader, "true");

                            try
                            {
                                internalTokenSource.CancelAfter(AzureVmImdsProbeTimeout);
                                await HttpClient.SendAsync(imdsProbeRequest, linkedTokenSource.Token).ConfigureAwait(false);

                                msiEnvironment = MsiEnvironment.Imds;
                            }
                            catch (OperationCanceledException)
                            {
                                // request to IMDS timed out (internal cancellation token cancelled), neither Azure VM IMDS nor App Services MSI are available
                                if (internalTokenSource.Token.IsCancellationRequested)
                                {
                                    throw new AzureServiceTokenProviderException(ConnectionString, resource, authority,
                                                                                 $"{AzureServiceTokenProviderException.ManagedServiceIdentityUsed} {AzureServiceTokenProviderException.MetadataEndpointNotListening}");
                                }

                                throw;
                            }
                        }
                }

#if net452
                if (msiEnvironment == MsiEnvironment.ServiceFabric)
                {
                    throw new AzureServiceTokenProviderException(ConnectionString, resource, authority,
                                                                 $"{AzureServiceTokenProviderException.ManagedServiceIdentityUsed} Service Fabric MSI not supported on .NET 4.5.2");
                }
#endif

                // If managed identity is specified, include client ID parameter in request
                string clientIdParameter = _managedIdentityClientId != default
                    ? $"&client_id={_managedIdentityClientId}"
                    : string.Empty;

                // endpoint and API version dependent on environment
                string endpoint = null, apiVersion = null;
                switch (msiEnvironment)
                {
                case MsiEnvironment.AppServices:
                    endpoint   = msiEndpoint;
                    apiVersion = AppServicesApiVersion;
                    break;

                case MsiEnvironment.Imds:
                    endpoint   = $"{ImdsEndpoint}{ImdsTokenRoute}";
                    apiVersion = ImdsTokenApiVersion;
                    break;

                case MsiEnvironment.ServiceFabric:
                    endpoint   = msiEndpoint;
                    apiVersion = serviceFabricApiVersion;
                    break;
                }

                // Craft request as per the MSI protocol
                var requestUrl = $"{endpoint}?resource={resource}{clientIdParameter}&api-version={apiVersion}";

                HttpRequestMessage getRequestMessage()
                {
                    HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrl);

                    switch (msiEnvironment)
                    {
                    case MsiEnvironment.AppServices:
                        request.Headers.Add(AppServicesHeader, msiHeader);
                        break;

                    case MsiEnvironment.Imds:
                        request.Headers.Add(AzureVMImdsHeader, "true");
                        break;

                    case MsiEnvironment.ServiceFabric:
                        request.Headers.Add(ServiceFabricHeader, msiHeader);
                        break;
                    }

                    return(request);
                }

                HttpResponseMessage response;
                try
                {
                    response = await HttpClient.SendAsyncWithRetry(getRequestMessage, _retryTimeoutInSeconds, cancellationToken).ConfigureAwait(false);
                }
                catch (HttpRequestException)
                {
                    throw new AzureServiceTokenProviderException(ConnectionString, resource, authority,
                                                                 $"{AzureServiceTokenProviderException.ManagedServiceIdentityUsed} {AzureServiceTokenProviderException.RetryFailure} {AzureServiceTokenProviderException.MsiEndpointNotListening}");
                }

                // If the response is successful, it should have JSON response with an access_token field
                if (response.IsSuccessStatusCode)
                {
                    PrincipalUsed.IsAuthenticated = true;

                    string jsonResponse = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

                    // Parse the JSON response
                    TokenResponse tokenResponse = TokenResponse.Parse(jsonResponse);

                    AccessToken token = AccessToken.Parse(tokenResponse.AccessToken);

                    // If token is null, then there has been a parsing issue, which means the access token format has changed
                    if (token != null)
                    {
                        PrincipalUsed.AppId    = token.AppId;
                        PrincipalUsed.TenantId = token.TenantId;
                    }

                    return(AppAuthenticationResult.Create(tokenResponse));
                }

                string errorStatusDetail = response.IsRetryableStatusCode()
                    ? AzureServiceTokenProviderException.RetryFailure
                    : AzureServiceTokenProviderException.NonRetryableError;

                string errorText = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

                throw new Exception($"{errorStatusDetail} MSI ResponseCode: {response.StatusCode}, Response: {errorText}");
            }
            catch (Exception exp)
            {
                if (exp is AzureServiceTokenProviderException)
                {
                    throw;
                }

                throw new AzureServiceTokenProviderException(ConnectionString, resource, authority,
                                                             $"{AzureServiceTokenProviderException.ManagedServiceIdentityUsed} {AzureServiceTokenProviderException.GenericErrorMessage} {exp.Message}");
            }
        }
Beispiel #12
0
        /// <summary>
        /// Get access token by authenticating to Azure AD using Integrated Windows Authentication (IWA),
        /// when domain is synced with Azure AD tenant.
        /// </summary>
        /// <param name="resource">Resource to access.</param>
        /// <param name="authority">Authority where resource is present.</param>
        /// <returns></returns>
        public override async Task <AppAuthenticationResult> GetAuthResultAsync(string resource, string authority,
                                                                                CancellationToken cancellationToken = default)
        {
            // If authority is not specified, start with common. Once known, after the first time token is acquired, use that.
            if (string.IsNullOrWhiteSpace(authority))
            {
                authority = string.IsNullOrEmpty(_currentUserTenant) ? $"{_azureAdInstance}common" : $"{_azureAdInstance}{_currentUserTenant}";
            }

            // Use ADAL's default token cache, instead of file based cache.
            // This prevents dependency on file and DPAPI, and enables service account scenarios.
            AppAuthenticationResult authResult = null;

            try
            {
                // See if token is present in cache
                authResult = await _authenticationContext.AcquireTokenSilentAsync(authority, resource, ClientId).ConfigureAwait(false);
            }
            catch
            {
                // If fails, use AcquireTokenAsync
            }

            // If token not in cache, acquire it
            if (authResult == null)
            {
                // This causes ADAL to use IWA
                UserCredential userCredential = new UserCredential();

                try
                {
                    authResult = await _authenticationContext.AcquireTokenAsync(authority, resource, ClientId, userCredential).ConfigureAwait(false);
                }
                catch (Exception exp)
                {
                    string message = $"{AzureServiceTokenProviderException.ActiveDirectoryIntegratedAuthUsed} " +
                                     $"{AzureServiceTokenProviderException.GenericErrorMessage} " +
                                     $"{exp.Message}";

                    if (exp.InnerException != null)
                    {
                        message += $"Inner Exception : {exp.InnerException.Message}";
                    }

                    throw new AzureServiceTokenProviderException(ConnectionString, resource, authority, message);
                }
            }

            var accessToken = authResult?.AccessToken;

            if (accessToken != null)
            {
                AccessToken token = AccessToken.Parse(accessToken);

                PrincipalUsed.UserPrincipalName = !string.IsNullOrEmpty(token.Upn) ? token.Upn : token.Email;
                PrincipalUsed.TenantId          = _currentUserTenant = token.TenantId;
                PrincipalUsed.IsAuthenticated   = true;

                return(authResult);
            }

            // If result is null, token could not be acquired, and no exception was thrown.
            throw new AzureServiceTokenProviderException(ConnectionString, resource, authority,
                                                         AzureServiceTokenProviderException.GenericErrorMessage);
        }
Beispiel #13
0
        public override async Task <AppAuthenticationResult> GetAuthResultAsync(string resource, string authority)
        {
            try
            {
                // Check if App Services MSI is available. If both these environment variables are set, then it is.
                string msiEndpoint = Environment.GetEnvironmentVariable("MSI_ENDPOINT");
                string msiSecret   = Environment.GetEnvironmentVariable("MSI_SECRET");
                var    isAppServicesMsiAvailable = !string.IsNullOrWhiteSpace(msiEndpoint) && !string.IsNullOrWhiteSpace(msiSecret);

                // If managed identity is specified, include client_id parameter in request
                string clientIdParameter = _managedIdentityClientId != default(string)
                    ? $"&client_id={_managedIdentityClientId}"
                    : string.Empty;

                // Craft request as per the MSI protocol
                var requestUrl = isAppServicesMsiAvailable
                    ? $"{msiEndpoint}?resource={resource}&api-version=2017-09-01"
                    : $"{AzureVmIdmsEndpoint}?resource={resource}{clientIdParameter}&api-version=2018-02-01";

                // Use the httpClient specified in the constructor. If it was not specified in the constructor, use the default httpclient.
                HttpClient httpClient = _httpClient ?? DefaultHttpClient;

                HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrl);

                if (isAppServicesMsiAvailable)
                {
                    request.Headers.Add("Secret", msiSecret);
                }
                else
                {
                    request.Headers.Add("Metadata", "true");
                }

                HttpResponseMessage response = await httpClient.SendAsync(request).ConfigureAwait(false);

                // If the response is successful, it should have JSON response with an access_token field
                if (response.IsSuccessStatusCode)
                {
                    PrincipalUsed.IsAuthenticated = true;

                    string jsonResponse = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

                    // Parse the JSON response
                    TokenResponse tokenResponse = TokenResponse.Parse(jsonResponse);

                    AccessToken token = AccessToken.Parse(tokenResponse.AccessToken);

                    // If token is null, then there has been a parsing issue, which means the access token format has changed
                    if (token != null)
                    {
                        PrincipalUsed.AppId    = token.AppId;
                        PrincipalUsed.TenantId = token.TenantId;
                    }

                    TokenResponse.DateFormat expectedDateFormat = isAppServicesMsiAvailable
                        ? TokenResponse.DateFormat.DateTimeString
                        : TokenResponse.DateFormat.Unix;

                    return(AppAuthenticationResult.Create(tokenResponse, expectedDateFormat));
                }

                string exceptionText = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

                throw new Exception($"MSI ResponseCode: {response.StatusCode}, Response: {exceptionText}");
            }
            catch (HttpRequestException)
            {
                throw new AzureServiceTokenProviderException(ConnectionString, resource, authority,
                                                             $"{AzureServiceTokenProviderException.ManagedServiceIdentityUsed} {AzureServiceTokenProviderException.MsiEndpointNotListening}");
            }
            catch (Exception exp)
            {
                throw new AzureServiceTokenProviderException(ConnectionString, resource, authority,
                                                             $"{AzureServiceTokenProviderException.ManagedServiceIdentityUsed} {AzureServiceTokenProviderException.GenericErrorMessage} {exp.Message}");
            }
        }