Notify all components that authentication has been changed in Blazor

Viewed 1615

I have a Blazor (version net5.0) component with a lot of html markups and here is some part of it :

<a class="@(IsAuthenticated?"":"hide")" href="#">My link for logged in users</a>

and here is my c# code (MyComponent.Razor):

    [CascadingParameter] private Task<AuthenticationState> authenticationState { get; set; }
    private AuthenticationState auth;
    public bool IsAuthenticated { get; set; }

    protected async override Task OnInitializedAsync()
    {
        auth = await authenticationState;
        var user = auth.User.Identity.IsAuthenticated;
        await base.OnInitializedAsync();
    }

and in my Login component after successful login I call StateHasChanged();

  void Login()
  {
    ...
    StateHasChanged();
  }

But after login no changes to MyComponent will be applied unless I refresh the page so the component rerender itself.

Note: I don't want to use AuthorizeView because as I mentioned there are lots of markups and components in the MyComponent and I don't want to have an AuthorizeView for every style or element that I want to change their behaviors based on user's authentication.

Update: I have the following code in my App.razor component:

<Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(Layout)" >
            <Authorizing>
                <text> my Custom Authotizing in app.razor ...</text>
            </Authorizing> 
            <NotAuthorized>
                <text> my Custom NOT Authotized in app.razor ...</text>
            </NotAuthorized>
        </AuthorizeRouteView>
    </Found>
    <NotFound>
        <CascadingAuthenticationState>
            <LayoutView Layout="@typeof(Layout)">
                <p>Sorry, there's nothing at this address.</p>
            </LayoutView>
        </CascadingAuthenticationState>
    </NotFound>
</Router>
1 Answers

Looking at AuthorizedView source code I finally figured out that I should use OnParametersSetAsync and a property :

<a class="@MyClassProp" href="#">My link for logged in users</a>

code:

public string MyClassProp { get; set; } = "hide";
public async Task<string> GetClassAsync()=> (await authenticationState).User.Identity.IsAuthenticated ? "" : "hide";
    
protected async override Task OnParametersSetAsync()
{
    MyClassProp = await GetClassAsync(); // also call GetClass in OnInitializedAsync
    StateHasChanged();
    await base.OnParametersSetAsync();
}

The IsAuthenticated property that I have been created in the question is always equals to its initial state and does not change with AuthenticationState being changed even if I reset its value in OnParametersSetAsync. Calling StateHasChanged() in Login and Logout methods is not necessary. If instead of using MyClassProp property, I call GetClassAsync from the markup, it does not work either.

Warning: The whole approach of hiding an element by setting a css class for unauthorized user have security issues, because the content still can be exposed or modified by user even by just using their browser.

Related