Problem with MAUI and Entra Login

Jai Holloway 115 Reputation points
2026-07-15T08:56:58.51+00:00

Hi there

I have downloaded the PublicSingletonClient to use for logging into our Azure platform for a MAUI app that we rewrote from Xamarin.

The app was working fine, but all of a sudden I am having problems. It fails to interactively login if you are already logged in. It goes through the login process for silent, then goes to the interactive bit and fails. It gives different MAUI errors each time it fails, but it's the login that's failing. I don't know how to get around this problem and the app now doesn't work at all. I need help please.

public async Task<string> SignInUserAndAcquireAccessToken(string[] scopes)
{
    Exception<NullReferenceException>.ThrowOn(() => this.PublicClientApplication == null, PCANotInitializedExceptionMessage);

    var existingUser = await FetchSignedInUserFromCache().ConfigureAwait(false);

    try
    {
        // 1. Try to sign-in the previously signed-in account
        if (existingUser != null)
        {
                    
            this.AuthResult = await this.PublicClientApplication
                .AcquireTokenSilent(scopes, existingUser)
                .ExecuteAsync()
                .ConfigureAwait(false);
        }
        else
        {
            if (this.IsBrokerInitialized)
            {
                Console.WriteLine("No accounts found in the cache. Trying Window's default account.");

                this.AuthResult = await this.PublicClientApplication
                    .AcquireTokenSilent(scopes, Microsoft.Identity.Client.PublicClientApplication.OperatingSystemAccount)
                    .ExecuteAsync()
                    .ConfigureAwait(false);
            }
            else
            {
                this.AuthResult = await SignInUserInteractivelyAsync(scopes);
            }
        }
    }
    catch (MsalUiRequiredException ex)
    {
        // A MsalUiRequiredException happened on AcquireTokenSilentAsync. This indicates you need to call AcquireTokenInteractive to acquire a token interactively
        Debug.WriteLine($"MsalUiRequiredException: {ex.Message}");
        this.AuthResult = await this.SignInUserInteractivelyAsync(scopes);
    }
    catch (MsalException msalEx)
    {
        Debug.WriteLine($"Error Acquiring Token interactively:{Environment.NewLine}{msalEx}");
        throw msalEx;
    }

    return this.AuthResult.AccessToken;
}


        public async Task<AuthenticationResult> SignInUserInteractivelyAsync(string[] scopes, IAccount existingAccount = null)
        {
            Exception<NullReferenceException>.ThrowOn(() => this.PublicClientApplication == null, PCANotInitializedExceptionMessage);

            if (this.PublicClientApplication == null)
                throw new NullReferenceException();

            // If the operating system has UI
            if (this.PublicClientApplication.IsUserInteractive())
            {

                if (PublicClientSingleton.Instance.UseEmbedded)
                {
#if ANDROID
                    return await this.PublicClientApplication.AcquireTokenInteractive(scopes)
						.WithLoginHint(existingAccount?.Username ?? String.Empty)
						.WithUseEmbeddedWebView(true)
						.WithParentActivityOrWindow(Platform.CurrentActivity)
						.ExecuteAsync()
						.ConfigureAwait(false);
#else
					return await this.PublicClientApplication.AcquireTokenInteractive(scopes)
                        .WithLoginHint(existingAccount?.Username ?? String.Empty)
                        .WithUseEmbeddedWebView(true)
                        .WithParentActivityOrWindow(new MainPage(new TourGrosses.ViewModels.MainPageViewModel(new TourGrosses.Services.ApiService())))
                        .ExecuteAsync()
                        .ConfigureAwait(false);
#endif
                }
                else
                {
                    SystemWebViewOptions systemWebViewOptions = new SystemWebViewOptions();
#if IOS
                    // Hide the privacy prompt in iOS
                    systemWebViewOptions.iOSHidePrivacyPrompt = true;
#endif
                    return await this.PublicClientApplication.AcquireTokenInteractive(scopes)
                        .WithLoginHint(existingAccount?.Username ?? String.Empty)
                        .WithSystemWebViewOptions(systemWebViewOptions)
                        .WithParentActivityOrWindow(PlatformConfig.Instance.ParentWindow)
                        .ExecuteAsync()
                        .ConfigureAwait(false);
                }
            }

            // If the operating system does not have UI (e.g. SSH into Linux), you can fallback to device code, however this
            // flow will not satisfy the "device is managed" CA policy.
            return await this.PublicClientApplication.AcquireTokenWithDeviceCode(scopes, (dcr) =>
            {
                Console.WriteLine(dcr.Message);
                return Task.CompletedTask;
            }).ExecuteAsync().ConfigureAwait(false);
        }
Developer technologies | .NET | .NET Multi-platform App UI
0 comments No comments

3 answers

Sort by: Most helpful
  1. AI answer

    2026-07-15T08:57:48.5266667+00:00

    The failure pattern matches the silent-first flow reaching interactive sign-in, which is expected when AcquireTokenSilent throws MsalUiRequiredException. That exception means interactive authentication is required because the cache cannot satisfy the request or extra user interaction is needed.

    For a .NET MAUI app, the most important fixes are:

    1. Keep the silent-first pattern The AcquireTokenSilent call should stay first, and MsalUiRequiredException should fall back to AcquireTokenInteractive. That part of the flow is correct.
    2. On Android, always pass the current activity to interactive sign-in AcquireTokenInteractive must use:
         .WithParentActivityOrWindow(Platform.CurrentActivity)
      
      Omitting the Android parent activity causes a runtime crash because MSAL cannot find a parent window for the sign-in UI.
    3. Ensure Platform.CurrentActivity is initialized In MainActivity.OnCreate, Platform.Init(this, savedInstanceState) must be called so Platform.CurrentActivity is set correctly before interactive sign-in runs.
    4. Do not create a new page just to supply a parent window This code is a likely problem:
         .WithParentActivityOrWindow(new MainPage(...))
      
      The parent window should be the platform window/activity configured by the app, not a newly created MAUI page instance. Use the platform-specific parent window that is initialized during app startup.
    5. For Android, verify the redirect URI and activity wiring The app registration must include an Android redirect URI in this format:
         msal{ClientId}://auth
      
      The Android activity handling the auth callback must use the same scheme and auth host in its intent filter.
    6. Set the Android parent window during startup In the MAUI Android setup, the current activity should be assigned as the parent window for authentication operations during MainActivity.OnCreate.

    A safer shape for the interactive call is:

    return await this.PublicClientApplication
        .AcquireTokenInteractive(scopes)
        .WithLoginHint(existingAccount?.Username ?? string.Empty)
    #if ANDROID
        .WithParentActivityOrWindow(Platform.CurrentActivity)
    #endif
        .ExecuteAsync();
    

    If embedded web view is required, keep it, but still use the correct parent activity/window.

    Also note:

    • If AcquireTokenSilent() fails with MsalUiRequiredException, that does not mean the app is broken by itself. It means the app must continue with interactive sign-in.
    • If the app is failing only when a user was previously signed in, the issue is more likely the interactive UI host/window setup than the silent token call.

    Recommended checks in order:

    1. In Platforms/Android/MainActivity.cs, confirm Platform.Init(this, savedInstanceState) is called.
    2. Confirm interactive Android sign-in uses .WithParentActivityOrWindow(Platform.CurrentActivity).
    3. Remove any use of new MainPage(...) as the parent window.
    4. Confirm the app registration redirect URI exactly matches msal{ClientId}://auth.
    5. Confirm the Android auth callback activity intent filter uses the same client ID-based scheme and auth host.
    6. Keep AcquireTokenSilent first, and only fall back to interactive on MsalUiRequiredException.

    One code issue should also be corrected:

    catch (MsalException msalEx)
    {
        Debug.WriteLine($"Error Acquiring Token interactively:{Environment.NewLine}{msalEx}");
        throw;
    }
    

    Using throw; preserves the original stack trace.


    AI-generated content may be incorrect. Read our transparency notes for more information.

    Was this answer helpful?

    1 person found this answer helpful.
  2. Jai Holloway 115 Reputation points
    2026-07-16T06:41:16.44+00:00

    The correct Enttitlements have been selected. As a note, this used to work. I was on leave for 2 weeks and it stopped working about a week before I went on leave. I actually have a working version on my phone, but I need to install it on another users phone and I can't get it to work.

    Was this answer helpful?


  3. Nancy Vo (WICLOUD CORPORATION) 7,985 Reputation points Microsoft External Staff Moderator
    2026-07-15T10:00:17.1933333+00:00

    Hello @Jai Holloway ,

    Thanks for your question.

    In your SignInUserInteractivelyAsync method, you have this line: .WithParentActivityOrWindow(new MainPage(...)).

    You may want to avoid using new MainPage() here. That creates a brand-new, invisible screen in the background. The Microsoft login popup needs to attach to the actual, active screen the user is currently looking at. Because it tries to attach to an invisible screen, the app gets confused and crashes.

    Also, when your silent login fails, it drops down to the catch (MsalUiRequiredException ex) block, but you are not passing the existingUser into the interactive method. This means the app forgets who is trying to log in.

    You can refer to these following steps:

    1. In your catch block, please make sure to pass the user context:
    catch (MsalUiRequiredException ex)
    {
        Debug.WriteLine($"MsalUiRequiredException: {ex.Message}");
        this.AuthResult = await this.SignInUserInteractivelyAsync(scopes, existingUser);
    }
    
    1. In your SignInUserInteractivelyAsync method, please do not create a new page. Point it to the actual active window:
    #else
    return await this.PublicClientApplication.AcquireTokenInteractive(scopes)
        .WithLoginHint(existingAccount?.Username ?? String.Empty)
        .WithUseEmbeddedWebView(true)
        .WithParentActivityOrWindow(PlatformConfig.Instance.ParentWindow)
        .ExecuteAsync()
        .ConfigureAwait(false);
    #endif
    

    Please check and let me know how it goes. If you encounter any issues, I'll be happy to assist further.

    I hope this addresses your question. If this response was helpful, please consider following the guidance to provide feedback. Thank you.

    Was this answer helpful?


Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.