Need help with a problem in my Blazor .NET 8 server application

Esma 20 Reputation points
2024-04-18T11:18:23.39+00:00

I'm facing an issue and I'm not sure who to ask for help. Could you please provide me with some suggestions? I've been unable to find my mistake despite my research.I've implemented user authentication in a Blazor .NET 8 server application, where I'm trying to store user information using session storage until they log out. On the homepage, I have some charts, and when I refresh the page to check if the user session is still valid, it fails and gives me this error.

using BlazorApp4.Services;
using Blazored.SessionStorage;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Components.Authorization;
using System.Security.Claims;
public class CustomAuthenticationStateProvider : AuthenticationStateProvider
{
    private ISessionStorageService _sessionStorage;
    private IUserService _userService;
    public CustomAuthenticationStateProvider(ISessionStorageService sessionStorage, IUserService userService)
    {
        _sessionStorage = sessionStorage;
        _userService = userService;
    }
    //prerendering sırasında ya da kullanıcı bilgisi olmadığında anonim kimlik döndürür
    public override Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        var anonymousUser = new ClaimsPrincipal(new ClaimsIdentity());
        return Task.FromResult(new AuthenticationState(anonymousUser));
    }
    public async Task LoadUserStateAsync()
    {
        var username = await _sessionStorage.GetItemAsStringAsync("username");
        ClaimsIdentity identity;
        if (!string.IsNullOrEmpty(username))
        {
            identity = new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, username) }, "CustomAuthType");
        }
        else
        {
            identity = new ClaimsIdentity();
        }
        var user = new ClaimsPrincipal(identity);
        var currentState = new AuthenticationState(user);
        //var snapshot = new AuthenticationState(new ClaimsPrincipal(identity));
        //await Task.Yield();
        NotifyAuthenticationStateChanged(Task.FromResult(currentState));
    }
    //public async Task LoadUserStateAsync()
    //{
    //    var username = await _sessionStorage.GetItemAsStringAsync("username");
    //    ClaimsIdentity identity = !string.IsNullOrEmpty(username)
    //        ? new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, username) }, "CustomAuthType")
    //        : new ClaimsIdentity();
    //    var user = new ClaimsPrincipal(identity);
    //    NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(user)));
    //}
    public async Task<bool> MarkUserAsAuthenticated(string username, string password)
    {
        var user = await _userService.ValidateUserCredentials(username, password);
        if (user != null)
        {
            await _sessionStorage.SetItemAsStringAsync("username", username);
            var identity = new ClaimsIdentity(new[]
            {
                new Claim(ClaimTypes.Name, username),
            }, CookieAuthenticationDefaults.AuthenticationScheme);
            var userPrincipal = new ClaimsPrincipal(identity);
            NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(userPrincipal)));
            return true;
        }
        return false;
    }
    public async Task MarkUserAsLoggedOut()
    {
        await _sessionStorage.RemoveItemAsync("username");
        var identity = new ClaimsIdentity();
        var user = new ClaimsPrincipal(identity);
        NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(user)));
    }
}

@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.FluentUI.AspNetCore.Components
@inherits LayoutComponentBase
@using FluentOrientation = Microsoft.FluentUI.AspNetCore.Components.Orientation;
@inject AuthenticationStateProvider AuthenticationStateProvider
<FluentLayout >
    <FluentHeader>
        Blazor App
    </FluentHeader>
    <CultureSelector />
    <FluentStack Orientation="FluentOrientation.Horizontal" Width="100%">
        <div>
            <FluentNavMenu @bind-Expanded="@Expanded" Width="200" Collapsible="true" Title="App Navigation Menu">
                <AuthorizeView>
                    <Authorized>
                        <FluentNavLink Href="/" Icon="@(new Icons.Regular.Size24.Home())">Anasayfa</FluentNavLink>
                        <FluentNavLink Href="personals" Icon="@(new Icons.Regular.Size24.People())">Personel Listesi</FluentNavLink>
                        <FluentNavLink Href="Account/Logout">Çıkış Yap</FluentNavLink>
                    </Authorized>
                    <NotAuthorized>
                        <FluentNavLink Href="Account/Login">Giriş Yap</FluentNavLink>
                    </NotAuthorized>
                </AuthorizeView>
            </FluentNavMenu>
        </div>
        <FluentBodyContent>
            <div class="content">
                @Body
            </div>
        </FluentBodyContent>
        <FluentToastProvider @rendermode=@InteractiveServer />
        <FluentDialogProvider @rendermode=@InteractiveServer />
        <FluentTooltipProvider @rendermode=@InteractiveServer />
        <FluentMessageBarProvider @rendermode=@InteractiveServer />
    </FluentStack>
</FluentLayout>
@code
{
    bool Expanded = true;
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await ((CustomAuthenticationStateProvider)AuthenticationStateProvider).LoadUserStateAsync();
            StateHasChanged();
        }
    }
}

@page "/"
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.Extensions.Localization
@using System.Text.Json
@using global::Microsoft.AspNetCore.Components;
@using Microsoft.FluentUI.AspNetCore.Components;
@using Microsoft.AspNetCore.Authorization
@inject IStringLocalizer<Resource> Loc
@inject PersonalService PersonalService
@inject IDbContextFactory<BlazorAppDbContext> DbFactory
@inject IJSRuntime JS
@inject IAuthorizationService AuthorizationService
@implements IAsyncDisposable
@attribute [Authorize]
@* <PageTitle>@Loc["PageTitle"]</PageTitle>
<h1>@Loc["HomeText"]</h1>
@Thread.CurrentThread.CurrentCulture;
@Thread.CurrentThread.CurrentUICulture;
@Loc["HomeText"] *@
<h4 class="personalNumber">Personel Sayısı: @personalCount</h4>
@*Charts*@
<div style="display:flex; margin:30px">
    <div id="sortedBarChart"></div>
    <div id="chartdiv"></div>
</div>
@* <FluentButton @onclick="GeneratePieChart">Pie Chart</FluentButton> *@
<div style="display:flex; margin:30px">
    <div id="variableRadiusPie"></div>
</div>
@code {
    private int personalCount;
    private BlazorAppDbContext Context { get; set; }
    //private IQueryable<Personal>? Personal;
    private List<PersonalViewModal> PersonalChartsList = new List<PersonalViewModal>();
    [CascadingParameter]
    public Task<AuthenticationState> AuthState{ get; set; }
    protected override async Task OnInitializedAsync()
    {
        Context = DbFactory.CreateDbContext();
        PersonalChartsList = await PersonalService.GetPersonalRolesCount();
        personalCount = await PersonalService.GetPersonalCountAsync();
    }
    //javascript fonksiyonlarını render işlemi tamamlandıktan sonra çalışmaktadır, amCharts gösterebilmek için
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await GeneratePieChart();
            await GenerateSortedBarChart();
            await GenerateVariableRadiusPieChart();
        }
    }
    public async ValueTask DisposeAsync()
    {
        try
        {
            // JavaScript tarafındaki kaynakları temizleme fonksiyonu
            await JS.InvokeVoidAsync("deInitializeCharts");
        }
        catch (JSDisconnectedException)
        {
            //bağlantı kesildiğinde oluşacak hataları yoksaymak için
        }
    }
    //Serializer işlemi
    public async Task JsSerializerMethodAsync(string methodName, object data)
    {
        var jsonData = JsonSerializer.Serialize(data);
        //chart.js yönlendirme
        await JS.InvokeVoidAsync(methodName, jsonData);
    }
    public string GetLocalizedRoleName(string role)
    {
        var key = $"RolesEnum_{Enum.Parse<EnumDeclarations.RolesEnum>(role)}";
        return Loc[key];
    }
    public string GetLocalizedIsActiveName(string value)
    {
        var key = $"IsActiveEnum_{Enum.Parse<EnumDeclarations.IsActive>(value)}";
        return Loc[key];
    }
    private async Task GeneratePieChart()
    {
        // int activeUserCount = await Context.Personal.CountAsync(p => p.Active == "Active");
        // int inActiveUserCount = await Context.Personal.CountAsync(p => p.Active == "InActive");
    //     var chartPieData = new[]
    // {
    //     new { category = "Aktif", value = activeUserCount },
    //     new { category = "Pasif", value = inActiveUserCount }
    // };
        var isActiveCount = await PersonalService.IsActiveStatusCount();
        var chartPieData = isActiveCount.Select(p => new
        {
            status = GetLocalizedIsActiveName(p.ActiveCharts),
            value = p.IsActiveCount
        }).ToArray();
        //Blazordaki verileri javascripte fonksiyonuna aktarabilmek için
        // var jsonData = JsonSerializer.Serialize(chartPieData);
        // await JS.InvokeVoidAsync("CreatePieChartFromJson", jsonData);
        await JsSerializerMethodAsync("CreatePieChartFromJson", chartPieData);
    }
    public async Task GenerateSortedBarChart()
    {
        var personalRolesCount = await PersonalService.GetPersonalRolesCount();
        //charts içerisinde göstereliecek değerlerin atanması
        var chartSortedBar = personalRolesCount.Select(p => new
        {
            roles = GetLocalizedRoleName(p.RoleCharts),
            value = p.RolesCount
        }).ToArray();
        // var jsonData2 = JsonSerializer.Serialize(chartSortedBar);
        // await JS.InvokeVoidAsync("CreateSortedBarFromJson", jsonData2);
        await JsSerializerMethodAsync("CreateSortedBarFromJson", chartSortedBar);
    }
    public async Task GenerateVariableRadiusPieChart()
    {
        var isActiveCount = await PersonalService.IsActiveStatusCount();
        var chartVariableRadiusPie = isActiveCount.Select(p => new
        {
            status = GetLocalizedIsActiveName(p.ActiveCharts),
            value = p.IsActiveCount
        }).ToArray();
        // var jsonData3 = JsonSerializer.Serialize(chartVariableRadiusPie);
        // await JS.InvokeVoidAsync("CreateVariablePieFromJson", jsonData3);
        await JsSerializerMethodAsync("CreateVariablePieFromJson", chartVariableRadiusPie);
    }
}

.NET
.NET
Microsoft Technologies based on the .NET software framework.
3,387 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,395 questions
{count} votes

Accepted answer
  1. Bruce (SqlWork.com) 56,526 Reputation points
    2024-04-18T18:00:56.4933333+00:00

    I don't understand storing user in browser local storage. if the blazor app is open in two tabs, they share the same local storage, but the login can be different in each tab.

    also as you have pre-render enabled, you can not access browser session from pre-render code (as there is no signal/r connection to the browser yet). you don't show the code that sets user in local storage, but be sure its not called during pre-render.

    when you first load the page hosting the Blazor app, the server creates a new Blazor app instance, and does a render to get the get pre-render html. then the Blazor app is shut down. when the browser loads the page it displays the pre-render html, and runs the javascript that opens a connection back to the server to start a new Blazor app instance that will be used for interactive mode (server to client).

    during the first pre-render a Blazor component can not access javascript or the dom.

    1 person found this answer helpful.
    0 comments No comments

0 additional answers

Sort by: Most helpful