|
1 | 1 | --- |
2 | | -title: ASP.NET Core server-side and Blazor Web App additional security scenarios |
| 2 | +title: ASP.NET Core Blazor additional server-side security scenarios |
| 3 | +ai-usage: ai-assisted |
3 | 4 | author: guardrex |
4 | 5 | description: Learn how to configure server-side Blazor and Blazor Web Apps for additional security scenarios. |
5 | 6 | monikerRange: '>= aspnetcore-3.1' |
6 | 7 | ms.author: wpickett |
7 | | -ms.date: 11/11/2025 |
| 8 | +ms.date: 08/26/2026 |
8 | 9 | uid: blazor/security/additional-scenarios |
9 | 10 | --- |
10 | | -# ASP.NET Core server-side and Blazor Web App additional security scenarios |
| 11 | +# ASP.NET Core Blazor additional server-side security scenarios |
11 | 12 |
|
12 | 13 | [!INCLUDE[](~/includes/not-latest-version.md)] |
13 | 14 |
|
@@ -1369,3 +1370,208 @@ The preceding example's placeholders: |
1369 | 1370 | In [Duende IdentityServer](https://duendesoftware.com/products/identityserver), tokens are revoked automatically by setting the `CoordinateLifetimeWithUserSession` client configuration property to `true`, which automatically cleans up associated tokens when a session ends. For more information, see [Session Cleanup and Logout (Duende documentation)](https://docs.duendesoftware.com/identityserver/ui/logout/session-cleanup/). |
1370 | 1371 |
|
1371 | 1372 | Built-in opaque access token support is under consideration for a future release of .NET. For more information, see [Opaque - reference token validation (`dotnet/aspnetcore` #46026)](https://github.com/dotnet/aspnetcore/issues/46026). |
| 1373 | +
|
| 1374 | +## Server-side Blazor app authorization patterns |
| 1375 | + |
| 1376 | +*For patterns that apply to Blazor WebAssembly apps, see <xref:blazor/security/webassembly/index#blazor-webassembly-authorization-patterns>.* |
| 1377 | + |
| 1378 | +Server-side Blazor apps (Blazor Web Apps, Blazor Server apps) usually adopt **either** of the following approaches to require authorization: |
| 1379 | + |
| 1380 | +* The app sets an authorization fallback policy that requires authorization globally across the app and applies the [`[AllowAnonymous]` attribute](xref:Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute) to resources (for example, Razor components, static assets) that don't require an authenticated user. For more information, see the [Global authorization via a fallback authorization policy](#global-authorization-via-a-fallback-authorization-policy) section. |
| 1381 | +* Instead of requiring global authorization for resources, the app applies the [`[Authorize]` attribute](xref:blazor/security/index#authorize-attribute) to resources that require an authorized user. For more information, see the [Local authorization via `[Authorize]` attributes](#local-authorization-via-authorize-attributes) section. |
| 1382 | + |
| 1383 | +### Global authorization via a fallback authorization policy |
| 1384 | + |
| 1385 | +The following demonstration code can be used with the [`BlazorWebAppAuthorization` sample app (`dotnet/AspNetCore.Docs.Samples` GitHub repository)](https://github.com/dotnet/AspNetCore.Docs.Samples/tree/main/security/authorization/BlazorWebAppAuthorization) ([how to download](xref:index#how-to-download-a-sample)). |
| 1386 | +
|
| 1387 | +Set the <xref:Microsoft.AspNetCore.Authorization.AuthorizationOptions.FallbackPolicy?displayProperty=nameWithType> to a policy with <xref:Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.RequireAuthenticatedUser%2A>, which only applies when there are no authorization attributes or explicit policies set for a given resource: |
| 1388 | + |
| 1389 | +:::moniker range=">= aspnetcore-6.0" |
| 1390 | + |
| 1391 | +```csharp |
| 1392 | +builder.Services.AddAuthorization(options => |
| 1393 | +{ |
| 1394 | + options.FallbackPolicy = options.DefaultPolicy; |
| 1395 | +}); |
| 1396 | +``` |
| 1397 | + |
| 1398 | +:::moniker-end |
| 1399 | + |
| 1400 | +:::moniker range="< aspnetcore-6.0" |
| 1401 | + |
| 1402 | +```csharp |
| 1403 | +services.AddAuthorization(options => |
| 1404 | +{ |
| 1405 | + options.FallbackPolicy = options.DefaultPolicy; |
| 1406 | +}); |
| 1407 | +``` |
| 1408 | + |
| 1409 | +:::moniker-end |
| 1410 | + |
| 1411 | +The framework's <xref:Microsoft.AspNetCore.Authorization.AuthorizationOptions.DefaultPolicy%2A?displayProperty=nameWithType> requires an authenticated user. Unless the app uses a [custom policy provider](xref:security/authorization/custom-authorization-policy-providers) with a custom default policy, assigning the framework's default policy (`options.DefaultPolicy`), as shown in the preceding example, is equivalent to using the following code: |
| 1412 | + |
| 1413 | +:::moniker range=">= aspnetcore-6.0" |
| 1414 | + |
| 1415 | +```csharp |
| 1416 | +builder.Services.AddAuthorization(options => |
| 1417 | +{ |
| 1418 | + options.FallbackPolicy = new AuthorizationPolicyBuilder() |
| 1419 | + .RequireAuthenticatedUser() |
| 1420 | + .Build(); |
| 1421 | +}); |
| 1422 | +``` |
| 1423 | + |
| 1424 | +:::moniker-end |
| 1425 | + |
| 1426 | +:::moniker range="< aspnetcore-6.0" |
| 1427 | + |
| 1428 | +```csharp |
| 1429 | +services.AddAuthorization(options => |
| 1430 | +{ |
| 1431 | + options.FallbackPolicy = new AuthorizationPolicyBuilder() |
| 1432 | + .RequireAuthenticatedUser() |
| 1433 | + .Build(); |
| 1434 | +}); |
| 1435 | +``` |
| 1436 | + |
| 1437 | +:::moniker-end |
| 1438 | + |
| 1439 | +The app requires an authenticated user for any resource where no specific policy is set. |
| 1440 | + |
| 1441 | +:::moniker range=">= aspnetcore-9.0" |
| 1442 | + |
| 1443 | +If the app's security specification doesn't call for protecting static assets, call <xref:Microsoft.AspNetCore.Builder.AuthorizationEndpointConventionBuilderExtensions.AllowAnonymous%2A?displayProperty=nameWithType> on <xref:Microsoft.AspNetCore.Builder.StaticAssetsEndpointRouteBuilderExtensions.MapStaticAssets%2A>: |
| 1444 | + |
| 1445 | +```csharp |
| 1446 | +app.MapStaticAssets().AllowAnonymous(); |
| 1447 | +``` |
| 1448 | + |
| 1449 | +To alternatively allow anonymous access for specific paths, apply the <xref:Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute> to the route pattern inside the endpoint convention lambda of <xref:Microsoft.AspNetCore.StaticAssets.StaticAssetsEndpointConventionBuilder.Add%2A?displayProperty=nameWithType>. |
| 1450 | + |
| 1451 | +> [!IMPORTANT] |
| 1452 | +> When only authorizing specific endpoints for anonymous access, the [Blazor script](xref:blazor/project-structure#location-of-the-blazor-script) and other Blazor static assets, such as stylesheets, scripts, and modules, must be taken into consideration. If public Razor component pages require the assets to render and function correctly, the assets must be made available anonymously as well because they're requested separately via Map Static Assets routing endpoint conventions or static files middleware. |
| 1453 | + |
| 1454 | +Place static assets for anonymous access into a single folder. In the following example, endpoint routes with the `/public/` path segment are served anonymously: |
| 1455 | + |
| 1456 | +```csharp |
| 1457 | +app.MapStaticAssets() |
| 1458 | + .Add(endpointBuilder => |
| 1459 | + { |
| 1460 | + if (endpointBuilder is RouteEndpointBuilder routeBuilder && |
| 1461 | + routeBuilder.RoutePattern.RawText?.Contains( |
| 1462 | + "/public/", StringComparison.OrdinalIgnoreCase) == true) |
| 1463 | + { |
| 1464 | + routeBuilder.Metadata.Add(new AllowAnonymousAttribute()); |
| 1465 | + } |
| 1466 | + }); |
| 1467 | +``` |
| 1468 | + |
| 1469 | +The next example demonstrates anonymously serving the uncompressed Blazor script (`_framework/blazor.web.{FINGERPRINT}.js`, where the `{FINGERPRINT}` placeholder is the file's fingerprint): |
| 1470 | + |
| 1471 | +```csharp |
| 1472 | +// using System.Text.RegularExpressions; |
| 1473 | +
|
| 1474 | +var regex = new Regex( |
| 1475 | + @"^_framework/blazor\.web\.[a-z0-9]{10}\.js$", RegexOptions.Compiled); |
| 1476 | + |
| 1477 | +app.MapStaticAssets() |
| 1478 | + .Add(endpointBuilder => |
| 1479 | + { |
| 1480 | + if (endpointBuilder is RouteEndpointBuilder routeBuilder && |
| 1481 | + regex.IsMatch(routeBuilder.RoutePattern.RawText ?? string.Empty)) |
| 1482 | + { |
| 1483 | + routeBuilder.Metadata.Add(new AllowAnonymousAttribute()); |
| 1484 | + } |
| 1485 | + }); |
| 1486 | +``` |
| 1487 | + |
| 1488 | +:::moniker-end |
| 1489 | + |
| 1490 | +:::moniker range="< aspnetcore-9.0" |
| 1491 | + |
| 1492 | +If the app's security specification doesn't call for protecting static assets, place the call to <xref:Microsoft.AspNetCore.Builder.StaticFileExtensions.UseStaticFiles%2A> ***before*** <xref:Microsoft.AspNetCore.Builder.AuthAppBuilderExtensions.UseAuthentication%2A> and <xref:Microsoft.AspNetCore.Builder.AuthorizationAppBuilderExtensions.UseAuthorization%2A>: |
| 1493 | + |
| 1494 | +```csharp |
| 1495 | +app.UseStaticFiles(); |
| 1496 | + |
| 1497 | +app.UseAuthentication(); |
| 1498 | +app.UseAuthorization(); |
| 1499 | +``` |
| 1500 | + |
| 1501 | +To alternatively allow anonymous access for specific paths, register a separate static files middleware before <xref:Microsoft.AspNetCore.Builder.AuthAppBuilderExtensions.UseAuthentication%2A> and <xref:Microsoft.AspNetCore.Builder.AuthorizationAppBuilderExtensions.UseAuthorization%2A> are called. A second call to <xref:Microsoft.AspNetCore.Builder.StaticFileExtensions.UseStaticFiles%2A> after authorization pipeline processing only serves other static assets if the user is authorized. |
| 1502 | + |
| 1503 | +> [!IMPORTANT] |
| 1504 | +> When only authorizing specific endpoints for anonymous access, the [Blazor script](xref:blazor/project-structure#location-of-the-blazor-script) and other Blazor static assets, such as stylesheets, scripts, and modules, must be taken into consideration. If public Razor component pages require the assets to render and function correctly, the assets must be made available anonymously as well because they're requested separately via static files middleware. |
| 1505 | + |
| 1506 | +In the following example, static assets in the app's `wwwroot/public` folder are served anonymously: |
| 1507 | + |
| 1508 | +```csharp |
| 1509 | +app.UseStaticFiles(new StaticFileOptions { |
| 1510 | + FileProvider = new Microsoft.Extensions.FileProviders.PhysicalFileProvider( |
| 1511 | + System.IO.Path.Combine(builder.Environment.WebRootPath, "public")), |
| 1512 | + RequestPath = "/public" |
| 1513 | +}); |
| 1514 | + |
| 1515 | +app.UseAuthentication(); |
| 1516 | +app.UseAuthorization(); |
| 1517 | + |
| 1518 | +app.UseStaticFiles(); |
| 1519 | +``` |
| 1520 | + |
| 1521 | +:::moniker-end |
| 1522 | + |
| 1523 | +Use an [`@using`](xref:mvc/views/razor#using) directive for the <xref:Microsoft.AspNetCore.Authorization?displayProperty=fullName> namespace with an [`@attribute`](xref:mvc/views/razor#attribute) directive for the [`[AllowAnonymous]` attribute](xref:Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute) to permit anonymous access to individual components. In the following example, the `Home` component sets the attribute. |
| 1524 | + |
| 1525 | +At the top of `Components/Pages/Home.razor`: |
| 1526 | + |
| 1527 | +```razor |
| 1528 | +@page "/" |
| 1529 | +@using Microsoft.AspNetCore.Authorization |
| 1530 | +@attribute [AllowAnonymous] |
| 1531 | +``` |
| 1532 | + |
| 1533 | +Often, it's convenient to apply authorization to an entire folder of components. In the following example, a user account pages' imports file sets the [`[AllowAnonymous]` attribute](xref:Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute), so users can anonymously reach the app's sign-in, sign-out, access denied, and invalid user pages in the `Components/Account/Pages` folder. |
| 1534 | + |
| 1535 | +In `Components/Account/Pages/_Imports.razor`: |
| 1536 | + |
| 1537 | +```razor |
| 1538 | +@using Microsoft.AspNetCore.Authorization |
| 1539 | +@attribute [AllowAnonymous] |
| 1540 | +``` |
| 1541 | + |
| 1542 | +:::moniker range=">= aspnetcore-5.0" |
| 1543 | + |
| 1544 | +If the app uses one or more endpoint convention builder instances to provide additional endpoints, such as for Identity components, the endpoint builder's method call chains a call to <xref:Microsoft.AspNetCore.Builder.AuthorizationEndpointConventionBuilderExtensions.AllowAnonymous%2A?displayProperty=nameWithType>. The following example maps additional Identity endpoints by calling `MapAdditionalIdentityEndpoints`, which returns an <xref:Microsoft.AspNetCore.Builder.IEndpointConventionBuilder>: |
| 1545 | + |
| 1546 | +```csharp |
| 1547 | +app.MapAdditionalIdentityEndpoints().AllowAnonymous(); |
| 1548 | +``` |
| 1549 | + |
| 1550 | +> [!NOTE] |
| 1551 | +> For an example of the preceding `MapAdditionalIdentityEndpoints` method, see [`IdentityComponentsEndpointRouteBuilderExtensions`](https://github.com/dotnet/AspNetCore.Docs.Samples/blob/main/security/authorization/BlazorWebAppAuthorization/Components/Account/IdentityComponentsEndpointRouteBuilderExtensions.cs) in the [`BlazorWebAppAuthorization` sample app (`dotnet/AspNetCore.Docs.Samples` GitHub repository)](https://github.com/dotnet/AspNetCore.Docs.Samples/tree/main/security/authorization/BlazorWebAppAuthorization). |
| 1552 | +
|
| 1553 | +:::moniker-end |
| 1554 | + |
| 1555 | +### Local authorization via `[Authorize]` attributes |
| 1556 | + |
| 1557 | +Apply [`[Authorize]` attributes](xref:blazor/security/index#authorize-attribute) ([API documentation](xref:Microsoft.AspNetCore.Authorization.AuthorizeAttribute)) to Razor components using ***either*** of the following approaches: |
| 1558 | + |
| 1559 | +* In the app's imports file, add an [`@using`](xref:mvc/views/razor#using) directive for the <xref:Microsoft.AspNetCore.Authorization?displayProperty=fullName> namespace with an [`@attribute`](xref:mvc/views/razor#attribute) directive for the [`[Authorize]` attribute](xref:blazor/security/index#authorize-attribute). |
| 1560 | + |
| 1561 | + `_Imports.razor`: |
| 1562 | + |
| 1563 | + ```razor |
| 1564 | + @using Microsoft.AspNetCore.Authorization |
| 1565 | + @attribute [Authorize] |
| 1566 | + ``` |
| 1567 | + |
| 1568 | + Imports files can be applied at any level of a folder hierarchy to apply an [`[Authorize]` attribute](xref:blazor/security/index#authorize-attribute) for that folder's components and its subfolders. |
| 1569 | + |
| 1570 | +* Add the [`[Authorize]` attribute](xref:blazor/security/index#authorize-attribute) to each Razor component that requires authorization under the [`@page`](xref:mvc/views/razor#page) directive with an [`@using`](xref:mvc/views/razor#using) directive for the <xref:Microsoft.AspNetCore.Authorization?displayProperty=fullName> namespace: |
| 1571 | + |
| 1572 | + ```razor |
| 1573 | + @using Microsoft.AspNetCore.Authorization |
| 1574 | + @attribute [Authorize] |
| 1575 | + ``` |
| 1576 | + |
| 1577 | + The [`@using`](xref:mvc/views/razor#using) directive for the <xref:Microsoft.AspNetCore.Authorization?displayProperty=fullName> namespace in the preceding example can be applied broadly to the app's components by placing it into the app's imports file (`_Imports.razor`) instead of in individual components. |
0 commit comments