OutputClaimTransformation in B2C SAML2 CustomPolicy - azure-ad-b2c

I have some b2c SAML CustomPolicy for one SP.
Here I have some OutputClaims. Now I want to send one Claim with some logic and tried to set OutputClaimTransformations.
My scenario:
I will send the claim "ABCString: 0,1" for Croatia and "ABCString: 0,2" for germany. 0 is some default value I need to send and the next value should be country dependent.
So I created one Transformation for defaultvalue, then one for country dependent, then I merge both in one collection and will join them in one string.
I attached my XML.
Problem is: the "ABCString" is never sent to application. The OutputClaims of the ClaimsTransformations are not used at all.
All examples I found were in Extension or Base File and are using "persistent" fields. I just want to build the value on request.
Some ideas for this?
my xml:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<TrustFrameworkPolicy
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://schemas.microsoft.com/online/cpim/schemas/2013/06"
PolicySchemaVersion="0.3.0.0"
TenantId="xxx.onmicrosoft.com"
PolicyId="B2C_1A_signup_signin_xxx-test"
PublicPolicyUri="http://xxx.onmicrosoft.com/B2C_1A_signup_signin_xxx-test" >
<BasePolicy>
<TenantId>xxx.onmicrosoft.com</TenantId>
<PolicyId>B2C_1A_TrustFrameworkExtensions</PolicyId>
</BasePolicy>
<BuildingBlocks>
<ClaimsSchema>
<ClaimType Id="ABCCollection">
<DisplayName>abc</DisplayName>
<DataType>stringCollection</DataType>
<UserHelpText>abc.</UserHelpText>
</ClaimType>
<ClaimType Id="ABCString">
<DisplayName>abcs</DisplayName>
<DataType>string</DataType>
<UserHelpText>abcs.</UserHelpText>
</ClaimType>
<ClaimType Id="DEFString">
<DisplayName>defs</DisplayName>
<DataType>string</DataType>
<UserHelpText>defs</UserHelpText>
</ClaimType>
<ClaimType Id="GHIString">
<DisplayName>ghis</DisplayName>
<DataType>string</DataType>
<UserHelpText>ghis.</UserHelpText>
</ClaimType>
</ClaimsSchema>
<ClaimsTransformations>
<ClaimsTransformation Id="SetDefault" TransformationMethod="AddParameterToStringCollection">
<InputClaims>
<InputClaim ClaimTypeReferenceId="ABCString" TransformationClaimType="collection" />
</InputClaims>
<InputParameters>
<InputParameter Id="item" DataType="string" Value="0" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="ABCCollection" TransformationClaimType="collection" />
</OutputClaims>
</ClaimsTransformation>
<ClaimsTransformation Id="GetCountrySpecific" TransformationMethod="LookupValue">
<InputClaims>
<InputClaim ClaimTypeReferenceId="extension_country" TransformationClaimType="inputParameterId" />
</InputClaims>
<InputParameters>
<InputParameter Id="CROATIA" DataType="string" Value="1" />
<InputParameter Id="GERMANY" DataType="string" Value="2" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="DEFString" TransformationClaimType="outputClaim" />
</OutputClaims>
</ClaimsTransformation>
<ClaimsTransformation Id="MergeDefaultWithCountry" TransformationMethod="AddItemToStringCollection">
<InputClaims>
<InputClaim ClaimTypeReferenceId="DEFString" TransformationClaimType="item" />
<InputClaim ClaimTypeReferenceId="ABCCollection" TransformationClaimType="collection" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="ABCCollection" TransformationClaimType="collection" />
</OutputClaims>
</ClaimsTransformation>
<ClaimsTransformation Id="ConvertTeamCollectionToString" TransformationMethod="StringJoin">
<InputClaims>
<InputClaim ClaimTypeReferenceId="ABCCollection" TransformationClaimType="inputClaim" />
</InputClaims>
<InputParameters>
<InputParameter DataType="string" Id="delimiter" Value="," />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="ABCString" TransformationClaimType="outputClaim" />
</OutputClaims>
</ClaimsTransformation>
</ClaimsTransformations>
<ContentDefinitions>
<ContentDefinition Id="api.signuporsignin">
<LoadUri>https://api.contoso.com/test/azure/html/xxx.cshtml</LoadUri>
<RecoveryUri>~/common/default_page_error.html</RecoveryUri>
<DataUri>urn:com:microsoft:aad:b2c:elements:contract:unifiedssp:1.2.0</DataUri>
<Metadata>
<Item Key="DisplayName">Signin and Signup</Item>
</Metadata>
</ContentDefinition>
</ContentDefinitions>
</BuildingBlocks>
<ClaimsProviders>
<ClaimsProvider>
<Domain>app.contoso.com</Domain>
<DisplayName>SAML2 for App</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="Saml2App">
<DisplayName>SAML2 for App</DisplayName>
<Protocol Name="None" />
<OutputTokenFormat>SAML2</OutputTokenFormat>
<Metadata>
<Item Key="IssuerUri">https://xxx.b2clogin.com/xxx.onmicrosoft.com/B2C_1A_signup_signin_xxx-test</Item>
</Metadata>
<CryptographicKeys>
<Key Id="MetadataSigning" StorageReferenceId="B2C_1A_mycert"/>
<Key Id="SamlAssertionSigning" StorageReferenceId="B2C_1A_mycert"/>
<Key Id="SamlMessageSigning" StorageReferenceId="B2C_1A_mycert"/>
</CryptographicKeys>
<InputClaims/>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="email" />
<OutputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="name" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Saml"/>
</TechnicalProfile>
<TechnicalProfile Id="SM-Saml">
<DisplayName>Session Management Provider</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.SSO.SamlSSOSessionProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
</ClaimsProviders>
<UserJourneys>
<UserJourney Id="SignUpOrSignInSAML2App">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin">
<ClaimsProviderSelections>
<ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninEmailExchange" />
</ClaimsProviderSelections>
<ClaimsExchanges>
<ClaimsExchange Id="LocalAccountSigninEmailExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Email" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="SignUpWithLogonEmailExchange" TechnicalProfileReferenceId="LocalAccountSignUpWithLogonEmail" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="3" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="4" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="Saml2App" />
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>
</UserJourneys>
<RelyingParty>
<DefaultUserJourney ReferenceId="SignUpOrSignInSAML2App" />
<UserJourneyBehaviors>
<ScriptExecution>Allow</ScriptExecution>
</UserJourneyBehaviors>
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="SAML2"/>
<Metadata>
<Item Key="PartnerEntity"><![CDATA[<?xml version="1.0" encoding="UTF-8" standalone="yes" ?><some definition>]]></Item>
<Item Key="client_id">xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx</Item>
<Item Key="IdTokenAudience">yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy</Item>
<Item Key="XmlSignatureAlgorithm">Sha256</Item>
</Metadata>
<CryptographicKeys>
<Key Id="MetadataSigning" StorageReferenceId="B2C_1A_mycert"/>
<Key Id="SamlAssertionSigning" StorageReferenceId="B2C_1A_mycert"/>
<Key Id="SamlMessageSigning" StorageReferenceId="B2C_1A_mycert"/>
</CryptographicKeys>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surname" />
<OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" />
<OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="" />
<OutputClaim ClaimTypeReferenceId="GHIString" DefaultValue="123" />
<OutputClaim ClaimTypeReferenceId="extension_country" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="GetCountrySpecific" />
<OutputClaimsTransformation ReferenceId="SetDefault" />
<OutputClaimsTransformation ReferenceId="MergeDefaultWithCountry" />
<OutputClaimsTransformation ReferenceId="ConvertTeamCollectionToString" />
</OutputClaimsTransformations>
<SubjectNamingInfo ClaimType="Email" Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" ExcludeAsClaim="false"/>
</TechnicalProfile>
</RelyingParty>
</TrustFrameworkPolicy>

I don't believe any output claims transformations are invoked for a relying party technical profile.
Rather, you must invoke the output claims transformations during the user journey, such as follows.
Define a new claims transformation technical profile that invokes the new output claims transformation:
<TechnicalProfile Id="ClaimsTransformation-CreateABCStringClaim">
<DisplayName>Create ABCString Claim</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.ClaimsTransformationProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="ABCString" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="GetCountrySpecific" />
<OutputClaimsTransformation ReferenceId="SetDefault" />
<OutputClaimsTransformation ReferenceId="MergeDefaultWithCountry" />
<OutputClaimsTransformation ReferenceId="ConvertTeamCollectionToString" />
</OutputClaimsTransformations>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
Add a new orchestration step, before the SendClaims orchestration step, that invokes the claims transformation technical profile:
<OrchestrationStep Order="4" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="ClaimsTransformation-CreateABCStringClaim" TechnicalProfileReferenceId="ClaimsTransformation-CreateABCStringClaim" />
</ClaimsExchanges>
</OrchestrationStep>

Related

Sign up customization with separate email verification using SendGrid

I am using Azure AD B2C with Custom Policies to implement a sign-up flow where user has to verify the email before proceeds to sign up page.
I found an example here https://github.com/azure-ad-b2c/samples/blob/master/policies/split-email-verification-and-signup/policy/SignUpOrSignIn_SplitEmailVerificationAndSignUp.xml#L61-L77 and I try to modify this code and add a DisplayControl and from there I implement the custom email verification flow.
The emailVerificationControl is also based on the other sample code found here https://github.com/azure-ad-b2c/samples/blob/master/policies/custom-email-verifcation-displaycontrol/policy/SendGrid/DisplayControl_TrustFrameworkExtensions.xml#L142-L163
<BuildingBlocks>
<ClaimsSchema>
<ClaimType Id="readonlyEmail">
<DisplayName>E-mail Address</DisplayName>
<DataType>string</DataType>
<UserInputType>Readonly</UserInputType>
</ClaimType>
<ClaimType Id="isForgotPassword">
<DisplayName>isForgotPassword</DisplayName>
<DataType>boolean</DataType>
<AdminHelpText>Whether the user has clicked Forgot Password</AdminHelpText>
</ClaimType>
<ClaimType Id="termsOfUseConsentChoice">
<DisplayName></DisplayName>
<DataType>string</DataType>
<UserInputType>CheckboxMultiSelect</UserInputType>
<Restriction>
<Enumeration Text=" I agree to the Terms Of Service" Value="AgreeToTermsOfUseConsentYes" SelectByDefault="false" />
</Restriction>
</ClaimType>
</ClaimsSchema>
<ClaimsTransformations>
<ClaimsTransformation Id="CreateReadonlyEmailClaim" TransformationMethod="FormatStringClaim">
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" TransformationClaimType="inputClaim" />
</InputClaims>
<InputParameters>
<InputParameter Id="stringFormat" DataType="string" Value="{0}" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="readonlyEmail" TransformationClaimType="outputClaim" />
</OutputClaims>
</ClaimsTransformation>
</ClaimsTransformations>
</BuildingBlocks>
<ClaimsProviders>
<ClaimsProvider>
<DisplayName>Email Verification</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="EmailVerification">
<DisplayName>Initiate Email Address Verification For Local Account</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.localaccountsignup</Item>
<Item Key="language.button_continue">Continue</Item>
</Metadata>
<DisplayClaims>
<DisplayClaim DisplayControlReferenceId="emailVerificationControl" />
</DisplayClaims>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="Verified.Email" Required="true" />
</OutputClaims>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>Local Account</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="LocalAccountSignUpWithReadOnlyEmail">
<DisplayName>Email signup</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="IpAddressClaimReferenceId">IpAddress</Item>
<Item Key="ContentDefinitionReferenceId">api.localaccountsignup</Item>
<Item Key="language.button_continue">Create</Item>
<Item Key="EnforceEmailVerification">False</Item>
</Metadata>
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="CreateReadonlyEmailClaim" />
</InputClaimsTransformations>
<InputClaims>
<InputClaim ClaimTypeReferenceId="readOnlyEmail" />
<InputClaim ClaimTypeReferenceId="termsOfUseConsentChoice" DefaultValue="AgreeToTermsOfUseConsentNo" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="readOnlyEmail" Required="true" />
<OutputClaim ClaimTypeReferenceId="newPassword" Required="true" />
<OutputClaim ClaimTypeReferenceId="reenterPassword" Required="true" />
<OutputClaim ClaimTypeReferenceId="termsOfUseConsentChoice" Required="true" />
<OutputClaim ClaimTypeReferenceId="executed-SelfAsserted-Input" DefaultValue="true" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" />
<OutputClaim ClaimTypeReferenceId="newUser" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AAD-UserWriteUsingLogonEmail" />
</ValidationTechnicalProfiles>
<!-- Sample: Disable session management for sign-up page -->
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
<TechnicalProfile Id="ForgotPassword">
<DisplayName>Forgot your password?</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.ClaimsTransformationProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="isForgotPassword" DefaultValue="true" AlwaysUseDefaultValue="true" />
</OutputClaims>
</TechnicalProfile>
<TechnicalProfile Id="SelfAsserted-LocalAccountSignin-Email">
<Metadata>
<Item Key="setting.forgotPasswordLinkOverride">ForgotPasswordExchange</Item>
</Metadata>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
</ClaimsProviders>
<UserJourneys>
<UserJourney Id="SignUpOrSignIn_Custom">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin">
<ClaimsProviderSelections>
<ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninEmailExchange" />
<ClaimsProviderSelection TargetClaimsExchangeId="ForgotPasswordExchange" />
</ClaimsProviderSelections>
<ClaimsExchanges>
<ClaimsExchange Id="LocalAccountSigninEmailExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Email" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="SignUpWithLogonEmailExchange" TechnicalProfileReferenceId="EmailVerification" />
<ClaimsExchange Id="ForgotPasswordExchange" TechnicalProfileReferenceId="ForgotPassword" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="3" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>isForgotPassword</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="LocalAccountSignUpWithReadOnlyEmail" TechnicalProfileReferenceId="LocalAccountSignUpWithReadOnlyEmail" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="4" Type="InvokeSubJourney">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>isForgotPassword</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<JourneyList>
<Candidate SubJourneyReferenceId="PasswordReset" />
</JourneyList>
</OrchestrationStep>
<OrchestrationStep Order="5" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="6" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
</UserJourney>
</UserJourneys>
<SubJourneys>
<SubJourney Id="PasswordReset" Type="Call">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="PasswordResetUsingEmailAddressExchange" TechnicalProfileReferenceId="LocalAccountDiscoveryUsingEmailAddress" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="NewCredentials" TechnicalProfileReferenceId="LocalAccountWritePasswordUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
</OrchestrationSteps>
</SubJourney>
</SubJourneys>
<RelyingParty>
<DefaultUserJourney ReferenceId="SignUpOrSignIn_Custom" />
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="OpenIdConnect" />
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surname" />
<OutputClaim ClaimTypeReferenceId="email" />
<OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub" />
<OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="" />
<OutputClaim ClaimTypeReferenceId="tenantId" AlwaysUseDefaultValue="true" DefaultValue="{Policy:TenantObjectId}" />
</OutputClaims>
<SubjectNamingInfo ClaimType="sub" />
</TechnicalProfile>
</RelyingParty>
Everything seems working fine so far. I can enter my email address and it will send the code using via SendGrid, I can click verify code button and it successfully verified and has a continue button.
1. The DisplayControl
2. Send code to email address
3. Verify email address and continue
But after I click the continue button the page just crash and says, "The page cannot be displayed because an internal server error has occured."
4. Sign up page does not show up, it crashed
The only changes I made is the DisplayControl and it works fine without it (which in this case it uses the Microsoft's default email verification service) so I suppose there is something I did wrong...I would like to get some help if anyone has done anything similar with Azure AD B2C before.
Thanks a lot in advance for your help!
Solved in this github issue: https://github.com/azure-ad-b2c/samples/issues/193
If you are using the following samples Split Email Verification and Custom Email Verification.
The only changes required from these sample policies is that you change the Output Claim from
<TechnicalProfile Id="EmailVerification">
<DisplayName>Initiate Email Address Verification For Local Account</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.localaccountsignup</Item>
<Item Key="language.button_continue">Continue</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" />
</InputClaims>
<DisplayClaims>
<DisplayClaim DisplayControlReferenceId="emailVerificationControl" />
</DisplayClaims>
<OutputClaims>
<!----------- CHANGE THIS LINE ------------------------------->
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="Verified.Email" Required="true" />
</OutputClaims>
</TechnicalProfile>
<TechnicalProfile Id="EmailVerification">
<DisplayName>Initiate Email Address Verification For Local Account</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.localaccountsignup</Item>
<Item Key="language.button_continue">Continue</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" />
</InputClaims>
<DisplayClaims>
<DisplayClaim DisplayControlReferenceId="emailVerificationControl" />
</DisplayClaims>
<OutputClaims>
<!----------- TO THIS LINE ------------------------------->
<OutputClaim ClaimTypeReferenceId="email" Required="true" />
</OutputClaims>
</TechnicalProfile>
I also noticed this must be added to the following technical profiles: [EmailVerification, LocalAccountDiscoveryUsingEmailAddress]
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" />
</InputClaims>
The UserJourney should look like
EmailVerification
LocalAccountSignUpWithReadOnlyEmail
AAD-UserReadUsingObjectId
JwtIssuer
PS: It's also important to note that your ContentDefinitions must be version greater than 2.0.0.

Azure-AD-B2C Token format

We have built a service that calls cosmos DB for custom claims to be sent in ID token. The output of the REST API is as per the expected token format below
{"personalAttributes":{"guid":"1b92e96e28b14737acac11d23dcdd3d0","familyName":"ABC","givenName":"PQR","name":"PQR","preferredUserName":"PQR","upn":"PQR#XYZ.com",
"physicalLocation":"London, UK"
"roles":"Engineer","Engineer","Engineer",
"scopes":{"scopeName":"Application 2 - Pipe Spool install","initiative":"nan","operation":"nan","WBS":"nan"},
{"roleName":"Engineer","jobPosition":"Engineer","rolePermissions":"read, report, archive"},
{"roleName":"Engineer","jobPosition":"Engineer","rolePermissions":"read, report, archive"},
{"roleName":"Engineer","jobPosition":"Engineer","rolePermissions":"read, report, archive"}}
However, when B2C includes this in the ID Token it is appending / and . We tried formatting the output which is resulting in B2C not even generating the ID token. PLease advise how we can overcome this
TIA
It looks like you are trying to ask B2C to output a complex object as a claim value. This isn't supported so B2C casting the object to a string - resulting the escaped JSON data you are seeing.
Edit for additional clarity: The personalAttributes value returned from CosmosDB is not a StringCollection (array) as you have defined in the ClaimsSchema. It is a JSON object. B2C does not support object claim types - you'll need to extract the exact values you want from the object.
<TrustFrameworkPolicy xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.microsoft.com/online/cpim/schemas/2013/06" PolicySchemaVersion="0.3.0.0" TenantId="xxxx.onmicrosoft.com" PolicyId="B2C_1A_oboTrustFrameworkExtensions" PublicPolicyUri="http://xxxx.onmicrosoft.com/B2C_1A_oboTrustFrameworkExtensions" TenantObjectId="3dc9a24f-ed39-4635-b182-e35aadde9cb2">
<BasePolicy>
<TenantId>xxxx.onmicrosoft.com</TenantId>
<PolicyId>B2C_1A_oboTrustFrameworkBase</PolicyId>
</BasePolicy>
<BuildingBlocks>
<ClaimsSchema>
<ClaimType Id="clientId">
<DisplayName>OIDC client id</DisplayName>
<DataType>string</DataType>
</ClaimType>
<ClaimType Id="personalAttributes">
<DisplayName>Custom claim</DisplayName>
<DataType>stringCollection</DataType>
</ClaimType>
<ClaimType Id="roles">
<DisplayName>Custom claim</DisplayName>
<DataType>stringCollection</DataType>
</ClaimType>
<ClaimType Id="scopes">
<DisplayName>Custom claim</DisplayName>
<DataType>stringCollection</DataType>
</ClaimType>
<ClaimType Id="tokenOwner">
<DisplayName>aud claim in OBO token</DisplayName>
<DataType>string</DataType>
</ClaimType>
<ClaimType Id="jsonSourceClaim">
<DisplayName>JSON output from Custom Service</DisplayName>
<DataType>string</DataType>
</ClaimType>
<ClaimType Id="GetClaimsFromJson">
<DisplayName>JSON output from Custom Service</DisplayName>
<DataType>string</DataType>
</ClaimType>
</ClaimsSchema>
<ClaimsTransformations>
<ClaimsTransformation Id="AssertClientIdAndAudAreEqual" TransformationMethod="AssertStringClaimsAreEqual">
<InputClaims>
<InputClaim ClaimTypeReferenceId="client_id" TransformationClaimType="inputClaim1" />
<InputClaim ClaimTypeReferenceId="tokenOwner" TransformationClaimType="inputClaim2" />
</InputClaims>
<InputParameters>
<InputParameter Id="stringComparison" DataType="string" Value="ordinalIgnoreCase" />
</InputParameters>
</ClaimsTransformation>
<ClaimsTransformation Id="GetClaimsFromJsonTransformation" TransformationMethod="GetClaimsFromJsonArray">
<InputClaims>
<InputClaim ClaimTypeReferenceId="jsonSourceClaim" TransformationClaimType="jsonSource" />
</InputClaims>
<InputParameters>
<InputParameter Id="errorOnMissingClaims" DataType="boolean" Value="false" />
<InputParameter Id="includeEmptyClaims" DataType="boolean" Value="false" />
<InputParameter Id="jsonSourceKeyName" DataType="string" Value="key" />
<InputParameter Id="jsonSourceValueName" DataType="string" Value="value" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="personalAttributes" />
<OutputClaim ClaimTypeReferenceId="roles" />
<OutputClaim ClaimTypeReferenceId="scopes" />
</OutputClaims>
</ClaimsTransformation>
</ClaimsTransformations>
</BuildingBlocks>
<ClaimsProviders>
<!-- Used for custom extension attribute storage -->
<ClaimsProvider>
<DisplayName>Azure Active Directory</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="AAD-Common">
<Metadata>
<Item Key="ApplicationObjectId">xxxxxx</Item>
<Item Key="ClientId">xxxxxx</Item>
</Metadata>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<Domain>bechtel</Domain>
<DisplayName>Bechtel AAD</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="Bechtel-AAD">
<DisplayName>Work or school account</DisplayName>
<Description>Login with your work or school account</Description>
<Protocol Name="OpenIdConnect" />
<Metadata>
<Item Key="METADATA">https://login.microsoftonline.com/xxxx/v2.0/.well-known/openid-configuration</Item>
<!-- Update the Client ID below to the Application ID -->
<Item Key="client_id">xxxxxx</Item>
<Item Key="response_types">code</Item>
<Item Key="scope">openid profile</Item>
<Item Key="response_mode">form_post</Item>
<Item Key="HttpBinding">POST</Item>
<Item Key="UsePolicyInRedirectUri">false</Item>
<Item Key="DiscoverMetadataByTokenIssuer">true</Item>
<!-- The key below allows you to specify each of the Azure AD tenants that can be used to sign in. Update the GUIDs below for each tenant. -->
<Item Key="ValidTokenIssuerPrefixes">https://login.microsoftonline.com/22d635a3-3930-4779-a82d-155e2d13b75e</Item>
<!-- The commented key below specifies that users from any tenant can sign-in. Uncomment if you would like anyone with an Azure AD account to be able to sign in. -->
<!--<Item Key="ValidTokenIssuerPrefixes">https://login.microsoftonline.com/</Item> -->
</Metadata>
<CryptographicKeys>
<Key Id="client_secret" StorageReferenceId="B2C_1A_BECHTELSECRET" />
</CryptographicKeys>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="issuerUserId" PartnerClaimType="oid" />
<OutputClaim ClaimTypeReferenceId="tenantId" PartnerClaimType="tid" />
<OutputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="given_name" />
<OutputClaim ClaimTypeReferenceId="surName" PartnerClaimType="family_name" />
<OutputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="name" />
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="preferred_username" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="socialIdpAuthentication" AlwaysUseDefaultValue="true" />
<!--OutputClaim ClaimTypeReferenceId="identityProvider" PartnerClaimType="iss" /-->
<OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="bechtel" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="CreateRandomUPNUserName" />
<OutputClaimsTransformation ReferenceId="CreateUserPrincipalName" />
<OutputClaimsTransformation ReferenceId="CreateAlternativeSecurityId" />
<OutputClaimsTransformation ReferenceId="CreateSubjectClaimFromAlternativeSecurityId" />
</OutputClaimsTransformations>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-SocialLogin" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>Facebook</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="Facebook-OAUTH">
<Metadata>
<Item Key="client_id">facebook_clientid</Item>
<Item Key="scope">email public_profile</Item>
<Item Key="ClaimsEndpoint">https://graph.facebook.com/me?fields=id,first_name,last_name,name,email</Item>
</Metadata>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>Local Account SignIn</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="login-NonInteractive">
<Metadata>
<Item Key="client_id">db6c8fe9-368f-48a0-af63-19ad47d91e90</Item>
<Item Key="IdTokenAudience">5169b8e9-5bcd-4b04-b5f1-d3ca801bbbef</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="client_id" DefaultValue="db6c8fe9-368f-48a0-af63-19ad47d91e90" />
<InputClaim ClaimTypeReferenceId="resource_id" PartnerClaimType="resource" DefaultValue="5169b8e9-5bcd-4b04-b5f1-d3ca801bbbef" />
</InputClaims>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>REST APIs</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="REST-GetCustomClaim">
<DisplayName>Get a custom claim</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://beciamobo.azurewebsites.net/CustomClaims</Item> -->
<Item Key="ServiceUrl">https://xxxxx.azurewebsites.net/xxxxx</Item>
<Item Key="SendClaimsIn">QueryString</Item>
<!-- Set AuthenticationType to Basic or ClientCertificate in production environments -->
<Item Key="AuthenticationType">None</Item>
<!-- REMOVE the following line in production environments -->
<Item Key="AllowInsecureAuthInProduction">true</Item>
</Metadata>
<InputClaims>
<!-- Claims sent to your REST API -->
<InputClaim ClaimTypeReferenceId="email" />
<InputClaim ClaimTypeReferenceId="scope" DefaultValue="{OIDC:Scope}" AlwaysUseDefaultValue="true" />
<InputClaim ClaimTypeReferenceId="client_id" DefaultValue="{OIDC:ClientId}" AlwaysUseDefaultValue="true" />
</InputClaims>
<OutputClaims>
<!-- Claims parsed from your REST API -->
<OutputClaim ClaimTypeReferenceId="personalAttributes" />
<OutputClaim ClaimTypeReferenceId="roles" />
<OutputClaim ClaimTypeReferenceId="scopes" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="GetClaimsFromJsonTransformation" />
</OutputClaimsTransformations>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>Self Asserted</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="ValidateOBORequest">
<DisplayName>User ID signup</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>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<InputClaims>
<InputClaim ClaimTypeReferenceId="tokenOwner" />
<InputClaim ClaimTypeReferenceId="client_id" DefaultValue="{OIDC:ClientId}" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="tokenOwner" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="AssertClientIdAndAudAreEqual" />
</OutputClaimsTransformations>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
</ClaimsProviders>
<UserJourneys>
<UserJourney Id="SUSI">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin">
<ClaimsProviderSelections>
<ClaimsProviderSelection TargetClaimsExchangeId="AADExchange" />
<!--ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninEmailExchange" /-->
</ClaimsProviderSelections>
<!--ClaimsExchanges>
<ClaimsExchange Id="LocalAccountSigninEmailExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Email" />
</ClaimsExchanges-->
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<!--Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions-->
<ClaimsExchanges>
<ClaimsExchange Id="AADExchange" TechnicalProfileReferenceId="Bechtel-AAD" />
<!--ClaimsExchange Id="SignUpWithLogonEmailExchange" TechnicalProfileReferenceId="LocalAccountSignUpWithLogonEmail" /-->
</ClaimsExchanges>
</OrchestrationStep>
<!-- For social IDP authentication, attempt to find the user account in the directory. -->
<OrchestrationStep Order="3" Type="ClaimsExchange">
<!--Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>authenticationSource</Value>
<Value>localAccountAuthentication</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions-->
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadUsingAlternativeSecurityId" TechnicalProfileReferenceId="AAD-UserReadUsingAlternativeSecurityId-NoError" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- This step reads any user attributes that we may not have received when authenticating using ESTS so they can be sent
in the token. -->
<OrchestrationStep Order="4" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>authenticationSource</Value>
<Value>socialIdpAuthentication</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- The previous step (SelfAsserted-Social) could have been skipped if there were no attributes to collect
from the user. So, in that case, create the user in the directory if one does not already exist
(verified using objectId which would be set from the last step if account was created in the directory. -->
<OrchestrationStep Order="5" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AADUserWrite" TechnicalProfileReferenceId="AAD-UserWriteUsingAlternativeSecurityId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="6" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="RESTGetCustomClaim" TechnicalProfileReferenceId="REST-GetCustomClaim" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="7" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
</UserJourney>
<UserJourney Id="OBO">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="Validate" TechnicalProfileReferenceId="ValidateOBORequest" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="3" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="RESTGetCustomClaim" TechnicalProfileReferenceId="REST-GetCustomClaim" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="4" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>
</UserJourneys>
</TrustFrameworkPolicy>
Please see policy below
<TrustFrameworkPolicy xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.microsoft.com/online/cpim/schemas/2013/06" PolicySchemaVersion="0.3.0.0" TenantId="xxxx.onmicrosoft.com" PolicyId="B2C_1A_oboTrustFrameworkExtensions" PublicPolicyUri="http://xxxx.onmicrosoft.com/B2C_1A_oboTrustFrameworkExtensions" TenantObjectId="3dc9a24f-ed39-4635-b182-e35aadde9cb2">
<BasePolicy>
<TenantId>xxxx.onmicrosoft.com</TenantId>
<PolicyId>B2C_1A_oboTrustFrameworkBase</PolicyId>
</BasePolicy>
<BuildingBlocks>
<ClaimsSchema>
<ClaimType Id="clientId">
<DisplayName>OIDC client id</DisplayName>
<DataType>string</DataType>
</ClaimType>
<ClaimType Id="personalAttributes">
<DisplayName>Custom claim</DisplayName>
<DataType>stringCollection</DataType>
</ClaimType>
<ClaimType Id="roles">
<DisplayName>Custom claim</DisplayName>
<DataType>stringCollection</DataType>
</ClaimType>
<ClaimType Id="scopes">
<DisplayName>Custom claim</DisplayName>
<DataType>stringCollection</DataType>
</ClaimType>
<ClaimType Id="tokenOwner">
<DisplayName>aud claim in OBO token</DisplayName>
<DataType>string</DataType>
</ClaimType>
<ClaimType Id="jsonSourceClaim">
<DisplayName>JSON output from Custom Service</DisplayName>
<DataType>string</DataType>
</ClaimType>
<ClaimType Id="GetClaimsFromJson">
<DisplayName>JSON output from Custom Service</DisplayName>
<DataType>string</DataType>
</ClaimType>
</ClaimsSchema>
<ClaimsTransformations>
<ClaimsTransformation Id="AssertClientIdAndAudAreEqual" TransformationMethod="AssertStringClaimsAreEqual">
<InputClaims>
<InputClaim ClaimTypeReferenceId="client_id" TransformationClaimType="inputClaim1" />
<InputClaim ClaimTypeReferenceId="tokenOwner" TransformationClaimType="inputClaim2" />
</InputClaims>
<InputParameters>
<InputParameter Id="stringComparison" DataType="string" Value="ordinalIgnoreCase" />
</InputParameters>
</ClaimsTransformation>
<ClaimsTransformation Id="GetClaimsFromJsonTransformation" TransformationMethod="GetClaimsFromJsonArray">
<InputClaims>
<InputClaim ClaimTypeReferenceId="jsonSourceClaim" TransformationClaimType="jsonSource" />
</InputClaims>
<InputParameters>
<InputParameter Id="errorOnMissingClaims" DataType="boolean" Value="false" />
<InputParameter Id="includeEmptyClaims" DataType="boolean" Value="false" />
<InputParameter Id="jsonSourceKeyName" DataType="string" Value="key" />
<InputParameter Id="jsonSourceValueName" DataType="string" Value="value" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="personalAttributes" />
<OutputClaim ClaimTypeReferenceId="roles" />
<OutputClaim ClaimTypeReferenceId="scopes" />
</OutputClaims>
</ClaimsTransformation>
</ClaimsTransformations>
</BuildingBlocks>
<ClaimsProviders>
<!-- Used for custom extension attribute storage -->
<ClaimsProvider>
<DisplayName>Azure Active Directory</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="AAD-Common">
<Metadata>
<Item Key="ApplicationObjectId">xxxxxx</Item>
<Item Key="ClientId">xxxxxx</Item>
</Metadata>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<Domain>bechtel</Domain>
<DisplayName>Bechtel AAD</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="Bechtel-AAD">
<DisplayName>Work or school account</DisplayName>
<Description>Login with your work or school account</Description>
<Protocol Name="OpenIdConnect" />
<Metadata>
<Item Key="METADATA">https://login.microsoftonline.com/xxxx/v2.0/.well-known/openid-configuration</Item>
<!-- Update the Client ID below to the Application ID -->
<Item Key="client_id">xxxxxx</Item>
<Item Key="response_types">code</Item>
<Item Key="scope">openid profile</Item>
<Item Key="response_mode">form_post</Item>
<Item Key="HttpBinding">POST</Item>
<Item Key="UsePolicyInRedirectUri">false</Item>
<Item Key="DiscoverMetadataByTokenIssuer">true</Item>
<!-- The key below allows you to specify each of the Azure AD tenants that can be used to sign in. Update the GUIDs below for each tenant. -->
<Item Key="ValidTokenIssuerPrefixes">https://login.microsoftonline.com/22d635a3-3930-4779-a82d-155e2d13b75e</Item>
<!-- The commented key below specifies that users from any tenant can sign-in. Uncomment if you would like anyone with an Azure AD account to be able to sign in. -->
<!--<Item Key="ValidTokenIssuerPrefixes">https://login.microsoftonline.com/</Item> -->
</Metadata>
<CryptographicKeys>
<Key Id="client_secret" StorageReferenceId="B2C_1A_BECHTELSECRET" />
</CryptographicKeys>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="issuerUserId" PartnerClaimType="oid" />
<OutputClaim ClaimTypeReferenceId="tenantId" PartnerClaimType="tid" />
<OutputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="given_name" />
<OutputClaim ClaimTypeReferenceId="surName" PartnerClaimType="family_name" />
<OutputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="name" />
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="preferred_username" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="socialIdpAuthentication" AlwaysUseDefaultValue="true" />
<!--OutputClaim ClaimTypeReferenceId="identityProvider" PartnerClaimType="iss" /-->
<OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="bechtel" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="CreateRandomUPNUserName" />
<OutputClaimsTransformation ReferenceId="CreateUserPrincipalName" />
<OutputClaimsTransformation ReferenceId="CreateAlternativeSecurityId" />
<OutputClaimsTransformation ReferenceId="CreateSubjectClaimFromAlternativeSecurityId" />
</OutputClaimsTransformations>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-SocialLogin" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>Facebook</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="Facebook-OAUTH">
<Metadata>
<Item Key="client_id">facebook_clientid</Item>
<Item Key="scope">email public_profile</Item>
<Item Key="ClaimsEndpoint">https://graph.facebook.com/me?fields=id,first_name,last_name,name,email</Item>
</Metadata>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>Local Account SignIn</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="login-NonInteractive">
<Metadata>
<Item Key="client_id">db6c8fe9-368f-48a0-af63-19ad47d91e90</Item>
<Item Key="IdTokenAudience">5169b8e9-5bcd-4b04-b5f1-d3ca801bbbef</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="client_id" DefaultValue="db6c8fe9-368f-48a0-af63-19ad47d91e90" />
<InputClaim ClaimTypeReferenceId="resource_id" PartnerClaimType="resource" DefaultValue="5169b8e9-5bcd-4b04-b5f1-d3ca801bbbef" />
</InputClaims>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>REST APIs</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="REST-GetCustomClaim">
<DisplayName>Get a custom claim</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://beciamobo.azurewebsites.net/CustomClaims</Item> -->
<Item Key="ServiceUrl">https://xxxxx.azurewebsites.net/xxxxx</Item>
<Item Key="SendClaimsIn">QueryString</Item>
<!-- Set AuthenticationType to Basic or ClientCertificate in production environments -->
<Item Key="AuthenticationType">None</Item>
<!-- REMOVE the following line in production environments -->
<Item Key="AllowInsecureAuthInProduction">true</Item>
</Metadata>
<InputClaims>
<!-- Claims sent to your REST API -->
<InputClaim ClaimTypeReferenceId="email" />
<InputClaim ClaimTypeReferenceId="scope" DefaultValue="{OIDC:Scope}" AlwaysUseDefaultValue="true" />
<InputClaim ClaimTypeReferenceId="client_id" DefaultValue="{OIDC:ClientId}" AlwaysUseDefaultValue="true" />
</InputClaims>
<OutputClaims>
<!-- Claims parsed from your REST API -->
<OutputClaim ClaimTypeReferenceId="personalAttributes" />
<OutputClaim ClaimTypeReferenceId="roles" />
<OutputClaim ClaimTypeReferenceId="scopes" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="GetClaimsFromJsonTransformation" />
</OutputClaimsTransformations>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>Self Asserted</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="ValidateOBORequest">
<DisplayName>User ID signup</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>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<InputClaims>
<InputClaim ClaimTypeReferenceId="tokenOwner" />
<InputClaim ClaimTypeReferenceId="client_id" DefaultValue="{OIDC:ClientId}" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="tokenOwner" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="AssertClientIdAndAudAreEqual" />
</OutputClaimsTransformations>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
</ClaimsProviders>
<UserJourneys>
<UserJourney Id="SUSI">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin">
<ClaimsProviderSelections>
<ClaimsProviderSelection TargetClaimsExchangeId="AADExchange" />
<!--ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninEmailExchange" /-->
</ClaimsProviderSelections>
<!--ClaimsExchanges>
<ClaimsExchange Id="LocalAccountSigninEmailExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Email" />
</ClaimsExchanges-->
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<!--Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions-->
<ClaimsExchanges>
<ClaimsExchange Id="AADExchange" TechnicalProfileReferenceId="Bechtel-AAD" />
<!--ClaimsExchange Id="SignUpWithLogonEmailExchange" TechnicalProfileReferenceId="LocalAccountSignUpWithLogonEmail" /-->
</ClaimsExchanges>
</OrchestrationStep>
<!-- For social IDP authentication, attempt to find the user account in the directory. -->
<OrchestrationStep Order="3" Type="ClaimsExchange">
<!--Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>authenticationSource</Value>
<Value>localAccountAuthentication</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions-->
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadUsingAlternativeSecurityId" TechnicalProfileReferenceId="AAD-UserReadUsingAlternativeSecurityId-NoError" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- This step reads any user attributes that we may not have received when authenticating using ESTS so they can be sent
in the token. -->
<OrchestrationStep Order="4" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>authenticationSource</Value>
<Value>socialIdpAuthentication</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- The previous step (SelfAsserted-Social) could have been skipped if there were no attributes to collect
from the user. So, in that case, create the user in the directory if one does not already exist
(verified using objectId which would be set from the last step if account was created in the directory. -->
<OrchestrationStep Order="5" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AADUserWrite" TechnicalProfileReferenceId="AAD-UserWriteUsingAlternativeSecurityId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="6" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="RESTGetCustomClaim" TechnicalProfileReferenceId="REST-GetCustomClaim" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="7" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
</UserJourney>
<UserJourney Id="OBO">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="Validate" TechnicalProfileReferenceId="ValidateOBORequest" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="3" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="RESTGetCustomClaim" TechnicalProfileReferenceId="REST-GetCustomClaim" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="4" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>
</UserJourneys>
</TrustFrameworkPolicy>
You need to apply a GetClaimFromJson transformation to extract claims from JSON.

Set value of input claim based on other input claim that uses claimresolvers in Azure B2C Custom policies

I have the below two input claims
<InputClaim ClaimTypeReferenceId="isAccessFlow" DefaultValue="{OAUTH-KV:access_flow}" AlwaysUseDefaultValue="true"/>
<InputClaim ClaimTypeReferenceId="prompt" DefaultValue="select_account"/>
I want to assign the value for 'prompt' input claim as 'select_account' only when the isAccessFlow value is not true.
I am unable to figure out how to write input claim transformations in this case to achieve the intended requirement.
Claim Types
<ClaimType Id="isAccessFlow">
<DisplayName>Access Flow</DisplayName>
<DataType>string</DataType>
</ClaimType>
<ClaimType Id="prompt">
<DisplayName>prompt</DisplayName>
<DataType>string</DataType>
</ClaimType>
Technical Profiles
<TechnicalProfile Id="ParseAccessFlow">
<DisplayName>Access Flow</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.ClaimsTransformationProtocolProvider,Web.TPEngine, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="IncludeClaimResolvingInClaimsHandling">true</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="isAccessFlow" DefaultValue="{OAUTH-KV:data_flow}" AlwaysUseDefaultValue="true"/>
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="isAccessFlow" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="AssignedAccessFlowValue" />
<OutputClaimsTransformation ReferenceId="ChangeToLower" />
</OutputClaimsTransformations>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
<TechnicalProfile Id="PromptFlow">
<DisplayName>Prompt</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.ClaimsTransformationProtocolProvider,Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="AssignedDefaultValue" />
</InputClaimsTransformations>
<InputClaims>
<InputClaim ClaimTypeReferenceId="prompt" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="prompt"/>
</OutputClaims>
</TechnicalProfile>
ClaimsTransformations
<ClaimsTransformation Id="AssignedAccessFlowValue" TransformationMethod="FormatStringClaim">
<InputClaims>
<InputClaim ClaimTypeReferenceId="isAccessFlow" TransformationClaimType="inputClaim" />
</InputClaims>
<InputParameters>
<InputParameter Id="stringFormat" DataType="string" Value="{0}" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="isAccessFlow" TransformationClaimType="outputClaim" />
</OutputClaims>
</ClaimsTransformation>
<ClaimsTransformation Id="ChangeToLower" TransformationMethod="ChangeCase">
<InputClaims>
<InputClaim ClaimTypeReferenceId="isAccessFlow" TransformationClaimType="inputClaim1" />
</InputClaims>
<InputParameters>
<InputParameter Id="toCase" DataType="string" Value="LOWER" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="isAccessFlow" TransformationClaimType="outputClaim" />
</OutputClaims>
</ClaimsTransformation>
<ClaimsTransformation Id="AssignedDefaultValue" TransformationMethod="CreateStringClaim">
<InputParameters>
<InputParameter Id="value" DataType="string" Value="select_account" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="prompt" TransformationClaimType="createdClaim" />
</OutputClaims>
</ClaimsTransformation>
User Journey
<UserJourney Id="PromptCheck">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AccessFlow" TechnicalProfileReferenceId="ParseAccessFlow" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>isAccessFlow</Value>
<Value>true</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="Assigned" TechnicalProfileReferenceId="PromptFlow" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="3" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>

aquireTokenSilent in msal Angular failing with Azure Custom Polices

We have custom policies for Azure B2C with MFA enabled.
After successful login, when trying to acquire access token using acquireTokenSilent(), getting the below errors
Refused to display 'https://accounts.google.com/signin/oauth?client_id=xxx&redirect_uri=xxxxx/oauth2/authresp&response_type=code&scope=email+profile&login_hintXXXXXXXXXXX' in a frame because it set 'X-Frame-Options' to 'deny'.
Followed by ClientAuthError: Token renewal operation failed due to timeout.
or
AADB2C90077: User does not have an existing session and request prompt parameter has a value of 'None'
I am getting these errors after every login. If I try to call acquireTokenPopup(), the popup is showing the MFA flow again and asking to authenticate the phone number which is not desired.
I have read many posts regarding these but unable to fix the issue.
Google technical profile:
<TechnicalProfile Id="Google-OAUTH">
<DisplayName>Google</DisplayName>
<Protocol Name="OAuth2" />
<Metadata>
<Item Key="ProviderName">google</Item>
<Item Key="authorization_endpoint">https://accounts.google.com/o/oauth2/auth</Item>
<Item Key="AccessTokenEndpoint">https://accounts.google.com/o/oauth2/token</Item>
<Item Key="ClaimsEndpoint">https://www.googleapis.com/oauth2/v1/userinfo</Item>
<Item Key="scope">email profile</Item>
<Item Key="HttpBinding">POST</Item>
<Item Key="UsePolicyInRedirectUri">0</Item>
<Item Key="IncludeClaimResolvingInClaimsHandling">true</Item>
<Item Key="client_id">XXXXXXXXXXXXXXXX</Item>
</Metadata>
<CryptographicKeys>
<Key Id="client_secret" StorageReferenceId="B2C_1A_GoogleSecret" />
</CryptographicKeys>
<InputClaims>
<InputClaim ClaimTypeReferenceId="loginHint" PartnerClaimType="login_hint" DefaultValue="{OIDC:LoginHint}" />
<InputClaim ClaimTypeReferenceId="prompt" PartnerClaimType="prompt" DefaultValue="{OAUTH-KV:account_prompt}" AlwaysUseDefaultValue="true"/>
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="issuerUserId" PartnerClaimType="id" />
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="email" />
<OutputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="name" />
<OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="google.com" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="socialIdpAuthentication" />
<OutputClaim ClaimTypeReferenceId="requiresMFA" DefaultValue="true"/>
<OutputClaim ClaimTypeReferenceId="isAccessFlow" DefaultValue="{OAUTH-KV:access_flow}" AlwaysUseDefaultValue="true"/>
<OutputClaim ClaimTypeReferenceId="prompt" DefaultValue="{OAUTH-KV:account_prompt}" AlwaysUseDefaultValue="true"/>
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="CreateRandomUPNUserName" />
<OutputClaimsTransformation ReferenceId="CreateUserPrincipalName" />
<OutputClaimsTransformation ReferenceId="CreateAlternativeSecurityId" />
<OutputClaimsTransformation ReferenceId="CreateSubjectClaimFromAlternativeSecurityId" />
</OutputClaimsTransformations>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-SocialLogin" />
</TechnicalProfile>
Phone Factor Technical profile:
<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.phonefactor1.1</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" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="Verified.strongAuthenticationPhoneNumber" PartnerClaimType="Verified.OfficePhone" />
<OutputClaim ClaimTypeReferenceId="newPhoneNumberEntered" PartnerClaimType="newPhoneNumberEntered" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-MFA" />
</TechnicalProfile>
MFA related orchestration steps:
<OrchestrationStep Order="7" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>isActiveMFASession</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>requiresMFA</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>isAccessFlow</Value>
<Value>true</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="PhoneFactor-Verify" TechnicalProfileReferenceId="PhoneFactor-InputOrVerify" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="8" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>newPhoneNumberEntered</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AADUserWriteWithObjectId" TechnicalProfileReferenceId="AAD-UserWritePhoneNumberUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
requiresMFA and isAccesssFow are two custom attributes i am using.
requiresMFA to stop MFA for few providers
isAccessFlow is to avoid MFA in acquire token popup.I am passing this in request object from frontend while calling acquireTokenPopup when silent calls are failing with any of the above errors.
Please let me who how i can avoid getting those errors. These attributes i used as a temporary workaround.

Custom B2C Policy for Username based Local Accounts

What do I have to change in the custom policy starter pack to make my sign-in/sing-up's policy username based not email based?
Add this technical profile to TrustFrameworkExtensions.xml to write the user to Azure AD
<TechnicalProfile Id="AAD-UserWriteUsingLogonName">
<Metadata>
<Item Key="Operation">Write</Item>
<Item Key="RaiseErrorIfClaimsPrincipalAlreadyExists">true</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInName" PartnerClaimType="signInNames.userName" Required="true" />
</InputClaims>
<PersistedClaims>
<PersistedClaim ClaimTypeReferenceId="signInName" PartnerClaimType="signInNames.userName" />
<PersistedClaim ClaimTypeReferenceId="email" PartnerClaimType="strongAuthenticationEmailAddress" />
<PersistedClaim ClaimTypeReferenceId="newPassword" PartnerClaimType="password" />
<PersistedClaim ClaimTypeReferenceId="displayName" DefaultValue="SomeDefaultDisplayNameValue" />
</PersistedClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="newUser" PartnerClaimType="newClaimsPrincipalCreated" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="localAccountAuthentication" />
<OutputClaim ClaimTypeReferenceId="userPrincipalName" />
</OutputClaims>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>
Add this technical profile to TrustFrameworkExtensions.xml that is called by the user journey. The critical change here is the <Item Key="LocalAccountType">Username</Item> and <Item Key="LocalAccountProfile">true</Item>
<TechnicalProfile Id="LocalAccountSignUpWithLogonName">
<DisplayName>User ID signup</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="IpAddressClaimReferenceId">IpAddress</Item>
<Item Key="ContentDefinitionReferenceId">api.localaccountsignup</Item>
<Item Key="LocalAccountType">Username</Item>
<Item Key="LocalAccountProfile">true</Item>
<Item Key="language.button_continue">Create</Item>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInName" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" Required="true" />
<OutputClaim ClaimTypeReferenceId="signInName" Required="true" />
<OutputClaim ClaimTypeReferenceId="newPassword" Required="true" />
<OutputClaim ClaimTypeReferenceId="reenterPassword" Required="true" />
<OutputClaim ClaimTypeReferenceId="email" Required="true" />
<OutputClaim ClaimTypeReferenceId="executed-SelfAsserted-Input" DefaultValue="true" />
<OutputClaim ClaimTypeReferenceId="newUser" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" />
<OutputClaim ClaimTypeReferenceId="userPrincipalName" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AAD-UserWriteUsingLogonName" />
</ValidationTechnicalProfiles>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>
Sigin Technical Profile for Reference
<TechnicalProfile Id="SelfAsserted-LocalAccountSignin-Username">
<DisplayName>Local Account Signin</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="SignUpTarget">SignUpWithLogonUsernameExchange</Item>
<Item Key="setting.operatingMode">Username</Item>
<Item Key="ContentDefinitionReferenceId">api.selfasserted</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInName" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="signInName" Required="true" />
<OutputClaim ClaimTypeReferenceId="password" Required="true" />
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="login-NonInteractive" />
</ValidationTechnicalProfiles>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>
Add this user journey to TrustFrameworkExtensions.xml
<UserJourney Id="SignUpOrSignIn">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin">
<ClaimsProviderSelections>
<ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninUsernameExchange" />
</ClaimsProviderSelections>
<ClaimsExchanges>
<ClaimsExchange Id="LocalAccountSigninUsernameExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Username" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="SignUpWithLogonUsernameExchange" TechnicalProfileReferenceId="LocalAccountSignUpWithLogonName" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- This step reads any user attributes that we may not have received when in the token. -->
<OrchestrationStep Order="3" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="4" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>
thanks to Omer for his help!

Resources