How to wait for 'AuthenticationContext.AcquireTokenAsync()' synchronouslly?

ElektroStudios picture ElektroStudios · May 21, 2016 · Viewed 18.5k times · Source

First of all, I'm not sure if this is important, but for the reasons mentioned by @Simon Mourier in him answer, I'm using the EXPERIMENTAL build of ADAL, this one.


In the code below, I would like to retrieve an AuthenticationResult synchronouslly, so, I will wait for completition of the authentication by AcquireTokenAsync method in a synchronous manner.

This is because a boolean flag should be set after the authorization is done (isAuthorized = true), but tgis need to happen in a synchronous way, because if not, then I can call other methods of the class that will throw a null reference because the call to AcquireTokenAsync didn't finished so the object is null.

The following code does not work, the method will never return, because the call to AcquireTokenAsync method seems that indefinitely freezes the thread.

C# (maybe wrong syntax due to online translation):

public void Authorize() {
    // Use the 'Microsoft.Experimental.IdentityModel.Clients.ActiveDirectory' Nuget package for auth.
    this.authContext = new AuthenticationContext(this.authUrl, this.cache);
    this.authResult = this.authContext.AcquireTokenAsync({ "https://outlook.office.com/mail.readwrite" }, 
                                                         null, this.clientIdB, this.redirectUriB, 
                                                         new PlatformParameters(PromptBehavior.Auto, this.windowHandleB)).Result;

    // Use the 'Microsoft.Office365.OutlookServices-V2.0' Nuget package from now on.
    this.client = new OutlookServicesClient(new Uri("https://outlook.office.com/api/v2.0"), () => Task.FromResult(this.authResult.Token));
    this.isAuthorizedB = true;
}

VB.NET:

Public Sub Authorize()

    ' Use the 'Microsoft.Experimental.IdentityModel.Clients.ActiveDirectory' Nuget package for auth.
    Me.authContext = New AuthenticationContext(Me.authUrl, Me.cache)

    Me.authResult =
        Me.authContext.AcquireTokenAsync({"https://outlook.office.com/mail.readwrite"}, 
                                         Nothing, Me.clientIdB, Me.redirectUriB,
                                         New PlatformParameters(PromptBehavior.Auto, Me.windowHandleB)).Result

    ' Use the 'Microsoft.Office365.OutlookServices-V2.0' Nuget package from now on.
    Me.client = New OutlookServicesClient(New Uri("https://outlook.office.com/api/v2.0"),
                                           Function() Task.FromResult(Me.authResult.Token))

    Me.isAuthorizedB = True

End Sub

I researched a little bit and I tried other two alternatives, but happens the same..

1st:

ConfiguredTaskAwaitable<AuthenticationResult> t = this.authContext.AcquireTokenAsync(scopeUrls.ToArray(), null, this.clientIdB, this.redirectUriB, new PlatformParameters(PromptBehavior.Auto, this.windowHandleB)).ConfigureAwait(false);

this.authResult = t.GetAwaiter.GetResult();

2nd:

this.authResult == RunSync(() => { return this.authContext.AcquireTokenAsync(scopeUrls.ToArray(), null, this.clientIdB, this.redirectUriB, new PlatformParameters(PromptBehavior.Auto, this.windowHandleB)); })

private AuthenticationResult RunSync<AuthenticationResult>(Func<Task<AuthenticationResult>> func)
{
    return Task.Run(func).Result;
}

Answer

Jeffrey Chen picture Jeffrey Chen · May 23, 2016

How to wait for 'AuthenticationContext.AcquireTokenAsync()' synchronouslly?

I suspect this issue is caused by calling the async method in the UI thread. Currently, my workaround is wrapping the call a new work thread.

    private void button1_Click(object sender, EventArgs e)
    {
        Authorize().Wait();
    }

    private Task Authorize()
    {
        return Task.Run(async () => {
            var authContext = new AuthenticationContext("https://login.microsoftonline.com/common");

            var authResult = await authContext.AcquireTokenAsync
            (new string[] { "https://outlook.office.com/mail.readwrite" },
             null,
             "{client_id}",
             new Uri("urn:ietf:wg:oauth:2.0:oob"),
             new PlatformParameters(PromptBehavior.Auto, null));
        });
    }