How to populate an attribute from other attributes - azure-ad-b2c

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>

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 format JSON without square brackets [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
This should be easy so what am I missing?
I'm needing to have claims generated in a JSON body for a subsequent REST API call to a service. I don't see a way to prevent GenerateJSON from including square brackets when there is a entity name at the top. The REST API service is choking on them.
Needing:
{"CustomerEntity":
{
"ExternalIdentityId": "9988875",
"FirstName":"David",
"LastName":"Longfellow"
}
}
Getting:
{"CustomerEntity": [
{
"ExternalIdentityId": "9988875",
"FirstName":"David",
"LastName":"Longfellow"
}
]
}
Try the following format (without the "0" before the second notation).
<ClaimsTransformations>
<ClaimsTransformation Id="GenerateRequestBody" TransformationMethod="GenerateJson">
<InputParameters>
<InputParameter Id="CustomerEntity.ExternalIdentityId" DataType="string" Value="9988875"/>
<InputParameter Id="CustomerEntity.FirstName" DataType="string" Value="David"/>
<InputParameter Id="CustomerEntity.LastName" DataType="string" Value="Longfellow"/>
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="requestBody" TransformationClaimType="outputClaim"/>
</OutputClaims>
</ClaimsTransformation>
</ClaimsTransformations>
Output:
{
"CustomerEntity":{
"ExternalIdentityId":"9988875",
"FirstName":"David",
"LastName":"Longfellow"
}
}
Sample 2
<ClaimsTransformation Id="GenerateRequestBody" TransformationMethod="GenerateJson">
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" TransformationClaimType="customerEntity.email" />
<InputClaim ClaimTypeReferenceId="objectId" TransformationClaimType="customerEntity.userObjectId" />
<InputClaim ClaimTypeReferenceId="givenName" TransformationClaimType="customerEntity.firstName" />
<InputClaim ClaimTypeReferenceId="surname" TransformationClaimType="customerEntity.lastName" />
</InputClaims>
<InputParameters>
<InputParameter Id="customerEntity.role.name" DataType="string" Value="Administrator"/>
<InputParameter Id="customerEntity.role.id" DataType="long" Value="1"/>
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="requestBody" TransformationClaimType="outputClaim"/>
</OutputClaims>
</ClaimsTransformation>
Output:
{
"customerEntity":{
"email":"john.s#contoso.com",
"userObjectId":"31ee61cf-521f-4132-8e19-11a3b6562666",
"firstName":"John",
"lastName":"Smith",
"role":{
"name":"Administrator",
"id": 1
}
}
}

Azure AD B2C - Populating email in input claim from previous orchestration step and Verify.email not working alongwith

First orchestration step is login- taking email output claim
Second step is email verify- I want to populate email from login and verify with email OTP.
Problem-
If email populated, verify button does not come.
If verify button comes, email not populated.
I was able to solve for this use case by using a DisplayControl within a DisplayClaim instead of using the Verified.Email in an OutputClaim.
First define these two attributes in your BuildingBlocks, ClaimsSchema:
<BuildingBlocks>
<ClaimsSchema>
<ClaimType Id="readOnlyEmail">
<DisplayName>Email</DisplayName>
<DataType>string</DataType>
<UserInputType>Readonly</UserInputType>
</ClaimType>
<ClaimType Id="verificationCode">
<DisplayName>Secondary Verification Code</DisplayName>
<DataType>string</DataType>
<UserHelpText>Enter your email verification code</UserHelpText>
<UserInputType>TextBox</UserInputType>
</ClaimType>
<!-- Other claims you have defined -->
</ClaimsSchema>
</BuildingBlocks>
We are going to copy the email attribute into the readOnlyEmail attribute so we can display it on the screen, and the verificationCode attribute is used when validating the email one-time-passcode (OTP). Stick with me here.
Define a ClaimsTransformation inside of BuildingBlocks, ClaimsTransformations that copies over the email into readOnlyEmail.
<BuildingBlocks>
<!-- ClaimsSchema -->
<ClaimsTransformations>
<ClaimsTransformation Id="CopyEmailAddress" TransformationMethod="CopyClaim">
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" TransformationClaimType="inputClaim"/>
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="readOnlyEmail" TransformationClaimType="outputClaim"/>
</OutputClaims>
</ClaimsTransformation>
</ClaimsTransformations>
<!-- ContentDefinitions, DisplayControls, etc. -->
</BuildingBlocks>
Modify the DataUri element in the ContentDefinition for api.selfasserted to make it version 2.0.0 and include the "contract" tag. You must do this to allow DisplayClaims to work.
The api.selfasserted ContentDefinition should now look like this:
<BuildingBlocks>
<!-- ClaimsSchema, ClaimsTransformations -->
<ContentDefinitions>
<ContentDefinition Id="api.selfasserted">
<LoadUri>~/tenant/templates/AzureBlue/selfAsserted.cshtml</LoadUri>
<RecoveryUri>~/common/default_page_error.html</RecoveryUri>
<DataUri>urn:com:microsoft:aad:b2c:elements:contract:selfasserted:2.0.0</DataUri>
<Metadata>
<Item Key="DisplayName">Collect information from user page</Item>
</Metadata>
</ContentDefinition>
</ContentDefinitions>
<!-- DisplayControls, etc. -->
</BuildingBlocks>
The only change here is to the DataUri element in the ContentDefinition. Notice that we updated it to include the word "contract" and it's version is now 2.0.0.
Then define a DisplayControl inside of BuildingBlocks, DisplayControls that uses our readOnlyEmail, and verficationCode ClaimTypes:
<BuildingBlocks>
<!-- ClaimsSchema, ClaimsTransformations, ContentDefinitions, etc -->
<DisplayControls>
<DisplayControl Id="emailVerificationControl" UserInterfaceControlType="VerificationControl">
<InputClaims>
<InputClaim ClaimTypeReferenceId="readOnlyEmail" />
</InputClaims>
<DisplayClaims>
<DisplayClaim ClaimTypeReferenceId="readOnlyEmail" />
<DisplayClaim ClaimTypeReferenceId="verificationCode" ControlClaimType="VerificationCode" />
</DisplayClaims>
<Actions>
<Action Id="SendCode">
<ValidationClaimsExchange>
<ValidationClaimsExchangeTechnicalProfile TechnicalProfileReferenceId="AadSspr-SendCode" />
</ValidationClaimsExchange>
</Action>
<Action Id="VerifyCode">
<ValidationClaimsExchange>
<ValidationClaimsExchangeTechnicalProfile TechnicalProfileReferenceId="AadSspr-VerifyCode" />
</ValidationClaimsExchange>
</Action>
</Actions>
</DisplayControl>
</DisplayControls>
</BuildingBlocks>
Notice here we are displaying readOnlyEmail and the second DisplayClaim for verificationCode is for collecting the OTP once it's been sent to your email.
Also notice the Actions which point to TechnicalProfiles we have yet to define. They correspond to buttons on the screen and will send the email and verify the code. Let's define those next. You can put them in whatever ClaimsProvider you'd like:
<TechnicalProfile Id="AadSspr-SendCode">
<DisplayName>Send Code</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.AadSsprProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="Operation">SendCode</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="readOnlyEmail" PartnerClaimType="emailAddress"/>
</InputClaims>
</TechnicalProfile>
<TechnicalProfile Id="AadSspr-VerifyCode">
<DisplayName>Verify Code</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.AadSsprProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="Operation">VerifyCode</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="verificationCode" PartnerClaimType="verificationCode" />
<InputClaim ClaimTypeReferenceId="readOnlyEmail" PartnerClaimType="emailAddress"/>
</InputClaims>
</TechnicalProfile>
These TechnicalProfiles are taking advantage of the AadSsprProtocolProvider to send and verify the code. MS has good documentation here: https://learn.microsoft.com/en-us/azure/active-directory-b2c/aad-sspr-technical-profile
Almost there. Now let's create a SelfAssertedAttributeProvider TechnicalProfile to take advantage of our new DisplayClaim that will do the inline verification.
Create this TechnicalProfile in whatever ClaimsProvider you'd like:
<TechnicalProfile Id="SelfAsserted-VerifyEmail">
<DisplayName>Verify Email</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>
<IncludeInSso>false</IncludeInSso>
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="CopyEmailAddress" />
</InputClaimsTransformations>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" />
<InputClaim ClaimTypeReferenceId="readOnlyEmail" />
</InputClaims>
<DisplayClaims>
<DisplayClaim DisplayControlReferenceId="emailVerificationControl" />
</DisplayClaims>
</TechnicalProfile>
This TechnicalProfile will take email as the input, copy it to our readOnlyEmail with the InputClaimsTransformation, and then the readOnlyEmail is used in our new emailVerificationControl DisplayClaim. That emailVerficationControl then shows the user a readOnly value of the email, has a button to send the code, and then the ability to verify it once it's been sent.
The last thing you need to do is use this new TechnicalProfile in your UserJourney, OrchestrationSteps like so:
<OrchestrationStep Order="X" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="SelfAsserted-VerifyEmail-CE" TechnicalProfileReferenceId="SelfAsserted-VerifyEmail"/>
</ClaimsExchanges>
</OrchestrationStep>
This technique solves the use case you were having trouble with.
We faced this same issue.
Needed to automatically fill in the email but also verify it.
The issue is that the value that is filled in is considered verified right away.
So we had to do it a bit indirectly.
First we defined an "originalEmail" claim:
<ClaimType Id="originalEmail">
<DisplayName>Original email</DisplayName>
<DataType>string</DataType>
<UserInputType>Readonly</UserInputType>
</ClaimType>
The signup technical profile then defines 2 input claims, 2 input transformations and 1 output claim (along with all the other stuff):
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="SetOriginalEmailFromEmail" />
<InputClaimsTransformation ReferenceId="SetEmailToNull" />
</InputClaimsTransformations>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" />
<InputClaim ClaimTypeReferenceId="originalEmail" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="originalEmail" />
</OutputClaims>
The first claim transformation copies the value from email to originalEmail:
<ClaimsTransformation Id="SetOriginalEmailFromEmail" TransformationMethod="FormatStringClaim">
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" TransformationClaimType="inputClaim" />
</InputClaims>
<InputParameters>
<InputParameter Id="stringFormat" DataType="string" Value="{0}" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="originalEmail" TransformationClaimType="outputClaim" />
</OutputClaims>
</ClaimsTransformation>
And the second transformation resets the email claim:
<ClaimsTransformation Id="SetEmailToNull" TransformationMethod="NullClaim">
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="email" TransformationClaimType="claim_to_null" />
</OutputClaims>
</ClaimsTransformation>
This way the email will show up empty at first, with the original email in a hidden field next to it.
You can then use JavaScript in a custom UI to copy the value over at load:
(function () {
setTimeout(function () {
copyOriginalEmailToEmail();
}, 500);
function copyOriginalEmailToEmail() {
var originalEmailField = document.getElementById('originalEmail');
var originalEmail = originalEmailField && originalEmailField.value;
if (originalEmail) {
document.getElementById('email').value = originalEmail;
}
}
}());
It's quite a hack but it has worked quite well.
Setting the value immediately causes the Send code button to disappear as well, so that's why the delay is there.
If there is an easier way to do this, I'd like to know too :D

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>

Update Display Name in Azure B2C custom policy

We are trying to update the Display Name in a custom Azure B2C policy using a Claims Transformation, but we can't get it to work. The policy executes with no errors, but the display name does not get updated.
We DO NOT want the user to input the display name.
There's a similar question answered here, but the transformation offered in the solution does not pass validation when uploading it to Azure.
Here's the updated transformation we are using:
<ClaimsTransformation Id="CreateDisplayNameFromFirstNameAndLastName" 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>
There is a sample that uses the above transformation here.
The transformation you have posted is correct. So I assume that your not using it in a correct order.
You have written, that you do not want your users to set up a display name. Therefore, you only have to inluce the transformation within the technical profile that actually writes the user's details into the Active Directory.
Example giving:
<TechnicalProfile Id="AAD-UserWriteUsingLogonEmail">
<Metadata>
<Item Key="Operation">Write</Item>
<Item Key="RaiseErrorIfClaimsPrincipalAlreadyExists">true</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<!-- Transform optional claims (given name, surname) to proper display name -->
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="CreateDisplayNameFromFirstNameAndLastName" />
</InputClaimsTransformations>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" PartnerClaimType="signInNames.emailAddress" Required="true" />
</InputClaims>
<PersistedClaims>
<!-- Required claims -->
<PersistedClaim ClaimTypeReferenceId="email" PartnerClaimType="signInNames.emailAddress" />
<PersistedClaim ClaimTypeReferenceId="newPassword" PartnerClaimType="password"/>
<PersistedClaim ClaimTypeReferenceId="displayName" DefaultValue="unknown" />
<PersistedClaim ClaimTypeReferenceId="passwordPolicies" DefaultValue="DisablePasswordExpiration" />
<!-- Optional claims. -->
<PersistedClaim ClaimTypeReferenceId="givenName" />
<PersistedClaim ClaimTypeReferenceId="surname" />
</PersistedClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="newUser" PartnerClaimType="newClaimsPrincipalCreated" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="localAccountAuthentication" />
<OutputClaim ClaimTypeReferenceId="userPrincipalName" />
<OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" />
</OutputClaims>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>
Now the transformation takes the optional name claims and create a proper display name before (input transformation) the values are written into the directory. If neither a given name nor a surname is set, the fallback value (unknown) will be used. Any read operation on the dictionary (Login etc.) will return the one-time transformed display name.
However, you can adjust your technical profile for collecting this information (e.g. LocalAccountSignUpWithLogonEmail) to require a value for those fields.
...
<OutputClaim ClaimTypeReferenceId="givenName" Required="true" />
<OutputClaim ClaimTypeReferenceId="surName" Required="true" />

Resources