In this episode, we are going to create an Azure AD B2C tenant and configure it for email authentication. We are going to create a new Blazor WebAssembly hosted application via the .NET Core CLI, which comes pre-loaded with everything you'll need to do auth with MSAL.
For a similar demo but using Blazor Server and Msal.Net in .NET 6, consider watching the following episode.
Episode | YouTube URL | GitHub Repo URL |
---|---|---|
MSAL Auth in Blazor Server | https://www.youtube.com/watch?v=AlRxwIOq4jQ | https://github.com/carlfranklin/msalauthinblazor |
The steps in this README and subsequent information was gleaned from the following documentation:
The following prerequisites are needed for this demo.
Download the latest version of the .NET 7.0 SDK here.
For this demo, we are going to use the latest version of Visual Studio 2022.
For this demo, the required workload is needed.
In order to build Blazor apps, the ASP.NET and web development workload needs to be installed, so if you do not have that installed let's do that now.
If you do not have an Azure subscription, go ahead and create one for free at here.
In the following demo we will perform the following actions:
- Create an Azure AD B2C Tenant
- Configure the applications and user flow
- Create a new Blazor WebAssembly hosted application using the .NET CLI
Sign in to Azure and go to the main menu, and click on New resource.
Enter Azure Active Directory B2C in the search box, and select Azure Active Directory B2C.
Click the Create button.
Select Create a new Azure AD B2C Tenant.
Enter the following:
Click on Review + create and you will be presented to the Validation screen.
Click on Create, and you will be presented to the Progress screen.
After a few minutes, you should be presented to the Success screen.
Click on the link provided to go to the Tenant Overview screen.
☝️ Take note of the Domain name, as we are going to need later.
📘 For more detailed instructions about creating Azure AD B2C Tenant follow this tutorial Tutorial: Create an Azure Active Directory B2C tenant to create an Azure AD B2C Tenant.
Go to App registrations and click on + New registration.
Enter the following settings and click on Register.
☝️ Take note of the Application (client) ID, as we are going to need later.
Click on Expose API and then on + Add a scope.
Enter the following values and then click on Add scope:
Setting | Value |
---|---|
Scope name | api_access |
Admin consent display name | API Access |
Admin consent description | Allows the Blazor application to access the server API endpoints. |
You will see the success message.
Click on App registrations on the top menu.
Then click on the + New registration link.
Enter the following values and click on Register.
For the Redirect URI specify
https://localhost/authentication/login-callback
Then click on API permissions, + Add Permission, and select My APIs, and click on AuthInBlazorWasm.
Then, make the following selections and enter the following value and click Add permissions.
Click on Grant admin consent for AuthInBlazorWasm and click Yes.
In the main Search box, enter azure ad b2c and select Azure AD B2C.
Click on User flows and then on + New User Flow.
Make the following selections and click on Create.
Enter the following values:
Before clicking Create, scroll down until you see Show more..., click the link and the select Display name under Collect attribute and click OK.
Now you can click Ok to create the User flow.
📘 For more detailed instructions about creating User Flows follow this tutorial Tutorial: Create user flows and custom policies in Azure Active Directory B2C
Now we are ready to create our Blazor Wasm application, using all the settings we need and took note of in the steps above.
We are going to use the .NET Core CLI to create our Blazor Wasm application by entering the following command on your Windows Terminal:
dotnet new blazorwasm -au IndividualB2C --aad-b2c-instance "{AAD B2C INSTANCE}" --api-client-id "{SERVER API APP CLIENT ID}" --app-id-uri "{SERVER API APP ID URI}" --client-id "{CLIENT APP CLIENT ID}" --default-scope "{DEFAULT SCOPE}" --domain "{TENANT DOMAIN}" -ho -o {APP NAME} -ssp "{SIGN UP OR SIGN IN POLICY}"
☝️ We need to replace all the placeholders with the values we took note of in previous steps.
To make it easier, I compiled a summary of all the values we need to replace, and where to get them from, as well as examples of their values based on my current settings.
☝️ Make sure you add a trailing forward slash to the end of your AAD B2C INSTANCE URL
Setting | Value | Example |
---|---|---|
{AAD B2C INSTANCE} | https://{TENANT_NAME}.b2clogin.com/ | https://authinblazorwasm.b2clogin.com/ |
{SERVER API APP CLIENT ID} | Application (client) ID for the server app | dd2a1a86-9226-4b0a-bd5b-192381901b42 |
{SERVER API APP ID URI} | Application ID URI | dd2a1a86-9226-4b0a-bd5b-192381901b42 |
Setting | Value | Example |
---|---|---|
{CLIENT APP CLIENT ID} | Application (client) ID for the client app | 9fa7bf36-ff95-4370-8ef0-0d3c23b414f5 |
{SCOPE} | Scope created in the client app | api_access |
Setting | Value | Example |
---|---|---|
{TENANT DOMAIN} | Primary/Publisher/Tenant domain | authinblazorwasm.onmicrosoft.com |
{SIGN UP OR SIGN IN POLICY} | Sign-up/sign-in user flow | B2C_1_susi |
Setting | Value | Example |
---|---|---|
{APP NAME} | Application name | AuthInBlazorWasm |
In my case, the command looked like this:
dotnet new blazorwasm -au IndividualB2C --aad-b2c-instance "https://authinblazorwasm.b2clogin.com/" --api-client-id "dd2a1a86-9226-4b0a-bd5b-192381901b42" --app-id-uri "dd2a1a86-9226-4b0a-bd5b-192381901b42" --client-id "9fa7bf36-ff95-4370-8ef0-0d3c23b414f5" --default-scope "api_access" --domain "authinblazorwasm.onmicrosoft.com" -ho -o AuthInBlazorWasm -ssp "B2C_1_susi"
The output location specified with the -o|--output
option creates a project folder if it doesn't exist and becomes part of the app's name.
⚠️ Avoid using dashes (-
) in the app name{APP NAME}
that break the formation of the OIDC app identifier. Logic in the Blazor WebAssembly project template uses the project name for an OIDC app identifier in the solution's configuration. Pascal case (BlazorSample
) or underscores (Blazor_Sample
) are acceptable alternatives. For more information, see Dashes in a hosted Blazor WebAssembly project name break OIDC security (dotnet/aspnetcore #35337).
Now, the application has been created, and the .NET Core CLI helped us adding all the code, and proper appsettings.json values, for both, our AuthInBlazorWasm.Client and AuthInBlazorWasm.Server application, making sure that we do not expose sensitive information on the client side.
Run the AuthInBlazorWasm.Server application, which will be set up as the Start-up application by default, and you should see the following.
Click on the Login link on the top right, and you should be presented to the Sign in/Sign up now screen. Since we do not have an account yet to access the secured parts of the application (Fetch Data) go ahead and click on Sign up now
📘 Fetch Data is secured via the [Authorize] attribute in the WeatherForecastController, so if you try to go to Fetch Data without Logging in, you will be prompted to login first. The Counter page is not secured, so you will not get prompted to log in if you go to the Counter page.
Enter your email and click on Send verification code.
Go to your email, and get the verification code, and enter it in the appropriate box, and click on the Verify code button.
Enter a new password twice and your Display name, and click on Create.
Once you create your account, you will be able to go to the Fetch Data page successfully, and see the weather data.
To recap, at this point, we allowed a new user (this guy known as Carl Franklin) to create an account in our Azure AD B2C tenant, using our Blazor Wasm application, to access the secured and so important random Weather Data.
Go back to your Azure AD B2C tenant, and click on Users.
Here you will see, and be able to administer all aspects of the user.
Click on the user and you will see all the options available, such as resetting password, revoke sessions, etc.
☝️ The rest of this content is taken right from the documentation at https://learn.microsoft.com/en-us/aspnet/core/blazor/security/webassembly/hosted-with-azure-active-directory-b2c?view=aspnetcore-7.0
This section pertains to the solution's Server app.
The support for authenticating and authorizing calls to ASP.NET Core web APIs with the Microsoft Identity Platform is provided by the Microsoft.Identity.Web
package.
☝️ For guidance on adding packages to .NET apps, see the articles under Install and manage packages at Package consumption workflow (NuGet documentation). Confirm correct package versions at NuGet.org.
The Server app of a hosted Blazor solution created from the Blazor WebAssembly template includes the Microsoft.Identity.Web.UI
package by default. The package adds UI for user authentication in web apps and isn't used by the Blazor framework. If the Server app won't be used to authenticate users directly, it's safe to remove the package reference from the Server app's project file.
The AddAuthentication
method sets up authentication services within the app and configures the JWT Bearer handler as the default authentication method. The AddMicrosoftIdentityWebApi method configures services to protect the web API with Microsoft Identity Platform v2.0. This method expects an AzureAdB2C
section in the app's configuration with the necessary settings to initialize authentication options.
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(Configuration.GetSection("AzureAdB2C"));
☝️ When a single authentication scheme is registered, the authentication scheme is automatically used as the app's default scheme, and it isn't necessary to state the scheme to AddAuthentication or via AuthenticationOptions. For more information, see Overview of ASP.NET Core Authentication and the ASP.NET Core announcement (aspnet/Announcements #490).
UseAuthorization ensures that any request attempting to access a protected resource without proper credentials fails.
app.UseAuthorization();
By default, the User.Identity.Name
isn't populated.
To configure the app to receive the value from the name
claim type:
-
Add a namespace for Microsoft.AspNetCore.Authentication.JwtBearer to
Program.cs
:using Microsoft.AspNetCore.Authentication.JwtBearer;
-
Configure the TokenValidationParameters.NameClaimType of the JwtBearerOptions in
Program.cs
:builder.Services.Configure<JwtBearerOptions>( JwtBearerDefaults.AuthenticationScheme, options => { options.TokenValidationParameters.NameClaimType = "name"; });
The appsettings.json
file contains the options to configure the JWT bearer handler used to validate access tokens.
"AzureAdB2C": {
"Instance": "https://{TENANT NAME}.b2clogin.com/",
"ClientId": "{SERVER API APP CLIENT ID}",
"Domain": "{TENANT NAME}.onmicrosoft.com",
"Scopes": "api_access",
"SignUpSignInPolicyId": "B2C_1_susi"
},
Example:
"AzureAdB2C": {
"Instance": "https://authinblazorwasm.b2clogin.com/",
"ClientId": "41451fa7-82d9-4673-8fa5-69eff5a761fd",
"Domain": "authinblazorwasm.onmicrosoft.com",
"Scopes": "api_access",
"SignUpSignInPolicyId": "B2C_1_susi",
}
The WeatherForecast controller (Controllers/WeatherForecastController.cs
) exposes a protected API with the [Authorize\]
attribute applied to the controller. It's important to understand that:
- The
[Authorize\]
attribute in this API controller is the only thing that protect this API from unauthorized access. - The
[Authorize\]
attribute used in the Blazor WebAssembly app only serves as a hint to the app that the user should be authorized for the app to work correctly.
[Authorize]
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
...
}
}
This section pertains to the solution's Client app.
When an app is created to use an Individual B2C Account (IndividualB2C
), the app automatically receives a package reference for the Microsoft Authentication Library (Microsoft.Authentication.WebAssembly.Msal
). The package provides a set of primitives that help the app authenticate users and obtain tokens to call protected APIs.
If adding authentication to an app, manually add the Microsoft.Authentication.WebAssembly.Msal
package to the app.
☝️ For guidance on adding packages to .NET apps, see the articles under Install and manage packages at Package consumption workflow (NuGet documentation). Confirm correct package versions at NuGet.org.
The Microsoft.Authentication.WebAssembly.Msal
package transitively adds the Microsoft.AspNetCore.Components.WebAssembly.Authentication
package to the app.
Support for HttpClient instances is added that include access tokens when making requests to the server project.
Program.cs:
builder.Services.AddHttpClient("{APP ASSEMBLY}.ServerAPI", client =>
client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();
builder.Services.AddScoped(sp => sp.GetRequiredService<IHttpClientFactory>()
.CreateClient("{APP ASSEMBLY}.ServerAPI"));
The placeholder {APP ASSEMBLY}
is the app's assembly name (for example, BlazorSample.Client
).
Support for authenticating users is registered in the service container with the AddMsalAuthentication extension method provided by the Microsoft.Authentication.WebAssembly.Msal
package. This method sets up the services required for the app to interact with the Identity Provider (IP).
Program.cs:
builder.Services.AddMsalAuthentication(options =>
{
builder.Configuration.Bind("AzureAdB2C", options.ProviderOptions.Authentication);
options.ProviderOptions.DefaultAccessTokenScopes.Add("{SCOPE URI}");
});
The AddMsalAuthentication method accepts a callback to configure the parameters required to authenticate an app. The values required for configuring the app can be obtained from the Azure Portal AAD configuration when you register the app.
Configuration is supplied by the wwwroot/appsettings.json
file:
{
"AzureAdB2C": {
"Authority": "https://{TENNANT NAME}.b2clogin.com/{TENANT NAME}.onmicrosoft.com/B2C_1_susi",
"ClientId": "{CLIENT APP CLIENT ID}",
"ValidateAuthority": false
}
}
In the preceding configuration, the {AAD B2C INSTANCE}
includes a trailing slash.
Example:
{
"AzureAdB2C": {
"Authority": "https://contoso.b2clogin.com/authinblazorwasm.onmicrosoft.com/B2C_1_susi",
"ClientId": "4369008b-21fa-427c-abaa-9b53bf58e538",
"ValidateAuthority": false
}
}
The default access token scopes represent the list of access token scopes that are:
- Included by default in the sign in request.
- Used to provision an access token immediately after authentication.
All scopes must belong to the same app per Azure Active Directory rules. Additional scopes can be added for additional API apps as needed:
builder.Services.AddMsalAuthentication(options =>
{
...
options.ProviderOptions.DefaultAccessTokenScopes.Add("{SCOPE URI}");
});
☝️ The Blazor WebAssembly template automatically adds a scheme of
api://
to the App ID URI argument passed in thedotnet new
command. When generating an app from the Blazor project template, confirm that the value of the default access token scope uses either the correct custom App ID URI value that you provided in the Azure portal or a value with one of the following formats:
-
When the publisher domain of the directory is trusted, the default access token scope is typically a value similar to the following example, where
API.Access
is the default scope name:options.ProviderOptions.DefaultAccessTokenScopes.Add( "api://41451fa7-82d9-4673-8fa5-69eff5a761fd/API.Access");
Inspect the value for a double scheme (
api://api://...
). If a double scheme is present, remove the firstapi://
scheme from the value. -
When the publisher domain of the directory is untrusted, the default access token scope is typically a value similar to the following example, where
API.Access
is the default scope name:options.ProviderOptions.DefaultAccessTokenScopes.Add( "https://contoso.onmicrosoft.com/41451fa7-82d9-4673-8fa5-69eff5a761fd/API.Access");
Inspect the value for an extra
api://
scheme (api://https://contoso.onmicrosoft.com/...
). If an extraapi://
scheme is present, remove theapi://
scheme from the value.
The Blazor WebAssembly template might be changed in a future release of ASP.NET Core to address these scenarios. For more information, see Double scheme for App ID URI with Blazor WASM template (hosted, single org) (dotnet/aspnetcore #27417).
Specify additional scopes with AdditionalScopesToConsent
:
options.ProviderOptions.AdditionalScopesToConsent.Add("{ADDITIONAL SCOPE URI}");
For more information, see the following sections of the Additional scenarios article:
The framework defaults to pop-up login mode and falls back to redirect login mode if a pop-up can't be opened. Configure MSAL to use redirect login mode by setting the LoginMode
property of MsalProviderOptions to redirect
:
builder.Services.AddMsalAuthentication(options =>
{
...
options.ProviderOptions.LoginMode = "redirect";
});
The default setting is popup
, and the string value isn't case sensitive.
The Microsoft.AspNetCore.Components.Authorization namespace is made available throughout the app via the _Imports.razor file:
@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.AspNetCore.Components.WebAssembly.Http
@using Microsoft.JSInterop
@using {APPLICATION ASSEMBLY}.Client
@using {APPLICATION ASSEMBLY}.Client.Shared
The Index page (wwwroot/index.html
) page includes a script that defines the AuthenticationService
in JavaScript. AuthenticationService
handles the low-level details of the OIDC protocol. The app internally calls methods defined in the script to perform the authentication operations.
<script src="_content/Microsoft.Authentication.WebAssembly.Msal/AuthenticationService.js"></script>
The App
component (App.razor
) is similar to the App
component found in Blazor Server apps:
- The CascadingAuthenticationState component manages exposing the AuthenticationState to the rest of the app.
- The AuthorizeRouteView component makes sure that the current user is authorized to access a given page or otherwise renders the
RedirectToLogin
component. - The
RedirectToLogin
component manages redirecting unauthorized users to the login page.
Due to changes in the framework across releases of ASP.NET Core, Razor markup for the App
component (App.razor
) isn't shown in this section. To inspect the markup of the component for a given release, use either of the following approaches:
-
Create an app provisioned for authentication from the default Blazor WebAssembly project template for the version of ASP.NET Core that you intend to use. Inspect the
App
component (App.razor) in the generated app. -
Inspect the
App
component (App.razor) in reference source.☝️ Documentation links to .NET reference source usually load the repository's default branch, which represents the current development for the next release of .NET. To select a tag for a specific release, use the Switch branches or tags dropdown list. For more information, see How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205).
The RedirectToLogin
component (Shared/RedirectToLogin.razor):
- Manages redirecting unauthorized users to the login page.
- Preserves the current URL that the user is attempting to access so that they can be returned to that page if authentication is successful.
@inject NavigationManager Navigation
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@using Microsoft.Extensions.Options
@inject IOptionsSnapshot<RemoteAuthenticationOptions<ApiAuthorizationProviderOptions>> Options
@code {
protected override void OnInitialized()
{
Navigation.NavigateToLogin(Options.Get(
Microsoft.Extensions.Options.Options.DefaultName)
.AuthenticationPaths.LogInPath);
}
}
The LoginDisplay
component (Shared/LoginDisplay.razor) is rendered in the MainLayout
component (Shared/MainLayout.razor) and manages the following behaviors:
- For authenticated users:
- Displays the current username.
- Offers a button to log out of the app.
- For anonymous users, offers the option to log in.
Due to changes in the framework across releases of ASP.NET Core, Razor markup for the LoginDisplay
component isn't shown in this section. To inspect the markup of the component for a given release, use either of the following approaches:
-
Create an app provisioned for authentication from the default Blazor WebAssembly project template for the version of ASP.NET Core that you intend to use. Inspect the
LoginDisplay
component in the generated app. -
Inspect the
LoginDisplay
component in reference source.☝️ Documentation links to .NET reference source usually load the repository's default branch, which represents the current development for the next release of .NET. To select a tag for a specific release, use the Switch branches or tags dropdown list. For more information, see How to select a version tag of ASP.NET Core source code (dotnet/AspNetCore.Docs #26205).
The page produced by the Authentication
component (Pages/Authentication.razor) defines the routes required for handling different authentication stages.
The RemoteAuthenticatorView component:
- Is provided by the
Microsoft.AspNetCore.Components.WebAssembly.Authentication
package. - Manages performing the appropriate actions at each stage of authentication.
@page "/authentication/{action}"
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
<RemoteAuthenticatorView Action="@Action" />
@code {
[Parameter]
public string Action { get; set; }
}
The FetchData
component shows how to:
- Provision an access token.
- Use the access token to call a protected resource API in the Server app.
The @attribute [Authorize\]
directive indicates to the Blazor WebAssembly authorization system that the user must be authorized in order to visit this component. The presence of the attribute in the Client app doesn't prevent the API on the server from being called without proper credentials. The Server app also must use [Authorize]
on the appropriate endpoints to correctly protect them.
IAccessTokenProvider.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 succeeds. Otherwise, the token request fails with an AccessTokenNotAvailableException, which is caught in a try-catch
statement.
In order to obtain the actual token to include in the request, the app must check that the request succeeded by calling tokenResult.TryGetToken(out var token)
.
If the request was successful, the token variable is populated with the access token. The AccessToken.Value property of the token exposes the literal string to include in the Authorization
request header.
If the request failed because the token couldn't be provisioned without user interaction:
- ASP.NET Core 7.0 or later: The app navigates to
AccessTokenResult.InteractiveRequestUrl
using the givenAccessTokenResult.InteractionOptions
to allow refreshing the access token. - ASP.NET Core 6.0 or earlier: The token result contains a redirect URL. Navigating to this URL takes the user to the login page and back to the current page after a successful authentication.
@page "/fetchdata"
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.WebAssembly.Authentication
@using {APP NAMESPACE}.Shared
@attribute [Authorize]
@inject HttpClient Http
...
@code {
private WeatherForecast[] forecasts;
protected override async Task OnInitializedAsync()
{
try
{
forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");
}
catch (AccessTokenNotAvailableException exception)
{
exception.Redirect();
}
}
}
-
Misconfiguration of the app or Identity Provider (IP)
The most common errors are caused by incorrect configuration. The following are a few examples:
- Depending on the requirements of the scenario, a missing or incorrect Authority, Instance, Tenant ID, Tenant domain, Client ID, or Redirect URI prevents an app from authenticating clients.
- An incorrect access token scope prevents clients from accessing server web API endpoints.
- Incorrect or missing server API permissions prevent clients from accessing server web API endpoints.
- Running the app at a different port than is configured in the Redirect URI of the Identity Provider's app registration.
Configuration sections of this article's guidance show examples of the correct configuration. Carefully check each section of the article looking for app and IP misconfiguration.
If the configuration appears correct:
- Analyze application logs.
- Examine the network traffic between the client app and the IP or server app with the browser's developer tools. Often, an exact error message or a message with a clue to what's causing the problem is returned to the client by the IP or server app after making a request. Developer tools guidance is found in the following articles:
- Google Chrome (Google documentation)
- Microsoft Edge
- Mozilla Firefox (Mozilla documentation)
- Decode the contents of a JSON Web Token (JWT) used for authenticating a client or accessing a server web API, depending on where the problem is occurring. For more information, see Inspect the content of a JSON Web Token (JWT).
The documentation team responds to document feedback and bugs in articles (open an issue from the This page feedback section) but is unable to provide product support. Several public support forums are available to assist with troubleshooting an app. We recommend the following:
The preceding forums are not owned or controlled by Microsoft.
For non-security, non-sensitive, and non-confidential reproducible framework bug reports, open an issue with the ASP.NET Core product unit. Don't open an issue with the product unit until you've thoroughly investigated the cause of a problem and can't resolve it on your own and with the help of the community on a public support forum. The product unit isn't able to troubleshoot individual apps that are broken due to simple misconfiguration or use cases involving third-party services. If a report is sensitive or confidential in nature or describes a potential security flaw in the product that attackers may exploit, see Reporting security issues and bugs (dotnet/aspnetcore GitHub repository).
-
Unauthorized client for AAD
💁 Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2] Authorization failed. These requirements were not met: DenyAnonymousAuthorizationRequirement: Requires an authenticated user.
Login callback error from AAD:
- Error:
unauthorized_client
- Description:
AADB2C90058: The provided application is not configured to allow public clients.
To resolve the error:
- In the Azure portal, access the app's manifest.
- Set the
allowPublicClient
attribute tonull
ortrue
.
- Error:
In this episode, we created an Azure AD B2C tenant and configured it for email authentication. Then we created a new Blazor WebAssembly hosted application via the .Net Core CLI. The template provisions an application with everything you need to do auth with MSAL.
For more information about the topics involved in this demo, check the links in the resources section below.
The complete code for this demo can be found in the link below.
Resource Title | Url |
---|---|
BlazorTrain with Carl Franklin | https://blazortrain.com |
The .NET Show with Carl Franklin | https://www.youtube.com/playlist?list=PL8h4jt35t1wgW_PqzZ9USrHvvnk8JMQy_ |
Download .NET 7.0 | https://dotnet.microsoft.com/en-us/download/dotnet/7.0 |
Secure a hosted ASP.NET Core Blazor WebAssembly app with Azure Active Directory B2C | https://learn.microsoft.com/en-us/aspnet/core/blazor/security/webassembly/hosted-with-azure-active-directory-b2c?view=aspnetcore-7.0 |
Tutorial: Create an Azure Active Directory B2C tenant | https://learn.microsoft.com/en-us/azure/active-directory-b2c/tutorial-create-tenant |
Tutorial: Create user flows and custom policies in Azure Active Directory B2C | https://learn.microsoft.com/en-us/azure/active-directory-b2c/tutorial-create-user-flows?pivots=b2c-user-flow |