Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove the UI Parent #676

Closed
dansiegel opened this issue Nov 5, 2018 · 5 comments
Closed

Remove the UI Parent #676

dansiegel opened this issue Nov 5, 2018 · 5 comments
Assignees
Milestone

Comments

@dansiegel
Copy link

Description

While I understand that the UI Parent may be necessary for the underlying API it would seem to me that this could be removed from the public API and what the developer needs to track.

The UIParent seems to be responsible for 2 things:

  1. Tracking the Main Activity for Xamarin.Android
  2. Determining whether to use an Embedded Browser or System Browser on iOS/Android.

This seems to me that some minor updates could be added which would then eliminate the need for users to track a UIParent which cannot be initialized in shared code, but rather must be initialized on the platform and somehow injected into shared code.

Proposed API

For the Android specific issue this could easily be handled by a platform initializer something like:

public class MainActivity : FormsAppCompatActivity
{
    protected override void OnCreate(Bundle savedInstanceState)
    { 
        // Eliminate the developer specific App.UIParent = new UIParent(this, true);
        // Don't really care what this is called
        Msal.Init(this);
    }
}

On the Embedded Browser Front, simply adding a property to the PCA seems to be the better solution here since this could be ignored on platforms where it isn't relevant but could be used on platforms that do use it.

var pca = new PublicClientApplication(...)
{
    UseEmbeddedBrowser = true
};
@jmprieur
Copy link
Contributor

jmprieur commented Nov 6, 2018

This will be considered part of the token request

@bgavrilMS
Copy link
Member

bgavrilMS commented Nov 6, 2018

Great idea, but I'd like to propose a different approach / API. We could have a factory / builder for UIParent that is a superset of all platform specific UIParent objects, i.e.

// Platform agnostic code ...

UIParent uiParent = UIParentBuilder
.UseEmbeddedBrowser(true)  // only affects iOS and Android
.UseCorporateNetwork(false) // only affects UWP
.WithOwnerWindow(windowAsObject) // pass Activity on Android, IWindow on net45 and UWP, other platforms not affected

Note that OwnerWindow is of type object and will be up-casted on each platform (to Activity on Android etc.). An exception will be thrown if the upcast fails.

For example, on a Xamarin app that has several heads (Android, iOS and UWP) plus a shared project (NetStandard), you'd wrap you auth code in method that allows you to pass the owner window, as follows:

// In the shared NetStandard code
public string PerformAuthAndGetToken(object parentWindow)
{
     // construct UIParent like above
     // construct PublicClientApp 
     // try to get token silent
     // get token interactive
}

// In Android head, from an activity:
app.PerformAuthAndGetToken(this);

// In UWP head
app.PerformAuthAndGetToken(this);

As such, UIParent does not need to be injected.

@dansiegel
Copy link
Author

I don't mind the pattern here, or even if it's implemented on the Platform level... What I would like to avoid is having to track an object (the UIParent or "Parent Window") from my shared (NetStandard) code base. The onus should be on MSAL not the consuming developer.

public class UIParentBuilder
{
    private static UIParentBuilder _instance;

    internal static UIParent BuildParent()
    {
        // use builder to create UIParent
    }

    public static UIParentBuilder UseEmbeddedBrowser(bool useEmbedded)
    {
        // set this in the instance;
        return _instance;
    }
}

For Xamarin projects I might have some initialization like:

public class MainActivity : FormsAppCompatActivity
{
    protected override void OnCreate(Bundle savedInstanceState)
    {
        UIParentBuilder.UseEmbeddedBrowser(true)
                       .WithActivity(this); 
        // perhaps an overload=> WithActivity(Func<Activity> getCurrentActivity)
    }
}

Thus freeing me up in shared (NetStandard) code to simply worry about Scopes, and any current account so I might have:

var account = await _pca.GetAccountAsync("serviceId");
var result = await _pca.AquireTokenAsync(scopes, account);

// or if I have a single user scheme, let MSAL get the account automatically
var result = await _pca.AquireTokenAsync(scopes);

@bgavrilMS
Copy link
Member

I have fixed this with AzureAD/azure-activedirectory-library-for-dotnet#1428 - there is now a UIParent ctor that takes in (object parent, bool useEmbeddedWebview). This is available on all platforms, including netstandard (note that it's hidden on some platforms using a bait-and-switch technique).

Will be available with the next release of MSAL 2.6.0-preview, for which we are now testing.

@jmprieur
Copy link
Contributor

This is now solved in MSAL.NET 2.6.0

@jmprieur jmprieur added this to the 2.6.0-preview milestone Dec 13, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants