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}"); } }
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) { 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}"); } }
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) { 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}"); } }