provider.SignInAsync() error from CommunityToolkit.Authentication.Uwp

Hong 1,526 Reputation points

The following code of a classic UWP app used for OneDrive access worked for a few years without any issues:

string[] scopes = new string[] { "Files.ReadWrite.AppFolder" };
ProviderManager.Instance.GlobalProvider = new WindowsProvider(scopes);
IProvider provider = ProviderManager.Instance.GlobalProvider;
await provider.SignInAsync();


await provider.SignInAsync() started to throw the exception recently:

Source = "CommunityToolkit.Authentication.Uwp"

Message = "Object reference not set to an instance of an object."

at CommunityToolkit.Authentication.WindowsProvider.<GetTokenAsync>d__38.MoveNext() in CommunityToolkit.Authentication\WindowsProvider.cs:line 186 at CommunityToolkit.Authentication.WindowsProvider.<SignInAsync>d__35.MoveNext() in CommunityToolkit.Authentication\WindowsProvider.cs:line 110

I don't know exactly when it started to have this, and what changes caused this. Could anyone offer a tip on how to find the culprit?

0 comments No comments

Sign in to comment

Answer accepted by question author

Jack Dang (WICLOUD CORPORATION) 18,970 Reputation points Microsoft External Staff Moderator

Hi @Hong ,

The problem does not seem to be your sign-in logic anymore. The crash is happening inside WindowsProvider.GetWebTokenRequest(), when the Toolkit tries to loop through _webAccountProviderConfig.MSATokenRequestProperties.

That means the Toolkit is using a config object that was not fully initialized. So this looks more like a Toolkit bug or package/version mismatch than an issue in your app code. Reinstalling the app would not fix that.

The next thing I would try is creating WindowsProvider with an explicit WebAccountProviderConfig, and making sure the MSA and AAD token property collections are initialized before sign-in.

For example:

var webAccountProviderConfig = new WebAccountProviderConfig
{
 WebAccountProviderType = WebAccountProviderType.Msa,
 MSATokenRequestProperties = new Dictionary<string, string>(),
 AADTokenRequestProperties = new Dictionary<string, string>()
};

var provider = new WindowsProvider(
 scopes,
 webAccountProviderConfig: webAccountProviderConfig,
 autoSignIn: false);

That sample is only for reference, so you may need to adjust it for your project and package version.

One important detail is that the public 7.1.x docs do not clearly show these token property members, but your decompiled code does. That suggests you may be using a different build or variant of the package.

If you can edit the Toolkit source, the cleanest fix is to add null checks in GetWebTokenRequest() before looping through MSATokenRequestProperties or AADTokenRequestProperties.

At this point, I would treat this as a library issue rather than an app issue. The sign-in dialog is showing correctly, and the crash happens only after that when the Toolkit builds the token request.

If the app is still being maintained, I would also consider moving away from CommunityToolkit.Authentication.Uwp over time. MSAL with Microsoft Graph is the more reliable long-term option.

  1. Jack Dang (WICLOUD CORPORATION) 18,970 Reputation points Microsoft External Staff Moderator

    Hi @Hong ,

    I have converted my comment to answer. Feel free to follow the instructions here. Thank you.

  2. Hong 1,526 Reputation points

    Thank you again.

    I will follow your suggestion to migrate to MSAL with Microsoft Graph in the future. I will not try to fix any issues related toCommunityToolkit.Authentication.Uwp again.


Sign in to comment

2 additional answers

  1. Jack Dang (WICLOUD CORPORATION) 18,970 Reputation points Microsoft External Staff Moderator

    Hi @Hong ,

    Thanks for reaching out.

    From the stack trace, this looks less like your app code suddenly breaking and more like the authentication flow underneath it no longer returning what WindowsProvider expects. The failure is happening inside WindowsProvider.GetTokenAsync(), so the likely issue is that the Windows account broker is returning an unexpected or incomplete token result, and the Toolkit ends up hitting a null reference while processing it.

    The first thing I would try is turning off the automatic silent sign-in in the constructor and handling the flow explicitly. WindowsProvider defaults autoSignIn to true, so it may already be kicking off a silent sign-in before your later SignInAsync() call runs. Driving that flow yourself is the cleanest short-term test and may avoid the overlap entirely:

    string[] scopes = { "Files.ReadWrite.AppFolder" };
    
    var provider = new WindowsProvider(scopes, autoSignIn: false);
    ProviderManager.Instance.GlobalProvider = provider;
    
    if (!await provider.TrySilentSignInAsync())
    {
      await provider.SignInAsync();
    }
    

    That sample is mainly meant as a reference for the sign-in flow, so you may need to adjust it a bit to fit your app structure and how your project initializes authentication.

    If that still fails, I would clear any cached account state next. This provider stores account information in local app settings, and if those cached values no longer line up with what Windows account services expect, sign-in can start failing even though the code has not changed. Clearing the app's local data, reinstalling the app, or removing and re-adding the Microsoft account under Windows account settings are all good checks. It is also worth trying the same build on a different Windows profile or another machine. If it only fails on one profile, that points much more strongly to stale local account or token state than to a regression in your app.

    One other thing to verify is that the provider is being created and used on a UI-capable thread. The interactive path uses AccountsSettingsPane, so if this is now being called from a different context than before, passing the correct DispatcherQueue into WindowsProvider is important.

    If you want to narrow down the culprit more precisely, try logging the full exception details and, if possible, step into the Toolkit source around GetTokenAsync(). If the token request is coming back as successful but the response payload is null or empty, that would confirm this is really an edge case between the Toolkit and the Windows broker rather than a normal sign-in error in your app.

    For the longer term, I would also keep in mind that CommunityToolkit.Authentication.Uwp is an older authentication stack and is not especially resilient to changes in the identity platform. If this app is still being maintained, moving the sign-in flow to MSAL with Microsoft Graph would be the more reliable path going forward, and it will also give you much better diagnostics when token acquisition fails.

    Hope this helps! If my answer was helpful, I would greatly appreciate it if you could follow the instructions here so others with the same problem can benefit as well.

    1. Hong 1,526 Reputation points

      Thank you for the detailed solution.

      Unfortunately, it still does not work. Let me provide more details.

      if (provider.State == ProviderState.SignedOut)
      {
       bool bSilentOk = await provider.TrySilentSignInAsync();
       if (bSilentOk)
       {
       //Great, do nothing and continue to use the provider
       }
       else
       {
       await provider.SignInAsync();
       }
      }
      

      await provider.SignInAsync() shows the following dialog:👁 User's image

      When an account is selected, the following exception

      System.NullReferenceException: 'Object reference not set to an instance of an object.'

      is thrown at

      foreach (KeyValuePair<string, string> mSATokenRequestProperty in _webAccountProviderConfig.MSATokenRequestProperties)

      in

      `System.NullReferenceException: 'Object reference not set to an instance of an object.'`
      ```is thrown at 
      
      `foreach (KeyValuePair<string, string> mSATokenRequestProperty in _webAccountProviderConfig.MSATokenRequestProperties)`
      
      in 
      
      
      
      ```go
      private WebTokenRequest GetWebTokenRequest(WebAccountProvider provider, string clientId, string[] scopes)
      	{
      		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
      		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
      		//IL_0023: Expected O, but got Unknown
      		string text = string.Join(' ', scopes);
      		WebTokenRequest val = (string.IsNullOrWhiteSpace(clientId) ? new WebTokenRequest(provider, text) : new WebTokenRequest(provider, text, clientId));
      		val.Properties.Add("resource", "https://graph.microsoft.com");
      		if (provider.Authority == "consumers")
      		{
      			foreach (KeyValuePair<string, string> mSATokenRequestProperty in _webAccountProviderConfig.MSATokenRequestProperties)
      			{
      				val.Properties.Add(mSATokenRequestProperty);
      			}
      		}
      		else if (provider.Authority == "organizations")
      		{
      			foreach (KeyValuePair<string, string> aADTokenRequestProperty in _webAccountProviderConfig.AADTokenRequestProperties)
      			{
      				val.Properties.Add(aADTokenRequestProperty);
      			}
      		}
      		return val;
      	}
      

      which is a method of Public class WindowsProvider : BaseProvider.

      VS shows authResult = await AuthenticateInteractiveAsync(scopes); throws the exception with the following stack:

      This exception was originally thrown at this call stack:
       CommunityToolkit.Authentication.WindowsProvider.GetWebTokenRequest(Windows.Security.Credentials.WebAccountProvider, string, string[]) in WindowsProvider.cs
       CommunityToolkit.Authentication.WindowsProvider.AuthenticateInteractiveAsync.AnonymousMethod__0() in WindowsProvider.cs
      

      from:

      public override async Task<string> GetTokenAsync(bool silentOnly = false)
      {
      	await SemaphoreSlim.WaitAsync();
      	try
      	{
      		string[] scopes = _scopes;
      		WebTokenRequestResult authResult = await AuthenticateSilentAsync(scopes);
      		WebTokenRequestResult obj = authResult;
      		if (obj == null || (int)obj.ResponseStatus > 0)
      		{
      			if (silentOnly)
      			{
      				return null;
      			}
      			authResult = await AuthenticateInteractiveAsync(scopes);
      		}
      		WebTokenRequestResult obj2 = authResult;
      		if (obj2 != null && (int)obj2.ResponseStatus == 0)
      		{
      			WebAccount webAccount = authResult.ResponseData[0].WebAccount;
      			await SetAccountAsync(webAccount);
      			return authResult.ResponseData[0].Token;
      		}
      		WebTokenRequestResult obj3 = authResult;
      		if (obj3 != null && (int)obj3.ResponseStatus == 1)
      		{
      			return null;
      		}
      		WebTokenRequestResult obj4 = authResult;
      		if (((obj4 != null) ? obj4.ResponseError : null) != null)
      		{
      			throw new Exception(authResult.ResponseError.ErrorCode + ": " + authResult.ResponseError.ErrorMessage);
      		}
      		throw new Exception("Token request was not successful, but is also missing an error message.");
      	}
      	catch (Exception ex)
      	{
      		throw ex;
      	}
      	finally
      	{
      		SemaphoreSlim.Release();
      	}
      }
      
      	private Task<WebTokenRequestResult> AuthenticateInteractiveAsync(string[] scopes)
      	{
      		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
      		//IL_0036: Expected O, but got Unknown
      		TaskCompletionSource<WebTokenRequestResult> tcs = new TaskCompletionSource<WebTokenRequestResult>();
      		if (!DispatcherQueue.TryEnqueue((DispatcherQueueHandler)async delegate
      		{
      			_ = 2;
      			try
      			{
      				WebAccount webAccount = _webAccount;
      				WebTokenRequestResult result;
      				if (webAccount != null)
      				{
      					WebAccountProvider webAccountProvider = webAccount.WebAccountProvider;
      					result = await WebAuthenticationCoreManager.RequestTokenAsync(GetWebTokenRequest(webAccountProvider, _webAccountProviderConfig.ClientId, scopes), webAccount);
      				}
      				else
      				{
      					WebAccountProvider provider = await ShowAccountSettingsPaneAndGetProviderAsync();
      					result = await WebAuthenticationCoreManager.RequestTokenAsync(GetWebTokenRequest(provider, _webAccountProviderConfig.ClientId, scopes));
      				}
      				tcs.SetResult(result);
      			}
      			catch (Exception exception)
      			{
      				tcs.SetException(exception);
      			}
      		}))
      		{
      			tcs.SetException(new InvalidOperationException("Failed to enqueue the operation."));
      		}
      		return tcs.Task;
      	}
      

      The sequence:

      GetTokenAsync > AuthenticateInteractiveAsync > GetWebTokenRequest > _webAccountProviderConfig.MSATokenRequestProperties > NullReferenceException

    2. Hong 1,526 Reputation points

      I have also tried uninstalling and reinstalling the app to no avail.

    3. Hong 1,526 Reputation points

      Thank you for all the help. The explicit WebAccountProviderConfig worked. Could you turn your last comment into an answer for me to accept?


    Sign in to comment
  2. AI answer

    The exception is thrown inside WindowsProvider.SignInAsync, specifically in GetTokenAsync, which indicates the internal provider state is null when the sign‑in is invoked. From the available APIs, the following points are relevant:

    1. WindowsProvider is an IProvider implementation that uses the Windows account system and the AccountsSettingsPane for authentication.
    2. The constructor has an autoSignIn parameter (default true) and a dispatcherQueue parameter:
       public WindowsProvider(
       string[] scopes = default,
       WebAccountProviderConfig? webAccountProviderConfig = default,
       AccountsSettingsPaneConfig? accountsSettingsPaneConfig = default,
       bool autoSignIn = true,
       Windows.System.DispatcherQueue dispatcherQueue = default);
      
      It attempts a silent sign‑in on construction when autoSignIn is true.
    3. WindowsProvider.TrySilentSignInAsync is the supported way to check whether a user is already signed in without UI:
       public override Task<bool> TrySilentSignInAsync();
      
    4. IProvider.TrySilentSignInAsync is exposed at the interface level as well:
       public Task<bool> TrySilentSignInAsync();
      

    Given that the same code worked for years and now fails with a NullReferenceException inside GetTokenAsync, the most likely cause is that the internal Windows account provider or token acquisition context is no longer initialized as expected in the current environment (for example, due to changes in available accounts, OS behavior, or how the AccountsSettingsPane is invoked).

    To isolate and work around the issue using the available APIs:

    1. Explicitly construct WindowsProvider and avoid relying solely on SignInAsync:
       string[] scopes = { "Files.ReadWrite.AppFolder" };
       var windowsProvider = new WindowsProvider(scopes);
       ProviderManager.Instance.GlobalProvider = windowsProvider;
       IProvider provider = ProviderManager.Instance.GlobalProvider;
      
    2. First attempt a silent sign‑in and only then call SignInAsync if needed:
       bool silentOk = await provider.TrySilentSignInAsync();
       if (!silentOk)
       {
       await provider.SignInAsync();
       }
      
      This uses the documented TrySilentSignInAsync path and may avoid hitting the null state that causes the exception.
    3. If the app is not running on the UI thread when creating WindowsProvider, pass an appropriate DispatcherQueue so that UI operations for the AccountsSettingsPane can be dispatched correctly. The constructor explicitly allows specifying a DispatcherQueue for UI updates.
    4. If the app previously relied on automatic silent sign‑in (autoSignIn = true), try disabling it and controlling the flow explicitly:
       var windowsProvider = new WindowsProvider(scopes, autoSignIn: false);
       ProviderManager.Instance.GlobalProvider = windowsProvider;
       IProvider provider = ProviderManager.Instance.GlobalProvider;
       
       bool silentOk = await provider.TrySilentSignInAsync();
       if (!silentOk)
       {
       await provider.SignInAsync();
       }
      
    5. Use ProviderManager consistently as the single source of the global provider:
       ProviderManager.Instance.GlobalProvider = windowsProvider;
       // Later, controls and helpers will use ProviderManager.Instance.GlobalProvider
      

    If the exception persists even when using TrySilentSignInAsync and explicit construction, the next step is to inspect the environment (Windows account availability, whether the AccountsSettingsPane can be shown, and whether the app is on a supported Windows build) and compare it with a machine where the same code still works, since the public API surface itself (constructor, SignInAsync, TrySilentSignInAsync) has not changed in the referenced version.


    References:

    AI-generated content may be incorrect. Read our transparency notes for more information.
Sign in to answer

Your answer