Blazor Authentication with Auth0
In this small series we’ll setup a Blazor Web Assembly Standalone 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
- Blazor with Auth0, using Swagger| OpenAPI
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.
Technology Used
- .NET 8
- Blazor Web Assembly (WASM)
- Auth0
The application we’ll be creating is a simple yet fully secured application based on two roles Customer
and 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.
Clone the following repository, but only the starter branch by using the following commands:
git clone https://github.com/vertonghenb/blazor-auth0 --branch starter
cd blazor-auth0/Server
dotnet run
Make sure you’re running on port 5001(HTTPS). HTTP will not work when working with authentication.
If you don’t have an Auth0 account yet, you can sign up for a free one.
After accessing the Auth0 Dashboard, move to the Applications section, and follow these steps:
- 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 of the newly created application.
- In the
Allowed Callback URLs
paste in the following link:https://localhost:5001/authentication/login-callback
- In the
Allowed Logout URLs
paste in the following link:https://localhost:5001
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_AUTH0_DOMAIN>
and <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 Client
folder:
dotnet add package Microsoft.AspNetCore.Components.WebAssembly.Authentication -v 8.0.8
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:
When the user is not authenticated, we want to redirect the user to the login page. This is where theRedirectToLogin
component comes in.
In the Client
add a folder called Auth
, create a new razor component called RedirectToLogin.razor
with the following contents:
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@inject NavigationManager Navigation
@code {
protected override void OnInitialized()
{
Navigation.NavigateToLogin("authentication/login");
}
}
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 LoginDisplay.razor
in the Client/Auth
folder with the following content:
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@inject NavigationManager Navigation
<AuthorizeView>
<Authorized>
Hello, @context.User.Identity?.Name!
<button class="nav-link btn btn-link" @onclick="BeginLogOut">Log out</button>
</Authorized>
<NotAuthorized>
<a href="authentication/login">Log in</a>
</NotAuthorized>
</AuthorizeView>
@code {
public void BeginLogOut()
{
Navigation.NavigateToLogout("authentication/logout");
}
}
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 that you can access the AuthContext in the AuthorizeView
component.
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 LoginDisplay
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 AccessControl
component.
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 Weather
page to protect it from unauthorized accesses. Open the Weather.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 (make sure it’s running on port 5001 (HTTPS) and once the app is running, click the FetchData menu item, you should be redirected to login.
Note the 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. You can disable sign-up in the Auth0 dashboard.
After authentication, you will be able to access the Weather
page and see the name in the top right corner.
If you got lost somewhere, you can find the commit up until now on the following link, but make sure to change the appsettings.json
.
Registering the API with Auth0
The data shown in the Weather page is loaded from the /WeatherForecast
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.
Similarly to what you did with the Blazor WASM application, you need to register the API with Auth0. So, head your browser to the Auth0 Dashboard, move to the API section, and follow these steps:
- 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 JSON Web Token Profile and signing algorithm as default and click the Create button.
In the server project under the Server
folder, open the appsettings.json
. Add the Auth0 section.
Replace the <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 Audience
property.
In the Server
folder, run the following command to install the library that will handle the authorization process:
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer -v 8.0.8
Then, open the Program.cs
file from the server project and apply the changes shown below:
You added the reference toMicrosoft.AspNetCore.Authentication.JwtBearer
amongst others and added the statements that configure the server to handle the authorization process through Auth0.
Finally, open the WeatherForecastController.cs
file in the Controllers
folder add an 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.
Tip: If you want to make sure that all the controllers are protected by authentication, you can use the
.RequireAuthorization()
function inProgram.cs.
app.MapControllers().RequireAuthorization();
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 -v 8.0.0
This package allows you to create named HTTP clients and customize their behaviour. 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:
The 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.
The actual 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…
Update 7/12/2022
There seems to be an issue with inconsistent logouts ( you might need to logout twice instead of once). There is an open issue on GitHub with a work-around for this.
Update 30/09/2024
The logout issue is resolved for new tenants starting on 14/11/2023.
Tracking can be done in this GitHub issue. If you have an older tenant, make sure to Enable Endpoint Discovery.