0

I am trying to get an access token from Azure AD, and the async call never finishes. I am not sure what I am doing wrong, maybe someone can point me in the right direction. This is the code:

private static void GetAccessTokenNonAsync()
    {
        Func<System.Threading.Tasks.Task> task = async () => { await GetAccessToken().ConfigureAwait(false); };
        task().Wait();
    }
    private static async Task<AuthenticationResult> GetAccessToken()
    {
        string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
        string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
        string clientSecret = ConfigurationManager.AppSettings["ida:ClientSecret"];
        string source = ConfigurationManager.AppSettings["ExchangeOnlineId"];

        var authContext = new AuthenticationContext(aadInstance, false);
        var credentials = new ClientCredential(clientId, clientSecret);
        var appRedirectUrl = HttpContext.Current.GetOwinContext().Request.Scheme + "://" + HttpContext.Current.GetOwinContext().Request.Host + HttpContext.Current.GetOwinContext().Request.PathBase + "/";
        var res =
             await
                 authContext.AcquireTokenByAuthorizationCodeAsync(
                     ((ClaimsIdentity)HttpContext.Current.User.Identity).FindFirst("AuthenticationCode").Value,
                     new Uri(appRedirectUrl), credentials, source).ConfigureAwait(false);
        Current.AccessToken = res.AccessToken;
        return res;

    }
    private static string AccessToken
    {
        get
        {
            return HttpContext.Current.Session["AccessToken"].ToString();
        }
        set { HttpContext.Current.Session["AccessToken"] = value; }
    }

And in my main method I call GetAccessTokenNonAsync()

sstan
  • 35,425
  • 6
  • 48
  • 66
Oana Marina
  • 277
  • 3
  • 19
  • 2
    Possible duplicate of [How would I run an async Task method synchronously?](http://stackoverflow.com/questions/5095183/how-would-i-run-an-async-taskt-method-synchronously) – Igor Aug 17 '16 at 20:33
  • 1
    @Igor: The accepted answer on that question has serious reentrancy problems. Please don't recommend it. – Stephen Cleary Aug 17 '16 at 22:25

4 Answers4

3

You're running into a deadlock scenario because you're blocking on async code. I describe the details on my blog.

Ideally, you shouldn't be using HttpContext.Current in any new code. It's been considered poor practice for quite a while, and it has been completely removed in ASP.NET Core (and is not coming back).

It looks like what you want to do is store the token in the session state, and (asynchronously) create it if necessary. The appropriate way to do this is to make the accessor asynchronous:

public static async Task<string> GetAccessTokenAsync()
{
  var result = HttpContext.Current.Session["AccessToken"] as string;
  if (result != null)
    return result;

  ...

  var res = await authContext.AcquireTokenByAuthorizationCodeAsync(...);
  result = res.AccessToken.ToString();
  HttpContext.Current.Session["AccessToken"] = result;
  return result;
}
Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • I don't think this solves my problem. I need to regenerate the access token when the user needs to use it, so I need it to be synchronously. I also user ConfigureAwait(false) on all async calls but it still locks the UI... – Oana Marina Aug 18 '16 at 13:11
  • @OanaMarina: That's not a problem; you're just regenerating it asynchronously instead of synchronously. `ConfigureAwait(false)` only works if you use it throughout the entire call stack; it's possible that `AcquireTokenByAuthorizationCodeAsync` isn't using it. – Stephen Cleary Aug 18 '16 at 14:42
  • If I regenerate it asynchronously I can't be sure it's going t be available on the next line of code where I am using it. I need to wait for it to be generated and then use it... – Oana Marina Aug 18 '16 at 15:22
  • @OanaMarina: You can just use `await` in the calling code, changing `GetAccessTokenNonAsync();` to `await GetAccessTokenAsync();`. – Stephen Cleary Aug 18 '16 at 17:25
2

The code that finally worked is this:

 private static void GetAccessTokenNonAsync()
    {
        var userId = ((ClaimsIdentity)HttpContext.Current.User.Identity).FindFirst("UserId").Value;

        var task = System.Threading.Tasks.Task.Run(async () =>
        {
            return await GetAccessToken( userId);
        });
        task.Wait();
        Current.AccessToken = task.Result.AccessToken;
        Current.AccessTokenExpiresOn = task.Result.ExpiresOn.ToString();
    }

    private static async Task<AuthenticationResult> GetAccessToken(string userId)
    {

        return await System.Threading.Tasks.Task.Factory.StartNew<AuthenticationResult>
        (
            () =>
            {
                string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
                string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
                string clientSecret = ConfigurationManager.AppSettings["ida:ClientSecret"];
                string source = ConfigurationManager.AppSettings["ExchangeOnlineId"];

                var authContext = new AuthenticationContext(aadInstance, false);
                var credentials = new ClientCredential(clientId, clientSecret);
                var res =
                    authContext.AcquireTokenSilentAsync(source, credentials,
                        new UserIdentifier(userId, UserIdentifierType.UniqueId)).Result;
                return res;
            }
        );
    }
Oana Marina
  • 277
  • 3
  • 19
1

You are using the async keyword improperly. Something like this should work:

public static void GetAccessTokenNonAsync()
{
    // Call the async method and get the resulting task.
    Task<AuthenticationResult> accessTokenTask = GetAccessToken();
    // Wait for the task to finish.
    accessTokenTask.Wait();
}

private async static Task<AuthenticationResult> GetAccessToken()
{
    return await Task.Factory.StartNew<AuthenticationResult>
    (
        () =>
        {
            // Insert code here.
            return new AuthenticationResult();
        }
    );
}
Joseph M. Shunia
  • 310
  • 2
  • 10
0

Since your async code returns something (uses Task signature), try doing this if you target latest .net framework:

private static void GetAccessTokenNonAsync()
{
    var result = GetAccessToken().ConfigureAwait(false).Result;
}
dlxeon
  • 1,932
  • 1
  • 11
  • 14