Skip to main content
This guide demonstrates how to integrate Auth0 with a .NET MAUI application using the Auth0.OidcClient.MAUI SDK. By the end, your app will support login, logout, and displaying user profile information across Android, iOS, macOS, and Windows from a single codebase. This guide uses Auth0.OidcClient.MAUI version 1.x.

Prerequisites

  • .NET 8 or .NET 9 SDK installed (download)
  • MAUI workload installed
  • An Auth0 account (sign up for free)
  • Visual Studio 2022 (17.8+), JetBrains Rider, or VS Code with the .NET MAUI extension
Verify your environment:
dotnet --version        # Should be 8.x or 9.x
dotnet workload list    # Should include maui
If the MAUI workload is missing, install it:
dotnet workload install maui

Get started

1

Configure your Auth0 application

Set up your Auth0 application so you have the credentials your MAUI app needs.
  1. Go to Auth0 Dashboard > Applications > Applications
  2. Select Create Application
  3. Enter a name for your app (for example, “My MAUI App”), select Native as the application type, and select Create
  4. Go to the Settings tab on the Application Details page
  5. Note the Domain and Client ID values — you need these later
On the Settings tab, scroll to Application URIs and configure the following URLs. .NET MAUI apps use a custom URI scheme (for example, myapp://callback) rather than an HTTP URL.Allowed Callback URLs:
myapp://callback
Allowed Logout URLs:
myapp://callback
Choose a scheme that is unique to your app. A reversed domain name works well, for example com.mycompany.myapp://callback.
Select Save Changes.
You have a Native application in Auth0 with your Domain and Client ID noted, and the callback and logout URLs configured.
2

Create your MAUI project

If you already have a .NET MAUI project, skip to Step 3. Otherwise, create one using the .NET CLI:
dotnet new maui -n MyMauiApp
cd MyMauiApp
3

Install the Auth0 MAUI SDK

Add the Auth0.OidcClient.MAUI NuGet package to your project:
dotnet add package Auth0.OidcClient.MAUI
Run dotnet restore to confirm the package installed without errors.
4

Configure platform-specific callback handling

.NET MAUI apps must register a callback handler on each platform so the system browser can redirect back to your app after authentication. Follow the instructions for each platform you are targeting.
Create a new file at Platforms/Android/WebAuthenticatorActivity.cs:
Platforms/Android/WebAuthenticatorActivity.cs
using Android.App;
using Android.Content;
using Android.Content.PM;

namespace MyMauiApp.Platforms.Android;

[Activity(NoHistory = true, LaunchMode = LaunchMode.SingleTop, Exported = true)]
[IntentFilter(new[] { Intent.ActionView },
              Categories = new[] { Intent.CategoryDefault, Intent.CategoryBrowsable },
              DataScheme = CALLBACK_SCHEME)]
public class WebAuthenticatorActivity : Microsoft.Maui.Authentication.WebAuthenticatorCallbackActivity
{
    const string CALLBACK_SCHEME = "myapp";
}
Replace myapp with the URI scheme you configured in Step 1.
The CALLBACK_SCHEME value must exactly match the scheme in your RedirectUri and the Allowed Callback URLs in Auth0.
5

Add login and logout

You need to create/modify three files: a ViewModel with the login/logout logic, a XAML page for the UI, and a code-behind file to wire them together.
Create the ViewModel at ViewModels/MainPageViewModel.cs:
ViewModels/MainPageViewModel.cs
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows.Input;
using Auth0.OidcClient;

namespace MyMauiApp.ViewModels;

public class MainPageViewModel : INotifyPropertyChanged
{
    private readonly Auth0Client _client;
    private string _name;
    private string _email;
    private bool _isAuthenticated;

    public event PropertyChangedEventHandler PropertyChanged;

    public string Name
    {
        get => _name;
        set { _name = value; OnPropertyChanged(); }
    }

    public string Email
    {
        get => _email;
        set { _email = value; OnPropertyChanged(); }
    }

    public bool IsAuthenticated
    {
        get => _isAuthenticated;
        set
        {
            _isAuthenticated = value;
            OnPropertyChanged();
            OnPropertyChanged(nameof(IsNotAuthenticated));
        }
    }

    public bool IsNotAuthenticated => !IsAuthenticated;

    public ICommand LoginCommand { get; }
    public ICommand LogoutCommand { get; }

    public MainPageViewModel(Auth0Client client)
    {
        _client = client;
        LoginCommand = new Command(async () => await LoginAsync());
        LogoutCommand = new Command(async () => await LogoutAsync());
    }

    private async Task LoginAsync()
    {
        var loginResult = await _client.LoginAsync();

        if (loginResult.IsError)
        {
            if (loginResult.Error == "UserCancel")
                return; // User closed the browser — not an error

            await Shell.Current.DisplayAlert("Login failed", loginResult.Error, "OK");
            return;
        }

        // Read user profile claims from the ID token
        Name = loginResult.User.FindFirst(c => c.Type == "name")?.Value;
        Email = loginResult.User.FindFirst(c => c.Type == "email")?.Value;
        IsAuthenticated = true;
    }

    private async Task LogoutAsync()
    {
        await _client.LogoutAsync();

        Name = null;
        Email = null;
        IsAuthenticated = false;
    }

    private void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}
Your project now has a ViewModel with login and logout commands, a data-bound XAML page, and wired-up code-behind.
6

Register services and instantiate the Auth0 client

Now register the Auth0Client, the ViewModel, and the page with dependency injection in MauiProgram.cs. This wires everything together so the Auth0 client is injected into the ViewModel, and the ViewModel is injected into the page:
MauiProgram.cs
using Auth0.OidcClient;
using MyMauiApp.ViewModels;

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
            });

        // Configure the Auth0 client with your credentials from Step 1
        builder.Services.AddSingleton(new Auth0Client(new Auth0ClientOptions
        {
            Domain = "{yourDomain}",
            ClientId = "{yourClientId}",
            RedirectUri = "myapp://callback",
            PostLogoutRedirectUri = "myapp://callback"
        }));

        // Register the page and ViewModel created in Step 5
        builder.Services.AddTransient<MainPage>();
        builder.Services.AddTransient<MainPageViewModel>();

        return builder.Build();
    }
}
Replace {yourDomain} and {yourClientId} with the values from your Auth0 application settings (Step 1). RedirectUri and PostLogoutRedirectUri are required for MAUI apps. Use the same callback URL you entered in the Auth0 Dashboard.
7

Run your app

Build and run your .NET MAUI applicationExpected flow:
  1. App launches and shows the Log In button
  2. Tap Log In → system browser opens with Auth0 Universal Login
  3. Complete authentication (sign up or log in)
  4. Browser redirects back to your app
  5. The app displays your name and email with a Log Out button
You now have a fully functional Auth0 login experience in your .NET MAUI application.

Troubleshooting

Symptom: The browser shows “Callback URL mismatch. The provided redirect_uri is not in the list of allowed callback URLs.”Fix:
  1. Confirm the Client ID in your code matches the application you configured in the Auth0 Dashboard
  2. Clear the Allowed Callback URLs field and retype myapp://callback manually — copy-paste often introduces invisible trailing spaces or newlines
  3. Ensure the match is exact: no trailing slash, lowercase only, no whitespace
  4. Select Save Changes in the Dashboard and verify the value persisted
  5. Check the browser address bar for the redirect_uri query parameter to see what your app is actually sending
Symptom: Browser opens for login but never redirects back to the app.Fix:
  1. Verify DataScheme in WebAuthenticatorActivity.cs matches your RedirectUri scheme
  2. Ensure the Activity has Exported = true
  3. Confirm the Allowed Callback URLs in Auth0 Dashboard match exactly
Symptom: Browser opens but a second instance of the app opens instead of resuming.Fix: Ensure Auth0.OidcClient.Platforms.Windows.Activator.Default.CheckRedirectionActivation() is called as the very first line of the App constructor in Platforms/Windows/App.xaml.cs, and that the protocol name in Package.appxmanifest matches your callback URI scheme.
Symptom: After login, the browser shows an error or nothing happens — the app never receives the callback.Fix: Your app must be a packaged (MSIX) application. Check your .csproj file for a <WindowsPackageType> element:
  • If it is set to None, protocol activation is not available. Remove the line or change it to <WindowsPackageType>MSIX</WindowsPackageType>.
  • If the element is absent, your app is already packaged by default — verify that Package.appxmanifest contains the <uap:Protocol> extension from Step 4.
Symptom: name, email, or picture claims are absent from loginResult.User.Fix: Verify that openid profile email are included in Auth0ClientOptions.Scope. If you have customized the scope, ensure openid is always present.

Next steps

You now have a working Auth0 integration in your .NET MAUI app. Explore these topics to extend your implementation:
The Auth0 MAUI SDK supports refresh tokens for silently renewing sessions without re-prompting the user.

Enable refresh tokens

Add offline_access to the Scope property:
builder.Services.AddSingleton(new Auth0Client(new Auth0ClientOptions
{
    Domain = "{yourDomain}",
    ClientId = "{yourClientId}",
    RedirectUri = "myapp://callback",
    PostLogoutRedirectUri = "myapp://callback",
    Scope = "openid profile email offline_access"
}));

Use refresh tokens

After login, store the refresh token and use it to silently renew the session:
// After login
var refreshToken = loginResult.RefreshToken;

// Later, renew the session
var refreshResult = await _client.RefreshTokenAsync(refreshToken);

if (!refreshResult.IsError)
{
    var newAccessToken = refreshResult.AccessToken;
    var newIdToken = refreshResult.IdentityToken;
}
If RefreshToken is null after login, ensure Allow Offline Access is enabled in your API settings in the Auth0 Dashboard (when using an audience parameter).
To get an access token scoped to your API, set the Scope and pass an audience parameter to LoginAsync():
var loginResult = await _client.LoginAsync(new
{
    audience = "https://myapi.example.com"
});

// The access token is now scoped to your API
var accessToken = loginResult.AccessToken;

var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization =
    new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken);

var response = await httpClient.GetAsync("https://myapi.example.com/posts");
Authenticate users within a specific Auth0 Organization:
var loginResult = await _client.LoginAsync(new
{
    organization = "org_abc123"
});
To learn more, see Organizations.
Use MaxAge to force re-authentication after a specified time:
new Auth0ClientOptions
{
    Domain = "{yourDomain}",
    ClientId = "{yourClientId}",
    RedirectUri = "myapp://callback",
    PostLogoutRedirectUri = "myapp://callback",
    MaxAge = TimeSpan.FromMinutes(30)
}
Tailor the Auth0 login page to match your brand, including colors, logos, and text.To learn more, see Customize Universal Login.

Additional resources

SDK Repository

Source code, samples, and API reference

Token Best Practices

Security best practices for tokens

PKCE Flow

How native apps authenticate securely

Community Forum

Get help from the Auth0 community