You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Authentication for client-side blazor applications
Blazor webassembly applications should be treated as Single Page Applications for the purposes of security. When it comes to authenticating single page applications there are several approaches, but the most common and extended one is to use an approach based on oAuth like Open ID Connect.
Blazor webassembly supports authenticating and authorizing your applications using Open ID Connect via a the Microsoft.AspNetCore.Components.WebAssembly.Authentication library.
This library provides a set of primitives that allow Blazor webassembly applications to seamlessly authenticate against ASP.NET Core backends using Identity with the ApiAuthorization support that we built on top of Identity Server and to authenticate against third-party Identity Providers so long as they support Open ID Connect.
The authentication support on the webassembly side is built on top of the oidc-client.js library which we use to handle the underlying authentication protocol details.
There are other possible options for authenticating single page applications, like using SameSite cookies, but as part of our design process we have settled on oAuth and specifically on Open ID Connect as the best option for authenticating Blazor webassembly applications. The reasons we chose to use a token based protocol compared to cookie are both functional and security related:
Using a token based protocol offers a smaller attack surface area, as the tokens are not sent in all requests.
You don't have to protect your server endpoints against CSRF as the tokens need to be sent explicitly.
This allows you to host Blazor webassembly applications alongside MVC or Razor pages applications.
Tokens have more limited permissions than cookies, for example they can't be used to manage the user account or change their password unless you implement that functionality explicitly.
Tokens have a shorter lifetime (1h by default) which limits the attack window and can be revoked if necessary.
Tokens (in this case self-contained tokens JWTs) offer guarantees to the client and the server about the authentication process.
The client has the means to detect and validate that the tokens it receives are legitimate and were emitted as part a given authentication process.
For example, if a third paryt tried to switch a token in the middle of the authentication process, the client can detect that and avoid using the token.
Tokens and in this case oAuth and Open ID Connect don't rely on the user agent behaving correctly to ensure that the application is secure.
Token based protocols (in this case oAuth and Open ID Connect) allow for authenticating and authorizing hosted and standalone applications with the same set of security caracteristics.
Authenticating Blazor webassembly applications with Open ID Connect
The Microsoft.AspNetCore.Components.WebAssembly.Authentication offers several primitives to implement authentication and authorization using Open ID Connect. In broad terms, the way authentication works is as follows:
When an anonymous user clicks the login button or tries to access a page with the [Authorize] attribute applied to it, gets redirected to the authentication/login page.
In this page, the authentication library prepares the application to be redirected to the authorization endpoint. This endpoint is outside of the Blazor webassembly application, can even be hosted in a separate origin and is responsible for determining whether the user is authenticated or not and to issue one or more tokens in response.
If the user is not authenticated, the user gets redirected to the underlying authentication system being used, in the common case this is ASP.NET Core Identity.
If the user was already authenticated, the authorization endpoint generates the appropriate response and redirects the browser back to authentication/login-callback.
When the Blazor webassembly application starts and loads the authentication/login-callback endpoint, the authentication response is processed and as a result the user is authenticated.
If something goes wrong during this process, the user is sent to the authentication/login-failed page and an error is displayed.
If the authentication process completes successfully, the user will be authenticated and optionally sent back to the protected url he was trying to originally visit.
The authentication middleware that is responsible for validating the request credentials and setting the user on the request context:
app.UseAuthentication();
The IdentityServer middleware that exposes the Open ID Connect endpoints:
app.UseIdentityServer();
AddApiAuthorization
This helper method configures IdentityServer to use our supported configuration. IdentityServer is a powerful and extensible framework for handling app security concerns. At the same time, that exposes unnecessary complexity for the most common scenarios. Consequently, a set of conventions and configuration options is provided to you that are considered a good starting point. Once your authentication needs change, the full power of IdentityServer is still available to customize authentication to suit your needs.
AddIdentityServerJwt
This helper method configures a policy scheme for the app as the default authentication handler. The policy is configured to let Identity handle all requests routed to any subpath in the Identity URL space "/Identity". The JwtBearerHandler handles all other requests. Additionally, this method registers an <<ApplicationName>>API API resource with IdentityServer with a default scope of <<ApplicationName>>API and configures the JWT Bearer token middleware to validate tokens issued by IdentityServer for the app.
WeatherForecastController
In the Controllers\WeatherForecastController.cs file, notice the [Authorize] attribute applied to the class that indicates that the user needs to be authorized based on the default policy to access the resource. The default authorization policy happens to be configured to use the default authentication scheme, which is set up by AddIdentityServerJwt to the policy scheme that was mentioned above, making the JwtBearerHandler configured by such helper method the default handler for requests to the app.
ApplicationDbContext
In the Data\ApplicationDbContext.cs file, notice the same DbContext is used in Identity with the exception that it extends ApiAuthorizationDbContext (a more derived class from IdentityDbContext) to include the schema for IdentityServer.
To gain full control of the database schema, inherit from one of the available Identity DbContext classes and configure the context to include the Identity schema by calling builder.ConfigurePersistedGrantContext(_operationalStoreOptions.Value) on the OnModelCreating method.
OidcConfigurationController
In the Controllers\OidcConfigurationController.cs file, notice the endpoint that's provisioned to serve the OIDC parameters that the client needs to use.
appsettings.json
In the appsettings.json file of the project root, there's a new IdentityServer section that describes the list of configured clients. In the following example, there's a single client. The client name corresponds to the app name and is mapped by convention to the OAuth ClientId parameter. The profile indicates the app type being configured. It's used internally to drive conventions that simplify the configuration process for the server. There are several profiles available, as explained in the Application profiles section.
In the appsettings.Development.json file of the project root, there's an IdentityServer section that describes the key used to sign tokens. When deploying to production, a key needs to be provisioned and deployed alongside the app, as explained in the Deploy to production section.
The application uses the Microsoft.AspNetCore.Components.WebAssembly.Authentication which contains the set of primitives that help the application authenticate users and get tokens to call protected APIs.
The support for authenticating users is plugged into the service container by the extension method provided inside the Microsoft.AspNetCore.Components.WebAssembly.Authentication package. This method sets up all the services needed for the application to interact with the existing authorization system.
builder.Services.AddApiAuthorization();
By default, it loads the configuration for the application by convention from _configuration/<<client-id>>. The client ID used by convention is the application assembly name. This url can be changed to point to a separate endpoint by calling the overload with options.
Index.html
The index.html page includes a script that defines the AuthenticationService in JavaScript that is used to handle the low level details of the Open ID Connect protocol. The Blazor webassembly application will call methods defined on this script internally to perform the authentication operations.
The App.razor component is very similar to the one that can be found in server-side Blazor apps:
The CascadingAuthenticationState component takes care of exposing the AuthenticationState to the rest of the application.
The AuthorizeRouteView makes sure that the current user is authorized to access a given page or otherwise renders the RedirectToLogin component.
The RedirectToLogin component takes care of redirecting unauthorized users to the login page.
<CascadingAuthenticationState><RouterAppAssembly="@typeof(Program).Assembly"><FoundContext="routeData"><AuthorizeRouteViewRouteData="@routeData" DefaultLayout="@typeof(MainLayout)"><NotAuthorized><RedirectToLogin />
</NotAuthorized></AuthorizeRouteView></Found><NotFound><LayoutViewLayout="@typeof(MainLayout)"><p>Sorry, there's nothing at this address.</p></LayoutView></NotFound></Router></CascadingAuthenticationState>
RedirectToLogin.razor
This component takes care of redirecting unauthorized users to the login page and making sure we preserve the current url they were trying to access so that they can return to that page once the authentication is successful.
This page defines the routes needed for handling different authentication stages in the application. It uses the RemoteauthenticatorView component that comes in the Microsoft.AspNetCore.Components.WebAssembly.Authentication and that takes care of performing the appropriate actions at each stage.
Several authentication aspects can be customized through this component as detailed in PLACEHOLDER.
This page shows how to provision an access token and use that access token to call a protected resource API on the hosting asp.net core application.
The @attribute [Authorize] indicates the blazor webassembly authorization system that the user must be authorized in order to visit this area of the application. It is fundamental to understand that this doesn't prevent the API on the server from being called without proper credentials and that the server needs to also use [Authorize] on the appropriate endpoints to correctly protect them.
AuthenticationService.RequestAccessToken(); takes care of requesting an access token that can be added to the request to call the API. If the token is cached or the service is able to provision a new access token without user interaction, the token request will succeed; otherwise, it will fail.
In order to get the actual token to include in the request you need to check that the request succedeed by calling tokenResult.TryGetToken(out var token).
If the request was successful, the token variable will be populated with the access token. The value property in the token exposes the literal string to include in the Authorization request header.
If the request failed because the token could not be provisioned without user interaction, the token result will contain a redirect url. As described in PLACEHOLDER navigating to this url will take you to the login page and back to the current page after a successful authentication.
Authenticating standalone applications using Microsoft.AspNetCore.Components.WebAssembly.Authentication
To create a new Blazor hosted application with authentication from within Visual Studio:
Open Visual Studio
Select File -> New Project -> Blazor Application
On the right top corner click on Change under the Authentication section
On the window select Individual User Accounts and press OK
Click on Create
After the project creation completes, you will see three projects
Overview of the Blazor Webassembly application
Project file
The application uses the Microsoft.AspNetCore.Components.WebAssembly.Authentication which contains the set of primitives that help the application authenticate users and get tokens to call protected APIs.
The support for authenticating users is plugged into the service container by the extension method provided inside the Microsoft.AspNetCore.Components.WebAssembly.Authentication package. This method sets up all the services needed for the application to interact with the existing authorization system.
Authentication support for standalone applications is offered using Open ID Connect, the AddOidcAuthentication method accepts a callback to configure the parameters needed to authenticate an application using Open ID Connect.
The values needed for configuring the application can be obtained from your Identity Provider (Microsoft, Google, or other Open ID Connect compliant identity provider) when you register your app with them.
Index.html
The index.html page includes a script that defines the AuthenticationService in JavaScript that is used to handle the low level details of the Open ID Connect protocol. The Blazor webassembly application will call methods defined on this script internally to perform the authentication operations.
The App.razor component is very similar to the one that can be found in server-side Blazor apps:
The CascadingAuthenticationState component takes care of exposing the AuthenticationState to the rest of the application.
The AuthorizeRouteView makes sure that the current user is authorized to access a given page or otherwise renders the RedirectToLogin component.
The RedirectToLogin component takes care of redirecting unauthorized users to the login page.
<CascadingAuthenticationState><RouterAppAssembly="@typeof(Program).Assembly"><FoundContext="routeData"><AuthorizeRouteViewRouteData="@routeData" DefaultLayout="@typeof(MainLayout)"><NotAuthorized><RedirectToLogin />
</NotAuthorized></AuthorizeRouteView></Found><NotFound><LayoutViewLayout="@typeof(MainLayout)"><p>Sorry, there's nothing at this address.</p></LayoutView></NotFound></Router></CascadingAuthenticationState>
RedirectToLogin.razor
This component takes care of redirecting unauthorized users to the login page and making sure we preserve the current url they were trying to access so that they can return to that page once the authentication is successful.
This page defines the routes needed for handling different authentication stages in the application. It uses the RemoteauthenticatorView component that comes in the Microsoft.AspNetCore.Components.WebAssembly.Authentication and that takes care of performing the appropriate actions at each stage.
Several authentication aspects can be customized through this component as detailed in PLACEHOLDER.
The AddAuthentication method sets up authentication services within the app and configures the JWT Bearer handler to be the default authentication method. The AddAzureADBearer method sets up the specific parameters in the JWT Bearer handler required to validate tokens emitted by the Azure Active Directory.
UseAuthentication and UseAuthorization ensure that the application tries to parse and validate tokens on incoming requests and that any request trying to access a protected resource without proper credentials fails.
app.UseAuthentication();
app.UseAuthorization();
appsettings.json
Contains the options to configure the JWT bearer handler used to validate access tokens.
This controller exposes a protected API with the [Authorize] attribute applied to the controller. It is important to understand that the [Authorize] attribute in this API controller is the only thing that protect this API from unauthorized access and that the [Authorize] attribute used in the Blazor webassembly application only serves as a hint to the application that the user needs to be authorized for the application to work correctly.
The application uses the Microsoft.Authentication.WebAssembly.Msal which contains the set of primitives that help the application authenticate users and get tokens to call protected APIs in Azure Active Directory.
The support for authenticating users is plugged into the service container by the extension method provided inside the Microsoft.Authentication.WebAssembly.Msal package. This method sets up all the services needed for the application to interact with the existing authorization system.
Authentication support for standalone applications is offered using Open ID Connect, the AddMsalAuthentication method accepts a callback to configure the parameters needed to authenticate the application with Azure Active Directory.
The authority is formed by appending the tenant id to the Azure Active Directory base url https://login.microsoftonline.com
The client id is the application id defined for the single page application when registering the app.
The default access token scopes represent the list of access token scopes that will be included by default in the sign in request and that will be used to provision an access token inmediately after. All scopes must belong to the same application as per Azure Active Directory rules.
Index.html
The index.html page includes a script that defines the AuthenticationService in JavaScript that is used to handle the low level details of the Open ID Connect protocol. The Blazor webassembly application will call methods defined on this script internally to perform the authentication operations.
The App.razor component is very similar to the one that can be found in server-side Blazor apps:
The CascadingAuthenticationState component takes care of exposing the AuthenticationState to the rest of the application.
The AuthorizeRouteView makes sure that the current user is authorized to access a given page or otherwise renders the RedirectToLogin component.
The RedirectToLogin component takes care of redirecting unauthorized users to the login page.
<CascadingAuthenticationState><RouterAppAssembly="@typeof(Program).Assembly"><FoundContext="routeData"><AuthorizeRouteViewRouteData="@routeData" DefaultLayout="@typeof(MainLayout)"><NotAuthorized><RedirectToLogin />
</NotAuthorized></AuthorizeRouteView></Found><NotFound><LayoutViewLayout="@typeof(MainLayout)"><p>Sorry, there's nothing at this address.</p></LayoutView></NotFound></Router></CascadingAuthenticationState>
RedirectToLogin.razor
This component takes care of redirecting unauthorized users to the login page and making sure we preserve the current url they were trying to access so that they can return to that page once the authentication is successful.
This component is added as part of the MainLayout.razor component and takes care of:
For authenticated users:
Displays the current user name.
Offers a button to log out of the application.
For anonymous users:
Offers the option to log in.
The template only supports authenticating users in Azure Active Directory. Other tasks like editing the user profile or registering new users need to be handled following your Azure Active Directory tenant policies.
This page defines the routes needed for handling different authentication stages in the application. It uses the RemoteauthenticatorView component that comes in the Microsoft.AspNetCore.Components.WebAssembly.Authentication and that takes care of performing the appropriate actions at each stage.
Several authentication aspects can be customized through this component as detailed in PLACEHOLDER.
This page shows how to provision an access token and use that access token to call a protected resource API on the hosting asp.net core application.
The @attribute [Authorize] indicates the blazor webassembly authorization system that the user must be authorized in order to visit this area of the application. It is fundamental to understand that this doesn't prevent the API on the server from being called without proper credentials and that the server needs to also use [Authorize] on the appropriate endpoints to correctly protect them.
AuthenticationService.RequestAccessToken(); takes care of requesting an access token that can be added to the request to call the API. If the token is cached or the service is able to provision a new access token without user interaction, the token request will succeed; otherwise, it will fail.
In order to get the actual token to include in the request you need to check that the request succedeed by calling tokenResult.TryGetToken(out var token).
If the request was successful, the token variable will be populated with the access token. The value property in the token exposes the literal string to include in the Authorization request header.
If the request failed because the token could not be provisioned without user interaction, the token result will contain a redirect url. As described in PLACEHOLDER navigating to this url will take you to the login page and back to the current page after a successful authentication.
Authenticating Blazor webassembly standalone applications using Azure Active Directory with Microsoft.Authentication.WebAssembly.Msal
To create a Blazor webassembly hosted application that uses Azure Active Directory for authentication follow these steps:
Create two applications in Azure Active Directory following the steps in PLACEHOLDER
Capture the following data:
Application ID (Client ID) for the single page application
Tenant ID for the Azure Active Directory tenant.
From the command line, run the following command:
dotnet new blazorwasm -au SingleOrg --client-id "<<single-page-client-id>>" --tenant-id "<<tenant-id>>"
Overview of the Blazor Webassembly application
Project file
The application uses the Microsoft.Authentication.WebAssembly.Msal which contains the set of primitives that help the application authenticate users and get tokens to call protected APIs in Azure Active Directory.
The support for authenticating users is plugged into the service container by the extension method provided inside the Microsoft.Authentication.WebAssembly.Msal package. This method sets up all the services needed for the application to interact with the existing authorization system.
Authentication support for standalone applications is offered using Open ID Connect, the AddMsalAuthentication method accepts a callback to configure the parameters needed to authenticate the application with Azure Active Directory.
The authority is formed by appending the tenant id to the Azure Active Directory base url https://login.microsoftonline.com
The client id is the application id defined for the single page application when registering the app.
When creating standalone applications we don't configure the application to request an access token to talk to an API as we don't know what API you plan to talk to. The code in Program.cs can be changed as described below to provision a token as part of the sign in flow:
The index.html page includes a script that defines the AuthenticationService in JavaScript that is used to handle the low level details of the Open ID Connect protocol. The Blazor webassembly application will call methods defined on this script internally to perform the authentication operations.
The App.razor component is very similar to the one that can be found in server-side Blazor apps:
The CascadingAuthenticationState component takes care of exposing the AuthenticationState to the rest of the application.
The AuthorizeRouteView makes sure that the current user is authorized to access a given page or otherwise renders the RedirectToLogin component.
The RedirectToLogin component takes care of redirecting unauthorized users to the login page.
<CascadingAuthenticationState><RouterAppAssembly="@typeof(Program).Assembly"><FoundContext="routeData"><AuthorizeRouteViewRouteData="@routeData" DefaultLayout="@typeof(MainLayout)"><NotAuthorized><RedirectToLogin />
</NotAuthorized></AuthorizeRouteView></Found><NotFound><LayoutViewLayout="@typeof(MainLayout)"><p>Sorry, there's nothing at this address.</p></LayoutView></NotFound></Router></CascadingAuthenticationState>
RedirectToLogin.razor
This component takes care of redirecting unauthorized users to the login page and making sure we preserve the current url they were trying to access so that they can return to that page once the authentication is successful.
This component is added as part of the MainLayout.razor component and takes care of:
For authenticated users:
Displays the current user name.
Offers a button to log out of the application.
For anonymous users:
Offers the option to log in.
The template only supports authenticating users in Azure Active Directory. Other tasks like editing the user profile or registering new users need to be handled following your Azure Active Directory tenant policies.
This page defines the routes needed for handling different authentication stages in the application. It uses the RemoteauthenticatorView component that comes in the Microsoft.AspNetCore.Components.WebAssembly.Authentication and that takes care of performing the appropriate actions at each stage.
Several authentication aspects can be customized through this component as detailed in PLACEHOLDER.
Authenticating Blazor webassembly hosted applications using Azure Active Directory B2C with Microsoft.Authentication.WebAssembly.Msal
To create a Blazor webassembly hosted application that uses Azure Active Directory B2C for authentication follow these steps:
Create two applications in Azure Active Directory B2C following the steps in PLACEHOLDER
Capture the following data:
Base url for the B2C tenant, typically something like https://<<tenant>>.b2clogin.com
Sign-up/sign-in policy to use for login and registering users. See here for details.
Application ID (Client ID) for the single page application
Domain for the Azure Active Directory B2C tenant, typically something like <>.onmicrosoft.com.
App ID URI for the Application with exposed APIs (Your server API application). Only capture the value, that you configured not the https://.../ prefix included. The template will compute that.
The default scope that you want to initially request, you can add more scopes or change the scopes in the Program.cs if you need to later.
API Application ID (Client ID) for the server API.
The AddAuthentication method sets up authentication services within the app and configures the JWT Bearer handler to be the default authentication method. The AddAzureADB2CBearer method sets up the specific parameters in the JWT Bearer handler required to validate tokens emitted by the Azure Active Directory.
UseAuthentication and UseAuthorization ensure that the application tries to parse and validate tokens on incoming requests and that any request trying to access a protected resource without proper credentials fails.
app.UseAuthentication();
app.UseAuthorization();
appsettings.json
Contains the options to configure the JWT bearer handler used to validate access tokens.
This controller exposes a protected API with the [Authorize] attribute applied to the controller. It is important to understand that the [Authorize] attribute in this API controller is the only thing that protect this API from unauthorized access and that the [Authorize] attribute used in the Blazor webassembly application only serves as a hint to the application that the user needs to be authorized for the application to work correctly.
The application uses the Microsoft.Authentication.WebAssembly.Msal which contains the set of primitives that help the application authenticate users and get tokens to call protected APIs in Azure Active Directory.
The support for authenticating users is plugged into the service container by the extension method provided inside the Microsoft.Authentication.WebAssembly.Msal package. This method sets up all the services needed for the application to interact with the existing authorization system.
The AddMsalAuthentication method accepts a callback to configure the parameters needed to authenticate the application with Azure Active Directory.
The authority is formed by appending the AAD B2C instance url, the domain and the sign-up/sign-in policy for the Azure Active Directory B2C tenant.
The client id is the application id defined for the single page application when registering the app.
The default access token scopes represent the list of access token scopes that will be included by default in the sign in request and that will be used to provision an access token inmediately after. All scopes must belong to the same application as per Azure Active Directory rules.
Index.html
The index.html page includes a script that defines the AuthenticationService in JavaScript that is used to handle the low level details of the Open ID Connect protocol. The Blazor webassembly application will call methods defined on this script internally to perform the authentication operations.
The App.razor component is very similar to the one that can be found in server-side Blazor apps:
The CascadingAuthenticationState component takes care of exposing the AuthenticationState to the rest of the application.
The AuthorizeRouteView makes sure that the current user is authorized to access a given page or otherwise renders the RedirectToLogin component.
The RedirectToLogin component takes care of redirecting unauthorized users to the login page.
<CascadingAuthenticationState><RouterAppAssembly="@typeof(Program).Assembly"><FoundContext="routeData"><AuthorizeRouteViewRouteData="@routeData" DefaultLayout="@typeof(MainLayout)"><NotAuthorized><RedirectToLogin />
</NotAuthorized></AuthorizeRouteView></Found><NotFound><LayoutViewLayout="@typeof(MainLayout)"><p>Sorry, there's nothing at this address.</p></LayoutView></NotFound></Router></CascadingAuthenticationState>
RedirectToLogin.razor
This component takes care of redirecting unauthorized users to the login page and making sure we preserve the current url they were trying to access so that they can return to that page once the authentication is successful.
This component is added as part of the MainLayout.razor component and takes care of:
For authenticated users:
Displays the current user name.
Offers a button to log out of the application.
For anonymous users:
Offers the option to log in, users can choose to register from within the login screen.
The template only supports authenticating users in Azure Active Directory B2C. Other tasks like editing the user profile need to be handled following your Azure Active Directory B2C tenant policies.
This page defines the routes needed for handling different authentication stages in the application. It uses the RemoteauthenticatorView component that comes in the Microsoft.AspNetCore.Components.WebAssembly.Authentication and that takes care of performing the appropriate actions at each stage.
Several authentication aspects can be customized through this component as detailed in PLACEHOLDER.
This page shows how to provision an access token and use that access token to call a protected resource API on the hosting asp.net core application.
The @attribute [Authorize] indicates the blazor webassembly authorization system that the user must be authorized in order to visit this area of the application. It is fundamental to understand that this doesn't prevent the API on the server from being called without proper credentials and that the server needs to also use [Authorize] on the appropriate endpoints to correctly protect them.
AuthenticationService.RequestAccessToken(); takes care of requesting an access token that can be added to the request to call the API. If the token is cached or the service is able to provision a new access token without user interaction, the token request will succeed; otherwise, it will fail.
In order to get the actual token to include in the request you need to check that the request succedeed by calling tokenResult.TryGetToken(out var token).
If the request was successful, the token variable will be populated with the access token. The value property in the token exposes the literal string to include in the Authorization request header.
If the request failed because the token could not be provisioned without user interaction, the token result will contain a redirect url. As described in PLACEHOLDER navigating to this url will take you to the login page and back to the current page after a successful authentication.
The application uses the Microsoft.Authentication.WebAssembly.Msal which contains the set of primitives that help the application authenticate users and get tokens to call protected APIs in Azure Active Directory.
The support for authenticating users is plugged into the service container by the extension method provided inside the Microsoft.Authentication.WebAssembly.Msal package. This method sets up all the services needed for the application to interact with the existing authorization system.
The AddMsalAuthentication method accepts a callback to configure the parameters needed to authenticate the application with Azure Active Directory.
The authority is formed by appending the AAD B2C instance url, the domain and the sign-up/sign-in policy for the Azure Active Directory B2C tenant.
The client id is the application id defined for the single page application when registering the app.
When creating standalone applications we don't configure the application to request an access token to talk to an API as we don't know what API you plan to talk to. The code in Program.cs can be changed as described below to provision a token as part of the sign in flow:
The index.html page includes a script that defines the AuthenticationService in JavaScript that is used to handle the low level details of the Open ID Connect protocol. The Blazor webassembly application will call methods defined on this script internally to perform the authentication operations.
The App.razor component is very similar to the one that can be found in server-side Blazor apps:
The CascadingAuthenticationState component takes care of exposing the AuthenticationState to the rest of the application.
The AuthorizeRouteView makes sure that the current user is authorized to access a given page or otherwise renders the RedirectToLogin component.
The RedirectToLogin component takes care of redirecting unauthorized users to the login page.
<CascadingAuthenticationState><RouterAppAssembly="@typeof(Program).Assembly"><FoundContext="routeData"><AuthorizeRouteViewRouteData="@routeData" DefaultLayout="@typeof(MainLayout)"><NotAuthorized><RedirectToLogin />
</NotAuthorized></AuthorizeRouteView></Found><NotFound><LayoutViewLayout="@typeof(MainLayout)"><p>Sorry, there's nothing at this address.</p></LayoutView></NotFound></Router></CascadingAuthenticationState>
RedirectToLogin.razor
This component takes care of redirecting unauthorized users to the login page and making sure we preserve the current url they were trying to access so that they can return to that page once the authentication is successful.
This component is added as part of the MainLayout.razor component and takes care of:
For authenticated users:
Displays the current user name.
Offers a button to log out of the application.
For anonymous users:
Offers the option to log in, users can choose to register from within the login screen.
The template only supports authenticating users in Azure Active Directory B2C. Other tasks like editing the user profile need to be handled following your Azure Active Directory B2C tenant policies.
This page defines the routes needed for handling different authentication stages in the application. It uses the RemoteauthenticatorView component that comes in the Microsoft.AspNetCore.Components.WebAssembly.Authentication and that takes care of performing the appropriate actions at each stage.
Several authentication aspects can be customized through this component as detailed in PLACEHOLDER.
Authenticating standalone applications with microsoft accounts
To create a Blazor webassembly standalone application that uses Microsoft accounts for authentication follow these steps:
Register an application following the steps in PLACEHOLDER
Capture the following data:
Application ID (Client ID) for the single page application
From the command line, run the following command:
dotnet new blazorwasm -au SingleOrg --client-id "<<single-page-client-id>>" --tenant-id "common"
After this, you should be able to log-in using a Microsoft account and request access tokens for Microsoft APIs in the same way it is done in standalone Blazor applications provided that you have configured your app correctly. For more details see PLACEHOLDER
When a single page application authenticates a user using Open ID Connect, the authentication state is kept locally within the single page application and in the Identity Provider in the form of a session cookie that is set as a result of the user introducing their credentials.
The tokens that the Identity Provider emits for the user typically are valid for short periods of time (about 1h normally) so the client application needs to regularly fetch new tokens. Otherwise, you would be logged-out after the tokens granted expired. In the majority of cases Open ID Connect clients are able to provision new tokens without requiring the user to authenticate again thanks to the authentication state or "session" that is kept within the Identity Provider.
There are however, some cases in which the client can't get a token without user interaction, for example, when for some reason the user explitly logged out from the identity provider. (For example if you visited https://login.microsoftonline.com and logged out). In those scenarios your application will not know inmediately that the user logged out, and any token that it migh have received might no longer be valid or it won't be able to provision a new one without user interaction once the current one expires.
This is not something specific to token based authentication, but it is part of the nature of single page applications. A single page application using cookies would also fail to call a server API if the authentication cookie got removed.
For this reason, when we are performing API calls to protected resources we need to be aware of the fact that provision a new access token to call the API might require the user authenticating again and that, even if we have a token that seems to be valid, the call to the server might fail because the token got revoked by the user.
When we request a token there are two possible outcomes:
The request succeeds and we have a valid token.
The request fails and we need to authenticate again to get a new token.
When a token request fails, we need to decide whether we want to save any current state before we perform a redirection. We can do several things with increasing levels of complexity:
We can store the current page state in session storage and during OnInitializeAsync check if it is there to restore before we continue when we return to the current page after a successful authentication.
We can add a query string parameter and use that as a way to signal the application that it needs to re-hidrate the previously saved state.
We can add a query string parameter with a unique identifier to store things in session storage without risking collisions with other items.
The example below shows how you can preserve the state before you redirect to the login page and how you recover the previous state afterwards using the second option.
<EditFormModel="User"@onsubmit="OnSaveAsync"><label>User<InputText @bind-Value="User.Name"/></label><label>Last name
<InputText @bind-Value="User.LastName"/></label></EditForm>
@code {
public class Profile
{
public string Name {get;set;}
public string LastName {get;set;}}
public Profile User { get;set;}=new Profile();protectedasyncoverride Task OnInitializedAsync(){varcurrentQuery=new Uri(Navigation.Uri).Query;if(currentQuery.Contains("state=resumeSavingProfile")){User=await JS.InvokeAsync<Profile>("sessionStorage.getState","resumeSavingProfile");}}publicasync Task OnSaveAsync(){varhttpClient=new HttpClient();
httpClient.BaseAddress =new Uri(Navigation.BaseUri);varresumeUri= Navigation.Uri +$"?state=resumeSavingProfile";vartokenResult=await AuthenticationService.RequestAccessToken(new AccessTokenRequestOptions
{ReturnUrl=resumeUri});if(tokenResult.TryGetToken(outvar token)){
httpClient.DefaultRequestHeaders.Add("Authorization",$"Bearer {token.Value}");await httpClient.PostJsonAsync("Save", User);}else{await JS.InvokeVoidAsync("sessionStorage.setState","resumeSavingProfile", User);
Navigation.NavigateTo(tokenResult.RedirectUrl);}}}
Saving application state before an authentication operation
During an authentication operation there are cases where you want to save the application state before the browser gets redirected to the Identity provider. This can be the case when you are using something like a state container and you want to restore the state after the authentication succeeds. In those scenarios, you can use a custom authentication state object to preserve your app specific state or a reference to it and restore that state once the authentication operation completes successfully:
@page "/authentication/{action}"
@inject JSRuntime JS
@inject StateContainer State
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
<RemoteAuthenticatorViewCore Action="@Action" AuthenticationState="AuthenticationState" OnLoginSucceded="RestoreState" OnLogoutSucceded="RestoreState"/>
@code{
public class ApplicationAuthenticationState : RemoteAuthenticationState
{
public string Id {get;set;}}
protected async override Task OnInitializedAsync(){
if (RemoteAuthenticationActions.IsAction(RemoteAuthenticationActions.LogIn, Action)){
AuthenticationState.Id = Guid.NewGuid().ToString();await JS.InvokeVoidAsync("sessionStorage.setKey", AuthenticationState.Id, State.Store());}}
public async Task RestoreState(ApplicationAuthenticationStatestate){var stored =await JS.InvokeAsync<string>("sessionStorage.getKey", state.Id);
State.FromStore(stored);}
public ApplicationAuthenticationState AuthenticationState { get;set;}=new ApplicationAuthenticationState();[Parameter]publicstring Action {get;set;}}
Requesting additional access tokens
Most applications only require an access token to talk to the protected resources that they work with, but in some scenarios a given application might need more than one token to talk to two or more resources. In these scenarios, the IAccessTokenProvider.RequestToken method provides an overload that allows you to provision a token with a given set of scopes.
By default, the Microsoft.AspNetCore.Components.WebAssembly.Authentication package uses these paths for representing different authentication states.
authentication/login is used for triggering a sign-in operation.
authentication/login-callback is used for handling the result of any sign-in operation.
authentication/login-failed is used for displaying error messages when the sign-in operation fails for some reason.
authentication/logout is used for triggering a sign-out operation.
authentication/logout-callback is used for handling the result of a sign-out operation.
authentication/logout-failed is used for displaying error messages when the sign-out operation fails for some reason.
authentication/logged-out is used to indicate that the user has successfully logout.
authentication/profile is used to trigger an operation to edit the user profile.
authentication/register is used to trigger an operation to register a new user.
All these paths are configurable in RemoteAuthenticationOptions<TProviderOptions>.AuthenticationPaths if you want to change the paths your application uses for any of the operations described above, you need to change the path in the options as well as making sure that you have a route that handles that path. For example, you can change all the paths to be prefixed by security instead as shown below:
If you plan to have completely different paths, you can do so as described above and simply render the RemoteAuthenticatorView with an explicit action parameter. For example:
This also means you can break the UI into different pages if you choose to do so.
Customizing the authentication user interface
RemoteAuthenticatorView includes a default set of UI pieces for each authentication state. You can customize each state by passing in your own RenderFragment. For example, to customize the text that gets displayed during the initial login proccess you can change the RemoteAuthenticatorView as follows:
@page "/security/{action}"
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
<RemoteAuthenticatorView Action="@Action"><LoggingIn>
You are about to be redirected to https://login.microsoftonline.com.</LoggingIn></RemoteAuthenticatorView>
@code{[Parameter] public string Action {get;set;}}
The remote authenticator view has one fragment that can be used per authentication route:
There are other possible options for authenticating single page applications, like using SameSite cookies, but as part of our design process we have settled on oAuth and specifically on Open ID Connect as the best option for authenticating Blazor webassembly applications.
Still it'd be nice to have some guidance on using SameSite cookies, which seems easier to get up and running and which integrates with how many ASP.Net sites already handle auth.
Still it'd be nice to have some guidance on using SameSite cookies, which seems easier to get up and running and which integrates with how many ASP.Net sites already handle auth.