Using multiple Azure B2C user flows from ASP.NET Core

This article shows how to use multiple Azure B2C user flows from a single ASP.NET Core application. Microsoft.Identity.Web is used to implement the authentication in the client. This is not so easy to implement with multiple schemes as the user flow policy is used in most client URLs and the Microsoft.Identity.Web package overrides an lot of the default settings. I solved this by implementing an account controller to handle the Azure B2C signup user flow initial request and set the Azure B2C policy. It can be useful to split the user flows in the client application, if the user of the application needs to be guided better.

Code https://github.com/damienbod/azureb2c-fed-azuread

The Azure B2C user flows can be implemented as simple user flows. I used a signup flow and a signin, signup flow.

The AddMicrosoftIdentityWebAppAuthentication is used to implement a standard Azure B2C client. There is no need to implement a second scheme and override the default settings of the Microsoft.Identity.Web client because we use a controller to select the flow.

string[]? initialScopes = Configuration.GetValue<string>(
	"UserApiOne:ScopeForAccessToken")?.Split(' ');

services.AddMicrosoftIdentityWebAppAuthentication(Configuration, "AzureAdB2C")
	.EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
	.AddInMemoryTokenCaches();

services.Configure<MicrosoftIdentityOptions>(
	OpenIdConnectDefaults.AuthenticationScheme, options =>
{
	options.Events.OnTokenValidated = async context =>
	{
		if (ApplicationServices != null && context.Principal != null)
		{
			using var scope = ApplicationServices.CreateScope();
			context.Principal = await scope.ServiceProvider
				.GetRequiredService<MsGraphClaimsTransformation>()
				.TransformAsync(context.Principal);
		}
	};
});

The AzureAdB2C app settings configures the sign in, sign up flow. The SignUpSignInPolicyId setting is used to configure the default user flow policy.

"AzureAdB2C": {
    "Instance": "https://b2cdamienbod.b2clogin.com",
    "ClientId": "8cbb1bd3-c190-42d7-b44e-42b20499a8a1",
    "Domain": "b2cdamienbod.onmicrosoft.com",
    "SignUpSignInPolicyId": "B2C_1_signup_signin",
    "TenantId": "f611d805-cf72-446f-9a7f-68f2746e4724",
    "CallbackPath": "/signin-oidc",
    "SignedOutCallbackPath ": "/signout-callback-oidc",
    // "ClientSecret": "--use-secrets--"
},

The AccountSignUpController is used to set the policy of the flow we would like to use. The SignUpPolicy method just challenges the Azure B2C OpenID Connect server with the correct policy.

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

namespace Microsoft.Identity.Web.UI.Areas.MicrosoftIdentity.Controllers;

[AllowAnonymous]
[Route("MicrosoftIdentity/[controller]/[action]")]
public class AccountSignUpController : Controller
{
    [HttpGet("{scheme?}")]
    public IActionResult SignUpPolicy(
        [FromRoute] string scheme,
        [FromQuery] string redirectUri)
    {
        scheme ??= OpenIdConnectDefaults.AuthenticationScheme;
        string redirect;
        if (!string.IsNullOrEmpty(redirectUri) && Url.IsLocalUrl(redirectUri))
        {
            redirect = redirectUri;
        }
        else
        {
            redirect = Url.Content("~/")!;
        }

        scheme ??= OpenIdConnectDefaults.AuthenticationScheme;

        var properties = new AuthenticationProperties 
           { RedirectUri = redirect };
        properties.Items[Constants.Policy] = "B2C_1_signup";
        return Challenge(properties, scheme);
    }
}

The Razor page opens a link to the new controller and challenges the OIDC server with the correct policy.

<li class="nav-item">
	<a class="nav-link text-dark" 
href="/MicrosoftIdentity/AccountSignUp/SignUpPolicy">Sign Up</a>
</li>

With this approach, an ASP.NET Core application can be extended with multiple user flows and the UI can be improved for the end user as required.

Links

https://docs.microsoft.com/en-us/azure/active-directory-b2c/overview

https://docs.microsoft.com/en-us/azure/active-directory-b2c/identity-provider-azure-ad-single-tenant

https://github.com/AzureAD/microsoft-identity-web

https://docs.microsoft.com/en-us/azure/active-directory/develop/microsoft-identity-web

https://docs.microsoft.com/en-us/azure/active-directory-b2c/identity-provider-local

https://docs.microsoft.com/en-us/azure/active-directory/

https://docs.microsoft.com/en-us/aspnet/core/security/authentication/azure-ad-b2c

https://github.com/azure-ad-b2c/azureadb2ccommunity.io

https://github.com/azure-ad-b2c/samples

https://docs.microsoft.com/en-us/aspnet/core/blazor/security/webassembly/graph-api

https://docs.microsoft.com/en-us/graph/sdks/choose-authentication-providers?tabs=CS#client-credentials-provider

4 comments

  1. […] Using multiple Azure B2C user flows from ASP.NET Core (Damien Bowden) […]

    1. Charles Southey · · Reply

      How would you implement this in a Blazor Server application?

  2. […] Using multiple Azure B2C user flows from ASP.NET Core – Damien Bowden […]

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: