-
Notifications
You must be signed in to change notification settings - Fork 0
06 Access Control in Razor Components
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.
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 aClaimsPrincipal
@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.
+-------------------+ 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.
You can control UI access using declarative or imperative patterns.
<AuthorizeView Roles="app.admin">
<NavLink href="/admin">Admin</NavLink>
</AuthorizeView>@if (user?.IsInRole("app.admin") == true)
{
<NavLink href="/admin">Admin</NavLink>
}Project roles:
app.userapp.admin-
weather-reader(client role forblazor-client)
See 05 Role-Based Authorization for how roles are created in Keycloak.
@page "/admin-dashboard"
@attribute [Authorize(Roles = "app.admin")]
<h3>Admin Dashboard</h3>@attribute [Authorize(Roles = "app.user,app.admin")]Define policy once:
builder.Services.AddAuthorization(o =>
{
o.AddPolicy("RequireAdmin", p => p.RequireRole("app.admin"));
});Use it anywhere:
@attribute [Authorize(Policy = "RequireAdmin")]@attribute [AllowAnonymous]<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);
}
}@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>
}| 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/tokensand/claimsin the Diagnostics Hub
to instantly inspect roles and token content.
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.
@page "/admin-only"
@attribute [Authorize(Roles = "app.admin")]
<h3>Admin Only</h3>
<p>Restricted content for administrators.</p>- Protected pages use
[Authorize]with correct roles or policies - Navigation hides admin-only links for non-admins
-
App.razorusesAuthorizeRouteView - 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.
- 02 Keycloak Concepts
- 03 Security Design
- 04 Architecture Diagrams
- 05 Role-Based Authorization
- 06 Access Control in Razor Components
- 11 Tests Guide
- 12 Integration with the Web API
- 13 Developer Experience and Troubleshooting
- 14 Extending BlazorKeycloak