Microsoft.AspNetCore.Components.Server.ServerAuthenticationStateProvider Cast works on Visual Studio Mac but now Windows

Nicholas Bolton 0 Reputation points
2023-09-17T19:32:04.5233333+00:00

Hello,

I recently went on vacation for about 3 weeks. I brought my laptop (2022 Macbook Air M2) and decided to learn C# and building web apps with Blazor. I built the app and it functioned fine for my standards. I even figured out user authentication.

My plan was to continue on the project from my desktop, which runs Windows 11. I had the project in a repo, so I made a Visual Studio project and simply cloned the repo and got to work. I tested it to see how it looked on my desktop and it looked the same as it did on my Mac. I thought great, until I tried logging into the app with my credentials. I received an error that is written below. I want to note that this error was not present while working on my Macbook, and only appears on my Windows machine. Running dotnet --info on both machines tells me that they are running the same version of .NET SDK (7.0.401) and the Host is of version 7.0.11.

System.InvalidCastException: Unable to cast object of type 'Microsoft.AspNetCore.Components.Server.ServerAuthenticationStateProvider' to type 'NeedBodies.Auth.UserAuthenticationStateProvider'.    at NeedBodies.Pages.Login.AuthenticateUserAsync() in C:\...\NeedBodies\Pages\Login.razor:line 64

Below is the code where the error is thrown.

@page "/login"

@using NeedBodies.Auth
@inject UserService userService
@inject IJSRuntime js
@inject AuthenticationStateProvider authStateProvider
@inject NavigationManager navManager

    @code{
        private string? strEmail { get; set; }
        private string? strPassword { get; set; }

        private bool isLoading = false;

        private async Task AuthenticateUserAsync()
        {

            isLoading = true;

            var user = userService.GetByEmail(strEmail);

            if (user == null || await user.CheckPassword(strPassword) != "success")
            {
                await js.InvokeVoidAsync("alert", "Invalid name/password");
                isLoading = false;
                return;
            }

			// error is here:
            var userAuthStateProvider = (UserAuthenticationStateProvider)authStateProvider;
            await userAuthStateProvider.UpdateAuthenticationState(new UserSession
            {
                Name = user.Name,
                Role = user.Role,
                ID = user.ID
            });

            isLoading = false;
            navManager.NavigateTo("/", true);

        }

    }

And also my UserAuthenticationStateProvider class is below:

using System.Security.Claims;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;


namespace NeedBodies.Auth
{
    public class UserAuthenticationStateProvider : AuthenticationStateProvider
    {

        private readonly ProtectedSessionStorage _sessionStorage;
        private ClaimsPrincipal _anonymous = new ClaimsPrincipal(new ClaimsIdentity());

        public UserAuthenticationStateProvider(ProtectedSessionStorage sessionStorage)
        {
            _sessionStorage = sessionStorage;
        }

        public override async Task<AuthenticationState> GetAuthenticationStateAsync()
        {
            try
            {
                var userSessionStorageResult = await _sessionStorage.GetAsync<UserSession>("UserSession");
                var userSession = userSessionStorageResult.Success ? userSessionStorageResult.Value : null;

                if (userSession == null)
                {
                    return await Task.FromResult(new AuthenticationState(_anonymous));
                }

                var claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>
                {
                    new Claim(ClaimTypes.Name, userSession.Name),
                    new Claim(ClaimTypes.Role, userSession.Role),
                    new Claim(ClaimTypes.Sid, userSession.ID)
                }, "UserAuth"));
                return await Task.FromResult(new AuthenticationState(claimsPrincipal));
            }
            catch
            {
                return await Task.FromResult(new AuthenticationState(_anonymous));
            }
        }

        public async Task UpdateAuthenticationState(UserSession userSession)
        {
            ClaimsPrincipal claimsPrincipal;

            if (userSession != null)
            {
                await _sessionStorage.SetAsync("UserSession", userSession);
                claimsPrincipal = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>
                {
                    new Claim(ClaimTypes.Name, userSession.Name),
                    new Claim(ClaimTypes.Role, userSession.Role),
                    new Claim(ClaimTypes.Sid, userSession.ID)
                }));
            }
            else
            {
                await _sessionStorage.DeleteAsync("UserSession");
                claimsPrincipal = _anonymous;
            }

            NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(claimsPrincipal)));
        }

    }
}

And finally, my Program.cs

using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
using Microsoft.AspNetCore.Components.Web;
using NeedBodies.Data;
using NeedBodies.Auth;
using Blazorise;
using Blazorise.Bootstrap;
using Blazorise.Icons.FontAwesome;
using Blazorise.Snackbar;

var builder = WebApplication.CreateBuilder(args);

builder.Services
    .AddBlazorise(options =>
    {
        options.Immediate = true;
    })
    .AddBootstrapProviders()
    .AddFontAwesomeIcons();

// Add services to the container.
builder.Services.AddAuthenticationCore();
builder.Services.AddAuthorizationCore();
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddScoped<ProtectedSessionStorage>();
builder.Services.AddScoped<AuthenticationStateProvider, UserAuthenticationStateProvider>();

// needed because async can't be in a constructor
HttpClient client = new HttpClient();
List<User> users;
try
{
    users = await client.GetFromJsonAsync<List<User>>(Utilities.httpAddress + "/users");
}
catch
{
    // can't connect to server
    return;
}
builder.Services.AddSingleton(sp =>
{
    var service = new UserService(users);
    return service;
});

builder.Services.AddServerSideBlazor();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.MapBlazorHub();
app.MapFallbackToPage("/_Host");

app.Run();


I am baffled as to why this would happen. I can't even write things I have tried because it doesn't make sense to me how the code works fine on my Macbook but not on Windows.

I am assuming this has something to do with Visual Studio on Mac/Windows, since the two programs seem to differ in lots of areas. But again, I have no clue.

If I need to provide any more information please let me know. Thank you!

ASP.NET Core
ASP.NET Core
A set of technologies in the .NET Framework for building web applications and XML web services.
3,541 questions
Blazor
Blazor
A free and open-source web framework that enables developers to create web apps using C# and HTML being developed by Microsoft.
1,102 questions
Visual Studio
Visual Studio
A family of Microsoft suites of integrated development tools for building applications for Windows, the web and mobile devices.
3,766 questions
{count} votes

1 answer

Sort by: Most helpful
  1. P a u l 8,931 Reputation points
    2023-09-17T19:49:53.0866667+00:00

    You're calling builder.Services.AddServerSideBlazor(); twice in your Program.cs

    I believe if you remove the second one it should work, or at least it'll replace the AuthenticationStateProvider with your UserAuthenticationStateProvider implementation.

    0 comments No comments