close icon
.NET

Add Authentication to .NET MAUI Apps with Auth0

Learn how to authenticate users of your .NET MAUI application using Auth0.

Last Updated On: October 10, 2022

Building desktop and mobile applications with one codebase is finally possible thanks to .NET MAUI. Let's explore how to also add authentication to them using Auth0.

.NET MAUI Applications

Two years after its initial announcement, .NET Multi-platform App UI (MAUI) is now generally available. The framework allows developers to build desktop and mobile applications for Windows, macOS, iOS, and Android, leveraging the .NET ecosystem and using one codebase. This is a huge milestone in the .NET ecosystem. In fact, the release of .NET MAUI completes the vision of .NET as a unified platform for building any type of application.

Technically, .NET MAUI is the evolution of Xamarin.Forms, which will still be supported but will not have new major releases. It builds upon the experience of Xamarin and offers a more consistent way to create multi-platform applications. Like Xamarin, .NET MAUI allows you to create applications using C# and XAML. Still, it simplifies the developer experience by adding support for a single project and providing multiple ways to add platform-specific code. The UI controls generated from XAML are highly-performant native controls, but you can also reuse existing Blazor components in your native application.

.NET MAUI introduces exciting new opportunities for .NET developers, but this article will focus on adding authentication to a simple MAUI app. If you want to learn more about .NET MAUI, read this blog post. To see the main differences between Xamarin and .NET MAUI, check out this article.

Prerequisites

At the time of writing, support for .NET MAUI is still being consolidated and documentation is lacking on some topics (including authentication support with OpenID Connect). To build and run the sample project of this article, you need the latest .NET 7.0 SDK. Also, depending on your development and target platforms, you may need additional components. Please, refer to this document to learn more and set up your development environment.

If you want to use Visual Studio to build .NET MAUI apps, you need Visual Studio 2022 17.3 or greater on Windows and Visual Studio for Mac 17.4 on macOS.

The Sample Application

The sample application you will build is the simplest you can create by starting from the standard MAUI template. This article will use the .NET CLI to create and build the project to offer a consistent cross-platform experience, but feel free to use the IDE and tool that you prefer.

To create the sample application, run the following command in a terminal window:

dotnet new maui -o MauiAuth0App

After a few seconds, you will get a MauiAuth0App folder with the MAUI project. To make sure that everything works as expected, we will run our newly created application. At this point, if you are using Visual Studio, you can select your target platform next to the run button, as shown below:

MAUI target platform in Visual Studio

If you are using the .NET CLI, you need to specify the target platform. The following commands run your MAUI app in the respective target platform:

# macOS target platform
dotnet build -t:Run -f net7.0-maccatalyst

# Android target platform
dotnet build -t:Run -f net7.0-android

# iOS target platform
dotnet build -t:Run -f net7.0-ios

# Windows target platform (⚠️ see the note below ⚠️)
dotnet build -t:Run -f net7.0-windows10.0.19041.0

⚠️ According to the resolution note for this issue, you should be able to build and run a Windows application using the command above. Unfortunately, at the time of writing, that command is not working as expected. You can build a .NET MAUI Windows app by running dotnet build -f net7.0-windows10.0.19041.0, but you can't launch it through the CLI on Windows.

After launching your app, you will see a screen like the following:

Running .NET MAUI app

Let's go over how to integrate this app with Auth0.

Register with Auth0

To start, let's register the app with Auth0. Use your Auth0 account to access your dashboard. If you don't yet have one, you can sign up for free. Once in the dashboard, move to the Applications section and follow these steps:

  1. Click on Create Application.
  2. Provide a friendly name for your application (for example, MAUI App) and choose Native as the application type.
  3. Finally, click the Create button.

These steps make Auth0 aware of your MAUI application. After creating the application, move to the Settings tab and take note of your Auth0 domain and client id. You will use them shortly.

Then, in the same form, scroll down to the Application URIs section and assign the value myapp://callback to both the Allowed Callback URLs and the Allowed Logout URLs fields.

The first value tells Auth0 which URL to call back after the user authenticates. The second value tells Auth0 which URL the user should be redirected to after their logout. Even if you are not building a web application, you will learn how your application can catch this URI.

Click the Save Changes button to apply them.

Add Authentication

Back in your MAUI project, add the OpenID Connect Client package by running the following command:

dotnet add package IdentityModel.OidcClient

Now you need to write the code that integrates your application with Auth0. You will do it in a few steps:

  • Create the Auth0 client, i.e., the component you need to let the user authenticate with Auth0.
  • Configure your MAUI app to use the Auth0 client.
  • Add the login button on the MAUI app UI.
  • Apply specific changes for each target platform.

Let's go through each step.

Create the Auth0 client

Let's start building the Auth0 client by creating an Auth0 folder in the project's root folder. In this folder, you will create a WebBrowserAuthenticator.cs file with the following content:

// Auth0/WebBrowserAuthenticator.cs

using IdentityModel.Client;
using IdentityModel.OidcClient.Browser;

namespace MauiAuth0App.Auth0;

public class WebBrowserAuthenticator : IdentityModel.OidcClient.Browser.IBrowser
{
  public async Task<BrowserResult> InvokeAsync(BrowserOptions options, CancellationToken cancellationToken = default)
  {
    try
    {
      WebAuthenticatorResult result = await WebAuthenticator.Default.AuthenticateAsync(
          new Uri(options.StartUrl),
          new Uri(options.EndUrl));

      var url = new RequestUrl(options.EndUrl)
          .Create(new Parameters(result.Properties));

      // Workaround for Facebook issue
      if (url.EndsWith("%23_%3D_"))
      {
        url = url.Substring(0, url.LastIndexOf("%23_%3D_"));
      }
      
      return new BrowserResult
      {
        Response = url,
        ResultType = BrowserResultType.Success
      };
    }
    catch (TaskCanceledException)
    {
      return new BrowserResult
      {
        ResultType = BrowserResultType.UserCancel,
        ErrorDescription = "Login canceled by the user."
      };
    }
  }
}

The WebBrowserAuthenticator class implements the IBrowser interface to handle the authentication step. In practice, this class is responsible for opening the system browser, which will show the user the Auth0 Universal Login page. The actual browser-based authentication is started using the WebAuthenticator instance provided by the Microsoft.Maui.Authentication namespace.

The Facebook issue workaround

You may have noticed that the implementation of the InvokeAsync() method above includes an if statement with a comment about a Facebook issue. In fact, if you use Facebook as a social login for this application, you will experience an issue and will not be able to log in.

Facebook appends the #_=_ string to the callback URL with the state parameter. See here for more details.
While browsers and web applications interpret this as a fragment identifier, in our case it is interpreted as additional characters and causes state validation to fail. The workaround removes the encoded #_=_ string from the URL before returning it for validation.

Now, let's create the Auth0ClientOptions.cs file with the following code in the same Auth0 folder:

// Auth0/Auth0ClientOptions.cs

namespace MauiAuth0App.Auth0;

public class Auth0ClientOptions
{
  public Auth0ClientOptions()
  {
    Scope = "openid";
    RedirectUri = "myapp://callback";
    Browser = new WebBrowserAuthenticator();
  }

  public string Domain { get; set; }

  public string ClientId { get; set; }

  public string RedirectUri { get; set; }

  public string Scope { get; set; }

  public IdentityModel.OidcClient.Browser.IBrowser Browser { get; set; }
}

The Auth0ClientOptions class defined here collects the configuration settings for Auth0. Among the others, notice the Browser property, which is initialized with an instance of the WebBrowserAuthenticator class we defined early.

Finally, let's define the actual Auth0 client by adding the Auth0Client.cs file to the Auth0 folder. This is its content:

// Auth0/Auth0Client.cs

using IdentityModel.OidcClient;

namespace MauiAuth0App.Auth0;

public class Auth0Client
{
  private readonly OidcClient oidcClient;
  
  public Auth0Client(Auth0ClientOptions options)
  {
    oidcClient = new OidcClient(new OidcClientOptions{
      Authority = $"https://{options.Domain}",
      ClientId = options.ClientId,
      Scope = options.Scope,
      RedirectUri = options.RedirectUri,
      Browser = options.Browser
    });
  }

  public IdentityModel.OidcClient.Browser.IBrowser Browser {
    get {
      return oidcClient.Options.Browser;
    }
    set {
      oidcClient.Options.Browser = value;
    }
  }

  public async Task<LoginResult> LoginAsync() {
    return await oidcClient.LoginAsync();
  }
}

The constructor of the Auth0Client class takes the options passed as an argument and creates an instance of the OIDC client configured with them. The class also exposes the Browser property and provides the LoginAsync() method to start the authentication process.

Configure your MAUI app

Once you have the Auth0 client, open the MauiProgram.cs file in the root folder of the project and apply the changes highlighted below:

// MauiProgram.cs

using Microsoft.Extensions.Logging;
using MauiAuth0App.Auth0;    // 👈 new code

namespace MauiAuth0App;

public static class MauiProgram
{
  public static MauiApp CreateMauiApp()
  {
    var builder = MauiApp.CreateBuilder();
    builder
      .UseMauiApp<App>()
      .ConfigureFonts(fonts =>
      {
        fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
        fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
      });
    
#if DEBUG
        builder.Logging.AddDebug();
#endif

    // 👇 new code
    builder.Services.AddSingleton<MainPage>();

    builder.Services.AddSingleton(new Auth0Client(new()
    {
      Domain = "<YOUR_AUTH0_DOMAIN>",
      ClientId = "<YOUR_CLIENT_ID>",
      Scope = "openid profile",
      RedirectUri = "myapp://callback"
    }));
    // 👆 new code
    
    return builder.Build();
  }
}

First, add a reference to the MauiAuth0App.Auth0 namespace. This makes the Auth0 client available in this context. Then, add the MainPage class as a singleton service to the application builder. Finally, create an instance of the Auth0 client passing the required options and add it as a singleton service to the application builder. Remember to replace the <YOUR_AUTH0_DOMAIN> and <YOUR_CLIENT_ID> placeholders with the respective values for your Auth0 domain and client ID taken from the Auth0 dashboard.

Add the login button

Now, let's modify the user interface to allow the user to authenticate. Open the MainPage.xaml file and change its content as follows:

<!-- MainPage.xaml -->
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MauiAuth0App.MainPage">
             
    <ScrollView>
        <VerticalStackLayout 
            Spacing="25" 
            Padding="30,0" 
            VerticalOptions="Center">
          
            <!-- 👇 new code -->
            <StackLayout
                x:Name="LoginView">
                <Button 
                    x:Name="LoginBtn"
                    Text="Log In"
                    SemanticProperties.Hint="Click to log in"
                    Clicked="OnLoginClicked"
                    HorizontalOptions="Center" />
            </StackLayout>
            <!-- 👆 new code -->

            <!-- 👇 new code -->
            <StackLayout
                x:Name="HomeView"
                IsVisible="false">
            <!-- 👆 new code -->
              <Image
                  Source="dotnet_bot.png"
                  SemanticProperties.Description="Cute dot net bot waving hi to you!"
                  HeightRequest="200"
                  HorizontalOptions="Center" />
                
              <!-- ...existing markup... -->
              
          <!-- 👇 new code -->
          </StackLayout>
          <!-- 👆 new code -->
        </VerticalStackLayout>
    </ScrollView>
</ContentPage>

You add a new <StackLayout> element within the existing <VerticalStackLayout>. This <StackLayout> element is named LoginView and contains the login button named LoginBtn. The login button invokes the OnLoginClicked method when it is clicked.

The <StackLayout> element is just a container of UI controls. You will use it to display and hide groups of controls based on the user's authentication state.

In fact, in addition to the login button, you wrap the existing controls in another <StackLayout> element named HomeView. In this case, the <StackLayout> element is marked as invisible. This markup prepares the UI to show only the login button when the application starts and the user is not yet authenticated.

To make the UI fully functional, edit the MainPage.xaml.cs file as follows:

// MainPage.xaml.cs

using MauiAuth0App.Auth0;    // 👈 new code

namespace MauiAuth0App;

public partial class MainPage : ContentPage
{
    int count = 0;
  // 👇 new code
  private readonly Auth0Client auth0Client;
  // 👆 new code

    public MainPage(Auth0Client client)
  // 👆 changed code
    {
        InitializeComponent();
    auth0Client = client;    // 👈 new code
    }

  //...existing code...
  
  // 👇 new code
  private async void OnLoginClicked(object sender, EventArgs e)
  {
    var loginResult = await auth0Client.LoginAsync();

    if (!loginResult.IsError)
    {
      LoginView.IsVisible = false;
      HomeView.IsVisible = true;
    }
    else
    {
      await DisplayAlert("Error", loginResult.ErrorDescription, "OK");
    }
  }
  // 👆 new code
}

Here you add a reference to the MauiAuth0App.Auth0 namespace and declare the auth0Client private variable. The Auth0 client instance is injected through the MainPage() constructor and is assigned to the auth0Clientprivate variable.

Then, you add the implementation of the OnLoginClicked() method. Within the body of this method, you invoke the LoginAsync()method of the Auth0 client to start the authentication process. If everything works fine, you will mark the LoginView <StackLayout> element as invisible and make the HomeView <StackLayout> element visible. Otherwise, an error dialog will be shown.

Well, the app is almost ready to run!

Apply Platform-Specific Changes

Before running your app and testing the Auth0 integration, you need to apply a few changes for each target platform. The changes you will apply are related to the WebAuthenticator functionality. Specifically, you need to instruct your app on how to handle the callback URI that Auth0 will redirect the user to after their authentication.

The .NET MAUI framework provides multiple ways to add platform-specific code, settings, and assets. One of these ways is based on the Platforms folder's content in your project. This folder contains a subfolder for each target platform. Only the platforms you want to target need to be configured. In this article, you will focus on Android, iOS, Mac, and Windows platforms.

Android settings

Move to the Platforms/Android folder and add a new file named WebAuthenticationCallbackActivity.cs. Add the following content to it:

// Platforms/Android/WebAuthenticationCallbackActivity.cs

using Android.App;
using Android.Content.PM;

namespace MauiAuth0App;

[Activity(NoHistory = true, LaunchMode = LaunchMode.SingleTop, Exported = true)]
[IntentFilter(new[] { Android.Content.Intent.ActionView },
              Categories = new[] { 
                Android.Content.Intent.CategoryDefault,
                Android.Content.Intent.CategoryBrowsable 
              },
              DataScheme = CALLBACK_SCHEME)]
public class WebAuthenticationCallbackActivity : Microsoft.Maui.Authentication.WebAuthenticatorCallbackActivity
{
    const string CALLBACK_SCHEME = "myapp";
}

This code defines the WebAuthenticationCallbackActivity class inheriting from WebAuthenticatorCallbackActivity. This class is marked as an intent filter that accepts myapp as the scheme for the callback URI.

Then, open the Platforms/Android/AndroidManifest.xml file and add the markup highlighted below to make the intent visible:

<!-- Platforms/Android/AndroidManifest.xml -->

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application android:allowBackup="true" 
               android:icon="@mipmap/appicon" 
               android:roundIcon="@mipmap/appicon_round" 
               android:supportsRtl="true"></application>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
  <!-- 👇 new code -->
  <queries>
    <intent>
      <action android:name="android.support.customtabs.action.CustomTabsService" />
    </intent>
  </queries>
  <!-- 👆 new code -->
</manifest>

The Android version of your MAUI app is ready to run!

Mac and iOS settings

The settings for macOS and iOS are the same. They just need to be applied in their specific folders.

For macOS, move to the Platforms/MacCatalyst folder, open the Info.plist file, and add the key shown below:

<!-- Platforms/MacCatalyst/Info.plist -->

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  
  <!-- ...existing keys... -->
  
  <!-- 👇 new code -->
  <key>CFBundleURLTypes</key>
  <array>
    <dict>
      <key>CFBundleURLName</key>
      <string>MauiAuth0App</string>
      <key>CFBundleURLSchemes</key>
      <array>
        <string>myapp</string>
      </array>
      <key>CFBundleTypeRole</key>
        <string>Editor</string>
    </dict>
  </array>
  <!-- 👆 new code -->
</dict>
</plist>

Add the same key to the Info.plist file in the Platforms/iOS folder.

Windows settings

To configure the Windows target of your MAUI app, you should move to the Platforms/Windows folder and modify the Package.appxmanifest file. Unfortunately, at the time of writing, there is an issue:

⚠️ Currently, you can't use WebAuthenticator on Windows due to a known issue. See also this for more info about the feature to be implemented. ⚠️

You will learn an alternative way to integrate Auth0 with your Windows target app later.

Anyway, the changes you would apply to the Package.appxmanifest file are the following:

<?xml version="1.0" encoding="utf-8"?>
<Package>

  <!-- ...existing markup... -->
  
  <Applications>
    <Application Id="App" 
                 Executable="$targetnametoken$.exe" 
                 EntryPoint="$targetentrypoint$">
      <uap:VisualElements
        DisplayName="$placeholder$"
        Description="$placeholder$"
        Square150x150Logo="$placeholder$.png"
        Square44x44Logo="$placeholder$.png"
        BackgroundColor="transparent">
        <uap:DefaultTile Square71x71Logo="$placeholder$.png"
                         Wide310x150Logo="$placeholder$.png"
                         Square310x310Logo="$placeholder$.png" />
        <uap:SplashScreen Image="$placeholder$.png" />
      </uap:VisualElements>
      <!-- 👇 new code -->
      <Extensions>
          <uap:Extension Category="windows.protocol">
          <uap:Protocol Name="myapp">
              <uap:DisplayName>MauiAuth0App</uap:DisplayName>
          </uap:Protocol>
          </uap:Extension>
      </Extensions>
      <!-- 👆 new code -->
    </Application>
  </Applications>

  <!-- ...existing markup... -->
  
</Package>

Run Your MAUI App

Now you can run your .NET MAUI app using Auth0 for authentication. Based on your development environment, you can run your targeted platform app using a physical device or a simulator. The user experience will be similar.

For example, after launching the app on a Mac with one of the methods discussed earlier, you will get the following screen:

The .NET MAUI application with the login button

Clicking the login button, a warning dialog appears informing you that you are heading to Auth0 to authenticate:

Warning dialog for the MAUI app

Click the Continue button and a browser instance opens showing the Auth0 Universal Login page. You can use this page to authenticate if you already have a user on your Auth0 tenant or to sign up for the application:

The Auth0 Universal Login page

After the authentication, you will access the home screen you got before adding authentication support:

Running .NET MAUI app

Now your MAUI app allows access only to authenticated users.

Leverage Auth0's authentication and authorization services in your .NET applications.

DOWNLOAD THE FREE EBOOK
.NET Identity with Auth0

Show the User Profile

A feature you may want for your MAUI application is the ability to show the user's profile data, such as their name and picture. Your application already has this data since the Auth0 client has been configured with the appropriate OpenID Connect scopes, i.e., the requests to obtain specific data about the user. To access and show this data, you need to make some changes to the main page of the application.

Open the MainPage.xaml file and add the markup highlighted below:

<!-- MainPage.xaml -->
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MauiAuth0App.MainPage">
             
    <ScrollView>
        <VerticalStackLayout 
            Spacing="25" 
            Padding="30,0" 
            VerticalOptions="Center">          
          <StackLayout x:Name="LoginView">
            
            <!-- ...existing markup... -->
            
          </StackLayout>

          <StackLayout x:Name="HomeView"
                       IsVisible="false">

            <!-- ...existing markup... -->
            
            <!-- 👇 new code -->
            <Image
                x:Name="UserPictureImg"
                SemanticProperties.Description="User's picture"
                HeightRequest="200"
                HorizontalOptions="Center" />

            <Label 
                x:Name="UsernameLbl"
                Text=""
                SemanticProperties.HeadingLevel="Level2"
                SemanticProperties.Description="User's name"
                FontSize="18"
                HorizontalOptions="Center" />
            <!-- 👆 new code -->
            
          </StackLayout>
        </VerticalStackLayout>
    </ScrollView>
</ContentPage>

You add two new elements to the <StackLayout> element named HomeView:

  • An image element named UserPictureImg, which will point to the user's picture.
  • A label element named UsernameLbl, which will show the user's name.

To assign the current user's data to these elements, open the MainPage.xaml.cs file and apply the following changes:

// MainPage.xaml.cs

using MauiAuth0App.Auth0;

namespace MauiAuth0App;

public partial class MainPage : ContentPage
{
  // ...existing code...
  
  private async void OnLoginClicked(object sender, EventArgs e)
  {
    var loginResult = await auth0Client.LoginAsync();

    if (!loginResult.IsError)
    {
      // 👇 new code
      UsernameLbl.Text = loginResult.User.Identity.Name;
      UserPictureImg.Source = loginResult.User
        .Claims.FirstOrDefault(c => c.Type == "picture")?.Value;
      // 👆 new code
      
      LoginView.IsVisible = false;
      HomeView.IsVisible = true;
    }
    else
    {
      await DisplayAlert("Error", loginResult.ErrorDescription, "OK");
    }
  }
}

The added code leverages the outcome of the authentication process stored in the loginResult variable. Specifically, it accesses the User property, which allows you to get the user profile data. You can see that the user's name is taken directly from the User.Identity.Name property while the picture is extracted from the User.Claims collection. This collection contains all the claims about the current user returned by Auth0.

That's all! Once you run your app and authenticate, you will get a screen similar to the following:

MAUI app with user profile

Add Logout

The application built so far allows a user to log in but not to log out. Let's implement this feature.

Start by opening the Auth0Client.cs file in the Auth0 folder and adding the LogoutAsync() method as shown below:

// Auth0/Auth0Client.cs

using IdentityModel.OidcClient;
using IdentityModel.OidcClient.Browser;  // 👈 new code
using IdentityModel.Client;              // 👈 new code

namespace MauiAuth0App.Auth0;

public class Auth0Client
{
  private readonly OidcClient oidcClient;
  
  // ...existing code...
  
  // 👇 new code
  public async Task<BrowserResult> LogoutAsync() {
    var logoutParameters = new Dictionary<string,string>
    {
      {"client_id", oidcClient.Options.ClientId },
      {"returnTo", oidcClient.Options.RedirectUri }
    };

    var logoutRequest = new LogoutRequest();
    var endSessionUrl = new RequestUrl($"{oidcClient.Options.Authority}/v2/logout")
      .Create(new Parameters(logoutParameters));
    var browserOptions = new BrowserOptions(endSessionUrl, oidcClient.Options.RedirectUri) 
    {
        Timeout = TimeSpan.FromSeconds(logoutRequest.BrowserTimeout),
        DisplayMode = logoutRequest.BrowserDisplayMode
    };

    var browserResult = await oidcClient.Options.Browser.InvokeAsync(browserOptions);

    return browserResult;
  }
  // 👆 new code
}

There are two new namespace references and the LogoutAsync() method definition. This method builds the logout endpoint URL and invokes it using the configured browser.

Now, head to the MainPage.xaml file and add the logout button as shown in the following:

<!-- MainPage.xaml -->
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MauiAuth0App.MainPage">
             
    <ScrollView>
            
            <!-- ...existing markup... -->
            
          <StackLayout x:Name="HomeView"
                       IsVisible="false">

            <!-- ...existing markup... -->
            
            <!-- 👇 new code -->
            <Button 
                x:Name="LogoutBtn"
                Text="Log Out"
                SemanticProperties.Hint="Click to log out"
                Clicked="OnLogoutClicked"
                HorizontalOptions="Center" />
            <!-- 👆 new code -->
            
          </StackLayout>
        </VerticalStackLayout>
    </ScrollView>
</ContentPage>

This is a button element named LogoutBtn that invokes the OnLogoutClicked() method when clicked.

Let's implement the OnLogoutClicked() method in the MainPage.xaml.cs file:

// MainPage.xaml.cs

using MauiAuth0App.Auth0;

namespace MauiAuth0App;

public partial class MainPage : ContentPage
{
  // ...existing code...
  
  // 👇 new code
  private async void OnLogoutClicked(object sender, EventArgs e)
  {
    var logoutResult = await auth0Client.LogoutAsync();

    if (!logoutResult.IsError) {
        HomeView.IsVisible = false;
        LoginView.IsVisible = true;
    } else {
        await DisplayAlert("Error", logoutResult.ErrorDescription, "OK");
    }
  }
  // 👆 new code
}

The OnLogoutClicked() method calls the LogoutAsync() method of the Auth0 client. If there is no error, the current view is hidden and the login view is shown. Otherwise, a message error is displayed.

Now your application has a logout button:

MAUI app with the logout button

Using a WebView

Before concluding this article, let's look at how we can address the issue with Windows and WebAuthenticator mentioned earlier. You can work around the problem by using a WebView instead of an external browser.

Using WebViews and external browsers with desktop applications is a highly debated topic. The OAuth2 security best practices suggest using an external browser for all native applications. However, not everyone agrees with these best practices since desktop applications have some usability and technical issues with external browsers. While interesting, this discussion is out of the scope of this article.

Move to the Auth0 folder and add a file named WebViewBrowserAuthenticator.cs. Put the following code in that file:

// Auth0/WebViewBrowserAuthenticator.cs

using IdentityModel.OidcClient.Browser;

namespace MauiAuth0App;

public class WebViewBrowserAuthenticator: IdentityModel.OidcClient.Browser.IBrowser
{
  private readonly WebView _webView;

  public WebViewBrowserAuthenticator(WebView webView)
  {
    _webView = webView;
  }

  public async Task<BrowserResult> InvokeAsync(BrowserOptions options, CancellationToken cancellationToken = default)
  {
    var tcs = new TaskCompletionSource<BrowserResult>();

    _webView.Navigated += (sender, e) =>
    {
      if (e.Url.StartsWith(options.EndUrl))
      {
        _webView.WidthRequest = 0;
        _webView.HeightRequest = 0;
        if (tcs.Task.Status != TaskStatus.RanToCompletion) 
        {
            tcs.SetResult(new BrowserResult { 
              ResultType = BrowserResultType.Success, 
              Response = e.Url.ToString() 
            });          
        }
      }

    };

    _webView.WidthRequest = 600;
    _webView.HeightRequest = 600;
    _webView.Source = new UrlWebViewSource { Url = options.StartUrl };

    return await tcs.Task;
  }
}

The WebViewBrowserAuthenticator class implements the IBrowser interface but uses a WebView passed to the constructor as an argument instead of the WebAuthenticator object.

In the MainPage.xaml file, add a WebView element as shown below:

<!-- MainPage.xaml -->
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MauiAuth0App.MainPage">
             
    <ScrollView>
        <VerticalStackLayout            
           Spacing="25" 
           Padding="30,0" 
           VerticalOptions="Center">
           <StackLayout
               x:Name="LoginView">
               <Button 
                   x:Name="LoginBtn"
                   Text="Log In"
                   SemanticProperties.Hint="Click to log in"
                   Clicked="OnLoginClicked"
                   HorizontalOptions="Center" />

               <!-- 👇 new code -->
               <WebView x:Name="WebViewInstance" />
               <!-- 👆 new code -->
            </StackLayout>

            <!-- ...existing markup... -->
           
        </VerticalStackLayout>
    </ScrollView>
</ContentPage>

The WebView element with the name WebViewInstance is added to the LoginView <StackLayout> element.

Now let's make the WebView operative only for the Windows platform. Open the MainPage.xaml.cs file and add the following lines of code:

// MainPage.xaml.cs

using MauiAuth0App.Auth0;

namespace MauiAuth0App;

public partial class MainPage : ContentPage
{
    int count = 0;
    private readonly Auth0Client auth0Client;
  
    public MainPage(Auth0Client client)
    {
    InitializeComponent();
    auth0Client = client;

    // 👇 new code
    #if WINDOWS
    auth0Client.Browser = new WebViewBrowserAuthenticator(WebViewInstance);
    #endif
    // 👆 new code
  }

  // ...existing code...
  
}

You notice here a conditional compilation statement that applies only to the Windows target platform. In this case, the Browser property of the Auth0 client will be assigned an instance of the WebViewBrowserAuthenticator class that uses the WebView we just added to the UI.

The last change applies to the MauiProgram.cs file. You need to customize the callback URI as shown here:

// MauiProgram.cs

using MauiAuth0App.Auth0;

namespace MauiAuth0App;

public static class MauiProgram
{
  public static MauiApp CreateMauiApp()
  {

    // ...existing code...
    
    builder.Services.AddSingleton(new Auth0Client(new()
    {
      Domain = "<YOUR_AUTH0_DOMAIN>"
      ClientId = "<YOUR_CLIENT_ID>"
      Scope = "openid profile",
      // 👇 new code
            #if WINDOWS
            RedirectUri = "http://localhost/callback"
            #else
      RedirectUri = "myapp://callback"
            #endif
      // 👆 new code
    }));

    return builder.Build();
  }
}

The original assignment for the RedirectUri property has been replaced by another conditional compilation statement. When Windows is the target platform, the callback URI will be http://localhost/callback instead of myapp://callback. This is needed because the WebView runs within your MAUI application, so there is no need for a custom scheme to make the browser call your app after the user authenticates. The redirection happens within the WebView, and you can control it.

Since you are using a different callback URI, you need to add the http://localhost/callback value to the Allowed Callback URLs and the Allowed Logout URLs fields of your application settings on the Auth0 dashboard.

These changes let Auth0 authentication work for your .NET MAUI app also on Windows. Instead of having the Auth0 Universal Login page in an external browser, you will see it embedded in your app, as in the following picture:

MAUI app on Windows using a WebView

Check out this article to learn how to call a protected API with a .NET MAUI application.

Summary

At the end of this article, you have the basic knowledge to add Auth0 authentication to your .NET MAUI application.

You learned how to create the Auth0 client component based on the OpenID Connect Client library, how to define the class that will launch the external browser for user authentication, and how to modify the UI to allow access only to authenticated users. Then you saw how to add platform-specific settings to allow the external browser to call your app after user authentication, and finally, how to run your app for a given platform.

After this first step, you showed the user's name and picture on the application UI and implemented the logout functionality.

Finally, you learned that a known issue prevents your MAUI app from authenticating users using an external browser on the Windows platform. To work around this problem, you implemented user authentication based on a WebView.

Throughout the article, you learned that the MAUI framework still has a few issues that need attention. Hopefully, they will be resolved in the near future so that some workarounds will not be required anymore.

You can download the full code of the project built in this article from this GitHub repository.

Take a look at this article to learn how your .NET MAUI application can call a protected API.

  • Twitter icon
  • LinkedIn icon
  • Faceboook icon