I have an application written in Blazor WebAssembly under .Net8. This app is hosted model which means that has a client and server project. For the intercommunication web api is used. The app is using authentication and authorization.
The Server Program.cs file
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.DataProtectionProvider = DataProtectionProvider.Create(Parameters.app_protector);
options.ExpireTimeSpan = TimeSpan.FromMinutes(60);
options.SlidingExpiration = true;
options.LoginPath = new PathString("/auth/login");
options.LogoutPath = new PathString("/auth/logout");
options.AccessDeniedPath = new PathString("/");
options.Cookie = new CookieBuilder();
options.Cookie.Name = Parameters.app_cookie;
options.Cookie.MaxAge = options.ExpireTimeSpan;
options.Cookie.SameSite = SameSiteMode.Strict; //TESTING WITH STRICT
options.Cookie.SecurePolicy = CookieSecurePolicy.None;
options.CookieManager = new ChunkingCookieManager();
options.EventsType = typeof(CustomCookieAuthenticationEvents);
});
app.UseAuthentication();
app.UseAuthorization();
The Server Controller for SignIn:
[HttpPost, Route("/api/auth/login")]
public IActionResult AuthLogin(Authentication authentication)
{
try
{
int auth_id = _IAuth.AuthLogin(authentication); //VALIDATE INFO IN DATABASE
if (auth_id != -1)
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Sid, auth_id.ToString()),
new Claim(ClaimTypes.Name, authentication.user_name.ToString()),
};
var claimsIdentity = new ClaimsIdentity(claims, "Authentication");
var properties = new AuthenticationProperties()
{
IsPersistent = true,
AllowRefresh = true
};
HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(claimsIdentity), properties);
}
return Ok(auth_id); //RETURN A NUMBER GRATER FROM -1 TO SIGN IN
}
catch { throw; }
}
The authentication in Client is handled via a CustomAuthStateProvider
HttpResponseMessage httpResponseMessage = await _httpClient.GetAsync("/api/auth/check");
if (httpResponseMessage.IsSuccessStatusCode)
{
HttpContent content = httpResponseMessage.Content;
AuthenticationData? authenticationData_ = await content.ReadFromJsonAsync<AuthenticationData?>();
authenticationData = CheckAuthenticationData(authenticationData_);
//HELPER MESSAGE FOR BROWSER
if (authenticationData != null)
{ Console.WriteLine("user_is_authorized"); }
else
{ Console.WriteLine("user_not_authorized"); }
}
Which gets a response from the Server Controller:
AuthenticationData? authenticationData = new AuthenticationData();
var authorizationResult = _authorizationService.AuthorizeAsync(User, UserPolicy).Result;
if (authorizationResult.Succeeded)
{
authenticationData.Sid = User.Claims.Where(x => x.Type == ClaimTypes.Sid).Select(x => x.Value).FirstOrDefault().NStringToString();
authenticationData.Name = User.Claims.Where(x => x.Type == ClaimTypes.Name).Select(x => x.Value).FirstOrDefault().NStringToString();
HttpContext.AuthenticateAsync();
}
return authenticationData;
Everything works fine when work locally, or an AzureWebApp or into Kubernetes Cluster, but as soon as we scale out with replicas more than 1, failures starting appear (in this case signalr methods I use for some messages are failing, but also authentication):

I have done some investigation and I think that the server holds some state of the authenticated user, as refreshing the page constantly one(1) of the three(3) times works, and considering that replicas are set to 3, it makes sense to me as kubernetes might have Round-Robin into loadbalancher as default method for distributing calls. I am also receiving the message "user_not_authorized" from the console write:

If am right, why server keeps any kind of state? Or any other idea which might I missed??