Email verification box disappears when passing email from Facebook as input claim to self-asserted profile - azure-ad-b2c

We want to force users to provide and verify their email address even when they reject passing their email claim from Facebook.
We created an orchestration step which calls the following technical profile after coming back from Facebook:
<TechnicalProfile Id="SelfAsserted-ConfirmEmailSocial">
<DisplayName>Confirm email social</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="ContentDefinitionReferenceId">api.selfasserted</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="Verified.Email" Required="true" />
</OutputClaims>
</TechnicalProfile>
If the user rejects the facebook setting, the code works as expected: The verification control is shown.
However, if the user accepts the facebook setting, both the input field and the verification control disappear. (And the email gets pre-populated)
(Someone explains here stack overflow that the control is too dumb to realize that you want to verify the input claim https://stackoverflow.com/a/44429492/509464) but we haven't been able to get the suggested solution to work.
How can we force validation of emails provided in a previous orchestration step?
Update:
It seems we could create a workaround by making the input claim read-only somehow. (But only if it was empty).

Yes the only option is making it read only, since otherwise we assume the passed in email is already verified, which it is with Facebook. If you change the email, then you must verify it. You could modify the JavaScript/css to show the controls. Or in the custom policy, use an input claim transform to copy the email claim into a new read-only claim id. Then pre-populate the form using input claim. Then display the claim using output claim.

Related

In Azure B2C Custom Policies, how do I display more than 2 distinct validation messages for validations that depend on Validation Technical Profiles?

Scenario
In my sign up flow, I have the requirement to show:
an email address field and a confirm email address field
a password field and a confirm password field
I have to validate that:
the text entered in the email address field matches the text entered in the confirm email address field
the text entered in the password field matches the text entered in the confirm password field
the email address entered for a user doesn't already exist in Azure B2C
These validations should display in the existing form, rather than a new page.
The only way I've been able to display validation messages inline as a result of the output from Validation Technical Profiles, is by using the two built in asserts, AssertStringClaimsAreEqual (for the email and password comparisons) and AssertBooleanClaimIsEqualToValue (for the existing user check). These cause messages, defined in the calling Claims Provider Technical Profile, to be displayed when the assert fails.
Problem
I have three error cases that require distinct messages, and I'm only able to define and display messages for two. Currently, I'm working around this by displaying "Email and confirm email, or password and confirm password do not match." for both the failed text comparisons, and "This email address is already in use, please sign in." for the existing user case. The existing user case is fine, but I would describe the single text comparison message as 'Okay, but doesn't really meet the acceptance criteria' and 'I can't believe there's not a better way to do this'.
Any help would be gratefully received at this point.
Current Code Snippets
The relevant Validation Technical Profiles:
<TechnicalProfile Id="EmailMatchValidator">
<DisplayName>Check if email and confirm email match</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.ClaimsTransformationProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="DoEmailsMatch"/>
</InputClaimsTransformations>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="emailsMatch" Required="true"/>
</OutputClaims>
</TechnicalProfile>
<TechnicalProfile Id="PasswordMatchValidator">
<DisplayName>Check if password and confirm password match</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.ClaimsTransformationProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="DoPasswordsMatch"/>
</InputClaimsTransformations>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="passwordsMatch" Required="true"/>
</OutputClaims>
</TechnicalProfile>
<TechnicalProfile Id="UserExistsValidator">
<DisplayName>Asset if user exists</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.ClaimsTransformationProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
<InputClaims>
<InputClaim ClaimTypeReferenceId="userExists"/>
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="userExists" Required="true"/>
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="DoesUserExist"/>
</OutputClaimsTransformations>
</TechnicalProfile>
These Technical Profiles reference Output Claims Transformations, which look like this:
<ClaimsTransformation Id="DoEmailsMatch" TransformationMethod="AssertStringClaimsAreEqual">
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" TransformationClaimType="inputClaim1"/>
<InputClaim ClaimTypeReferenceId="confirmEmail" TransformationClaimType="inputClaim2"/>
</InputClaims>
<InputParameters>
<InputParameter Id="stringComparison" DataType="string" Value="ordinalIgnoreCase"/>
</InputParameters>
</ClaimsTransformation>
<ClaimsTransformation Id="DoPasswordsMatch" TransformationMethod="AssertStringClaimsAreEqual">
<InputClaims>
<InputClaim ClaimTypeReferenceId="password" TransformationClaimType="inputClaim1"/>
<InputClaim ClaimTypeReferenceId="confirmpassword" TransformationClaimType="inputClaim2"/>
</InputClaims>
<InputParameters>
<InputParameter Id="stringComparison" DataType="string" Value="ordinalIgnoreCase"/>
</InputParameters>
</ClaimsTransformation>
<ClaimsTransformation Id="DoesUserExist" TransformationMethod="AssertBooleanClaimIsEqualToValue">
<InputClaims>
<InputClaim ClaimTypeReferenceId="userExists" TransformationClaimType="inputClaim"/>
</InputClaims>
<InputParameters>
<InputParameter Id="valueToCompareTo" DataType="boolean" Value="false"/>
</InputParameters>
</ClaimsTransformation>
DoEmailsMatch and DoPasswordsMatch are straightforward enough, using the AssertStringClaimsAreEqual built-in assert to display a message defined in the Metadata of the calling Claim Provider Technical Profile:
<Item Key="UserMessageIfClaimsTransformationStringsAreNotEqual">Email and confirm email, or password and confirm password do not match.</Item>
DoesUserExist relies on AssertBooleanClaimIsEqualToValue, which means it needs a boolean to work with, to decide whether to display the other message defined in the Metadata:
<Item Key="UserMessageIfClaimsTransformationBooleanValueIsNotEqual">This email address is already in use, please sign in.</Item>
The code that sets that boolean is probably out of scope for this question, but in short, it gets set by a read on the AAD database that returns a matching User Principal Name in a claim, or no claim at all. This undergoes a Claim Transformation using DoesClaimExist to set the boolean userExists claim, used by DoesUserExist above.
For the third one, do an AAD Read operation instead, and it has a built in error message for when the use exists.
https://learn.microsoft.com/en-us/azure/active-directory-b2c/active-directory-technical-profile#ui-elements
You can find this used in the AAD B2C starter pack for the default sign up flow.
For password matching, use password and reenter password, use the built in way
https://github.com/Azure-Samples/active-directory-b2c-custom-policy-starterpack/blob/master/LocalAccounts/TrustFrameworkBase.xml#L648
Now you should only need the 1 claims transform for email matching.

B2C: Localization for messages originating from REST API

I am currently implementing this example to use an rest API during the user registration.
The basic idea is that the API throws an 409 Conflict error to interrupt the registration.
// Can I return a special "StringId" or something here for localization?
return new ConflictObjectResult(new B2CResponseModel($"A verification email sent to you. Please open your mail box and click on the link. If you didn't receive the email, please click on the 'Send verification email' button.", HttpStatusCode.Conflict));
I want to show the user a message that is localized to their current language. I would prefer to do the localization within the custom policies, but I would also accept a solution within the API (would need to get the User Language for this).
Is there a way to do this localization? Like returning a StringId via API and using this within the policy?
I am also considering not returning an error from the API, to show the message in a new screen instead (like How to display error returned from custom REST API endpoint in a subsequent orchestration step?). However, localization options for this elude me as well.
In case anybody is looking for a way to send the user's locale to the REST API:
https://learn.microsoft.com/nb-no/azure/active-directory-b2c/claim-resolver-overview
<TechnicalProfile Id="REST-API-SendVerificationEmail">
<DisplayName>Sign-Up send link</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="ServiceUrl">https://xxxx</Item>
<Item Key="AuthenticationType">None</Item>
<Item Key="SendClaimsIn">Body</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" />
<InputClaim ClaimTypeReferenceId="userLanguage" DefaultValue="{Culture:LanguageName}" />
<InputClaim ClaimTypeReferenceId="policyId" PartnerClaimType="policy" DefaultValue="{Policy:PolicyId}" />
<InputClaim ClaimTypeReferenceId="scope" DefaultValue="{OIDC:scope}" />
<InputClaim ClaimTypeReferenceId="clientId" DefaultValue="{OIDC:ClientId}" />
</InputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
Hope this is similar to this
See the answer by Jas Suri. Pass the localisation parameter to API and return the localised message or can return an error code and based on that display translated message using policy itself.

In the PhoneFactor-InputOrVerify Technical Profile (B2C custom user flow), is it possible to allow the user to change their default mobile number?

In Azure B2C custom policies, our business process requirements mean we need control over whether:
The user may not replace the mobile number we have on record for them (i.e. first-time password set experience), or
We present the number we hold on record for them by default, but allow the user to overtype and save this number with a new number (e.g. logged-in edit profile experience).
The question: Is there a way I can allow the user to replace the number we hold on record for them with a new number, perhaps by the addition of a new button [Provide New Number] in this series:
[Send Code] [Call Me] [New Button To Provide New Number] [Cancel]
PhoneFactor-InputOrVerify sample screen (image)
The code below is a slight modification of the similarly-named policy from the B2C Custom Policy Starterpack.
Note how in this example we're providing the mobile phone number value stored in the mobile property of the user object as strongAuthenticationNumber. This is because there is currently no way of setting the MFA number programmatically. (Thanks for your help, Chris Padgett.)
Other than this, the policy is as per starterpack.
<ClaimsProvider>
<DisplayName>PhoneFactor</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="PhoneFactor-InputOrVerify">
<DisplayName>PhoneFactor</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.PhoneFactorProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="ContentDefinitionReferenceId">api.phonefactor</Item>
<Item Key="ManualPhoneNumberEntryAllowed">true</Item>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="CreateUserIdForMFA" />
</InputClaimsTransformations>
<InputClaims>
<InputClaim ClaimTypeReferenceId="userIdForMFA" PartnerClaimType="UserId" />
<!--InputClaim ClaimTypeReferenceId="strongAuthenticationPhoneNumber" /-->
<InputClaim ClaimTypeReferenceId="mobile" PartnerClaimType="strongAuthenticationPhoneNumber" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="Verified.strongAuthenticationPhoneNumber" PartnerClaimType="Verified.OfficePhone" />
<OutputClaim ClaimTypeReferenceId="newPhoneNumberEntered" PartnerClaimType="newPhoneNumberEntered" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-MFA" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
I suspect there may be some metadata setting to enable a button or text input box as described, but to my knowledge there is no documentation for this yet.
You must create a custom flow for this that:
Prompts the end user to sign in with a local or external account.
Reads the current phone number for the end user from Azure AD.
Prompts the end user to verify their current phone number.
Prompts the end user to enter and verify their new phone number.
Writes the new phone number for the end user to Azure AD.
See this sample policy.

Invoke Rest API on email verification ADB2C custom policy

The default functionality of ADB2C for email verification verifies the verification code sent to the corresponding email.
However it does not check whether that email is already registered or not. Until now we have been doing this by calling a Rest API on clicking create. But now it is required to check if that email is already registered or not at the email verification step.
Is there a way to call a Rest API at email verification step? If yes, then how can it be done?
You could separate the input of email address and user data to a different page, and then just do the email verification on a next page. That way you can check if the user exists on the previous page and verify the email on the next.
You also do not need to call out to a REST endpoint to check if they exist. You can use a technical profile that inherits the AAD-Common Technical profile to see if they exist.
<TechnicalProfile Id="DoesUserExist">
<Metadata>
<Item Key="Operation">Read</Item>
<Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">false</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" Required="true" PartnerClaimType="signInNames" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="UserSearchOID" PartnerClaimType="objectId" />
</OutputClaims>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
</TechnicalProfile>

ad b2c allow user to change MFA setting

how would I allow the user to change their MFA setting like their phone number? I don't see any option for them to be able to do that easily.
Try this. B2C MFA reset was the reason I wrote it.
http://gordon.byers.me/azure/resetting-a-users-azure-ad-multi-factor-mfa-requirement/
As it's powershell you could put it inside an Azure function and call it via HTTP to allow the user to self serve.
Currently it is not possible to change an Azure AD B2C users' MFA settings.
There's already an ask for this ask in the Azure AD B2C forum that you should vote for:
https://feedback.azure.com/forums/169401-azure-active-directory/suggestions/15334329-change-security-info
The MFA phone number can be changed with custom policies. When you create a UserJourney that invokes a TechnicalProfile that does not take the strongAuthenticationPhoneNumber as InputClaim, IEF acts as if the user registers for MFA for the first time.
Of course you need to think about security measures, since it is a second factor that the user changes (e.g. ask the user to input some data, that can be validated, before allowing the user to change the phonenumber). Otherwise the use of MFA makes no sense.
Apparently, user's can't do that as stated by Saca 😢
Admins 👮 can do this on the user's behalf via the Azure Portal though:
Users -> All users -> Pick the user you're interested in -> Update Phone under Authentication contact info
Looks like this is now possible with custom policies.
There is a full example here: https://github.com/azure-ad-b2c/samples/tree/master/policies/edit-mfa-phone-number
In case the link breaks, the key part appears to be this:
<TechnicalProfiles>
<TechnicalProfile Id="PhoneFactor-EditAndVerify">
<DisplayName>PhoneFactor</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.PhoneFactorProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="ContentDefinitionReferenceId">api.phonefactor</Item>
<Item Key="ManualPhoneNumberEntryAllowed">true</Item>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="CreateUserIdForMFA" />
</InputClaimsTransformations>
<InputClaims>
<InputClaim ClaimTypeReferenceId="userIdForMFA" PartnerClaimType="UserId" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="Verified.strongAuthenticationPhoneNumber" PartnerClaimType="Verified.OfficePhone" />
<OutputClaim ClaimTypeReferenceId="newPhoneNumberEntered" PartnerClaimType="newPhoneNumberEntered" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>

Resources