Share via

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!

Developer technologies | .NET | Blazor
Developer technologies | Visual Studio | Other
Developer technologies | Visual Studio | Other

A family of Microsoft suites of integrated development tools for building applications for Windows, the web, mobile devices and many other platforms. Miscellaneous topics that do not fit into specific categories.

Developer technologies | ASP.NET Core | Other

1 answer

Sort by: Most helpful
  1. P a u l 10,766 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.

    Was this answer helpful?

    0 comments No comments

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.