MergeBehavior when overriding collection elements in custom B2C Policies - azure-ad-b2c

I am trying to structure my base & ext policies so that base remains pretty static. To do this I am overriding claims, etc in the ext policy - which works well. However, when trying to override a Validation Technical profile, it appears that the new items are being added at the beginning of the collection and not the end as I would have expected.
So if I have this:
base.xml
<TechnicalProfile Id="LocalAccountSignUpWithLogonEmail-NoVerify">
<DisplayName>Email signup</DisplayName>
… <!-- stuff removed for brevity -->
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AAD-UserWriteUsingLogonEmail" />
</ValidationTechnicalProfiles>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>
and this
ext.xml
<TechnicalProfile Id="LocalAccountSignUpWithLogonEmail-NoVerify">
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="API-UpdateUser" />
<ValidationTechnicalProfile ReferenceId="API-GetUser" />
</ValidationTechnicalProfiles>
</TechnicalProfile>
They execute in the order of
API-UpdateUser
API-GetUser
AAD-UserWriteUsingLogonEmail
Is there anyway to control this order as I need the user to be created before the API stuff executes? i.e. Execute in order 3, 1, 2

From what I have learned, if the override adds an element with the same elementname and ID, it replaces that element from the base policy.
So, if the extension policy contains:
````
<TechnicalProfile Id="LocalAccountSignUpWithLogonEmail-NoVerify">
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="API-UpdateUser" />
<ValidationTechnicalProfile ReferenceId="API-GetUser" />
<ValidationTechnicalProfile ReferenceId="AAD-UserWriteUsingLogonEmail" />
</ValidationTechnicalProfiles>
</TechnicalProfile>
````
then "AAD-UserWriteUsingLogonEmail" won't be invoked 2 times, it will be invoked once, as third validation profile, because the referenceId="AAD-UserWriteUsingLogonEmail" overrides the declaration of that element in the base policy file.
The documentation is not very clear about this, and the closest doc I have found is in https://github.com/Azure-Samples/active-directory-b2c-advanced-policies/blob/master/Documentation/Features%20part%206.md, where it says:
Same element in the child policy and parent policy implies, child
policy is overriding that element of the parent policy
That documentation doesn't make clear what "same element" means, it seems to me to be the same XPath and same ID (with the ID being whatever attribute is the unique id of the element).

You need to overwrite all of them in the correct order in the extension policy. For example, if your base policy has following validation technical profile AAD-UserWriteUsingLogonEmail
In the extension policy, you add AAD-UserWriteUsingLogonEmail again, in the correct order:
API-UpdateUser
API-GetUser
AAD-UserWriteUsingLogonEmail

Related

Why does Azure B2C refuse to accept <ContentDefinitions> and <Localization> in my custom policy if it comes before <ClaimsTransformations>?

I am building a custom Azure B2C Policy. It is built on top of the B2C Starter Pack of policies, and my policy inherits the provided B2C_1A_TrustFrameworkExtensions. The policy is already working just fine. Now, I want to do some UI customization and I am trying to add a <ContentDefinitions> section and a <Localization> section in my <BuildingBlocks>.
Except, every single time I add a <ContentDefinitions> section or a <Localization> section B2C complains that the <ClaimsTransformations> section is now unexpected. But, I found out that if I add the <ContentDefinitions> and <Localization> section AFTER the <ClaimsTransformations> section it accepts it.
Shouldn't this be agnostic? What difference does it make what order the entries are in? Is this a bug, or something I'm missing?
The element 'BuildingBlocks' in namespace
'http://schemas.microsoft.com/online/cpim/schemas/2013/06' has invalid
child element 'ClaimsTransformations' in namespace
'http://schemas.microsoft.com/online/cpim/schemas/2013/06'. List of
possible elements expected: 'DisplayControls' in namespace
'http://schemas.microsoft.com/online/cpim/schemas/2013/06'
This does not work:
<BasePolicy>
<TenantId>{Settings:Tenant}</TenantId>
<PolicyId>B2C_1A_TrustFrameworkExtensions</PolicyId>
</BasePolicy>
<BuildingBlocks>
<ContentDefinitions/>
<ClaimsTransformations>
<ClaimsTransformation Id="TEST" TransformationMethod="CreateStringClaim">
<InputParameters>
<InputParameter Id="value" DataType="string" Value="TEST" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="signInName" TransformationClaimType="createdClaim" />
</OutputClaims>
</ClaimsTransformation>
</ClaimsTransformations>
</BuildingBlocks>
This works:
<BasePolicy>
<TenantId>{Settings:Tenant}</TenantId>
<PolicyId>B2C_1A_TrustFrameworkExtensions</PolicyId>
</BasePolicy>
<BuildingBlocks>
<ClaimsTransformations>
<ClaimsTransformation Id="TEST" TransformationMethod="CreateStringClaim">
<InputParameters>
<InputParameter Id="value" DataType="string" Value="TEST" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="signInName" TransformationClaimType="createdClaim" />
</OutputClaims>
</ClaimsTransformation>
</ClaimsTransformations>
<ContentDefinitions/>
</BuildingBlocks>
The element 'BuildingBlocks' in namespace
'http://schemas.microsoft.com/online/cpim/schemas/2013/06' has invalid child element 'ClaimsTransformations' in namespace
The above error refers that In the Buildingsblocks of the chlidren order is not arranged in correct format.
The correct format is
<BuildingBlocks>
<ClaimsSchema>
...
</ClaimsSchema>
<Predicates>
...
</Predicates>
<PredicateValidations>
...
</PredicateValidations>
<ClaimsTransformations>
...
</ClaimsTransformations>
<ContentDefinitions>
...
</ContentDefinitions>
<Localization>
...
</Localization>
<DisplayControls>
...
</DisplayControls>
</BuildingBlocks>
In your first code you have processed you have arranged like
<BuildingBlocks>
<ContentDefinitions/>
</BuildingBlocks>
Here the children order is wrong that may caused you invalid child element.
In your second code you have arranged in correct order like i given above that why it is worked.
Reference:
BuildingBlocks - Azure AD B2C | Microsoft Docs

How to populate an attribute from other attributes

To my sign up screen, I added 2 built-in fields: First Name and Last Name. In the Users | All users
list, a new user appears with the Name of "unknown". That is because I did not add the "Display Name" attribute. I don't want to make the user fill this additional field. Is it possible to set the Display Name to be populated on the back end as First Name + " " + Last Name?
I believe your two options are:
Enable Custom HTML/JavaScript for your User Interface that will effectively hide the Display Name field, and combine the First Name and Last Name, respectively.
Use the more complex, but powerful Identity Experience Framework and leverage a ClaimsTransformation:
Example:
<ClaimsTransformation Id="Generate-DisplayName" TransformationMethod="FormatStringMultipleClaims">
<InputClaims>
<InputClaim ClaimTypeReferenceId="givenname" TransformationClaimType="inputClaim1"/>
<InputClaim ClaimTypeReferenceId="surname" TransformationClaimType="inputClaim2"/>
</InputClaims>
<InputParameters>
<InputParameter Id="stringFormat" DataType="string" Value="{0} {1}"/>
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="displayName" TransformationClaimType="outputClaim"/>
</OutputClaims>
</ClaimsTransformation>

Trying to get date of Birth for a user if it is is not an Adult via <ValidationTechnicalProfiles>

I am trying to collect the date of birth for a user if he specifies that he is Under18....
I have defined 2 claims as follows
<ClaimType Id="extension_DateofBirth">
<DisplayName>Date of Birth</DisplayName>
<DataType>dateTime</DataType>
<UserInputType>DateTimeDropdown</UserInputType>
</ClaimType>
<!--Adding Custom Attribute for AgeGroup -->
<ClaimType Id="extension_AgeGroup">
<DisplayName>Your Age Group</DisplayName>
<DataType>string</DataType>
<UserInputType>DropdownSingleSelect</UserInputType>
<Restriction>
<Enumeration Text="Under 18" Value="Under18" />
<Enumeration Text="Adult" Value="Adult" />
</Restriction>
</ClaimType>
Now in my
I have added the following code in
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="Get-DOB" ContinueOnError="false">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="false">
<Value>extension_AgeGroup</Value>
<Value>Under18</Value>
<Action>SkipThisValidationTechnicalProfile</Action>
</Precondition>
</Preconditions>
</ValidationTechnicalProfile>
</ValidationTechnicalProfiles>
and another Technical Profile as follows that is referenced "Get-DOB". which is as follows
<!--Adding write of DOB based on validation Profile-->
<TechnicalProfile Id="Get-DOB">
<InputClaims>
<InputClaim ClaimTypeReferenceId="extension_DateofBirth" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="extension_DateofBirth" />
</OutputClaims>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
</TechnicalProfile>
</TechnicalProfiles>
I have 2 issues:
if I select Under18 the DOB should appear on the screen which it is not.
After I select Under18 and press continue the explorer gets stuck on
we are processing your information forever
Hi I got this working by getting the preconditions done in the User Journey and not using validation technical Profiles

Azure B2C Sign up age restriction using custom policies

I'm trying to configure a B2C tenant using policies instead of user flows. For that, I'm using the SocialAndLocalAccount template as start base.
In the sign up page, I added a custom claim to ask the user about his/her birth date. If the user provides a date which make him/her an under age (+18), I would like to display a verification failed message (like the one you can set by using predicates) and prevent the user to be able to sign up by the create button being disabled.
So far, this is what I've got:
-I created two new claims to store a boolean value that would tell me if the user is under age or not and a second one to store the current time.
<ClaimType Id="systemDateTime">
<DisplayName>Today's date</DisplayName>
<DataType>dateTime</DataType>
</ClaimType>
<ClaimType Id="isNotUnderAge">
<DisplayName>Indicates whether user being under age or not</DisplayName>
<DataType>boolean</DataType>
<AdminHelpText>User must be over 18</AdminHelpText>
</ClaimType>
I added to ClaimsTransformation to get the current date and compare it to the selected one (date comparison):
<!-- Check user under age -->
<ClaimsTransformation Id="GetSystemDateTime" TransformationMethod="GetCurrentDateTime">
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="systemDateTime" TransformationClaimType="currentDateTime" />
</OutputClaims>
</ClaimsTransformation>
<ClaimsTransformation Id="CheckBirthDateIsNotUnderAge" TransformationMethod="DateTimeComparison">
<InputClaims>
<InputClaim ClaimTypeReferenceId="birthDate" TransformationClaimType="firstDateTime" />
<InputClaim ClaimTypeReferenceId="systemDateTime" TransformationClaimType="secondDateTime" />
</InputClaims>
<InputParameters>
<InputParameter Id="operator" DataType="string" Value="later than" />
<InputParameter Id="timeSpanInSeconds" DataType="int" Value="568025136" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="isNotUnderAge" TransformationClaimType="result" />
</OutputClaims>
</ClaimsTransformation>
Until this point, I think I'might be able to tell if the user is under age or not. From here on, I'm not sure how to continue in order to achieve my goal. I've been reading documentation but didn't come to a solution and I'm unsure that this would be the best approach.
Have you ever came across a similar restriction? If so, which is the best way to achieve this and where can I find any tips that help me resolve my issue?
Thank you very much!
EDIT 1:
I've found this question which explains a similar problem if not the same and it make me ask myself, is it possible to achieve this by using ClaimsTransformations and Predicates or is the REST API the only way to get the expected result?
It looks like you have the right idea as long as you want to blanket ban all users under 18. One option is you can use a validation technical profile to call AssertBooleanClaimIsEqualToValue transformation, which will display a custom error message on the sign-in page if the user is under 18 and won't let them continue. See Microsoft Documentation: Boolean Claim Transformations for example.
If you really want the Continue button to grey out, you could display isNotUnderage as a readonly claim and hide it with CSS, and then use JS to grey out the button based on its value.
The third option is to add an OrchestrationStep before presenting the user with your signup options with a precondition to skip if user is over 18 that will display a new Self-Asserted Technical Profile you can set up to display an error message (just a paragraph claim) and remove the continue button from. The downside here is that it requires you collect the user's age before offering them sign-up options which can complicate things.

Unable to Get Paragraph InputType to Display Any Text In Azure B2C IEF

I am having some trouble using the Paragraph UserInput Type available to Azure B2C IEF. I would like to use the Paragraph element because it would make localization a lot easier. However, no matter what I have tried, I am unable to get the Paragraph element to display any text.
I have tried to follow the documentation: https://learn.microsoft.com/en-us/azure/active-directory-b2c/claimsschema#paragraph and I have also contacted the B2C Team via GitHub and was recommended I assign default values.
ClaimType
<ClaimType Id="UserExistsErrorMessage">
<DisplayName>Error Message</DisplayName>
<DataType>string</DataType>
<UserInputType>Paragraph</UserInputType>
</ClaimType>
In my technical profile I am assigning a default value
<OutputClaim ClaimTypeReferenceId="UserExistsErrorMessage" DefaultValue="Test">
I am expecting the paragraph to display the text that I have assigned. Instead all I am getting is a blank < p>< /p> HTML tags when reviewing the source during testing.
To set the value of Paragraph UserInputType, please set default value using input claim.
<InputClaims>
<InputClaim ClaimTypeReferenceId="UserExistsErrorMessage" DefaultValue="Test"/>
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="UserExistsErrorMessage"/>
</OutputClaims>
I believe it's the Enumeration value that must be set to the display value:
<ClaimType Id="UserExistsErrorMessage">
<DisplayName>Error Message</DisplayName>
<DataType>string</DataType>
<UserInputType>Paragraph</UserInputType>
<Restriction>
<Enumeration Text="Test" Value="This is a test message." />
</Restriction>
</ClaimType>

Resources