Blazor Authentication with Auth0
In this small series we’ll setup a Blazor Web Assemly Application which communicates with a C# REST API. We’ll protect the endpoints with Auth0 as Identity Provider based on Roles (RBAC).
Articles in this series:
- Blazor Authentication with Auth0 (this one)
- Blazor Authorization with Auth0
- Blazor With Auth0, using the Management API
In this article you’ll learn:
- How to authenticate your users with Auth0
- Login with a social provider (Google in this case)
- Protect your Blazor Client
- Protect your REST API
- Connect the authentication flow between the Client and the Server.
- .NET 6
- Blazor Web Assembly (WASM)
The application we’ll be creating is a simple yet fully secured application based on two roles
Administrator. We’ll call the App Weather Station which returns data only to those who are logged-in and Administrators are the only ones who can add new forecasts to the app.
Final Result (from this post)
The use cases
- As a customer I want to see weatherforecasts.
- As an administrator I want to add new forecasts.
What is Role Based Access Control (RBAC)?
Within an organization, roles are created for various job functions. The permissions to perform certain operations are assigned to specific roles. Members or staff (or other system users) are assigned particular roles, and through those role assignments acquire the permissions needed to perform particular system functions. Since users are not assigned permissions directly, but only acquire them through their role (or roles), management of individual user rights becomes a matter of simply assigning appropriate roles to the user’s account; this simplifies common operations, such as adding a user, or changing a user’s department.
How can Auth0 help us?
Auth0 provides authentication and authorization as a service. We are here to give developers and companies the building blocks they need to secure their applications without having to become security experts. You can connect any application (written in any language or on any stack) to Auth0 and define the identity providers you want to use (how you want your users to log in). Based on your app’s technology, choose one of our SDKs (or call our API), and hook it up to your app. Now each time a user tries to authenticate, Auth0 will verify their identity and send the required information back to your app.
Start by creating a simple Blazor Web Assembly Hosted application
dotnet new blazorwasm -o WeatherStation --hosted
Let’s first create a revamped version of the
WeatherForecastController so that we can Add Forecasts to it later on. The forecasts should be a static list so we can mutate them as an
Administrator later in the article.
Replace the contents of
WeatherForecastController.cs with the following:
Registering the Blazor WASM App with Auth0
If you don’t have an Auth0 account yet, you can sign up for a free one right now.
- Click the Create Application button.
- Provide a friendly name for your application (Weather Station Client) and select Single Page Web Applications as the application type.
- Finally, click the Create button.
- Move to the Settings Tab
- In the
Allowed Callback URLspaste in the following link:
- In the
Allowed Logout URLspaste in the following link:
Finally, click the Save Changes button to apply the changes.
Configure your Blazor app for Authentication
Now, you need to configure your Blazor project by applying some changes to make it aware of Auth0. So, move to the
Client/wwwroot folder and create an
appsettings.json file with the following content:
Replace the placeholders
<YOUR_CLIENT_ID> with the respective values taken from the Auth0 dashboard.
Now, add the authentication package to the Blazor client project by running the following command in the
dotnet add package Microsoft.AspNetCore.Components.WebAssembly.Authentication
After adding the package, still in the
Client folder, edit the
Program.cs file by replacing its content with the following C# code:
You added the call to
AddOidcAuthentication() with specific options. In particular, you specified to use the parameters from the
Auth0 section of the
appsettings.json configuration file. Also, you specified the type of authentication and authorization flow you want to use; in this specific case, the Authorization Code flow is recommended.
To complete the implementation of authentication support in your application, open the
index.html file under the
Client/wwwroot folder and add the reference to the
AuthenticationService.js script as shown below:
Adjust the UI of your Blazor app
At this point, you prepared the infrastructure for your Blazor app to support authentication. Now you need to make some changes to the User Interface so Authorization can be used.
Then, open the
App.razor file in the same folder and replace its content with the following:
You used the
AuthorizeRouteView Blazor component to customize the content according to the user's authentication status. The
CascadingAuthenticationState component will propagate the current authentication state to the inner components so that they can work on it consistently.
The next step is to create a new Razor component that allows the user to log in and to see their name when authenticated. So, create a new file named
AccessControl.razor in the
Client/Shared folder with the following content:
The component uses the
AuthorizeView component to show different content according to the user's authentication status. Basically, it shows the Log in link when the user is not authenticated. It shows the name of the user and the Log out link when the user is authenticated.
Note the URL the user is redirected to when they click the Log out link (
authentication/logout). You will learn about that URL in a moment.
Now, open the
MainLayout.razor file in the
Shared folder and add the
AccessControl component just before the About link. The final code should look like the following:
When you registered your Blazor app with Auth0, you specified a few URLs in the form
https://localhost:5001/authentication/* as allowed URLs for login callback and logout. You also used the logout URL in the
To manage these URLs, you need to implement a page responsible for handling different authentication stages. For this purpose, create a new
Authentication.razor file in the
Pages folder with the following code:
As you can see, this component implements a page containing the
RemoteAuthenticatorView component. This component manages the users' authentication status and interacts with the authorization server on the Auth0 side. While the login interaction doesn't require any specific code, you need to manage the logout transaction. In fact, by design Blazor clears your authentication state on the client side but doesn't disconnect you from Auth0. To close your session on the Auth0 side, you need to explicitly call the logout endpoint, as shown in the code above.
Finally, you need to add the
Authorize attribute to the
FetchData.razor page to protect it from unauthorized accesses. Open the
FetchData.razor file in the
Pages folder and add the attribute as shown below:
At this point, you can test the authentication integration. Start the server and once the app is running, click the FetchData menu item, you should see the following screen:
Log In in the upper right corner. By clicking on it, the Auth0 Universal Login page is shown, and the authentication process takes place. Notice that you can also sign-in with Google by default which is nice. Other providers (Facebook, LinkedIn, …) already exist and can be added if you want to. If you don’t have a Google account you can use the Sign-up tab to create a new account.
After authentication, you will be able to access the
FetchData page and see the name or e-mail address.
Registering the API with Auth0
The data shown in the FetchData page is loaded from the
/Weather API implemented in the server project. This API is not protected, so any client could access it. In fact, the Blazor WASM client is able to access it without any problem. However, in a production-ready scenario, you need to protect the API to prevent unauthorized access.
- Click the Create API button.
- Provide a friendly name for your API (for example, Weather Station API) and a unique identifier (also known as audience) in the URL format (for example, https://api.weather.com).
- Leave the signing algorithm to RS256 and click the Create button.
In the server project under the
Server folder, open the
appsettings.json. Its content look like the following:
<YOUR_AUTH0_DOMAIN> placeholder with the Auth0 domain value you used for the Blazor WASM client. Also, replace the
<YOUR_API_IDENTIFIER> placeholder with the unique identifier you defined for your API in the Auth0 Dashboard: it should be
https://api.weather.com, if you kept the suggested value. This value will be used as the
Audience in the access token.
In the Client’s
appsettings.json it’s now time to also set the
Audience property to the same as the one from your API
https://api.weather.com in our case.
Still in the client, adjust
Program.cs to also supply the
Server folder, run the following command to install the library that will handle the authorization process:
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
Then, open the
Program.cs file from the server project and apply the changes shown below:
You added the reference to the
Microsoft.AspNetCore.Authentication.JwtBearer namespace and added the statements that configure the server to handle the authorization process through Auth0.
Finally, open the
WeatherController.cs file in the
Controllers folder add a
Authorize attribute, so that users need to be authentication before making the call.
Currently you won’t be able to make a call to the server, since we did not provide an access token yet. Let’s fix that next.
Calling the Protected API
To enable your Blazor WASM application to access the protected API, you need to get an access token from Auth0 and provide it along with your API call. You might think to write some code that attaches this token when you make an HTTP request to the server. However, you can centralize the access token attachment to your API calls in a straightforward way.
Start by moving to the
Client folder and installing the
Microsoft.Extensions.Http package with the following command:
dotnet add package Microsoft.Extensions.Http
This package allows you to create named HTTP clients and customize their behavior. In your case, you will create an HTTP client that automatically attaches an access token to each HTTP request and throws if there isn’t one.
Next, move to the
program.cs file in the client and replace it’s contents with the following:
AddHttpClient() method defines a named
HttpClient instance (
WeatherAPI) with the current server's address as the base address to use when requesting a resource. Also, the
BaseAddressAuthorizationMessageHandler class is added to the
HttpClient instance as the HTTP message handler. This class is provided by the
Microsoft.AspNetCore.Components.WebAssembly.Authentication namespace and is responsible for attaching the access token to any HTTP request to the application's base URI.
HttpClient instance is created by the
CreateClient() method of the
IHttpClientFactory service implementation, which solves socket exhaustion. More about socket exhaustion and why we’re using
HttpClientFactory instead of a plain’ol
HttpClient, can be read in the Microsoft Documentation here.
We ❤ open source so the source code can be found on Github
Kudo’s to Andrea Chiarelli, which set the baseline for this series.
In the next article we cover how to add roles and protect certain endpoints with Role Based Access Control since article is becoming pretty long already…