Developer forum

Forum » Dynamicweb 10 » Error During User Login via Azure AD B2C Authentication

Error During User Login via Azure AD B2C Authentication

Dynamicweb Employee
Nang Shwe Yea Oo
Reply

Dear Team,

We are currently configuring user authentication for our e-commerce site using Microsoft Identity Services.

Microsoft has announced that **Azure AD B2C is end of sale as of May 1, 2025**, and the recommended replacement is **Microsoft Entra External ID (CIAM)**.

Azure AD B2C uses the following OpenID endpoint URL structure:
https://{tenant-name}.b2clogin.com/

Whereas Microsoft Entra External ID uses a different endpoint structure:
https://{custom-domain}.ciamlogin.com/

As we cannot create Azure AD B2C, we used Entra External ID instead. Based on this, we configured our backend authentication settings to use the Entra External ID (CIAM) endpoints. However, when a user attempts to sign in from the e-commerce site, the login fails and returns an OpenID configuration error. Kindly see the attached images for reference.

DW backend provider setting:

Error encountered when the user attempts to log in:

Our question is: **Does Dynamicweb version 10 support Microsoft Entra External ID (CIAM) OpenID endpoint URLs, or is it limited to the Azure AD B2C (b2clogin.com) format?**. If Azure AD B2C tenants can no longer be created, is there a supported or recommended alternative approach for integrating authentication with Dynamicweb 10? We would appreciate your guidance or confirmation on this.

Thank you in advance.

With regards,

Shwe Yea


Replies

 
Nicolai Pedersen Dynamicweb Employee
Nicolai Pedersen
Reply

Microsoft Entra External ID (CIAM) uses a different authority model (ciamlogin.com) than both classic Entra ID (login.microsoftonline.com) and Azure AD B2C (policy-based URLs).

Because of that, it doesn’t fit cleanly into the existing built-in providers in Dynamicweb 10:

  • Microsoft Entra is hard-wired to login.microsoftonline.com

  • Azure AD B2C assumes the B2C policy URL structure

So CIAM will not work out-of-the-box with either of those providers.

We will add a dedicated Microsoft Entra External ID (CIAM) login provider to Dynamicweb 10 in the end-of-February R0 release, to keep the setup explicit and avoid hidden configuration or breaking changes.

Until that release is available, you can solve this by adding a custom external login provider. I’ve attached a ready-to-use provider implementation that:

  • Uses the ciamlogin.com authority

  • Follows Dynamicweb’s standard /signin-{scheme} callback convention

  • Integrates cleanly with the existing external authentication pipeline

You can compile it as a custom AddIn and load it into your solution until the built-in provider ships.

This keeps you unblocked now, and gives you a clean upgrade path once the official provider is included in DW10.

 

The code:
 

using System.Security.Claims;
using Dynamicweb.Extensibility.AddIns;
using Dynamicweb.Extensibility.Editors;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;

namespace Dynamicweb.ExternalAuthentication;

/// <summary>
/// Microsoft Entra External ID (CIAM) login provider
/// Authority/metadata is hosted on {tenantSubdomain}.ciamlogin.com
/// </summary>
[AddInName("Entra External ID (CIAM)"), AddInDescription("Microsoft Entra External ID (CIAM) login provider.")]
public sealed class EntraExternalIdLoginProvider : BaseOAuthLoginProvider, IUpdateOpenIdConnectOptions
{
    private const string CredentialsGroupName = "External credentials";
    private const string TenantGroupName = "Tenant";

    /// <inheritdoc />
    [AddInParameterGroup(CredentialsGroupName)]
    [AddInLabel("Authentication scheme"), AddInParameter("ProviderScheme")]
    [AddInParameterEditor(typeof(TextParameterEditor),
        "required;info=A unique scheme id for this provider instance. Redirect URI must be https://yoursite.com/signin-{scheme} (e.g. https://yoursite.com/signin-ciam).")]
    public override string ProviderScheme { get; set; } = "ciam";

    [AddInParameterGroup(CredentialsGroupName)]
    [AddInLabel("Application (client) ID"), AddInParameter("ClientId")]
    [AddInParameterEditor(typeof(TextParameterEditor),
        "required;info=The Application (client) ID from the App Registration in the External ID tenant.")]
    public string? ClientId { get; set; }

    [AddInParameterGroup(CredentialsGroupName)]
    [AddInLabel("Client secret (Value)"), AddInParameter("ClientSecret")]
    [AddInParameterEditor(typeof(TextParameterEditor),
        "required;info=A valid client secret value from Certificates & secrets.")]
    public string? ClientSecret { get; set; }

    /// <inheritdoc />
    [AddInParameterGroup("Frontend")]
    [AddInLabel("Authentication error page"), AddInParameter("ErrorPage")]
    [AddInParameterEditor(typeof(PageSelectEditor),
        "info=The page the user will be redirected to if authentication fails.")]
    public override string? ErrorPage { get; set; }

    [AddInParameterGroup(TenantGroupName)]
    [AddInLabel("Tenant subdomain"), AddInParameter("TenantSubdomain")]
    [AddInParameterEditor(typeof(TextParameterEditor),
        "required;info=The CIAM tenant subdomain used in the login host. Example: contoso (for https://contoso.ciamlogin.com/...).")]
    public string? TenantSubdomain { get; set; }

    [AddInParameterGroup(TenantGroupName)]
    [AddInLabel("Tenant ID"), AddInParameter("TenantId")]
    [AddInParameterEditor(typeof(TextParameterEditor),
        "required;info=The Directory (tenant) ID (GUID) of the External ID tenant. Used in the authority path: https://{tenantSubdomain}.ciamlogin.com/{tenantId}/v2.0")]
    public string? TenantId { get; set; }

    void IUpdateOpenIdConnectOptions.SetAuthenticationOptions(OpenIdConnectOptions options)
    {
        ArgumentNullException.ThrowIfNull(options);

        if (string.IsNullOrWhiteSpace(TenantSubdomain))
            throw new InvalidOperationException("TenantSubdomain is required.");

        if (string.IsNullOrWhiteSpace(TenantId))
            throw new InvalidOperationException("TenantId is required.");

        if (string.IsNullOrWhiteSpace(ClientId))
            throw new InvalidOperationException("ClientId is required.");

        if (string.IsNullOrWhiteSpace(ClientSecret))
            throw new InvalidOperationException("ClientSecret is required.");

        // CIAM well-known metadata lives under:
        // https://{tenantSubdomain}.ciamlogin.com/{tenantId}/v2.0/.well-known/openid-configuration
        options.Authority = $"https://{TenantSubdomain}.ciamlogin.com/{TenantId}/v2.0";

        options.SignInScheme = SignInManager.ExternalAuthenticationScheme;
        options.ClientId = ClientId;
        options.ClientSecret = ClientSecret;
        options.CallbackPath = CallbackPath;

        // Keep aligned with existing Entra provider behavior in DW:
        options.ResponseType = OpenIdConnectResponseType.IdToken;

        // Typical minimal scopes; add more if you need them.
        options.Scope.Add("email");

        options.Events.OnRemoteFailure = OnRemoteFailure;

        options.Events.OnTokenValidated = ctx =>
        {
            if (ctx.Principal?.Identity is ClaimsIdentity identity)
            {
                // Ensure a standard Name claim exists
                var name = identity.Claims.SingleOrDefault(x => x.Type == "name");
                if (name is not null && !identity.HasClaim(c => c.Type == ClaimTypes.Name))
                    identity.AddClaim(new Claim(ClaimTypes.Name, name.Value));
            }

            return Task.CompletedTask;
        };
    }
}

 

You must be logged in to post in the forum