blazorazure-ad-b2cblazor-webassembly

Using different layout for unauthenticated users in Blazor WASM


I created a new Blazor WASM app targeting .NET 8 -- 8.0.8 to be more specific. The app uses Azure AD B2C for user management and I want to use a different layout for pages designed for public/unauthenticated users.

I was able to get the template that comes out of the box working fine but obviously that template uses a single layout.

I tried using the solution suggested by Steve Sanderson here but when I implemented that clicking the "Login" link no longer works. It doesn't open the popup with user sign-in/sign up page hosted by Azure AD B2C. It also throws the following error:

enter image description here

Here are the changes I've made:

First, I created a very simple PublicLayout.razor that looks like this:

@inherits LayoutComponentBase

<div style="height: 100%;">
    <main>
        <div class="top-row px-4 auth">
            <LoginDisplay />
        </div>

        <article class="content px-4">
            <div style="position: absolute; top: 50%; left: 50%;">
                <div style="text-align: center;">
                    <img src="images/logo.png" alt="My App" width="350" />
                    <p style="font-size: 36px; font-weight: bold; color: #d7d7d7;">
                        Hello stranger!
                    </p>
                </div>
            </div>
        </article>
    </main>
</div>

I then updated App.razor to the following as suggested by Sanderson in response to the GitHub question:

<CascadingAuthenticationState>
    <AuthorizeView>
        <Authorized>
            @RouterWithLayout(typeof(MainLayout))
        </Authorized>
        <NotAuthorized>
            @RouterWithLayout(typeof(PublicLayout))
        </NotAuthorized>
    </AuthorizeView>
</CascadingAuthenticationState>

@code {
    RenderFragment RouterWithLayout(Type layoutType) => __builder =>
    {
        <Router AppAssembly="@typeof(Program).Assembly">
            <Found Context="routeData">
                <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@layoutType" />
                <FocusOnNavigate RouteData="@routeData" Selector="h1" />
            </Found>
            <NotFound>
                <PageTitle>Not found</PageTitle>
                <LayoutView Layout="@layoutType">
                    <p>Sorry, there's nothing at this address.</p>
                </LayoutView>
            </NotFound>
        </Router>
    };
}

Any suggestions on how I can use a different layout for the public/unauthenticated pages of my Blazor WASM app?


Solution

  • Here's how I solved this. I suspect this is obvious to most but this is my first Blazor app so I'm not yet fully proficient in this technology.

    I modified my MainLayout.razor which looks like this now:

    @inherits LayoutComponentBase
    
    <div>
        <AuthorizeView>
            <Authorized>
                <div class="sidebar">
                    <NavMenu />
                </div>
    
                <div class="top-row px-4 auth">
                    <LoginDisplay />
                </div>
            </Authorized>
            <NotAuthorized>
                <div>
                    <LoginDisplay />
                </div>
            </NotAuthorized>
        </AuthorizeView>
    
        <main>
            <article class="content px-4">
                @Body
            </article>
        </main>
    </div>
    

    I think there's room to optimize this layout but basically, whatever I don't want to display if the user hasn't logged in goes inside the <Authorized></Authorized> section of <AuthorizeView>.

    The only reason why <LoginDisplay> component is in both <Authorized> and <NotAuthorized> sections is that I removed CSS classes from the <div> so that it displays properly but as I said, there's a lot of room here for optimization.

    And I left all the common elements outside the <AuthorizeView> so that they display in both cases e.g. @Body.

    P.S. One last thought on using different layout components for authenticated and anonymous users is that all my research showed me that this particular scenario, though pretty common, wasn't treated as an important matter. There were solutions here and there but they all seemed kind of cumbersome to me. I suspect this is because the team at Microsoft expects an approach like the one I outlined here.

    In other words, using a single primary layout and handling authenticated and anonymous scenarios using <AuthorizeView> appears to be what they expect. At least, this is the feeling I get.

    Obviously, it's easy enough to create a completely different layout and use it anywhere in the app simply by specifying it inside the component i.e. @layout SomeOtherLayout. Here's the documentation on that. HTH.