Copilot for Microsoft 365 Tech Accelerator
Feb 28 2024 07:00 AM - Feb 29 2024 10:30 AM (PST)
Microsoft Tech Community
How to use embedded web UI of MSAL.NET on WPF on .NET Core
Published Apr 17 2020 12:40 AM 7,080 Views

Azure AD B2C is a powerful service for providing business-to-customer identity.


Of cause, you can also use Azure AD B2C sign feature on WPF on .NET Core. However, at now(April 17, 2020), there are few limitations:


  1. You can't use embedded web browser UI.(Have to use System browsers as default)
  2. You can't use 'http://localhost'(No port) redirect URL.(The feature will be released soon:

In this article, I will try implements custom web ui for embedded web browser UI for WPF on .NET Core.

How to impl?

That's really simple!
Just implements Microsoft.Identity.Client.Extensibility.ICustomWebUi interface. There is a single method:


Task<Uri> AcquireAuthorizationCodeAsync(Uri authorizationUri, Uri redirectUri, CancellationToken cancellationToken)


The return value is an URI that has code=CODE parameters.

If there are something wrong during authentication flow, then throws MsalExtensionException.

Impl a Browser Window and the interface

To show a custom web UI, create a window that has a WebBrowser control.


<Window x:Class="EmbeddedMsalCustomWebUi.Wpf.Internal.EmbeddedWebUiWindow"
        Title="EmbeddedWebUiWindow" Height="450" Width="800">
        <WebBrowser x:Name="webBrowser"
                    Navigating="WebBrowser_Navigating" />


And then, implements the code behind.


using Microsoft.Identity.Client.Extensibility;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Windows;
using System.Windows.Navigation;

namespace EmbeddedMsalCustomWebUi.Wpf.Internal
    public partial class EmbeddedWebUiWindow : Window
        private readonly Uri _authorizationUri;
        private readonly Uri _redirectUri;
        private readonly TaskCompletionSource<Uri> _taskCompletionSource;
        private readonly CancellationToken _cancellationToken;
        private CancellationTokenRegistration _token;

        public EmbeddedWebUiWindow(
            Uri authorizationUri,
            Uri redirectUri,
            TaskCompletionSource<Uri> taskCompletionSource,
            CancellationToken cancellationToken)
            _authorizationUri = authorizationUri;
            _redirectUri = redirectUri;
            _taskCompletionSource = taskCompletionSource;
            _cancellationToken = cancellationToken;

        private void WebBrowser_Navigating(object sender, NavigatingCancelEventArgs e)
            if (!e.Uri.ToString().StartsWith(_redirectUri.ToString()))
                // not redirect uri case

            // parse query string
            var query = HttpUtility.ParseQueryString(e.Uri.Query);
            if (query.AllKeys.Any(x => x == "code"))
                // It has a code parameter.
                // error.
                    new MsalExtensionException(
                        $"An error occurred, error: {query.Get("error")}, error_description: {query.Get("error_description")}"));


        private void Window_Loaded(object sender, RoutedEventArgs e)
            _token = _cancellationToken.Register(() => _taskCompletionSource.SetCanceled());
            // navigating to an uri that is entry point to authorization flow.

        private void Window_Closed(object sender, EventArgs e)


Implements AcquireAuthorizationCodeAsync method using the above window.


using EmbeddedMsalCustomWebUi.Wpf.Internal;
using Microsoft.Identity.Client.Extensibility;
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;

namespace EmbeddedMsalCustomWebUi.Wpf
    /// <summary>
    /// Provides embedded web ui for WPF on .NET Core.
    /// The web ui is using WebBrowser control(Trident engine).
    /// </summary>
    public class EmbeddedBrowserWebUi : ICustomWebUi
        public const int DefaultWindowWidth = 600;
        public const int DefaultWindowHeight = 800;

        private readonly Window _owner;
        private readonly string _title;
        private readonly int _windowWidth;
        private readonly int _windowHeight;
        private readonly WindowStartupLocation _windowStartupLocation;

        public EmbeddedBrowserWebUi(Window owner, 
            string title = "Sign in",
            int windowWidth = DefaultWindowWidth,
            int windowHeight = DefaultWindowHeight,
            WindowStartupLocation windowStartupLocation = WindowStartupLocation.CenterOwner)
            _owner = owner ?? throw new ArgumentNullException(nameof(owner));
            _title = title;
            _windowWidth = windowWidth;
            _windowHeight = windowHeight;
            _windowStartupLocation = windowStartupLocation;

        public Task<Uri> AcquireAuthorizationCodeAsync(Uri authorizationUri, Uri redirectUri, CancellationToken cancellationToken)
            var tcs = new TaskCompletionSource<Uri>();
            _owner.Dispatcher.Invoke(() =>
                new EmbeddedWebUiWindow(authorizationUri,
                    Owner = _owner,
                    Title = _title,
                    Width = _windowWidth,
                    Height = _windowHeight,
                    WindowStartupLocation = _windowStartupLocation,

            return tcs.Task;
I decided using WPF WebBrowser control because it is easest way to use WebBrowser control on WPF. Other options are using old edge engine or new edge engine(the status is developer preview now).
If you would like to use them, then please replace WebBrowser control to one you want.



Create an app for WPF client on Azure AD B2C tenant as public client app and check redirect URL.



And create an another app on B2C tenant, and export an API from the app, then add the permission from the client app.


And If you haven't created sign in user flow on the tenant, then create it.


Collect following items to use WPF app:

  • Application ID(Client ID) (GUID)
  • Tenant ID (GUID)
  • Redirect URI: it is on the Authentication page on the B2C portal.
  • Azure AD B2C Authority: https://{tenant name}{tenant name}{user flow name}.
  • Scope: It is an URL (https://{tenant name}{guid}/{name}) that is able to get at API permissions page of another app.

And then create a WPF App(.NET Core) project, and create an IPublicClientApplication instance on Startup event of App class.


PublicClientApplication = PublicClientApplicationBuilder.Create("{your client id}")
    .WithRedirectUri("{your redirect uri}")
    .WithTenantId("your tenant id")
    .WithB2CAuthority("{your azure ad b2c authority}")


The last step! Use EmbeddedBrowserWebUi class that implemented on this article with AcquireTokenInteractive method.


 var r = await PublicClientApplication
     .AcquireTokenInteractive(new[] { "{your scope}" })
     .WithCustomWebUi(new EmbeddedBrowserWebUi(this)) // here


It works as below:



Completed source code

The custom web ui and test app codes are on following github repo:


If you would like to try EmbeddedCustomWebUi class on your code, then you can get it from NuGet:


This is a just sample code. It is not tested for production.


Version history
Last update:
‎Apr 17 2020 06:18 AM
Updated by: