umbraco / Umbraco-CMS

Umbraco is a free and open source .NET content management system helping you deliver delightful digital experiences.
https://umbraco.com
MIT License
4.37k stars 2.64k forks source link

Umbraco 9 and above does not support Microsoft Azure B2C integration with multiple type of authentication for the same member #14124

Open Phil-Ouimet opened 1 year ago

Phil-Ouimet commented 1 year ago

Which Umbraco version are you using? (Please write the exact version, example: 10.1.0)

10.4.0

Bug summary

We are working with Umbraco 10.4.0 and have run into an issue with this particular change introduce in v9 https://github.com/umbraco/Umbraco-CMS/issues/10656 In our project we have configured Microsoft Azure AD B2C as the identity provider for members. This type of integration completely delegates available authentication options to the external provider managed signup and sign-in page. A single provider registered in Umbraco can therefore associate multiple authentication types to a single Umbraco member. This is not supported by the index currently in place on the umbracoExternalLogin table.

Specifics

We have tried to remove the index as a first test and this solve this specific problem but seems to cause a side effect when attempting to delete a member. The Umbraco code assumes there will be only on one umbracoExternalLogin entry and fails.

Steps to reproduce

  1. Configure the Azure AD B2C using the Umbraco member authentication builder
  2. Create an new account on the Azure AD B2C page with a user name and password
  3. Login into umbraco as a member with this account
  4. Logout and on login again on the Azure AD B2C sign-in page this time using another type of authentication(Google, Microsoft,etc) associated with same email.
  5. View the following error on the ExternalLoginController callback
    Failed to external link user. The following errors happened: ["Cannot insert duplicate key row in object 'dbo.umbracoExternalLogin' with unique index 'IX_umbracoExternalLogin_LoginProvider'. The duplicate key value is (UmbracoMembers.AzureAdB2C, edd1fbe9-3fdf-49a0-9e78-14dffcf07f41).
    The statement has been terminated."]

    This because the loginProvider and userOrMemberKey columns in this case would be identical but the providerKey differs based on the type of authentication the member used in Azure B2C.

Expected result / actual result

Adjust/remove the index constraints to support this scenario in future versions and ensure it does not impact other areas like the member delete functionality.


_This item has been added to our backlog AB#29465_

github-actions[bot] commented 1 year ago

Hi there @Phil-Ouimet!

Firstly, a big thank you for raising this issue. Every piece of feedback we receive helps us to make Umbraco better.

We really appreciate your patience while we wait for our team to have a look at this but we wanted to let you know that we see this and share with you the plan for what comes next.

We wish we could work with everyone directly and assess your issue immediately but we're in the fortunate position of having lots of contributions to work with and only a few humans who are able to do it. We are making progress though and in the meantime, we will keep you in the loop and let you know when we have any questions.

Thanks, from your friendly Umbraco GitHub bot :robot: :slightly_smiling_face:

Domitnator commented 1 year ago

@Phil-Ouimet

I had a slightly different scenario but maybe it can help you too. In the openid configuration, map the NameClaimType to the nameidentifier (instead of the email)

options.TokenValidationParameters.NameClaimType = ClaimTypes.NameIdentifier;

The ID (in my case a GUID) of the user will now be used instead of the emailaddress which will make the external-login unique.

Phil-Ouimet commented 1 year ago

Thanks @Domitnator however in our client's case it is not an option since there is some business logic that needs to be matched on the members principal name directly (email) to a single member account in Umbraco,

In this case for example my account philouimet@company.com can use a username/password identity, a microsoft.com account, a google account, etc.. or any openId provider registered in Azure B2C.
I can log into Azure B2C with any of these identities and each has it's unique internal object Id in the directory,

The following sample assumes my userOrMemberKey created in Umbraco when I first logged in was {ACAF4C98-6417-44CA-99AD-E2F72857D149} The loginProvider is always UmbracoMembers.AzureAdB2C. The providerKey contains the internal object Id from Azure B2C for each object. The umbracoExternalLogin table contains for a B2C user the following

userOrMemberKey loginProvider providerKey
ACAF4C98-6417-44CA-99AD-E2F72857D149 UmbracoMembers.AzureAdB2C 878F5458-CC89-4862-9ADF-2CF362708307
ACAF4C98-6417-44CA-99AD-E2F72857D149 UmbracoMembers.AzureAdB2C 3A65ABDF-6662-4AF1-80C9-B8B94EC78BA5
ACAF4C98-6417-44CA-99AD-E2F72857D149 UmbracoMembers.AzureAdB2C 3EB9E4D6-7D2F-4827-8591-B42C548E9852

From our initial tests the table structure will support most functionalities except for the index on the table and members delete which assume a one to one match for loginProvider and providerKey.

nikolajlauridsen commented 1 year ago

Hey @Phil-Ouimet just had a look at reproducing this and it does indeed seem to be the case, and it seems perfectly reasonable that we should support multiple types of authentication for members using B2C 👍

I'll have a chat with the teams just so we're on the same page 😄

As a side note, just for my own curiosity, since I'll be honest I'm not an expert in B2C, but when I reproduced this I had to create two users in the B2C with the same username (email), to allow for both methods of login, I'm just wondering if that's the same case for you? I guess it makes sense though since it looks like in the user page that there can only be one source pr. user, so I think it's expected, and you're just expected to link the two different users to the same user in your consuming application?

Anyways that's a bit of an aside 😄 here's a picture for reference:

image

nikolajlauridsen commented 1 year ago

We've had a talk about this internally and we've decided we need to have a look at this and investigate a bit more, to best determine how we can solve this.

We want to make sure that the different provider keys is actually how everything is expected to work, and then move on from there 👍

Phil-Ouimet commented 1 year ago

Hi @nikolajlauridsen , we're about to start a project for a new client on Umbraco 12. There are still discussions about using Azure B2C as the identity provider if the blocking issue associated with this item was resolved. Any chance that a fix may happen in the next months?

Phil-Ouimet commented 6 months ago

Hi, Any update @lassefredslund on this issue? I see that it was tagged as sprint candidate :)

lassefredslund commented 6 months ago

Hi @Phil-Ouimet Yeah, we've had it in the sprint and also investigated the issue. Unfortunately, it's not an easy fix for us. Azure B2C is not following the same standard as other providers, and it looks like an issue on their end. Anyway, it's not realistic that we'll get around implementing a fix soon. I hope you'll be able to work around the issue.

Phil-Ouimet commented 6 months ago

@lassefredslund No unfortunately there are no solution available to our client to work around this without a bit of hack. From the initial look we saw that it work if we removed the index Umbraco puts on the table that enforce uniqueness but that breaks any delete of members and may have other side effects. Since our client wants to be able to evolve on the platform for the upcoming years that is not an option.

Has Umbraco been used in similar scenarios with other commercial identity provider aggregators? Can you recommend one that would support this type of authentication flow? Example: Auth0, Amazon Cognito, SAP Gigya, etc?