KeyVault GetSecretAsync never returns - c#

The sample code for working with KeyVault inside a web application has the following code in it:
public static async Task<string> GetSecret(string secretId)
{
var secret = await keyVaultClient.GetSecretAsync(secretId);
return secret.Value;
}
I've incorporated the KeyVaultAccessor object included in the sample in my application in order to test it. The call is executed as part of a query to one of my web api controller methods:
var secret = KeyVaultAccessor.GetSecret("https://superSecretUri").Result;
Unfortunately, the call never returns and the query hangs indefintely...
What might be the reason, because frankly I have no clue where to start...?

This is the common deadlock issue that I describe in full on my blog. In short, the async method is attempting to return to the ASP.NET request context after the await completes, but that request only allows one thread at a time, and there is already a thread in that context (the one blocked on the call to Result). So the task is waiting for the context to be free, and the thread is blocking the context until the task completes: deadlock.
The proper solution is to use await instead of Result:
var secret = await KeyVaultAccessor.GetSecret("https://superSecretUri");

I've used the following code to override the synchronization context:
var secret = Task.Run(async () => await KeyVaultAccessor.GetSecretAsync("https://superSecretUri")).Result;
This still lets you use .Result if you're in a non-async method

Unfortunately, the call never returns and the query hangs indefinitely...
You have a classic deadlock. That's why you shouldn't block on async code. Behind the scenes, the compiler generates a state-machine and captures something called a SynchronizationContext. When you synchronously block the calling thread, the attempt to post the continuation back onto that same context causes the deadlock.
Instead of synchronously blocking with .Result, make your controller async and await on the Task returned from GetSecret:
public async IHttpActionResult FooAsync()
{
var secret = await KeyVaultAccessor.GetSecretAsync("https://superSecretUri");
return Ok();
}
Side note - Async methods should follow naming conventions and be postfixed with Async.

Use the rest api...
public class AzureKeyVaultClient
{
public string GetSecret(string name, string vault)
{
var client = new RestClient($"https://{vault}.vault.azure.net/");
client.Authenticator = new AzureAuthenticator($"https://vault.azure.net");
var request = new RestRequest($"secrets/{name}?api-version=2016-10-01");
request.Method = Method.GET;
var result = client.Execute(request);
if (result.StatusCode != HttpStatusCode.OK)
{
Trace.TraceInformation($"Unable to retrieve {name} from {vault} with response {result.Content}");
var exception = GetKeyVaultErrorFromResponse(result.Content);
throw exception;
}
else
{
return GetValueFromResponse(result.Content);
}
}
public string GetValueFromResponse(string content)
{
var result = content.FromJson<keyvaultresponse>();
return result.value;
}
public Exception GetKeyVaultErrorFromResponse(string content)
{
try
{
var result = content.FromJson<keyvautlerrorresponse>();
var exception = new Exception($"{result.error.code} {result.error.message}");
if(result.error.innererror!=null)
{
var innerException = new Exception($"{result.error.innererror.code} {result.error.innererror.message}");
}
return exception;
}
catch(Exception e)
{
return e;
}
}
class keyvaultresponse
{
public string value { get; set; }
public string contentType { get; set; }
}
class keyvautlerrorresponse
{
public keyvaulterror error {get;set;}
}
class keyvaulterror
{
public string code { get; set; }
public string message { get; set; }
public keyvaulterror innererror { get; set; }
}
class AzureAuthenticator : IAuthenticator
{
private string _authority;
private string _clientId;
private string _clientSecret;
private string _resource;
public AzureAuthenticator(string resource)
{
_authority = WebConfigurationManager.AppSettings["azure:Authority"];
_clientId = WebConfigurationManager.AppSettings["azure:ClientId"];
_clientSecret = WebConfigurationManager.AppSettings["azure:ClientSecret"];
_resource = resource;
}
public AzureAuthenticator(string resource, string tennant, string clientid, string secret)
{
//https://login.microsoftonline.com/<tennant>/oauth2/oken
_authority = authority;
//azure client id (web app or native app
_clientId = clientid;
//azure client secret
_clientSecret = secret;
//vault.azure.net
_resource = resource;
}
public void Authenticate(IRestClient client, IRestRequest request)
{
var token = GetS2SAccessTokenForProdMSA().AccessToken;
//Trace.WriteLine(String.Format("obtained bearer token {0} from ADAL and adding to rest request",token));
request.AddHeader("Authorization", String.Format("Bearer {0}", token));
}
public AuthenticationResult GetS2SAccessTokenForProdMSA()
{
return GetS2SAccessToken(_authority, _resource, _clientId, _clientSecret);
}
private AuthenticationResult GetS2SAccessToken(string authority, string resource, string clientId, string clientSecret)
{
var clientCredential = new ClientCredential(clientId, clientSecret);
AuthenticationContext context = new AuthenticationContext(authority, false);
AuthenticationResult authenticationResult = context.AcquireToken(
resource,
clientCredential);
return authenticationResult;
}
}
}

This generic method can be used to override the deadlock issue:
public static T SafeAwaitResult<T>(Func<Task<T>> f)
{
return Task.Run(async () => await f()).Result;
}
Use:
SafeAwaitResult(() => foo(param1, param2));

Related

Dependency injection problem - Initializing remote service connection

In my .Net Core 3.0 app I want to use the Microsoft Graph Nuget library. I have created a connection class that authenticates my application using [MSAL][1] and then creates the connection and returns this. My idea was to inject this connection object in the constructor using Dependency Injection. However, since the method that creates the connection is async, I seem to have a problem how to use it in the constructor.
My Connect Class
public class AuthorizeGraphApi: IAuthorizeGraphApi
{
private readonly IConfiguration _config;
public AuthorizeGraphApi(IConfiguration config)
{
_config = config;
}
public async Task<GraphServiceClient> ConnectToAAD()
{
string accessToken = await GetAccessTokenFromAuthorityAsync();
var graphServiceClient = new GraphServiceClient(new DelegateAuthenticationProvider((requestMessage) => {
requestMessage
.Headers
.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
return Task.FromResult(0);
}));
return graphServiceClient;
}
private async Task<string> GetAccessTokenFromAuthorityAsync()
{
// clientid, authUri, etc removed for this example.
IConfidentialClientApplication _conn;
_conn = ConfidentialClientApplicationBuilder.Create(clientId)
.WithClientSecret(clientSecret)
.WithAuthority(new Uri(authUri))
.Build();
string[] scopes = new string[] { $"api://{clientId}/.default" };
AuthenticationResult result = null;
// AcquireTokenForClient only has async method.
result = await _conn.AcquireTokenForClient(scopes)
.ExecuteAsync();
return result.AccessToken;
}
}
My Graph Service to send requests
public class AzureIntuneService
{
private readonly IAuthorizeGraphApi _graphClient;
public AzureIntuneService(IAuthorizeGraphApi client)
{
//Gives: cannot implicitely convert to Threading.Tasks.Task.... error
_graphClient = client.ConnectToAAD();
}
public async Task<IList<string>> GetAADInformationAsync()
{
// then here, use the graphClient object for the request...
var payload = await _graphClient.Groups.Request().GetAsync();
return payload
}
}
I register the above classess in my startup as follows:
services.AddScoped<IAuthorizeGraphApi, AuthorizeGraphApi>();
The idea was that this way, I don't need to call the _graphClient in each method. How can I inject the connection object in a correct way? Or what are the best practices regarding this (injecting connection objects)?
One way would be to store a reference to the Task and make sure any public methods that use the connection are async:
public class AzureIntuneService
{
private readonly Task<GraphServiceClient> _graphClientTask;
public AzureIntuneService(IAuthorizeGraphApi client)
{
_graphClientTask = client.ConnectToAAD();
}
public async Task<IList<string>> GetAADInformationAsync()
{
var client = await _graphClientTask; // Get the client when connected
var payload = await client.Groups.Request().GetAsync();
return payload;
}
}
Constructors aren't async and should never be used to initialize anything async. The only way to workaround it is to do sync-over-async by doing a .Result which is always a problem.
In your case, the GraphServiceClient that takes in DelegateAuthenticationProvider, accepts an AuthenticateRequestAsyncDelegate. This allows you to have an async delegate to construct the client.
So now you can do
new DelegateAuthenticationProvider(async requestMessage =>
{
string accessToken = await GetAccessTokenFromAuthorityAsync();
//rest of code here
}
)
and this allows you to change your ConnectToAAD signature to just return a GraphServiceClient and not a Task<GraphServiceClient>.
When you need async data you have to look away from the regular constructor and create a factory method (private static function). Something like below:
public sealed class MyClass
{
private MyData asyncData;
private MyClass() { ... }
private async Task<MyClass> InitializeAsync()
{
asyncData = await GetDataAsync();
return this;
}
public static Task<MyClass> CreateAsync()
{
var ret = new MyClass();
return ret.InitializeAsync();
}
}
public static async Task UseMyClassAsync()
{
MyClass instance = await MyClass.CreateAsync();
...
}
More here: https://blog.stephencleary.com/2013/01/async-oop-2-constructors.html

Xamarin.Forms OAuth Token authentication

I tried make it as you suggested (answer below this). Unfortunately still I have this problem.
This is my new TokenService.cs:
class TokenService
{
TokenKeeper tokenkeeper;
public TokenService()
{
tokenkeeper = new TokenKeeper();
}
public async void AwaitGetToken()
{
tokenkeeper = new TokenKeeper();
tokenkeeper = await GetToken();
}
private async Task<TokenKeeper> GetToken()
{
string stringuri = string.Format("{0}/token", RestAccess.HostUrl);
var dict = new Dictionary<string, string>();
dict.Add("grant_type", "password");
dict.Add("username", RestAccess.User);
dict.Add("password", RestAccess.Pass);
dict.Add("client_id", RestAccess.Client_ID);
dict.Add("client_secret", RestAccess.Client_secret);
tokenkeeper=new TokenKeeper();
var httpclient = new HttpClient();
try
{
var req = new HttpRequestMessage(HttpMethod.Post, stringuri) { Content = new FormUrlEncodedContent(dict) };
var res = await httpclient.SendAsync(req);
if (res.IsSuccessStatusCode)
{
var content = await res.Content.ReadAsStringAsync();
tokenkeeper = JsonConvert.DeserializeObject<TokenKeeper>(content);
return tokenkeeper;
}
return tokenkeeper;
}
catch
{
return tokenkeeper;
}
finally
{
httpclient.CancelPendingRequests();
httpclient.Dispose();
}
}
}
My tokenkeeper is simple class:
public class TokenKeeper
{
public string access_token { get; set; }
public string refresh_token { get; set; }
public TokenKeeper()
{
access_token = "";
refresh_token = "";
}
}
In my calling code I have as below:
...
tokenservice.AwaitGetToken();
GetXFMapUserFromAzureAndSetVariable(RestAccess.User);
_navigationService.NavigateAsync("ZleceniaListContentPage", par);
...
tokenservice.AwaitGetToken() and GEtXFMapUserFromAzureAndSetVariable(RestAccess.User) they are similar. Both await but tokenservice.AwaitGetToken() is POST and GEtXFMapUserFromAzureAndSetVariable is GET.
GEtXFMapUserFromAzureAndSetVariable is working correctly but if I call tokenservice.AwaitGetToken() the aplication get the token but before it's continue. At the end the instruction GEtXFMapUserFromAzureAndSetVariable it is being done tokenservice.AwaitGetToken() responds.
How can I wait with call GEtXFMapUserFromAzureAndSetVariable until I receive a reply from tokenservice.AwaitGetToken() ???
Delete your AwaitGetToken method, it is useless.
Now make you GetToken method public and call it with await:
await tokenService.GetToken();
await GetXFMapUserFromAzureAndSetVariable(RestAccess.User);
await _navigationService.NavigateAsync("ZleceniaListContentPage", par);
Basically you always need to await methods returning Task, or it will run in a parallel fashion, and you will lose consistency in your code.
You seem very confused by Task and async/await, I will strongly advise you to read tutorials and docs on it:
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/

How to wait for async method to finish when calling a external service in .net core?

I have seen some of the existing questions regarding async waiting for completion , However for me none of the solution work.
I am using a C# wrapper for connecting to sales force https://github.com/developerforce/Force.com-Toolkit-for-NET/
In the below method i want to wait for the method UsernamePasswordAsync to complete execution so that i can get the values from the auth object.
public async Task<Token> GetTokenForSalesForce()
{
Token token = null;
try
{
var auth = new AuthenticationClient();
await auth.UsernamePasswordAsync(configuration.Value.ClientId, configuration.Value.ClientSecert,
configuration.Value.SFUsername, configuration.Value.SFPassword,
configuration.Value.SFBaseUrl);
if (!string.IsNullOrEmpty(auth.AccessToken) && !string.IsNullOrEmpty(auth.InstanceUrl))
{
token = new Token
{
BearerToken = auth.AccessToken,
InstanceURL = auth.InstanceUrl,
ApiVersion = auth.ApiVersion
};
}
}
catch (Exception ex)
{
throw ex;
}
return token;
}
public async Task<List<SFDashboardResponse>> GetOrderCountFromSalesForce(Token token)
{
List<SFDashboardResponse> sFDashboardResponses = new List<SFDashboardResponse>();
try
{
var client = new ForceClient(token.InstanceURL, token.BearerToken, token.ApiVersion);
var response = await client.QueryAsync<SFDashboardResponse>("SELECT something ");
var records = response.Records;
}
catch(Exception e)
{
}
return sFDashboardResponses;
}
The signature in the library is
public async Task WebServerAsync(string clientId, string clientSecret, string redirectUri, string code, string tokenRequestEndpointUrl)
{
}
The problem is while the method wait for await to be first execute another thread executes the other part of the orignal caller.
I call it from here
public IActionResult post()
{
var authtoken = _salesForceService.GetTokenForSalesForce();
var response = _salesForceService.GetOrderCountFromSalesForce(authtoken.Result);
DashboardModel dashboardModel = null;
if (authtoken.Status == TaskStatus.RanToCompletion)
{
fill the object
}
return Ok(dashboardModel);
}
You can wrap the IActionResult with a Task and await on the tasks below.
public async Task<IActionResult> post()
{
var authtoken = await _salesForceService.GetTokenForSalesForce();
var response = await _salesForceService.GetOrderCountFromSalesForce(authtoken);
DashboardModel dashboardModel = //fill the object
return Ok(dashboardModel);
}
At least this is what you are asking for as far as I understand, if its another problem let me know.
EDIT 1:
This is just my suggestion/opinion.
Personally I dont really like having the code wrapped in try-catch everywhere, this way the code can be hard to read and maintain. You really should consider centralizing exception handling in one place, you could have a base controller or just a middleware like this one:
public class ErrorHandlingMiddleware
{
private readonly RequestDelegate _next;
public ErrorHandlingMiddleware(RequestDelegate next)
{
this._next = next;
}
public async Task Invoke(HttpContext context, ILogger logger)
{
try
{
await _next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex, logger);
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception exception, ILogger logger)
{
logger.Log(exception);
//do something
return context.Response.WriteAsync(... something ...); //Maybe some JSON message or something
}
}
The you just register it as a middleware in the Configure method like below:
app.UseMiddleware<ErrorHandlingMiddleware>();

API Integration Test for Login controller using Identity server4 in asp.net core

I am having issues with testing Login Controller using IdentityServer4. It throws the following error:
{System.Net.Http.WinHttpException (0x80072EFD): A connection with the server could not be established
I am trying to generate the access Token using ResourceOwnerPassword, for which I have implemented IResourceOwnerPasswordValidator. I get the error in UserAccessToken.cs class when I call the RequestResourcePasswordAsync.
I am pretty sure it is because of the handler. Because if I use a handler in my test class and call the TokenClient with that handler I do get access Token but then I cannot test my Login Controller.
LoginController.cs
[HttpPost]
public async Task<IActionResult> Login([FromBody]LoginViewModel user)
{
var accessToken = await UserAccessToken.GenerateTokenAsync(user.Username, user.Password);
var loginToken = JsonConvert.DeserializeObject(accessToken);
return Ok(loginToken);
}
UserAccessToken.cs
public async Task<string> GenerateTokenAsync(string username, string password)
{
var tokenUrl = "http://localhost:5000/connect/token";
var tokenClient = new TokenClient(tokenUrl,"ClientId","ClientPassword");
var tokenResponse = await tokenClient.RequestResourceOwnerPasswordAsync(username, password, SecurityConfig.PublicApiResourceId);
if (tokenResponse.IsError)
{
throw new AuthenticationFailedException(tokenResponse.Error);
}
return tokenResponse.Json.ToString();
}
TestClass.cs
[Fact]
public async Task Login()
{
var client = _identityServer.CreateClient();
var data = new StringContent(JsonConvert.SerializeObject(new LoginViewModel { Username = "1206", Password = "5m{F?Hk92/Qj}n7Lp6" }), Encoding.UTF8, "application/json");
var dd = await client.PostAsync("http://localhost:5000/login", data);
var ss = dd;
}
IdentityServerSetup.cs //Integration Test Setup
public class IdentityServerSetup
{
private TestServer _identityServer;
private const string TokenEndpoint = "http://localhost:5000/connect/token";
public HttpMessageHandler _handler;
//IF I use this code I do get a AccessToken
public async Task<string> GetAccessTokenForUser(string userName, string password, string clientId, string clientSecret, string apiName = "integrapay.api.public")
{
var client = new TokenClient(TokenEndpoint, clientId, clientSecret, innerHttpMessageHandler: _handler);
var response = await client.RequestResourceOwnerPasswordAsync(userName, password, apiName);
return response.AccessToken;
}
}
Well, you have already answered the question yourself: The problem is with the HttpHandler the TokenClient uses. It should use the one provided by the TestServer to successfully communicate with it instead of doing actual requests to localhost.
Right now, UserAccessToken requires a TokenClient. This is a dependency of your class, so you should refactor the code to pass in a TokenClient instead of generating it yourself. This pattern is called Dependency Injection and is ideal for cases like yours, where you might have different requirements in your tests than in your production setup.
You could make the code look like this:
UserAccessToken.cs
public class UserAccessToken
{
private readonly TokenClient _tokenClient;
public UserAccessToken(TokenClient tokenClient)
{
_tokenClient = tokenClient;
}
public async Task<string> GenerateTokenAsync(string username, string password)
{
var tokenUrl = "http://localhost:5000/connect/token";
var tokenResponse = await _tokenClient.RequestResourceOwnerPasswordAsync(username, password, SecurityConfig.PublicApiResourceId);
if (tokenResponse.IsError)
{
throw new AuthenticationFailedException(tokenResponse.Error);
}
return tokenResponse.Json.ToString();
}
}
TestHelpers.cs
public static class TestHelpers
{
private static TestServer _testServer;
private static readonly object _initializationLock = new object();
public static TestServer GetTestServer()
{
if (_testServer == null)
{
InitializeTestServer();
}
return _testServer;
}
private static void InitializeTestServer()
{
lock (_initializationLock)
{
if (_testServer != null)
{
return;
}
var webHostBuilder = new WebHostBuilder()
.UseStartup<IntegrationTestsStartup>();
var testServer = new TestServer(webHostBuilder);
var initializationTask = InitializeDatabase(testServer);
initializationTask.ConfigureAwait(false);
initializationTask.Wait();
testServer.BaseAddress = new Uri("http://localhost");
_testServer = testServer;
}
}
}
IntegrationTestsStartup.cs
public class IntegrationTestsStartup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<TokenClient>(() =>
{
var handler = TestUtilities.GetTestServer().CreateHandler();
var client = new TokenClient(TokenEndpoint, clientId, clientSecret, innerHttpMessageHandler: handler);
return client;
};
services.AddTransient<UserAccessToken>();
}
}
LoginController.cs
public class LoginController : Controller
{
private readonly UserAccessToken _userAccessToken;
public LoginController(UserAccessToken userAccessToken)
{
_userAccessToken = userAccessToken;
}
[HttpPost]
public async Task<IActionResult> Login([FromBody]LoginViewModel user)
{
var accessToken = await _userAccessToken .GenerateTokenAsync(user.Username, user.Password);
var loginToken = JsonConvert.DeserializeObject(accessToken);
return Ok(loginToken);
}
}
Here's one of my GitHub projects that makes use of the TestServer class and shows how I'm using it. It's not using IdentityServer4, though.

DEADLOCK Xamarin

I am trying to develop my first Xamarin App.
I have two classes for webservices:
RestClient: which makes the request and get the json String
Helper Class: which should get the Json String and deserialized it to the Object Type.
I know that the Wait Method is not the best option, but I have tried so many different suggested versions but it doesn't work. Every try ended in a Deadlock. Every Thread works in the Background. How can I get my Data back to the UI?
code of my RestClient Class:
class RestClient
{
public static string base_url = #"our Restservice address";
// public string completeUrl { get; set; }
HttpClient client;
public RestClient()
{
client = new HttpClient();
client.BaseAddress = new Uri(base_url);
//client.MaxResponseContentBufferSize = 256000;
}
public async Task<String> GetData(string endpoint)
{
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.GetAsync(endpoint);
if (response.IsSuccessStatusCode)
{
string result = await response.Content.ReadAsStringAsync();
return result;
}
else
{
return null;
}
}
Code of my Helper Class
public SupplierHelper()
{
}
public async Task<Suppliers> getData()
{
RestClient restClient = new RestClient();
string result = await restClient.GetData("suppliers/13");
return JsonConvert.DeserializeObject<Suppliers>(result);
}
Code of my VievModelClass
public class AccountViewModel : BaseViewModel
{
public static SupplierHelper supHelper;
public static Suppliers sup;
public string Name { set; get; }
public string Address { set; get; }
public AccountViewModel()
{
loadSupplier().Wait();
}
public async Task loadSupplier()
{
supHelper = new SupplierHelper();
sup = await supHelper.getData();
}
}
Task.Run(loadSupplier).Wait(); will solve your problem.
Your deadlock is caused by async method trying to execute continuation on caller thread, but caller thread is blocked until that async method completes.
.Wait() is worse than "not the best option" - it'll actively cause a circular wait in any environment that has a synchronization context. Read this article for more details.
If you must call this to init the object correctly, you could use an async Factory method or something.

Categories