Forum Libraries, Components, and Plugins
We are excited to announce that the Xamarin Forums are moving to the new Microsoft Q&A experience. Q&A is the home for technical questions and answers at across all products at Microsoft now including Xamarin!

We encourage you to head over to Microsoft Q&A for .NET for posting new questions and get involved today.

Need help integrating MSAL 2.x async auth code into Activity/Fragment lifecycle. (Andriod native)

Looks like I have multiple threads: "Task.Run(async () => ..."

invoking this code:

    public static async Task<string> GetAuthToken(Android.App.Activity activity)
    {
        GStateMan gsm = AppClass.AppInstance.GetGlobalStateManager();
        PublicClientApplication pca = gsm.GetPublicClientApplication();

        string[] scopes = new string[] { "User.Read", "https://webservices.mydomain/MyApp//user_impersonation"};

        var accounts = await pca.GetAccountsAsync();

        AuthenticationResult result = null;

        try
        {
            result = await pca.AcquireTokenSilent(scopes, accounts.FirstOrDefault())
                .ExecuteAsync();
        }
        catch (MsalUiRequiredException e)
        {
            try
            {
                result = await pca.AcquireTokenInteractive(scopes)
                    .WithParentActivityOrWindow(activity)
                    .WithUseEmbeddedWebView(true)
                    .ExecuteAsync();
            }
            catch (Exception ex) { Debug.WriteLine("WTF!!?? why this fails? " + ex); }
        }
        catch (Exception mex) { Debug.WriteLine("WTF2!!?? why this fails too? " + mex); }
        return result?.AccessToken;
    }

-- at the same time. Worse luck, I think I have generated a race where the user can beat MSAL to the finish line, and nuke the Activity before all the threads finish. I'm observing this problem while going down the "silent" branch. Can anyone give advice about a preferred pattern, or links to articles to help tame this beast? Thanks.

Answers

  • LandLuLandLu Member, Xamarin Team Xamurai

    Task.Run() will open a new thread to run the code block. The system will skip this secondary task and run the beneath code immediately as it is not marked as an await task.
    Why not add async to your lifecycle like:

    protected async override void OnStart()
    {
        base.OnStart();
        TextView textView = FindViewById<TextView>(Resource.Id.textView);
        var accesstoken = await GetAuthToken(this);
        textView.Text = accesstoken;
    }
    

    Then the code below will wait for its completion.

  • zambinnizambinni Member ✭✭

    Hmm,... I generally try not to async lifecycle methods b/c it will freeze the UI while it waits for the aysnc stuff to finish. This particular library introduces significant latency in operations too. On top of that, I have some code that chains activities, one to another to another, etc. It can get 4 or 5 levels deep, and several of those in the middle might need to do protected web service requests. Each WS request needs an auth token. If the user hits the back button several times in rapid succession, the result is catastrophic.

  • LandLuLandLu Member, Xamarin Team Xamurai

    You could put your asynchronized code in the OnStart event.
    It won't block your UI thread. It only caused the code beneath the task to be called after its completion.

  • zambinnizambinni Member ✭✭

    I'm not quite sure what the assertion you are making here is. Is it not the case that when execution gets to the await, control returns to the caller, at which point, OnResume gets invoked, and I have lost my opportunity to do something to the UI while I am waiting for auth library to sort itself out?

    Maybe I have a fundamental misunderstanding about how the Android lifecycle and UI drawing are related in a non-forms based (couldn't find a way to effectively bind data to UI) application?

  • LandLuLandLu Member, Xamarin Team Xamurai

    If you want to achieve an authentication effect try to complete this request before displaying a new activity/fragment.
    We could use a loading view to disable the user interfaction when querying the access token.
    Then present a new activity to display the user information if it returns the expected information.
    What I mean is to send this authentication request on the current activity instead of putting it in the lifecycle event of a new activity.

  • zambinnizambinni Member ✭✭

    I think we are getting closer to the crux of the issue. I recall reading in some of the documentation something along the lines of "do not store the token..." for later use. Fair enough, they expire periodically. Problem is, 3/4 of the functionality of my app requires a call to a protected web service. Worse than that, some Activities chain to new ones that also require a call to a service endpoint. Each request to an endpoint requires a token. What I am finding is that the gymnastics dance that I have to do with the auth mechanism, at every tap is quite cumbersome, is introducing a lot of latency, and instability if the user tries to run past an activity, for instance while backing through a trail of breadcrumbs. Putting a spinner/thermometer to let the user know that I'm temporarily freezing the UI after every tap is not a great solution. I want something that works like an SSL pipe.

  • LandLuLandLu Member, Xamarin Team Xamurai

    We should try not to store the access token. I think what we really need to store is the information from the second request with the access token.
    Try to require the entire information of the user in a single request and store it on in the local storage. then we could display this kind of data at any place we want.
    If you want to display the information from the server it always needs some time to complete the session. And there's no way to make the user not aware of this process. The application needs to block the interaction when requesting or present temporary fake data.

Sign In or Register to comment.