RemoteAuthenticationService race condition in blazor WASM
- Dominant language
- C#
- Stars
- 38.4k
- Forks
- 10.9k
- Avg merge
- 2d 5h
- Merged PRs (30d)
- 276
Description
### Is there an existing issue for this?
- [x] I have searched the existing issues
### Describe the bug
Hi.
The issue I'm encountering occurs during MSAL login in a blazor WASM app. After logging in through the login.microsoft page I'm getting **Arg_NullReferenceException** error in the browser console. This happened after I upgraded my project from .NET 9 to .NET 10. I tried using 10.0.3, 10.0.7, 10.0.8 and 10.0.9 packages to no avail. I created a new barebones project and it seems to be working. I changed my project to be nearly 1:1 in both structure and packages as the empty template and the issue still occurs. I did a dotnet clean, nuget clean, and removed most of external packages to test in "isolation" - nothing helped.
I don't use any custom authentication handling - here's how i register MSAL:
```csharp
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAd", options.ProviderOptions.Authentication);
options.ProviderOptions.LoginMode = "redirect";
var clientScope = builder.Configuration.GetValue("AzureAd:ClientId");
if (string.IsNullOrWhiteSpace(clientScope))
{
throw new ArgumentNullException(nameof(clientScope));
}
options.ProviderOptions.DefaultAccessTokenScopes.Add("api://" + clientScope + "/.default");
})
```
**RedirectToLogin.razor**
```razor
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@inject NavigationManager Navigation
@code {
protected override void OnInitialized()
{
Navigation.NavigateToLogin("authentication/login");
}
}
```
**App.razor**
```razor
@if (context.User.Identity?.IsAuthenticated != true)
{
}
else
{
You are not authorized to access this resource.
}
```
**Authentication.razor**
```razor
@page "/authentication/{action}"
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@attribute [AllowAnonymous]
@code {
[Parameter] public string? Action { get; set; }
}
```
### Expected Behavior
The login should complete without any errors and the user should be authorized.
### Steps To Reproduce
I analysed the **AuthenticationService.js** file and the **RemoteAuthenticationService** class and debugged them step by step.
What I've noticed is that **CompleteSignInAsync**
```csharp
var result = await JsRuntime.InvokeAsync>("AuthenticationService.completeSignIn", context.Url);
```
returns null - although in the browser storage i can see MSAL entries.
I modified the AuthenticationService.js and mocked the completeSignIn method like this:
```js
async completeSignIn() {
console.warn("SIGN IN MOCKED!");
return {
status: "success",
state: {
returnUrl: "http://localhost:5175/"
}
};
}
```
I attached a logger to the **IJSRuntime InvokeAsync** methods and noticed that when **CompleteSignIn** is called by the **RemoteAuthenticationService** it quickly returns null. But when I InvokeAsync the same method with the same parameters later in the code (by pressing a button for example), I'm getting a correctly deserialized response.
I added an arbitrary delay of 3s to any InvokeAsync calls of the **RemoteAuthenticationService**. Now suddenly all my issues are gone. **CompleteSignIn** return data. **GetUser()** returns a user also (which it didn't before) and all is well. This seems to be a race conditon. From what I've tested the delay that fixes this problem needs to be about 100-300ms. I'd like to emphasise that I'm getting this race condition even on a mocked **AuthenticationService.js** which shouldn't depend on any internal state of the js auth pipeline, so it's almost like early js interop calls are broken.
Not sure why this happens but I'm currently working on it so if I figure anything else out I'll report this.
### Exceptions (if any)
```
Unhandled exception rendering component: Arg_NullReferenceException
System.NullReferenceException: Arg_NullReferenceException
at Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticationService`3.d__31[[Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticationState, Microsoft.AspNetCore.Components.WebAssembly.Authentication, Version=10.0.7.0, Culture=neutral, PublicKeyToken=adb9793829ddae60],[Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteUserAccount, Microsoft.AspNetCore.Components.WebAssembly.Authentication, Version=10.0.7.0, Culture=neutral, PublicKeyToken=adb9793829ddae60],[Microsoft.Authentication.WebAssembly.Msal.Models.MsalProviderOptions, Microsoft.Authentication.WebAssembly.Msal, Version=10.0.7.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]].MoveNext()
at Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticationService`3.d__21[[Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticationState, Microsoft.AspNetCore.Components.WebAssembly.Authentication, Version=10.0.7.0, Culture=neutral, PublicKeyToken=adb9793829ddae60],[Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteUserAccount, Microsoft.AspNetCore.Components.WebAssembly.Authentication, Version=10.0.7.0, Culture=neutral, PublicKeyToken=adb9793829ddae60],[Microsoft.Authentication.WebAssembly.Msal.Models.MsalProviderOptions, Microsoft.Authentication.WebAssembly.Msal, Version=10.0.7.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]].MoveNext()
at Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticatorViewCore`1.d__86[[Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticationState, Microsoft.AspNetCore.Components.WebAssembly.Authentication, Version=10.0.7.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]].MoveNext()
at Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticatorViewCore`1.d__84[[Microsoft.AspNetCore.Components.WebAssembly.Authentication.RemoteAuthenticationState, Microsoft.AspNetCore.Components.WebAssembly.Authentication, Version=10.0.7.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]].MoveNext()
at Microsoft.AspNetCore.Components.ComponentBase.CallStateHasChangedOnAsyncCompletion(Task task)
at Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync()
at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState)
```
### .NET Version
_No response_
### Anything else?
_No response_
Contributor guide
Assessment
This issue has not been assessed yet.