title | author | description | monikerRange | ms.author | ms.custom | ms.date | uid |
---|---|---|---|---|---|---|---|
Secure ASP.NET Core Blazor WebAssembly with ASP.NET Core Identity |
guardrex |
Learn how to secure Blazor WebAssembly apps with ASP.NET Core Identity. |
>= aspnetcore-8.0 |
riande |
mvc |
11/14/2023 |
blazor/security/webassembly/standalone-with-identity |
Standalone Blazor WebAssembly apps can be secured with ASP.NET Core Identity by following the guidance in this article.
Instead of using the default UI provided by ASP.NET Core Identity for SPA and Blazor apps, which is based on Razor Pages, call xref:Microsoft.AspNetCore.Routing.IdentityApiEndpointRouteBuilderExtensions.MapIdentityApi%2A in a backend API to add JSON API endpoints for registering and logging in users with ASP.NET Core Identity. Identity API endpoints also support advanced features, such as two-factor authentication and email verification.
On the client, call the /register
endpoint to register a user with their email address and password:
var result = await _httpClient.PostAsJsonAsync(
"register", new
{
email,
password
});
On the client, log in a user with cookie authentication using the /login
endpoint with useCookies
query string set to true
:
var result = await _httpClient.PostAsJsonAsync(
"login?useCookies=true", new
{
email,
password
});
The backend server API establishes cookie authentication with a call to xref:Microsoft.AspNetCore.Identity.IdentityCookieAuthenticationBuilderExtensions.AddIdentityCookies%2A on the authentication builder:
builder.Services
.AddAuthentication(IdentityConstants.ApplicationScheme)
.AddIdentityCookies();
For native and mobile scenarios where some clients don't support cookies, the login API provides a parameter to request tokens. A custom token (one that is proprietary to the ASP.NET Core Identity platform) is issued that can be used to authenticate subsequent requests. The token should be passed in the Authorization
header as a bearer token. A refresh token is also provided. This token allows the app to request a new token when the old one expires without forcing the user to log in again.
The tokens are not standard JSON Web Tokens (JWTs). The use of custom tokens is intentional, as the built-in Identity API is meant primarily for simple scenarios. The token option is not intended to be a fully-featured identity service provider or token server, but instead an alternative to the cookie option for clients that can't use cookies.
The following guidance begins the process of implementing token-based authentication with the login API. Custom code is required to complete the implementation. For more information, see xref:security/authentication/identity/spa.
Instead of the backend server API establishing cookie authentication with a call to xref:Microsoft.AspNetCore.Identity.IdentityCookieAuthenticationBuilderExtensions.AddIdentityCookies%2A on the authentication builder, the server API sets up bearer token auth with the xref:Microsoft.Extensions.DependencyInjection.BearerTokenExtensions.AddBearerToken%2A extension method. Specify the scheme for bearer authentication tokens with xref:Microsoft.AspNetCore.Identity.IdentityConstants.BearerScheme%2A?displayProperty=nameWithType.
In Backend/Program.cs
, change the authentication services and configuration to the following:
builder.Services
.AddAuthentication()
.AddBearerToken(IdentityConstants.BearerScheme);
In BlazorWasmAuth/Identity/CookieAuthenticationStateProvider.cs
, remove the useCookies
query string parameter in the LoginAsync
method of the CookieAuthenticationStateProvider
:
- /login?useCookies=true
+ /login
At this point, you must provide custom code to parse the xref:Microsoft.AspNetCore.Authentication.BearerToken.AccessTokenResponse on the client and manage the access and refresh tokens. For more information, see xref:security/authentication/identity/spa.
For additional Identity scenarios provided by the API, see xref:security/authentication/identity/spa:
- Secure selected endpoints
- Token authentication
- Two-factor authentication (2FA)
- Recovery codes
- User info management
In this article, sample apps serve as a reference for standalone Blazor WebAssembly apps that access ASP.NET Core Identity through a backend web API. The demonstration includes two apps:
Backend
: A backend web API app that maintains a user identity store for ASP.NET Core Identity.BlazorWasmAuth
: A standalone Blazor WebAssembly frontend app with user authentication.
Access the sample apps through the latest version folder from the repository's root with the following link. The samples are provided for .NET 8 or later. See the README
file in the BlazorWebAssemblyStandaloneWithIdentity
folder for steps on how to run the sample apps.
View or download sample code (how to download)
The backend web API app maintains a user identity store for ASP.NET Core Identity.
The app uses the following NuGet packages:
Microsoft.AspNetCore.Identity
Microsoft.AspNetCore.Identity.EntityFrameworkCore
Microsoft.EntityFrameworkCore.InMemory
Microsoft.AspNetCore.OpenApi
Swashbuckle.AspNetCore
If your app is to use a different EF Core database provider than the in-memory provider, don't create a package reference in your app for Microsoft.EntityFrameworkCore.InMemory
.
If your app won't adopt Swagger/OpenAPI, don't create package references for Microsoft.AspNetCore.OpenApi
and Swashbuckle.AspNetCore
.
In the app's project file (.csproj
), invariant globalization is configured.
App settings configure backend and frontend URLs:
Backend
app (BackendUrl
):https://localhost:7211
BlazorWasmAuth
app (FrontendUrl
):https://localhost:7171
The Backend.http
file can be used for testing the weather data request. Note that the BlazorWasmAuth
app must be running to test the endpoint, and the endpoint is hardcoded into the file. For more information, see xref:test/http-files.
The following setup and configuration is found in the app's Program
file.
User identity with cookie authentication is added by calling xref:Microsoft.Extensions.DependencyInjection.AuthenticationServiceCollectionExtensions.AddAuthentication%2A and xref:Microsoft.AspNetCore.Identity.IdentityCookieAuthenticationBuilderExtensions.AddIdentityCookies%2A. Services for authorization checks are added by a call to xref:Microsoft.Extensions.DependencyInjection.PolicyServiceCollectionExtensions.AddAuthorizationBuilder%2A.
Only recommended for demonstrations, the app uses the EF Core in-memory database provider for the database context registration (xref:Microsoft.Extensions.DependencyInjection.EntityFrameworkServiceCollectionExtensions.AddDbContext%2A). The in-memory database provider makes it easy to restart the app and test the registration and login user flows. However, each run starts with a fresh database. If the database is changed to SQLite, users are saved between sessions, but the database must be created through migrations,as shown in the EF Core getting started tutorial. You can use other relational providers such as SQL Server for your production code.
Configure Identity to use the EF Core database and expose the Identity endpoints via the calls to xref:Microsoft.Extensions.DependencyInjection.IdentityServiceCollectionExtensions.AddIdentityCore%2A, xref:Microsoft.Extensions.DependencyInjection.IdentityEntityFrameworkBuilderExtensions.AddEntityFrameworkStores%2A, and xref:Microsoft.AspNetCore.Identity.IdentityBuilderExtensions.AddApiEndpoints%2A.
A Cross-Origin Resource Sharing (CORS) policy is established to permit requests from the frontend and backend apps. Fallback URLs are configured for the CORS policy if app settings don't provide them:
Backend
app (BackendUrl
):https://localhost:5001
BlazorWasmAuth
app (FrontendUrl
):https://localhost:5002
Services and endpoints for Swagger/OpenAPI are included for web API documentation and development testing.
Routes are mapped for Identity endpoints by calling MapIdentityApi<AppUser>()
.
A logout endpoint (/Logout
) is configured in the middleware pipeline to sign users out.
To secure an endpoint, add the xref:Microsoft.AspNetCore.Builder.AuthorizationEndpointConventionBuilderExtensions.RequireAuthorization%2A extension method to the route definition. For a controller, add the [Authorize]
attribute to the controller or action.
For more information on basic patterns for initialization and configuration of a xref:Microsoft.EntityFrameworkCore.DbContext instance, see DbContext Lifetime, Configuration, and Initialization in the EF Core documentation.
A standalone Blazor WebAssembly frontend app demonstrates user authentication and authorization to access a private webpage.
The app uses the following NuGet packages:
Microsoft.AspNetCore.Components.WebAssembly.Authentication
Microsoft.Extensions.Http
Microsoft.AspNetCore.Components.WebAssembly
Microsoft.AspNetCore.Components.WebAssembly.DevServer
The Models
folder contains the app's models:
FormResult
(Identity/Models/FormResult.cs
): Response for login and registration.UserBasic
(Identity/Models/UserBasic.cs
): Basic user information to register and login.UserInfo
(Identity/Models/UserInfo.cs
): User info from identity endpoint to establish claims.
The IAccountManagement
interface (Identity/CookieHandler.cs
) provides account management services.
The CookieAuthenticationStateProvider
class (Identity/CookieAuthenticationStateProvider.cs
) handles state for cookie-based authentication and provides account management service implementations described by the IAccountManagement
interface. The LoginAsync
method explicitly enables cookie authentication via the useCookies
query string value of true
.
The CookieHandler
class (Identity/CookieHandler.cs
) ensures cookie credentials are sent with each request to the backend web API, which handles Identity and maintains the Identity data store.
The wwwroot/appsettings.file
provides backend and frontend URL endpoints.
The App
component exposes the authentication state as a cascading parameter. For more information, see xref:blazor/security/index#expose-the-authentication-state-as-a-cascading-parameter.
The MainLayout
component and NavMenu
component use the AuthorizeView
component to selectively display content based on the user's authentication status.
The following components handle common user authentication tasks, making use of IAccountManagement
services:
Register
component (Components/Identity/Register.razor
)Login
component (Components/Identity/Login.razor
)Logout
component (Components/Identity/Logout.razor
)
The PrivatePage
component (Components/Pages/PrivatePage.razor
) requires authentication and shows the user's claims.
Services and configuration is provided in the Program
file (Program.cs
):
- The cookie handler is registered as a scoped service.
- Authorization services are registered.
- The custom authentication state provider is registered as a scoped service.
- The account management interface (
IAccountManagement
) is registered. - The base host URL is configured for a registered HTTP client instance.
- The base backend URL is configured for a registered HTTP client instance that's used for auth interactions with the backend web API. The HTTP client uses the cookie handler to ensure that cookie credentials are sent with each request.
Call xref:Microsoft.AspNetCore.Components.Authorization.AuthenticationStateProvider.NotifyAuthenticationStateChanged%2A?displayProperty=nameWithType when the user's authentication state changes. For an example, see the LoginAsync
and LogoutAsync
methods of the CookieAuthenticationStateProvider
class (Identity/CookieAuthenticationStateProvider.cs
).
Warning
The xref:Microsoft.AspNetCore.Components.Authorization.AuthorizeView component selectively displays UI content depending on whether the user is authorized. All content within a Blazor WebAssembly app placed in an xref:Microsoft.AspNetCore.Components.Authorization.AuthorizeView component is discoverable without authentication, so sensitive content should be obtained from a backend server-based web API after authentication succeeds. For more information, see the following resources:
- xref:blazor/security/index#authorizeview-component
- xref:blazor/call-web-api
- xref:blazor/security/webassembly/additional-scenarios
For security reasons, role claims aren't sent back from the manage/info
endpoint to create UserInfo.Claims
for users of the BlazorWasmAuth
app.
To create role claims on your own, make a separate request in the GetAuthenticationStateAsync
method of the CookieAuthenticationStateProvider
after the user is authenticated to a custom web API in the Backend
project that provides user roles from the user data store. We plan to provide guidance on this subject. The work is tracked by Role claims guidance in standalone WASM w/Identity article (dotnet/AspNetCore.Docs #31045).
- What's new with identity in .NET 8
AuthenticationStateProvider
service