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()

2

4 Answers 4

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;
}
Sign up to request clarification or add additional context in comments.

4 Comments

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...
@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.
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...
@OanaMarina: You can just use await in the calling code, changing GetAccessTokenNonAsync(); to await GetAccessTokenAsync();.
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;
            }
        );
    }

1 Comment

you re code worked! You really saved me from a lot of trouble. I m not sure why it worked though!
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();
        }
    );
}

Comments

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;
}

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.