Neil Neil - 2 months ago 6
C# Question

Why does async/await method in attribute never return

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?

Answer
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.