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

19,629

Solution 1

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

Solution 2

I fought this problem for 2 days on Xamarin.Android. It just never returns from the AquireTokenAsync method. The answer is almost comical. You need to add the following in your MainActivity.cs:

    protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
    {
        base.OnActivityResult(requestCode, resultCode, data);
        AuthenticationAgentContinuationHelper.SetAuthenticationAgentContinuationEventArgs(requestCode, resultCode, data);
    }

Funny thing is, it actually says ContinuationHelper... smh.

Solution 3

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

AcquireTokenAsync() returns a "Task". You just have to wait for it using ".Wait()". In your main program you just have to do this:

Task<AuthenticationResult> res = authContext.AcquireTokenAsync(resourceUri, clientID, new Uri(redirectUri), new PlatformParameters(PromptBehavior.Auto));
res.Wait();
Console.WriteLine(res.Result.AccessToken);
Share:
19,629
ElektroStudios
Author by

ElektroStudios

Updated on June 17, 2022

Comments

  • ElektroStudios
    ElektroStudios almost 2 years

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