Skip to content

Commit e8a8499

Browse files
Patterns for applying authz across Blazor apps (#37501)
* Patterns for applying authz across Blazor apps * Updates * Updates * Updates * Updates * Updates * Updates * Updates * Update aspnetcore/blazor/security/webassembly/index.md Co-authored-by: Wade Pickett <wpickett@microsoft.com> --------- Co-authored-by: Wade Pickett <wpickett@microsoft.com>
1 parent d51b3b9 commit e8a8499

5 files changed

Lines changed: 232 additions & 19 deletions

File tree

aspnetcore/blazor/fundamentals/static-files.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
---
22
title: ASP.NET Core Blazor static files
3+
ai-usage: ai-assisted
34
author: guardrex
45
description: Learn how to configure and manage static files for Blazor apps.
56
monikerRange: '>= aspnetcore-3.1'
67
ms.author: wpickett
7-
ms.date: 11/11/2025
8+
ms.date: 08/24/2026
89
uid: blazor/fundamentals/static-files
910
---
1011
# ASP.NET Core Blazor static files
@@ -505,7 +506,7 @@ To create additional file mappings with a <xref:Microsoft.AspNetCore.StaticFiles
505506
app.UseStaticFiles();
506507
```
507508

508-
* You can avoid interfering with serving `_framework/blazor.server.js` by using <xref:Microsoft.AspNetCore.Builder.MapWhenExtensions.MapWhen%2A> to execute a custom static file middleware:
509+
* You can avoid interfering with serving `_framework/blazor.server.js` by using <xref:Microsoft.AspNetCore.Builder.MapWhenExtensions.MapWhen%2A> to execute a custom static files middleware:
509510

510511
```csharp
511512
app.MapWhen(ctx => !ctx.Request.Path
@@ -538,7 +539,7 @@ Add the following `using` statement to the top of the server project's `Program`
538539
using Microsoft.Extensions.FileProviders;
539540
```
540541

541-
In the server project's `Program` file ***before*** the call to <xref:Microsoft.AspNetCore.Builder.StaticFileExtensions.UseStaticFiles%2A>, add the following code:
542+
In the server project's `Program` file ***before*** any calls to <xref:Microsoft.AspNetCore.Builder.StaticAssetsEndpointRouteBuilderExtensions.MapStaticAssets%2A> and <xref:Microsoft.AspNetCore.Builder.StaticFileExtensions.UseStaticFiles%2A>, add the following code:
542543

543544
```csharp
544545
var secondaryProvider = new PhysicalFileProvider(

aspnetcore/blazor/security/additional-scenarios.md

Lines changed: 209 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
---
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
34
author: guardrex
45
description: Learn how to configure server-side Blazor and Blazor Web Apps for additional security scenarios.
56
monikerRange: '>= aspnetcore-3.1'
67
ms.author: wpickett
7-
ms.date: 11/11/2025
8+
ms.date: 08/26/2026
89
uid: blazor/security/additional-scenarios
910
---
10-
# ASP.NET Core server-side and Blazor Web App additional security scenarios
11+
# ASP.NET Core Blazor additional server-side security scenarios
1112

1213
[!INCLUDE[](~/includes/not-latest-version.md)]
1314

@@ -1369,3 +1370,208 @@ The preceding example's placeholders:
13691370
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/).
13701371
13711372
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.

aspnetcore/blazor/security/index.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ author: guardrex
55
description: Learn about Blazor authentication and authorization scenarios.
66
monikerRange: '>= aspnetcore-3.1'
77
ms.author: wpickett
8-
ms.date: 11/11/2025
8+
ms.date: 08/26/2026
99
uid: blazor/security/index
1010
---
1111
# ASP.NET Core Blazor authentication and authorization
@@ -1811,6 +1811,7 @@ PII refers any information relating to an identified or identifiable natural per
18111811
:::moniker range=">= aspnetcore-6.0"
18121812

18131813
* Server-side and Blazor Web App resources
1814+
* [Authorization patterns](xref:blazor/security/additional-scenarios#server-side-blazor-app-authorization-patterns)
18141815
* [Quickstart: Add sign-in with Microsoft to an ASP.NET Core web app](/entra/identity-platform/quickstart-v2-aspnet-core-webapp)
18151816
* [Quickstart: Protect an ASP.NET Core web API with Microsoft identity platform](/entra/identity-platform/quickstart-v2-aspnet-core-web-api)
18161817
* <xref:host-and-deploy/proxy-load-balancer>: Includes guidance on:
@@ -1829,12 +1830,14 @@ PII refers any information relating to an identified or identifiable natural per
18291830
* [Awesome Blazor: Authentication](https://github.com/AdrienTorris/awesome-blazor#authentication) community sample links
18301831
* <xref:blazor/hybrid/security/index>
18311832
* [Opaque (reference) access token support](xref:blazor/security/additional-scenarios#opaque-reference-access-token-support)
1833+
* [Blazor WebAssembly authorization patterns](xref:blazor/security/webassembly/index#blazor-webassembly-authorization-patterns)
18321834

18331835
:::moniker-end
18341836

18351837
:::moniker range="< aspnetcore-6.0"
18361838

18371839
* Server-side Blazor resources
1840+
* [Authorization patterns](xref:blazor/security/additional-scenarios#server-side-blazor-app-authorization-patterns)
18381841
* [Quickstart: Add sign-in with Microsoft to an ASP.NET Core web app](/entra/identity-platform/quickstart-v2-aspnet-core-webapp)
18391842
* [Quickstart: Protect an ASP.NET Core web API with Microsoft identity platform](/entra/identity-platform/quickstart-v2-aspnet-core-web-api)
18401843
* <xref:host-and-deploy/proxy-load-balancer>: Includes guidance on:
@@ -1852,5 +1855,6 @@ PII refers any information relating to an identified or identifiable natural per
18521855
* [Build a custom version of the Authentication.MSAL JavaScript library](xref:blazor/security/webassembly/additional-scenarios#build-a-custom-version-of-the-authenticationmsal-javascript-library)
18531856
* [Awesome Blazor: Authentication](https://github.com/AdrienTorris/awesome-blazor#authentication) community sample links
18541857
* [Opaque (reference) access token support](xref:blazor/security/additional-scenarios#opaque-reference-access-token-support)
1858+
* [Blazor WebAssembly authorization patterns](xref:blazor/security/webassembly/index#blazor-webassembly-authorization-patterns)
18551859

18561860
:::moniker-end

aspnetcore/blazor/security/webassembly/index.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
---
22
title: Secure ASP.NET Core Blazor WebAssembly
3+
ai-usage: ai-assisted
34
author: guardrex
45
description: Learn how to secure Blazor WebAssembly apps as single-page applications (SPAs).
56
monikerRange: '>= aspnetcore-3.1'
67
ms.author: wpickett
78
ms.custom: sfi-ropc-nochange
8-
ms.date: 11/11/2025
9+
ms.date: 08/26/2026
910
uid: blazor/security/webassembly/index
1011
---
1112
# Secure ASP.NET Core Blazor WebAssembly
@@ -170,9 +171,11 @@ The following authentication scenarios are covered in the <xref:blazor/security/
170171

171172
:::moniker-end
172173

173-
## Require authorization for the entire app
174+
## Blazor WebAssembly authorization patterns
174175

175-
Apply the [`[Authorize]` attribute](xref:blazor/security/index#authorize-attribute) ([API documentation](xref:Microsoft.AspNetCore.Authorization.AuthorizeAttribute)) to each Razor component of the app using ***one*** of the following approaches:
176+
*For patterns that apply to server-side Blazor apps (Blazor Web Apps, Blazor Server apps), see <xref:blazor/security/additional-scenarios#server-side-blazor-app-authorization-patterns>.*
177+
178+
Unlike server-side Blazor apps, Blazor WebAssembly apps don't support setting an <xref:Microsoft.AspNetCore.Authorization.AuthorizationOptions.FallbackPolicy?displayProperty=nameWithType> to a policy with <xref:Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.RequireAuthenticatedUser%2A>. Therefore, the only supported pattern for Blazor WebAssembly apps is to apply the [`[Authorize]` attribute](xref:blazor/security/index#authorize-attribute) ([API documentation](xref:Microsoft.AspNetCore.Authorization.AuthorizeAttribute)) to Razor components using ***one*** of the following approaches:
176179

177180
* 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).
178181

@@ -199,9 +202,6 @@ Apply the [`[Authorize]` attribute](xref:blazor/security/index#authorize-attribu
199202
@attribute [Authorize]
200203
```
201204

202-
> [!NOTE]
203-
> Setting an <xref:Microsoft.AspNetCore.Authorization.AuthorizationOptions.FallbackPolicy?displayProperty=nameWithType> to a policy with <xref:Microsoft.AspNetCore.Authorization.AuthorizationPolicyBuilder.RequireAuthenticatedUser%2A> is **not** supported.
204-
205205
## Use one identity provider app registration per app
206206

207207
:::moniker range=">= aspnetcore-8.0"

0 commit comments

Comments
 (0)