4

Ok so bear with me this might take some explaining, i have a simple Account controller thus;

[RoutePrefix("api/account")]
[Authorize]
[HmacAuthentication]
public class AccountController : ApiController
{
    public async Task<IHttpActionResult> Register(UserModel userModel)
    {
        if (!this.ModelState.IsValid)
        {
            return this.BadRequest(this.ModelState);
        }

        IdentityResult result = await this._userService.RegisterUser(userModel);

        var errorResult = this.GetErrorResult(result);
        if (errorResult != null)
        {
            return errorResult;
        }

        return this.Ok();
    }
}

The HmacAuthentication attribute is here:

public class HmacAuthenticationAttribute : Attribute, IAuthenticationFilter
{
    public Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
    {
        ...

        var isValid = this.IsValidRequest(req, appId, incomingBase64Signature, nonce, requestTimeStamp);

        if (isValid.Result)
        {
            ...
        }
        ...
    }

    private async Task<bool> IsValidRequest(
        HttpRequestMessage req, 
        string appId, 
        string incomingBase64Signature, 
        string nonce, 
        string requestTimeStamp)
    {
        ...
        var user = await this.UserService.FindUser(userId); // this never gets a return value
        ...
    }
}

The method called in the UserService is this:

public async Task<ApplicationUserModel> FindUser(int id)
{
    var user = await this._userBusiness.FindAsync(id);
    return this.MapToModel(user);
}

and in the business class is this:

public async Task<ApplicationUser> FindAsync(int id)
{
    var result = await this._userManager.FindByIdAsync(id);
    return result;
}

The problem that i am facing is that when the Register method is called the HmacAuthentication attribute fires and the AuthenticateAsync filter method executes. The call in IsValidRequest to lookup a user is never actually getting a return value, if i try a request through postman it never completes.

Can anyone help with this please?

4
  • What do you do with isValid? Do you do an await on it anywhere? Commented Jul 4, 2015 at 1:57
  • 1
    Can you tell if the code is stuck at the line isValid.Result? If so, you are probably experiencing a deadlock. Read here for why you usually do not want to use Task.Result to wait on a task. Commented Jul 4, 2015 at 2:03
  • sometimes var is a bad thing, it makes you forget the await... Commented Jul 4, 2015 at 2:04
  • @sstan thanks i just assumed was the above line since the debugger never got that far, the link helped as well as the answer below Commented Jul 4, 2015 at 2:15

2 Answers 2

5
public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
    {
        ...

        var isValid = await this.IsValidRequest(req, appId, incomingBase64Signature, nonce, requestTimeStamp);

        if (isValid)
        {
            ...
        }
        ...
    }

As the compiler suggested, you can only use the await keyword inside of a method marked as async. So I have updated the signature for AuthenticateAsync accordingly. Also note that you can just check isValid now instead of doing isValid.Result since the value of the boolean will be available past the await line.

This can be a little confusing as the interface for IAuthenticationFilter doesn't specify async (interfaces can't, they can only indicate that the method will return a Task). It is up to the implementer to determine whether or not the method will simply return the task or will supply an async so that values can be awaited inside the method body.

Sign up to request clarification or add additional context in comments.

3 Comments

I tried that but i get a compile time error The 'await' operator can only be used in a method or lambda marked with the 'async' modifier the method i am calling from is on a framework interface public Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
I didnt realize you could decorate the method with async that wasnt your method i am still trying to get my head around the await/async, but that did the trick, also thanks on the comment above from @sstan it was indeed blocked on the isValid.Result assumed it was the preceeding line since i never got that far when stepping through
You can only do it on interface methods that return Tasks. async/await definitely takes a little bit to wrap your head around but it is insanely powerful and once you get into it you'll find yourself wondering how you ever coded without it :)
4

The accepted answer is correct in how to fix your problem, but doesn't explain why this happens.

This:

var isValid = this.IsValidRequest(
                  req, 
                  appId, 
                  incomingBase64Signature, 
                  nonce, 
                  requestTimeStamp);

if (isValid.Result)

Is causing your code to deadlock. You're synchronously blocking on an async method. This happens because when the compiler sees an async method, it generates a state-machine which takes everything after your first await and wraps it as a continuation. The compiler also sees if there are any synchronization contexts involved, and if there are, it attempts to marshal the continuation back onto the AspNetSynchronizationContext, which is blocked by your .Result call, effectively causing a deadlock.

This is why you shouldn't block on async code

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.