21 KiB
title | author | description | monikerRange | ms.author | ms.custom | ms.date | no-loc | uid | ||
---|---|---|---|---|---|---|---|---|---|---|
ASP.NET Core Blazor authentication and authorization | guardrex | Learn about Blazor authentication and authorization scenarios. | >= aspnetcore-3.0 | riande | mvc | 11/12/2019 |
|
security/blazor/index |
ASP.NET Core Blazor authentication and authorization
ASP.NET Core supports the configuration and management of security in Blazor apps.
Security scenarios differ between Blazor Server and Blazor WebAssembly apps. Because Blazor Server apps run on the server, authorization checks are able to determine:
- The UI options presented to a user (for example, which menu entries are available to a user).
- Access rules for areas of the app and components.
Blazor WebAssembly apps run on the client. Authorization is only used to determine which UI options to show. Since client-side checks can be modified or bypassed by a user, a Blazor WebAssembly app can't enforce authorization access rules.
Authentication
Blazor uses the existing ASP.NET Core authentication mechanisms to establish the user's identity. The exact mechanism depends on how the Blazor app is hosted, Blazor Server or Blazor WebAssembly.
Blazor Server authentication
Blazor Server apps operate over a real-time connection that's created using SignalR. Authentication in SignalR-based apps is handled when the connection is established. Authentication can be based on a cookie or some other bearer token.
The Blazor Server project template can set up authentication for you when the project is created.
Visual Studio
Follow the Visual Studio guidance in the xref:blazor/get-started article to create a new Blazor Server project with an authentication mechanism.
After choosing the Blazor Server App template in the Create a new ASP.NET Core Web Application dialog, select Change under Authentication.
A dialog opens to offer the same set of authentication mechanisms available for other ASP.NET Core projects:
- No Authentication
- Individual User Accounts – User accounts can be stored:
- Within the app using ASP.NET Core's Identity system.
- With Azure AD B2C.
- Work or School Accounts
- Windows Authentication
Visual Studio Code
Follow the Visual Studio Code guidance in the xref:blazor/get-started article to create a new Blazor Server project with an authentication mechanism:
dotnet new blazorserver -o {APP NAME} -au {AUTHENTICATION}
Permissible authentication values ({AUTHENTICATION}
) are shown in the following table.
Authentication mechanism | {AUTHENTICATION} value |
---|---|
No Authentication | None |
Individual Users stored in the app with ASP.NET Core Identity. |
Individual |
Individual Users stored in Azure AD B2C. |
IndividualB2C |
Work or School Accounts Organizational authentication for a single tenant. |
SingleOrg |
Work or School Accounts Organizational authentication for multiple tenants. |
MultiOrg |
Windows Authentication | Windows |
The command creates a folder named with the value provided for the {APP NAME}
placeholder and uses the folder name as the app's name. For more information, see the dotnet new command in the .NET Core Guide.
Blazor WebAssembly authentication
In Blazor WebAssembly apps, authentication checks can be bypassed because all client-side code can be modified by users. The same is true for all client-side app technologies, including JavaScript SPA frameworks or native apps for any operating system.
Add a package reference for Microsoft.AspNetCore.Components.Authorization to the app's project file.
Implementation of a custom AuthenticationStateProvider
service for Blazor WebAssembly apps is covered in the following sections.
AuthenticationStateProvider service
Blazor Server apps include a built-in AuthenticationStateProvider
service that obtains authentication state data from ASP.NET Core's HttpContext.User
. This is how authentication state integrates with existing ASP.NET Core server-side authentication mechanisms.
AuthenticationStateProvider
is the underlying service used by the AuthorizeView
component and CascadingAuthenticationState
component to get the authentication state.
You don't typically use AuthenticationStateProvider
directly. Use the AuthorizeView component or Task approaches described later in this article. The main drawback to using AuthenticationStateProvider
directly is that the component isn't notified automatically if the underlying authentication state data changes.
The AuthenticationStateProvider
service can provide the current user's xref:System.Security.Claims.ClaimsPrincipal data, as shown in the following example:
@page "/"
@using Microsoft.AspNetCore.Components.Authorization
@inject AuthenticationStateProvider AuthenticationStateProvider
<button @onclick="@LogUsername">Write user info to console</button>
@code {
private async Task LogUsername()
{
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
var user = authState.User;
if (user.Identity.IsAuthenticated)
{
Console.WriteLine($"{user.Identity.Name} is authenticated.");
}
else
{
Console.WriteLine("The user is NOT authenticated.");
}
}
}
If user.Identity.IsAuthenticated
is true
and because the user is a xref:System.Security.Claims.ClaimsPrincipal, claims can be enumerated and membership in roles evaluated.
For more information on dependency injection (DI) and services, see xref:blazor/dependency-injection and xref:fundamentals/dependency-injection.
Implement a custom AuthenticationStateProvider
If you're building a Blazor WebAssembly app or if your app's specification absolutely requires a custom provider, implement a provider and override GetAuthenticationStateAsync
:
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components.Authorization;
namespace BlazorSample.Services
{
public class CustomAuthStateProvider : AuthenticationStateProvider
{
public override Task<AuthenticationState> GetAuthenticationStateAsync()
{
var identity = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Name, "mrfibuli"),
}, "Fake authentication type");
var user = new ClaimsPrincipal(identity);
return Task.FromResult(new AuthenticationState(user));
}
}
}
The CustomAuthStateProvider
service is registered in Startup.ConfigureServices
:
// using Microsoft.AspNetCore.Components.Authorization;
// using BlazorSample.Services;
services.AddScoped<AuthenticationStateProvider, CustomAuthStateProvider>();
Using the CustomAuthStateProvider
, all users are authenticated with the username mrfibuli
.
Expose the authentication state as a cascading parameter
If authentication state data is required for procedural logic, such as when performing an action triggered by the user, obtain the authentication state data by defining a cascading parameter of type Task<AuthenticationState>
:
@page "/"
<button @onclick="@LogUsername">Log username</button>
@code {
[CascadingParameter]
private Task<AuthenticationState> authenticationStateTask { get; set; }
private async Task LogUsername()
{
var authState = await authenticationStateTask;
var user = authState.User;
if (user.Identity.IsAuthenticated)
{
Console.WriteLine($"{user.Identity.Name} is authenticated.");
}
else
{
Console.WriteLine("The user is NOT authenticated.");
}
}
}
[!NOTE] In a Blazor WebAssembly app component, add the
Microsoft.AspNetCore.Components.Authorization
namespace (@using Microsoft.AspNetCore.Components.Authorization
).
If user.Identity.IsAuthenticated
is true
, claims can be enumerated and membership in roles evaluated.
Set up the Task<AuthenticationState>
cascading parameter using the AuthorizeRouteView
and CascadingAuthenticationState
components:
<Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound>
<CascadingAuthenticationState>
<LayoutView Layout="@typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</CascadingAuthenticationState>
</NotFound>
</Router>
Authorization
After a user is authenticated, authorization rules are applied to control what the user can do.
Access is typically granted or denied based on whether:
- A user is authenticated (signed in).
- A user is in a role.
- A user has a claim.
- A policy is satisfied.
Each of these concepts is the same as in an ASP.NET Core MVC or Razor Pages app. For more information on ASP.NET Core security, see the articles under ASP.NET Core Security and Identity.
AuthorizeView component
The AuthorizeView
component selectively displays UI depending on whether the user is authorized to see it. This approach is useful when you only need to display data for the user and don't need to use the user's identity in procedural logic.
The component exposes a context
variable of type AuthenticationState
, which you can use to access information about the signed-in user:
<AuthorizeView>
<h1>Hello, @context.User.Identity.Name!</h1>
<p>You can only see this content if you're authenticated.</p>
</AuthorizeView>
You can also supply different content for display if the user isn't authenticated:
<AuthorizeView>
<Authorized>
<h1>Hello, @context.User.Identity.Name!</h1>
<p>You can only see this content if you're authenticated.</p>
</Authorized>
<NotAuthorized>
<h1>Authentication Failure!</h1>
<p>You're not signed in.</p>
</NotAuthorized>
</AuthorizeView>
The content of <Authorized>
and <NotAuthorized>
tags can include arbitrary items, such as other interactive components.
Authorization conditions, such as roles or policies that control UI options or access, are covered in the Authorization section.
If authorization conditions aren't specified, AuthorizeView
uses a default policy and treats:
- Authenticated (signed-in) users as authorized.
- Unauthenticated (signed-out) users as unauthorized.
Role-based and policy-based authorization
The AuthorizeView
component supports role-based or policy-based authorization.
For role-based authorization, use the Roles
parameter:
<AuthorizeView Roles="admin, superuser">
<p>You can only see this if you're an admin or superuser.</p>
</AuthorizeView>
For more information, see xref:security/authorization/roles.
For policy-based authorization, use the Policy
parameter:
<AuthorizeView Policy="content-editor">
<p>You can only see this if you satisfy the "content-editor" policy.</p>
</AuthorizeView>
Claims-based authorization is a special case of policy-based authorization. For example, you can define a policy that requires users to have a certain claim. For more information, see xref:security/authorization/policies.
These APIs can be used in either Blazor Server or Blazor WebAssembly apps.
If neither Roles
nor Policy
is specified, AuthorizeView
uses the default policy.
Content displayed during asynchronous authentication
Blazor allows for authentication state to be determined asynchronously. The primary scenario for this approach is in Blazor WebAssembly apps that make a request to an external endpoint for authentication.
While authentication is in progress, AuthorizeView
displays no content by default. To display content while authentication occurs, use the <Authorizing>
element:
<AuthorizeView>
<Authorized>
<h1>Hello, @context.User.Identity.Name!</h1>
<p>You can only see this content if you're authenticated.</p>
</Authorized>
<Authorizing>
<h1>Authentication in progress</h1>
<p>You can only see this content while authentication is in progress.</p>
</Authorizing>
</AuthorizeView>
This approach isn't normally applicable to Blazor Server apps. Blazor Server apps know the authentication state as soon as the state is established. Authorizing
content can be provided in a Blazor Server app's AuthorizeView
component, but the content is never displayed.
[Authorize] attribute
The [Authorize]
attribute can be used in Razor components:
@page "/"
@attribute [Authorize]
You can only see this if you're signed in.
[!NOTE] In a Blazor WebAssembly app component, add the
Microsoft.AspNetCore.Authorization
namespace (@using Microsoft.AspNetCore.Authorization
) to the examples in this section.
[!IMPORTANT] Only use
[Authorize]
on@page
components reached via the Blazor Router. Authorization is only performed as an aspect of routing and not for child components rendered within a page. To authorize the display of specific parts within a page, useAuthorizeView
instead.
The [Authorize]
attribute also supports role-based or policy-based authorization. For role-based authorization, use the Roles
parameter:
@page "/"
@attribute [Authorize(Roles = "admin, superuser")]
<p>You can only see this if you're in the 'admin' or 'superuser' role.</p>
For policy-based authorization, use the Policy
parameter:
@page "/"
@attribute [Authorize(Policy = "content-editor")]
<p>You can only see this if you satisfy the 'content-editor' policy.</p>
If neither Roles
nor Policy
is specified, [Authorize]
uses the default policy, which by default is to treat:
- Authenticated (signed-in) users as authorized.
- Unauthenticated (signed-out) users as unauthorized.
Customize unauthorized content with the Router component
The Router
component, in conjunction with the AuthorizeRouteView
component, allows the app to specify custom content if:
- Content isn't found.
- The user fails an
[Authorize]
condition applied to the component. The[Authorize]
attribute is covered in the [Authorize] attribute section. - Asynchronous authentication is in progress.
In the default Blazor Server project template, the App.razor file demonstrates how to set custom content:
<Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)">
<NotAuthorized>
<h1>Sorry</h1>
<p>You're not authorized to reach this page.</p>
<p>You may need to log in as a different user.</p>
</NotAuthorized>
<Authorizing>
<h1>Authentication in progress</h1>
<p>Only visible while authentication is in progress.</p>
</Authorizing>
</AuthorizeRouteView>
</Found>
<NotFound>
<CascadingAuthenticationState>
<LayoutView Layout="@typeof(MainLayout)">
<h1>Sorry</h1>
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</CascadingAuthenticationState>
</NotFound>
</Router>
The content of <NotFound>
, <NotAuthorized>
, and <Authorizing>
tags can include arbitrary items, such as other interactive components.
If the <NotAuthorized>
element isn't specified, the AuthorizeRouteView
uses the following fallback message:
Not authorized.
Notification about authentication state changes
If the app determines that the underlying authentication state data has changed (for example, because the user signed out or another user has changed their roles), a custom AuthenticationStateProvider
can optionally invoke the method NotifyAuthenticationStateChanged
on the AuthenticationStateProvider
base class. This notifies consumers of the authentication state data (for example, AuthorizeView
) to rerender using the new data.
Procedural logic
If the app is required to check authorization rules as part of procedural logic, use a cascaded parameter of type Task<AuthenticationState>
to obtain the user's xref:System.Security.Claims.ClaimsPrincipal. Task<AuthenticationState>
can be combined with other services, such as IAuthorizationService
, to evaluate policies.
@inject IAuthorizationService AuthorizationService
<button @onclick="@DoSomething">Do something important</button>
@code {
[CascadingParameter]
private Task<AuthenticationState> authenticationStateTask { get; set; }
private async Task DoSomething()
{
var user = (await authenticationStateTask).User;
if (user.Identity.IsAuthenticated)
{
// Perform an action only available to authenticated (signed-in) users.
}
if (user.IsInRole("admin"))
{
// Perform an action only available to users in the 'admin' role.
}
if ((await AuthorizationService.AuthorizeAsync(user, "content-editor"))
.Succeeded)
{
// Perform an action only available to users satisfying the
// 'content-editor' policy.
}
}
}
[!NOTE] In a Blazor WebAssembly app component, add the
Microsoft.AspNetCore.Authorization
andMicrosoft.AspNetCore.Components.Authorization
namespaces:@using Microsoft.AspNetCore.Authorization @using Microsoft.AspNetCore.Components.Authorization
Authorization in Blazor WebAssembly apps
In Blazor WebAssembly apps, authorization checks can be bypassed because all client-side code can be modified by users. The same is true for all client-side app technologies, including JavaScript SPA frameworks or native apps for any operating system.
Always perform authorization checks on the server within any API endpoints accessed by your client-side app.
Troubleshoot errors
Common errors:
-
Authorization requires a cascading parameter of type Task<AuthenticationState>. Consider using CascadingAuthenticationState to supply this.
-
null
value is received forauthenticationStateTask
It's likely that the project wasn't created using a Blazor Server template with authentication enabled. Wrap a <CascadingAuthenticationState>
around some part of the UI tree, for example in App.razor as follows:
<CascadingAuthenticationState>
<Router AppAssembly="typeof(Startup).Assembly">
...
</Router>
</CascadingAuthenticationState>
The CascadingAuthenticationState
supplies the Task<AuthenticationState>
cascading parameter, which in turn it receives from the underlying AuthenticationStateProvider
DI service.