Skip to content

06 Access Control in Razor Components

PhilipQuarles edited this page Dec 6, 2025 · 6 revisions

06 Access Control in Razor Components — Beginner-Friendly (V67 Edition)

This page explains how Blazor Server enforces access control in pages and UI components,
how roles from Keycloak flow through the system, and how to combine UI checks with real backend authorization for end-to-end safety.

Goal: Give you polished, copy-paste-ready patterns with clear diagrams and wiki-style cross-links.


📘 How Blazor Knows Who You Are

After logging into Keycloak, the Blazor Server app issues a server-side authentication ticket
and the browser receives only a small, secure cookie (.BK.Auth, HttpOnly + Secure).

Components access the current user via:

  • <AuthorizeView>
  • AuthenticationStateProvider → provides a ClaimsPrincipal
@using Microsoft.AspNetCore.Components.Authorization
@inject AuthenticationStateProvider Auth

@code {
    private ClaimsPrincipal? user;

    protected override async Task OnInitializedAsync()
    {
        var state = await Auth.GetAuthenticationStateAsync();
        user = state.User;
    }
}

Tip: <AuthorizeView> is ideal for declarative “show/hide UI by role” scenarios.


🔍 Diagram: Identity Flow (Login → Claims → UI)

 +-------------------+     Authorization Code + PKCE     +-----------------------+
 |     Keycloak      |  ───────────────────────────────▶ |   Blazor Server App   |
 | (Identity Provider)|                                   | (Confidential Client) |
 +-------------------+                                    +-----------------------+
          ▲                                                       │
          │ redirect                                              │ Auth Cookie (.BK.Auth)
          │                                                       ▼
 +---------------------+                                  +-----------------------+
 | Browser / User      | ◀──────────── navigation ─────▶ |   Razor Components    |
 +---------------------+                                  +-----------------------+

User logs in → Blazor redeems tokens → issues auth cookie →
Components read the user’s ClaimsPrincipal → UI responds to roles.


🎛 Show / Hide UI Elements by Role

You can control UI access using declarative or imperative patterns.

Option A — Declarative (recommended)

<AuthorizeView Roles="app.admin">
    <NavLink href="/admin">Admin</NavLink>
</AuthorizeView>

Option B — Imperative

@if (user?.IsInRole("app.admin") == true)
{
    <NavLink href="/admin">Admin</NavLink>
}

Project roles:

  • app.user
  • app.admin
  • weather-reader (client role for blazor-client)

See 05 Role-Based Authorization for how roles are created in Keycloak.


🔒 Protect Entire Pages or Components

Restrict a page to admins only

@page "/admin-dashboard"
@attribute [Authorize(Roles = "app.admin")]

<h3>Admin Dashboard</h3>

Allow multiple roles

@attribute [Authorize(Roles = "app.user,app.admin")]

Policy-based (recommended)

Define policy once:

builder.Services.AddAuthorization(o =>
{
    o.AddPolicy("RequireAdmin", p => p.RequireRole("app.admin"));
});

Use it anywhere:

@attribute [Authorize(Policy = "RequireAdmin")]

Allow anonymous access

@attribute [AllowAnonymous]

🧭 App.razor — Recommended Router Setup

<CascadingAuthenticationState>
    <Router AppAssembly="@typeof(App).Assembly">
        <Found Context="routeData">
            <AuthorizeRouteView RouteData="routeData" DefaultLayout="typeof(MainLayout)">
                <NotAuthorized>
                    <RedirectToLogin />
                </NotAuthorized>
                <Authorizing>
                    <p>Checking permissions…</p>
                </Authorizing>
            </AuthorizeRouteView>
        </Found>
        <NotFound>
            <LayoutView Layout="typeof(MainLayout)">
                <p>Sorry, there’s nothing at this address.</p>
            </LayoutView>
        </NotFound>
    </Router>
</CascadingAuthenticationState>

Example redirect helper:

@inject NavigationManager Nav

<p>You are not authorized. Redirecting to login…</p>

@code {
    protected override void OnAfterRender(bool firstRender)
    {
        if (firstRender)
            Nav.NavigateTo("/login", forceLoad: true);
    }
}

🧩 Debugging: View All Claims (Beginner-Friendly)

@page "/debug/claims"
@using Microsoft.AspNetCore.Components.Authorization
@inject AuthenticationStateProvider Auth

@if ((await Auth.GetAuthenticationStateAsync()).User.Identity?.IsAuthenticated == true)
{
    <ul>
    @foreach (var c in (await Auth.GetAuthenticationStateAsync()).User.Claims)
    {
        <li>@c.Type: @c.Value</li>
    }
    </ul>
}
else
{
    <p>Not signed in.</p>
}

Role origin paths

Source Token JSON Path
Realm roles realm_access.roles
Client roles resource_access[clientId].roles
Flattened role claims Added by .NET’s built-in role mapping

Use /debug/tokens and /claims in the Diagnostics Hub
to instantly inspect roles and token content.


💡 Diagram: Role Enforcement (UI + API)

            Razor UI                              Web API
 ─────────────────────────────────────────────────────────────────────

 User visits page
        │
        ▼
   Razor Component
 [Authorize] check
        │
        ├── allowed → render UI
        └── denied  → redirect to login

                     Meanwhile...

 Blazor Server attaches access token to API calls
        │
        ▼
      API Endpoint
     [Authorize] / Policy check
        │
        ├── allowed → return data
        └── denied  → 403 Forbidden

UI checks improve UX — but only API checks truly protect data.


🧱 Complete Example — Admin-Only Page

@page "/admin-only"
@attribute [Authorize(Roles = "app.admin")]

<h3>Admin Only</h3>
<p>Restricted content for administrators.</p>

✅ Quick Checklist

  • Protected pages use [Authorize] with correct roles or policies
  • Navigation hides admin-only links for non-admins
  • App.razor uses AuthorizeRouteView
  • Claims viewer or Diagnostics Hub page exists
  • Web API enforces the same policies (server = source of truth)

Using these patterns you can confidently secure UI behavior in Blazor Server
while relying on the Web API for true enforcement — confidence through testing.

Clone this wiki locally