How to use Active Directory Authentication Library (ADAL) for .NET on .NET Core 3.0 WPF apps
Published Apr 05 2019 12:55 AM 21.4K Views
Microsoft

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!!

 

 

 

9 Comments
Copper Contributor

This is awesome but how can I get Dispatcher class.. Is not finding in WindowsBase.dll.  Thoughts?

Microsoft

Thank you for a comment @Babu Pillai.

Dispatcher class is System.Windows.Threading.Dispatcher class.

The class is in WindowsBase.dll.

 

I've added a repository link at bottom of article for to be able to see full version code.

Could you check the code?

If you cann't fix an error, then please let me know.

 

Copper Contributor

Thanks .. will check again..

 

Microsoft

@Babu Pillai I'm happy to help.

As you can see in the repo's code, I get a Dispatcher instance from MainWindow class.

Copper Contributor

Hello @KazukiOta ,

 

I am trying to replicate the same in windows forms and i have observed that latest version of .netcore 3 preview 4 and windows form tool kit (for web view) are not compatible with each other so i reduced the version of webview to 6.0.0-preview1.

 

Now when i create a object

new PlatformParameters(promptbehaviour, new CustomWebUi(DispatcherObject) it throws an error as it takes it as atype.

 

Please ignore the spelling mistakes as i m typing from mobile. Please let me know if you have tried this on windows forms.

 

Thanks

Microsoft

@altafJebran 

Thank you for your comment.

I have never used WinForms on .NET Core.

I'll try this on WinForms on .NET Core after.

Copper Contributor

How can i apply this example in a .net core multiplatform evironment where there's no WindowsBase since it can run on linux, too?

Microsoft

@ciacco 
WPF and WinForms on .NET Core are only available to Windows.

So, you can't run the sample app on linux.

If you would like to create desktop apps for linux, Uno Platform or Avalonia UI Framework might be fit.

- Uno Platform: https://platform.uno/

- Avalonia UI Framework: https://avaloniaui.net/

Microsoft

Hi @KazukiOta,

 

Thanks for the great article! Do you have suggestion how to implement this for a console based App? In the console app (C#), it seems the Dispatcher object is not available...

 

 

Version history
Last update:
‎Apr 14 2019 07:21 PM
Updated by: