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!