> ## Documentation Index
> Fetch the complete documentation index at: https://auth0.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Add login to your .NET MAUI application

> This guide demonstrates how to integrate Auth0 with a .NET MAUI application using the Auth0.OidcClient.MAUI SDK.

export const HowToSchema = () => <script type="application/ld+json">
    {'{"@context":"https://schema.org","@type":"HowTo"}'}
  </script>;

<HowToSchema />

<Accordion title="Use AI to integrate Auth0" icon="microchip-ai" iconType="solid" defaultOpen>
  If you use an AI coding assistant like Claude Code, Cursor, or GitHub Copilot, you can add Auth0 authentication automatically in minutes using [agent skills](https://agentskills.io/home).

  **Install:**

  ```bash theme={null}
  npx skills add auth0/agent-skills --skill auth0-quickstart --skill auth0-maui
  ```

  **Then ask your AI assistant:**

  ```text theme={null}
  Add Auth0 authentication to my .NET MAUI app
  ```

  Your AI assistant will automatically create your Auth0 application, fetch credentials, install the Auth0 OidcClient MAUI SDK, configure callback URLs, and implement login/logout flows. [Full agent skills documentation →](/docs/quickstart/agent-skills)
</Accordion>

This guide demonstrates how to integrate Auth0 with a .NET MAUI application using the [Auth0.OidcClient.MAUI SDK](https://github.com/auth0/auth0-oidc-client-net). 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**.

<Note>
  ## Prerequisites

  * **.NET 8 or .NET 9 SDK** installed ([download](https://dotnet.microsoft.com/download))
  * **MAUI workload** installed
  * An **Auth0 account** ([sign up for free](https://auth0.com/signup))
  * **Visual Studio 2022** (17.8+), **JetBrains Rider**, or **VS Code** with the .NET MAUI extension
</Note>

Verify your environment:

```shellscript theme={null}
dotnet --version        # Should be 8.x or 9.x
dotnet workload list    # Should include maui
```

If the MAUI workload is missing, install it:

```shellscript theme={null}
dotnet workload install maui
```

## Get started

<Steps>
  <Step title="Configure your Auth0 application" stepNumber={1}>
    Set up your Auth0 application so you have the credentials your MAUI app needs.

    1. Go to **[Auth0 Dashboard](https://manage.auth0.com/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:**

    ```text theme={null}
    myapp://callback
    ```

    **Allowed Logout URLs:**

    ```text theme={null}
    myapp://callback
    ```

    <Tip>
      Choose a scheme that is unique to your app. A reversed domain name works well, for example `com.mycompany.myapp://callback`.
    </Tip>

    Select **Save Changes**.

    <Check>
      You have a Native application in Auth0 with your **Domain** and **Client ID** noted, and the callback and logout URLs configured.
    </Check>
  </Step>

  <Step title="Create your MAUI project" stepNumber={2}>
    If you already have a .NET MAUI project, skip to Step 3. Otherwise, create one using the .NET CLI:

    ```shellscript theme={null}
    dotnet new maui -n MyMauiApp
    cd MyMauiApp
    ```
  </Step>

  <Step title="Install the Auth0 MAUI SDK" stepNumber={3}>
    Add the `Auth0.OidcClient.MAUI` NuGet package to your project:

    ```shellscript theme={null}
    dotnet add package Auth0.OidcClient.MAUI
    ```

    <Check>
      Run `dotnet restore` to confirm the package installed without errors.
    </Check>
  </Step>

  <Step title="Configure platform-specific callback handling" stepNumber={4}>
    .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.

    <Tabs>
      <Tab title="Android">
        Create a new file at `Platforms/Android/WebAuthenticatorActivity.cs`:

        ```csharp Platforms/Android/WebAuthenticatorActivity.cs lines theme={null}
        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.

        <Note>
          The `CALLBACK_SCHEME` value must exactly match the scheme in your `RedirectUri` and the **Allowed Callback URLs** in Auth0.
        </Note>
      </Tab>

      <Tab title="Windows">
        The Auth0 callback flow on Windows relies on URI protocol activation, which requires your app to be **packaged** (MSIX). The default .NET MAUI template creates a packaged app, so no changes are needed if you used `dotnet new maui` or the Visual Studio MAUI template.

        <Info>
          If your `.csproj` contains `<WindowsPackageType>None</WindowsPackageType>`, your app is **unpackaged** and protocol activation will not work. Remove that line or set it to `<WindowsPackageType>MSIX</WindowsPackageType>` to use a packaged app. To learn more, see the [.NET MAUI Windows packaging documentation](https://learn.microsoft.com/en-us/dotnet/maui/windows/deployment-overview).
        </Info>

        Two changes are required: registering the URI protocol and handling protocol activation.

        **1. Register the protocol** in `Platforms/Windows/Package.appxmanifest`. Add the `<Extensions>` block inside the existing `<Application>` element:

        ```xml Platforms/Windows/Package.appxmanifest lines theme={null}
        <Applications>
          <Application Id="App" Executable="$targetnametoken$.exe" EntryPoint="$targetentrypoint$">
            <Extensions>
              <uap:Extension Category="windows.protocol">
                <uap:Protocol Name="myapp"/>
              </uap:Extension>
            </Extensions>
          </Application>
        </Applications>
        ```

        **2. Handle activation** in `Platforms/Windows/App.xaml.cs`. Add the `CheckRedirectionActivation` call as the **first line** in the constructor:

        ```csharp Platforms/Windows/App.xaml.cs lines theme={null}
        public App()
        {
            if (Auth0.OidcClient.Platforms.Windows.Activator.Default.CheckRedirectionActivation())
                return;

            this.InitializeComponent();
        }
        ```
      </Tab>

      <Tab title="iOS / macOS">
        No platform-specific configuration is required. The SDK uses `ASWebAuthenticationSession` via MAUI's `WebAuthenticator` automatically.
      </Tab>
    </Tabs>
  </Step>

  <Step title="Add login and logout" stepNumber={5}>
    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.

    <Tabs>
      <Tab title="MainPageViewModel.cs">
        Create the ViewModel at `ViewModels/MainPageViewModel.cs`:

        ```csharp ViewModels/MainPageViewModel.cs lines theme={null}
        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));
            }
        }
        ```
      </Tab>

      <Tab title="MainPage.xaml">
        Wire up the UI in `MainPage.xaml`:

        ```xml MainPage.xaml lines theme={null}
        <?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="MyMauiApp.MainPage">

            <VerticalStackLayout Spacing="20" Padding="30" VerticalOptions="Center">

                <!-- Shown when logged out -->
                <Button Text="Log In"
                        Command="{Binding LoginCommand}"
                        IsVisible="{Binding IsNotAuthenticated}" />

                <!-- Shown when logged in -->
                <Label Text="{Binding Name, StringFormat='Welcome, {0}!'}"
                       IsVisible="{Binding IsAuthenticated}"
                       FontSize="24"
                       HorizontalOptions="Center" />

                <Label Text="{Binding Email}"
                       IsVisible="{Binding IsAuthenticated}"
                       HorizontalOptions="Center" />

                <Button Text="Log Out"
                        Command="{Binding LogoutCommand}"
                        IsVisible="{Binding IsAuthenticated}" />

            </VerticalStackLayout>
        </ContentPage>
        ```
      </Tab>

      <Tab title="MainPage.xaml.cs">
        Set the BindingContext in `MainPage.xaml.cs`:

        ```csharp MainPage.xaml.cs lines theme={null}
        namespace MyMauiApp;

        public partial class MainPage : ContentPage
        {
            public MainPage(MainPageViewModel viewModel)
            {
                InitializeComponent();
                BindingContext = viewModel;
            }
        }
        ```
      </Tab>
    </Tabs>

    <Check>
      Your project now has a ViewModel with login and logout commands, a data-bound XAML page, and wired-up code-behind.
    </Check>
  </Step>

  <Step title="Register services and instantiate the Auth0 client" stepNumber={6}>
    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:

    ```csharp MauiProgram.cs lines theme={null}
    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.
  </Step>

  <Step title="Run your app" stepNumber={7}>
    Build and run your .NET MAUI application

    **Expected 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
  </Step>
</Steps>

<Check>
  You now have a fully functional Auth0 login experience in your .NET MAUI application.
</Check>

***

## Troubleshooting

<AccordionGroup>
  <Accordion title="Callback URL mismatch">
    **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
  </Accordion>

  <Accordion title="Android: app does not return from browser">
    **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
  </Accordion>

  <Accordion title="Windows: login appears to hang">
    **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.
  </Accordion>

  <Accordion title="Windows: protocol activation does not work">
    **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.
  </Accordion>

  <Accordion title="Missing claims in user profile">
    **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.
  </Accordion>
</AccordionGroup>

***

## Next steps

You now have a working Auth0 integration in your .NET MAUI app. Explore these topics to extend your implementation:

<Accordion title="Refresh tokens">
  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:

  ```csharp expandable lines theme={null}
  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:

  ```csharp expandable lines theme={null}
  // 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;
  }
  ```

  <Info>
    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).
  </Info>
</Accordion>

<Accordion title="Call a protected API">
  To get an access token scoped to your API, set the `Scope` and pass an `audience` parameter to `LoginAsync()`:

  ```csharp expandable lines theme={null}
  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");
  ```
</Accordion>

<Accordion title="Organizations (B2B/enterprise)">
  Authenticate users within a specific Auth0 Organization:

  ```csharp expandable lines theme={null}
  var loginResult = await _client.LoginAsync(new
  {
      organization = "org_abc123"
  });
  ```

  To learn more, see [Organizations](/docs/manage-users/organizations).
</Accordion>

<Accordion title="Force re-authentication">
  Use `MaxAge` to force re-authentication after a specified time:

  ```csharp expandable lines theme={null}
  new Auth0ClientOptions
  {
      Domain = "{yourDomain}",
      ClientId = "{yourClientId}",
      RedirectUri = "myapp://callback",
      PostLogoutRedirectUri = "myapp://callback",
      MaxAge = TimeSpan.FromMinutes(30)
  }
  ```
</Accordion>

<Accordion title="Customize Universal Login">
  Tailor the Auth0 login page to match your brand, including colors, logos, and text.

  To learn more, see [Customize Universal Login](/docs/customize/universal-login-pages).
</Accordion>

***

## Additional resources

<CardGroup cols={3}>
  <Card title="SDK Repository" icon="github" href="https://github.com/auth0/auth0-oidc-client-net">
    Source code, samples, and API reference
  </Card>

  <Card title="Token Best Practices" icon="key" href="/docs/secure/tokens/token-best-practices">
    Security best practices for tokens
  </Card>

  <Card title="PKCE Flow" icon="shield" href="/docs/get-started/authentication-and-authorization-flow/authorization-code-flow-with-pkce">
    How native apps authenticate securely
  </Card>

  <Card title="Community Forum" icon="comments" href="https://community.auth0.com/">
    Get help from the Auth0 community
  </Card>
</CardGroup>
