How to use Active Directory Authentication Library (ADAL) for .NET on .NET Core 3.0 WPF apps

This post has been republished via RSS; it originally appeared at: Windows Dev AppConsult articles.

This article is as of .NET Core 3.0 Preview 3.

In .NET Framework, we are using Active Directory Authentication Library for .NET to authenticate user.

WPF apps has been using the following method for authenticate user:

 

AuthenticationResult AuthenticationContext.AcquireTokenAsync(string resource, stirng clientId, Uri redirectUri, PlatformParameters parameters)

Like as below:

var authContext = new AuthenticationContext("https://login.microsoftonline.com/common");
var result = await authContext.AcquireTokenAsync("https://graph.microsoft.com", "your app id", new Uri("urn:ietf:wg:oauth:2.0:oob"), new PlatformParameters(PromptBehavior.Auto));
MessageBox.Show(result.AccessToken);

If you migrated an app that has above code to .NET Core 3.0 Preview 3, then a compile error would be occurred.

error CS1729: 'PlatformParameters' does not contain a constructor that takes 1 arguments

Remove the parameter of PlatformParameters constructor, the compile error will be fixed.

var authContext = new AuthenticationContext("https://login.microsoftonline.com/common");
var result = await authContext.AcquireTokenAsync("https://graph.microsoft.com", "your app id", new Uri("urn:ietf:wg:oauth:2.0:oob"), new PlatformParameters());
MessageBox.Show(result.AccessToken);

However, unfortunately NotImplementedException is occurred.

notimpl.jpg

How to fix it?

In ADAL for .NET v5.0.2-preview, there is an ICustomWebUi interface for PlatformParameters constructor.

We can implement a custom Web user interface using the ICustomWebUi.

Let's implement the interface. At first, to use UWP WebView control for modern sign in experience.

WPF WebBrowser control still can use, however it is not enough for modern Web experience.

  • Add Microsoft.Toolkit.Wpf.UI.Controls.WebView package version 6.0.0-preview3
  • Update Microsoft.IdentityModel.Clients.ActiveDirectory package to version 5.0.2-preview

So, let's write a implementation for ICustomWebUi interface like as below:

 

class CustomWebUi : ICustomWebUi
{
    private readonly Dispatcher _dispatcher;

    public CustomWebUi(Dispatcher dispatcher)
    {
        _dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher))
    }

    public Task<Uri> AcquireAuthorizationCodeAsync(Uri authorizationUri, Uri redirectUr
    {
        var tcs = new TaskCompletionSource<Uri>();

        _dispatcher.InvokeAsync(() =>
        {
            var webView = new WebView();
            var w = new Window
            {
                Title = "Auth",
                WindowStyle = WindowStyle.ToolWindow,
                Content = webView,
            };
            w.Loaded += (_, __) => webView.Navigate(authorizationUri);


            webView.NavigationCompleted += (_, e) =>
            {
                System.Diagnostics.Debug.WriteLine(e.Uri);
                if (e.Uri.Query.Contains("code="))
                {
                    tcs.SetResult(e.Uri);
                    w.DialogResult = true;
                    w.Close();
                }
                if (e.Uri.Query.Contains("error="))
                {
                    tcs.SetException(new Exception(e.Uri.Query));
                    w.DialogResult = false;
                    w.Close();
                }
            };
            webView.UnsupportedUriSchemeIdentified += (_, e) =>
            {
                if (e.Uri.Query.Contains("code="))
                {
                    tcs.SetResult(e.Uri);
                    w.DialogResult = true;
                    w.Close();
                }
                else
                {
                    tcs.SetException(new Exception($"Unknown error: {e.Uri}"));
                    w.DialogResult = false;
                    w.Close();
                }
            };

            if (w.ShowDialog() != true && !tcs.Task.IsCompleted)
            {
                tcs.SetException(new Exception("canceled"));
            }
        });

        return tcs.Task;
    }
}

Let's use the class at calling the AcquireTokenAsync method.

 

 

var authContext = new AuthenticationContext("https://login.microsoftonline.com/common");
var result = await authContext.AcquireTokenAsync("https://graph.microsoft.com",
    "your app id",
    new Uri("urn:ietf:wg:oauth:2.0:oob"),
    new PlatformParameters(PromptBehavior.Auto, new CustomWebUi(Dispatcher)));
MessageBox.Show(result.AccessToken);

Now, we can get an access token after finished AcquireTokenAsync method.

 

Sample code

You can check full version sample code at following repository.

https://github.com/runceel/dotnet-core-wpf-adal-sample

 

Happy coding!!

 

 

 

REMEMBER: these articles are REPUBLISHED. Your best bet to get a reply is to follow the link at the top of the post to the ORIGINAL post! BUT you're more than welcome to start discussions here:

This site uses Akismet to reduce spam. Learn how your comment data is processed.