I created an Azure AD B2C tenant with custom policies last year. Now I am trying to upload the same policies (with IDs changed as necessary) to a new tenant that we have just created and I get the following error when uploading the reset-password policy:
Validation failed: 1 validation error(s) found in policy "B2C_1A_PASSWORDRESET" of tenant "xxx.onmicrosoft.com".Persisted claims for technical profile "AAD-FlipMigratedFlag" in policy "B2C_1A_PasswordReset" of tenant "xxx.onmicrosoft.com" must have one of the following claims: userPrincipalName
These policies implement the Seamless Migration approach to user migration, based on samples in the following repositories:
https://github.com/Azure-Samples/active-directory-b2c-custom-policy-starterpack
https://github.com/azure-ad-b2c/samples
https://github.com/azure-ad-b2c/user-migration
As suggested by the error message, I have tried adding userPrincipalName to the PersistedClaims for the AAD-FlipMigratedFlag technical profile, but I get the same error when uploading the policy.
I have also tried re-uploading the existing, working reset-password policy to the existing, working tenant, and I get the same error. Note that in this case I am re-uploading the exact same policy that has already been successfully uploaded and has been working for a year.
So the question is: what has changed and what do I need to do to fix this error?
Here are the relevant parts of my custom policy files. If there are any other parts you need to see, just let me know and I'll add them.
PasswordReset.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_PasswordReset"
PublicPolicyUri="http://xxx.onmicrosoft.com/B2C_1A_PasswordReset">
<BasePolicy>
<TenantId>xxx.onmicrosoft.com</TenantId>
<PolicyId>B2C_1A_TrustFrameworkExtensions</PolicyId>
</BasePolicy>
<RelyingParty>
<DefaultUserJourney ReferenceId="PasswordReset" />
<UserJourneyBehaviors>
<ScriptExecution>Allow</ScriptExecution>
</UserJourneyBehaviors>
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="OpenIdConnect" />
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub"/>
<OutputClaim ClaimTypeReferenceId="tenantId" AlwaysUseDefaultValue="true" DefaultValue="{Policy:TenantObjectId}" />
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="emails" />
</OutputClaims>
<SubjectNamingInfo ClaimType="sub" />
</TechnicalProfile>
</RelyingParty>
</TrustFrameworkPolicy>
TrustFrameworkExtensions.xml
<?xml version="1.0" encoding="utf-8" ?>
<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_TrustFrameworkExtensions"
PublicPolicyUri="http://xxx.onmicrosoft.com/B2C_1A_TrustFrameworkExtensions">
<BasePolicy>
<TenantId>xxx.onmicrosoft.com</TenantId>
<PolicyId>B2C_1A_TrustFrameworkBase</PolicyId>
</BasePolicy>
<BuildingBlocks>
<ClaimsSchema>
<!-- Holds the value of the migration status on the Azure AD B2C account -->
<ClaimType Id="extension_IsMigrationRequired">
<DisplayName>extension_IsMigrationRequired</DisplayName>
<DataType>boolean</DataType>
<AdminHelpText>extension_IsMigrationRequired</AdminHelpText>
<UserHelpText>extension_IsMigrationRequired</UserHelpText>
</ClaimType>
<!-- Holds the value of whether the authentication succeeded at the legacy IdP -->
<ClaimType Id="tokenSuccess">
<DisplayName>tokenSuccess</DisplayName>
<DataType>boolean</DataType>
<AdminHelpText>tokenSuccess</AdminHelpText>
<UserHelpText>tokenSuccess</UserHelpText>
</ClaimType>
<!-- Holds the value 'false' when the legacy IdP authentication succeeded -->
<ClaimType Id="migrationRequired">
<DisplayName>migrationRequired</DisplayName>
<DataType>boolean</DataType>
<AdminHelpText>migrationRequired</AdminHelpText>
<UserHelpText>migrationRequired</UserHelpText>
</ClaimType>
</ClaimsSchema>
</BuildingBlocks>
<ClaimsProviders>
<ClaimsProvider>
<DisplayName>Local Account Password Reset - Write Password</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="LocalAccountWritePasswordUsingObjectId">
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="Get-requiresMigration-status-password-reset" ContinueOnError="false" />
<ValidationTechnicalProfile ReferenceId="AAD-FlipMigratedFlag" ContinueOnError="false">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>extension_IsMigrationRequired</Value>
<Value>False</Value>
<Action>SkipThisValidationTechnicalProfile</Action>
</Precondition>
</Preconditions>
</ValidationTechnicalProfile>
<ValidationTechnicalProfile ReferenceId="AAD-UserWritePasswordUsingObjectId" />
</ValidationTechnicalProfiles>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>Local Account Password Reset - Read migration flag</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="Get-requiresMigration-status-password-reset">
<Metadata>
<Item Key="Operation">Read</Item>
<Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">true</Item>
<Item Key="UserMessageIfClaimsPrincipalDoesNotExist">An account could not be found for the provided user ID.</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="objectId" Required="true" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="extension_IsMigrationRequired" DefaultValue="false" />
</OutputClaims>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>Local Account Password Reset - Flip migration flag</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="AAD-FlipMigratedFlag">
<Metadata>
<Item Key="Operation">Write</Item>
<Item Key="RaiseErrorIfClaimsPrincipalAlreadyExists">false</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="objectId" Required="true" />
</InputClaims>
<PersistedClaims>
<PersistedClaim ClaimTypeReferenceId="objectId" />
<PersistedClaim ClaimTypeReferenceId="migrationRequired" PartnerClaimType="extension_IsMigrationRequired" DefaultValue="false" AlwaysUseDefaultValue="true"/>
<!-- NOTE: I added this but still get the error -->
<PersistedClaim ClaimTypeReferenceId="userPrincipalName" />
</PersistedClaims>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>Azure Active Directory</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="AAD-Common">
<Metadata>
<!--Insert b2c-extensions-app application ID here, for example: 11111111-1111-1111-1111-111111111111-->
<Item Key="ClientId">xxx</Item>
<!--Insert b2c-extensions-app application ObjectId here, for example: 22222222-2222-2222-2222-222222222222-->
<Item Key="ApplicationObjectId">xxx</Item>
</Metadata>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
</ClaimsProviders>
</TrustFrameworkPolicy>
TrustFrameworkBase.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_TrustFrameworkBase"
PublicPolicyUri="http://xxx.onmicrosoft.com/B2C_1A_TrustFrameworkBase">
<ClaimsProviders>
<ClaimsProvider>
<TechnicalProfiles>
<TechnicalProfile Id="LocalAccountDiscoveryUsingEmailAddress">
<DisplayName>Reset password using email address</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.localaccountpasswordreset</Item>
<Item Key="UserMessageIfClaimsTransformationBooleanValueIsNotEqual">Your account has been locked. Contact your support person to unlock it, then try again.</Item>
<Item Key="IncludeClaimResolvingInClaimsHandling">true</Item>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<IncludeInSso>false</IncludeInSso>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="Verified.Email" Required="true" />
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="userPrincipalName" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AAD-UserReadUsingEmailAddress" />
</ValidationTechnicalProfiles>
</TechnicalProfile>
<TechnicalProfile Id="AAD-UserWritePasswordUsingObjectId">
<Metadata>
<Item Key="Operation">Write</Item>
<Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">true</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="objectId" Required="true" />
</InputClaims>
<PersistedClaims>
<PersistedClaim ClaimTypeReferenceId="objectId" />
<PersistedClaim ClaimTypeReferenceId="newPassword" PartnerClaimType="password"/>
</PersistedClaims>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
</TechnicalProfile>
<TechnicalProfile Id="AAD-Common">
<DisplayName>Azure Active Directory</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.AzureActiveDirectoryProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<!-- We need this here to suppress the SelfAsserted provider from invoking SSO on validation profiles. -->
<IncludeInSso>false</IncludeInSso>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfiles>
</ClaimsProvider>
</ClaimsProviders>
<UserJourneys>
<UserJourney Id="PasswordReset">
<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>
<OrchestrationStep Order="3" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>
</UserJourneys>
</TrustFrameworkPolicy>
I was hoping someone from MS would chime in on this. Since that hasn't happened, I'll go ahead and post my solution.
MS has a bad habit of creating schema where the order of the nodes matter. That was part of the problem here. In addition, the displayName claim was also added to the required list.
So after some trial-and-error, this version of the AAD-FlipMigratedFlag claims provider ended up being the solution:
<ClaimsProvider>
<DisplayName>Local Account Password Reset - Flip migration flag</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="AAD-FlipMigratedFlag">
<Metadata>
<Item Key="Operation">Write</Item>
<Item Key="RaiseErrorIfClaimsPrincipalAlreadyExists">false</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="objectId" Required="true" />
</InputClaims>
<PersistedClaims>
<PersistedClaim ClaimTypeReferenceId="objectId" />
<PersistedClaim ClaimTypeReferenceId="displayName" />
<PersistedClaim ClaimTypeReferenceId="userPrincipalName" />
<PersistedClaim ClaimTypeReferenceId="migrationRequired" PartnerClaimType="extension_IsMigrationRequired" DefaultValue="false" AlwaysUseDefaultValue="true"/>
</PersistedClaims>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
Have yout tried to add the userPrincipalName claim to the OutputClaims section of PasswordReset.xml?
Related
Application 1 creates a url with id_token that contains all the necessary info to validate a claim (as of now simply validate the user with email & signed claim).
a. Create a self-signed certificate (New-SelfSignedCertificate )
b. For the same, used tokenbuilder to create a URL as :
https://tenant.b2clogin.com/tenant.onmicrosoft.com/oauth2/v2.0/authorize?p=B2C_1A_SIGNIN_WITH_EMAIL&client_id=9994db7f-2dc7-4bac-bf8a-93bf04067890&redirect_uri=https://myredirecturl.com/&nonce=80c307115..&scope=openid&response_type=id_token&id_token_hint=eyJhbGciOiJSUzI1NiIsImtpZCI6IlZWWWwwbUE0YlFYOXg0LVZVTGpfb0VUWHd0VSIsIng1dCI6I......nNpb25fYWdlbmN5
c. Hosted the application with self-signed certificate (Example :
https://b2cidtokenbuilder.azurewebsites.net/)
d. In policy blade created a token with the self-signed certificate (following steps in here)
e. In TrustFrameworkBase updated issuer_secret for JwtIssuer Technical Profile
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_AABIDToken" />
<Key Id="issuer_refresh_token_key" StorageReferenceId="B2C_1A_TokenEncryptionKeyContainer" />
</CryptographicKeys>
f. In B2C_1A_SIGNIN_WITH_EMAIL policy added the
<Metadata
<Item Key="METADATA">https://tenant.b2clogin.com/tenant.onmicrosoft.com/v2.0/.well-known/openid-configuration?p=B2C_1A_SIGNUP_SIGNIN</Item>
<Item Key="issuer">https://b2cidtokenbuilder.azurewebsites.net/</Item>
</Metadata>
g. Once I click the AD B2C url I am expecting that the policy B2C_1A_SIGNIN_WITH_EMAIL will allow me to login to the application with an access_token.
(Based on info here I should be able to login to my application once the accesstoken has been created by my policy.
But in my current application what I see is not access_token is getting created but an id_token is created with all the info and since there is no access-token my application is asking for AD B2C login (prompt).
https://myapplication.com/#id_token=eyJ0eXAiOiJKV1QiLCJhbGciOiGVla2FiMmNkZXYuYjJjbG9naW4uY29tLzM4NGIzNTdiLTIzMWUtNGY3MS1iYThhLWUxNzBlZDFiMzA1ZC92Mi4....................kLTE5NDYtNGRkYy04YWViLWY5ZDFiMWVmNWMyMSIsImF1Z
Should I be setting some sessionmanagement TP?
My policy is
<?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="tenant.onmicrosoft.com"
PolicyId="B2C_1A_signin_with_email"
PublicPolicyUri="http://tenant.onmicrosoft.com/B2C_1A_signin_with_email">
<BasePolicy>
<TenantId>tenant.onmicrosoft.com</TenantId>
<PolicyId>B2C_1A_TrustFrameworkExtensions</PolicyId>
</BasePolicy>
<BuildingBlocks>
<ClaimsSchema>
<!--Sample: Stores the error message for unsolicited request (a request without id_token_hint) and user not found-->
<ClaimType Id="errorMessage">
<DisplayName>Error</DisplayName>
<DataType>string</DataType>
<UserHelpText>Add help text here</UserHelpText>
<UserInputType>Paragraph</UserInputType>
</ClaimType>
</ClaimsSchema>
<ClaimsTransformations>
<!--Sample: Initiates the errorMessage claims type with the error message-->
<ClaimsTransformation Id="CreateUnsolicitedErrorMessage" TransformationMethod="CreateStringClaim">
<InputParameters>
<InputParameter Id="value" DataType="string" Value="You cannot sign-in without invitation. Please click the link in the email" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="errorMessage" TransformationClaimType="createdClaim" />
</OutputClaims>
</ClaimsTransformation>
<!--Sample: Initiates the errorMessage claims type with the error message user not found-->
<ClaimsTransformation Id="CreateUserNotFoundErrorMessage" TransformationMethod="CreateStringClaim">
<InputParameters>
<InputParameter Id="value" DataType="string" Value="You aren't registered in the system! Please contact the Help Desk on 0800 xxx yyy and ask to be added to the system" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="errorMessage" TransformationClaimType="createdClaim" />
</OutputClaims>
</ClaimsTransformation>
</ClaimsTransformations>
</BuildingBlocks>
<ClaimsProviders>
<!--Sample: This technical profile specifies how B2C should validate your token, and what claims you want B2C to extract from the token.
The METADATA value in the TechnicalProfile meta-data is required.
The “IdTokenAudience” and “issuer” arguments are optional (see later section)-->
<ClaimsProvider>
<DisplayName>My ID Token Hint ClaimsProvider</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="IdTokenHint_ExtractClaims">
<DisplayName> My ID Token Hint TechnicalProfile</DisplayName>
<Protocol Name="None" />
<Metadata>
<!--Sample action required: replace with your endpoint location -->
<Item Key="METADATA">https://teanant.b2clogin.com/tenant.onmicrosoft.com/v2.0/.well-known/openid-configuration?p=B2C_1A_SIGNUP_SIGNIN</Item>
<Item Key="issuer">https://b2cidtokenbuilder.azurewebsites.net/</Item>
</Metadata>
<OutputClaims>
<!--Sample: Read the email cliam from the id_token_hint-->
<OutputClaim ClaimTypeReferenceId="email" />
</OutputClaims>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>Self Asserted</DisplayName>
<TechnicalProfiles>
<!-- Demo: Show error message-->
<TechnicalProfile Id="SelfAsserted-Error">
<DisplayName>Unsolicited error message</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>
<!-- Sample: Remove the continue button-->
<Item Key="setting.showContinueButton">false</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="errorMessage"/>
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="errorMessage"/>
</OutputClaims>
</TechnicalProfile>
<!-- Demo: Show unsolicited error message-->
<TechnicalProfile Id="SelfAsserted-Unsolicited">
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="CreateUnsolicitedErrorMessage" />
</InputClaimsTransformations>
<IncludeTechnicalProfile ReferenceId="SelfAsserted-Error" />
</TechnicalProfile>
<!-- Demo: Show user not found error message-->
<TechnicalProfile Id="SelfAsserted-UserNotFound">
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="CreateUserNotFoundErrorMessage" />
</InputClaimsTransformations>
<IncludeTechnicalProfile ReferenceId="SelfAsserted-Error" />
</TechnicalProfile>
<TechnicalProfile Id="AAD-UserReadUsingEmailAddress-Hint">
<Metadata>
<Item Key="Operation">Read</Item>
<Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">false</Item>
<Item Key="UserMessageIfClaimsPrincipalDoesNotExist">An account could not be found for the provided user ID.</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" PartnerClaimType="signInNames.emailAddress" Required="true" />
</InputClaims>
<OutputClaims>
<!-- Required claims -->
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="localAccountAuthentication" />
<!-- Sample: Change the output email claims-->
<!--<OutputClaim ClaimTypeReferenceId="email" />-->
<OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" />
<OutputClaim ClaimTypeReferenceId="extension_userId" />
<OutputClaim ClaimTypeReferenceId="extension_tenantAbbreviation" />
<OutputClaim ClaimTypeReferenceId="extension_dateTimeFormat" />
</OutputClaims>
<!-- <OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="AssertAccountEnabledIsTrue" />
</OutputClaimsTransformations> -->
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
</ClaimsProviders>
<UserJourneys>
<UserJourney Id="SignInWithEmail">
<OrchestrationSteps>
<!--Sample: Read the input claims from the id_token_hint-->
<OrchestrationStep Order="1" Type="GetClaims" CpimIssuerTechnicalProfileReferenceId="IdTokenHint_ExtractClaims" />
<!-- Sample: Check if user tries to run the policy without invitation -->
<OrchestrationStep Order="2" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>email</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="SelfAsserted-Unsolicited" TechnicalProfileReferenceId="SelfAsserted-Unsolicited" />
</ClaimsExchanges>
</OrchestrationStep>
<!--Sample: Read the user properties from the directory-->
<OrchestrationStep Order="3" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadUsingEmailAddress" TechnicalProfileReferenceId="AAD-UserReadUsingEmailAddress-Hint"/>
</ClaimsExchanges>
</OrchestrationStep>
<!-- Sample: Check whether the user not existed in the directory -->
<OrchestrationStep Order="4" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="SelfAssertedUserNotFound" TechnicalProfileReferenceId="SelfAsserted-UserNotFound" />
</ClaimsExchanges>
</OrchestrationStep>
<!--Sample: Issue an access token-->
<OrchestrationStep Order="5" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer"/>
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb"/>
</UserJourney>
</UserJourneys>
<RelyingParty>
<DefaultUserJourney ReferenceId="SignInWithEmail" />
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="OpenIdConnect" />
<!--Sample: Set the input claims to be read from the id_token_hint-->
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="correlationId" DefaultValue="{Context:CorrelationId}" />
<OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" />
<OutputClaim ClaimTypeReferenceId="extension_userId" />
<OutputClaim ClaimTypeReferenceId="extension_tenantAbbreviation" />
<OutputClaim ClaimTypeReferenceId="extension_dateTimeFormat" />
<OutputClaim ClaimTypeReferenceId="tenantId" AlwaysUseDefaultValue="true" DefaultValue="{Policy:TenantObjectId}" />
<OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub" />
</OutputClaims>
<SubjectNamingInfo ClaimType="sub" />
</TechnicalProfile>
</RelyingParty>
</TrustFrameworkPolicy>
I'm trying to use custom technical profile for Local Account in SignUpOrSignIn user journey.
I have Created the following technical profile in my customtrustframeworkextensions.xml (base:trustframeworkextensions.xml):
<ClaimsProvider>
<DisplayName>Local Account</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="CustomLocalAccountSignUpWithLogonEmail">
<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>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="Verified.Email" Required="true" />
<OutputClaim ClaimTypeReferenceId="newPassword" Required="true" />
<OutputClaim ClaimTypeReferenceId="reenterPassword" Required="true" />
<OutputClaim ClaimTypeReferenceId="executed-SelfAsserted-Input" DefaultValue="true" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" />
<OutputClaim ClaimTypeReferenceId="newUser" />
<OutputClaim ClaimTypeReferenceId="extension_XXX" />
<!-- Optional claims, to be collected from the user -->
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surName" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="REST-ValidateProfile" />
</ValidationTechnicalProfiles>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
REST-ValidateProfile looks like the following:
<ClaimsProvider>
<DisplayName>REST APIs</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="REST-ValidateProfile">
<DisplayName>Check yyy and zzz Rest API</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<!-- Set the ServiceUrl with your own REST API endpoint -->
<Item Key="ServiceUrl">https://asd</Item>
<Item Key="SendClaimsIn">Body</Item>
<!-- Set AuthenticationType to Basic or ClientCertificate in production environments -->
<Item Key="AuthenticationType">ApiKeyHeader</Item>
<!-- REMOVE the following line in production environments -->
<Item Key="AllowInsecureAuthInProduction">false</Item>
</Metadata>
<CryptographicKeys>
<Key Id="Api-key" StorageReferenceId="B2C_1A_key" />
</CryptographicKeys>
<InputClaims>
<!-- Claims sent to your REST API -->
<InputClaim ClaimTypeReferenceId="email" />
<InputClaim ClaimTypeReferenceId="extension_xxx" PartnerClaimType="xxx" />
</InputClaims>
<OutputClaims>
<!-- Claims parsed from your REST API -->
<OutputClaim ClaimTypeReferenceId="extension_yyy" PartnerClaimType="yyy" />
<OutputClaim ClaimTypeReferenceId="extension_zzz" PartnerClaimType="zzz" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
</ClaimsProvider>
I have modified the OrchestrationStep to use custom technical profile in user journey:
<OrchestrationStep Order="2" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="SignUpWithLogonEmailExchange" TechnicalProfileReferenceId="CustomLocalAccountSignUpWithLogonEmail" />
</ClaimsExchanges>
</OrchestrationStep>
When I run my Custom policy and select SignUp the browser shows error: "The page cannot be displayed because an internal server error has occurred."
There are some more spesific details in Application insights:
Exception Message:Output claim type "objectId" specified in the technical profile with id "CustomLocalAccountSignUpWithLogonEmail" in policy "B2C_1A_DEV_signup_signin" of tenant does not specify a UserInputType or a DefaultValue, and is not retrieved from a ValidationTechnicalProfile either., Exception Type:InvalidDefinitionException
Everything works ok when I use name "LocalAccountSignUpWithLogonEmail" for edited technical profile and the ClaimsExChange Looks like this:
<ClaimsExchange Id="SignUpWithLogonEmailExchange" TechnicalProfileReferenceId="CustomLocalAccountSignUpWithLogonEmail" />
But when I change name of the modified technical profile, policy doesn't work anymore. It seems to me that claims exchange doesn't work or something. I don't get why because I can't find any other places that refers to LocalAccountSignUpWithLogonEmail.
I want to use custom technical profile because want want to remove some outputclaims whitout touching the base policies.
The technicalProfile "CustomLocalAccountSignUpWithLogonEmail" has an output claim of the objectID which is common when you write something. The most common patterns is:
Technical profile
-> Validation Technical 1 profile
-> Validation Technical 2 profile
Being, validation technical profile 1 maybe calls your REST to validate the profile, then you call validation technical profile 2 to perform a write operation into the directory. When you write into the directory, it outputs an objectID which will output it into your technical profile and that error will go away.
I am trying to use custom policies of Azure Ad B2C because we have a complex flow of self-registration for external users.
I am trying to use custom attributes, and I followed the dedicated tutorial.
I keep getting the error "Unable to validate the information provided" nevertheless.
1 - I added a section for AAD-Common with the ids of the b2c-extensionregistered app.
2 - I created custom attributes using the Azure AD B2C UI in the dedicated section.
3 - I declared this attribute in the ClaimSchemas section.
4 - I added the attribute in the self-asserted sign-up technical profile.
5 - I added the attribute to be persisted as a persistent claim in technical profile validating the self-sign-up.
6 - I uploaded the extension file on my Azure AD B2C custom policy page.
Can you tell me what I do wrong?
Thanks!
I double-checked several times the tutorial against my work before posting here.
Here is my extension file:
<?xml version="1.0" encoding="utf-8" ?>
<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="wynsureformercer.onmicrosoft.com" PolicyId="B2C_1A_TrustFrameworkExtensions" PublicPolicyUri="http://mytenant.onmicrosoft.com/B2C_1A_TrustFrameworkExtensions">
<BasePolicy>
<TenantId>mytenant.onmicrosoft.com</TenantId>
<PolicyId>B2C_1A_TrustFrameworkLocalization</PolicyId>
</BasePolicy>
<BuildingBlocks>
<ClaimsSchema>
<ClaimType Id="custom_employee_id">
<DisplayName>Employee Id</DisplayName>
<DataType>string</DataType>
<UserHelpText/>
<UserInputType>TextBox</UserInputType>
</ClaimType>
</ClaimsSchema>
</BuildingBlocks>
<ClaimsProviders>
<ClaimsProvider>
<DisplayName>Azure Active Directory</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="AAD-Common">
<Metadata>
<!--Insert b2c-extensions-app application ID here, for example: 11111111-1111-1111-1111-111111111111-->
<Item Key="ClientId">f8070b57-64ba-blablabla</Item>
<!--Insert b2c-extensions-app application ObjectId here, for example: 22222222-2222-2222-2222-222222222222-->
<Item Key="ApplicationObjectId">a6ae751f-7d74-blablabla</Item>
</Metadata>
</TechnicalProfile>
<TechnicalProfile Id="AAD-UserWriteUsingLogonEmail">
<Metadata>
<Item Key="Operation">Write</Item>
<Item Key="RaiseErrorIfClaimsPrincipalAlreadyExists">true</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<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" />
<PersistedClaim ClaimTypeReferenceId="custom_employee_id" />
</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>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>Local Account SignIn</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="login-NonInteractive">
<Metadata>
<Item Key="client_id">ProxyIdentityExperienceFrameworkAppId</Item>
<Item Key="IdTokenAudience">IdentityExperienceFrameworkAppId</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="client_id" DefaultValue="3fb15547-7986-416b-b6ba-4d84bee6770a" />
<InputClaim ClaimTypeReferenceId="resource_id" PartnerClaimType="resource" DefaultValue="1215af09-c958-4d92-a356-b1316895ff31" />
</InputClaims>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>Local Account</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="LocalAccountSignUpWithLogonEmail">
<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>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="Verified.Email" Required="true" />
<OutputClaim ClaimTypeReferenceId="newPassword" Required="true" />
<OutputClaim ClaimTypeReferenceId="reenterPassword" Required="true" />
<OutputClaim ClaimTypeReferenceId="executed-SelfAsserted-Input" DefaultValue="true" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" />
<OutputClaim ClaimTypeReferenceId="newUser" />
<!-- Optional claims, to be collected from the user -->
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surName" />
<OutputClaim ClaimTypeReferenceId="custom_employee_id" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AAD-UserWriteUsingLogonEmail" />
</ValidationTechnicalProfiles>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
</ClaimsProviders>
<!--UserJourneys>
</UserJourneys-->
</TrustFrameworkPolicy>
When using a custom attribute in custom policies, you must prefix the claim type ID with extension_ to allow the correct data mapping to take place within the Azure AD B2C directory.
Also, make sure to copy the Object ID of the b2c-extensions-app in your portal to ApplicationObjectId and Application (client) ID of b2c-extensions-app to the client id in your custom policy correctly.
I am struggling a lot with a custom policy for Azure AD B2C. I have based my policies on the jit-migration-v2 files: https://github.com/azure-ad-b2c/user-migration/tree/master/jit-migration-v2
I added 4 new custom attributes and included them in the TrustFrameworkExtension.xml file: extension_creditorId, extension_likvidoUserId, extension_likvidoRole & extension_title.
Here is my complete TrustFrameworkExtension.xml file, where you can see that I added these attributes as output claims and persisted claims:
<?xml version="1.0" encoding="utf-8" ?>
<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="{Settings:Tenant}" PolicyId="B2C_1A_JITMigraion_TrustFrameworkExtensions" PublicPolicyUri="http://{Settings:Tenant}/B2C_1A_JITMigraion_TrustFrameworkExtensions">
<BasePolicy>
<TenantId>{Settings:Tenant}</TenantId>
<PolicyId>B2C_1A_JITMigraion_TrustFrameworkBase</PolicyId>
</BasePolicy>
<BuildingBlocks>
<ClaimsSchema>
<!--Demo: This claim indicates whether the user need to migrate-->
<ClaimType Id="needToMigrate">
<DisplayName>needToMigrate</DisplayName>
<DataType>string</DataType>
<AdminHelpText>Indicates whether the user need to migrate</AdminHelpText>
<UserHelpText>Indicates whether the user need to migrate</UserHelpText>
</ClaimType>
<ClaimType Id="useInputPassword">
<DisplayName>useInputPassword</DisplayName>
<DataType>boolean</DataType>
</ClaimType>
<ClaimType Id="extension_creditorId">
<DisplayName>Creditor ID</DisplayName>
<DataType>int</DataType>
<AdminHelpText>The ID of the creditor this user belongs to</AdminHelpText>
<UserHelpText>The ID of the creditor this user belongs to</UserHelpText>
</ClaimType>
<ClaimType Id="extension_likvidoUserId">
<DisplayName>Likvido user ID</DisplayName>
<DataType>string</DataType>
<AdminHelpText>The ID of the user in the Likvido DB</AdminHelpText>
<UserHelpText>The ID of the user in the Likvido DB</UserHelpText>
</ClaimType>
<ClaimType Id="extension_likvidoRole">
<DisplayName>Likvido user role</DisplayName>
<DataType>string</DataType>
<AdminHelpText>The role of the user in Likvido</AdminHelpText>
<UserHelpText>The role of the user in Likvido</UserHelpText>
</ClaimType>
<ClaimType Id="extension_title">
<DisplayName>Likvido user title</DisplayName>
<DataType>string</DataType>
<AdminHelpText>The title of the user</AdminHelpText>
<UserHelpText>The title of the user</UserHelpText>
</ClaimType>
</ClaimsSchema>
</BuildingBlocks>
<ClaimsProviders>
<ClaimsProvider>
<DisplayName>Azure Active Directory</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="AAD-Common">
<Metadata>
<!--Insert b2c-extensions-app application ID here, for example: 11111111-1111-1111-1111-111111111111-->
<Item Key="ClientId">{Settings:B2CExtensionsAppClientId}</Item>
<!--Insert b2c-extensions-app application ObjectId here, for example: 22222222-2222-2222-2222-222222222222-->
<Item Key="ApplicationObjectId">{Settings:B2CExtensionsAppObjectId}</Item>
</Metadata>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<!-- Local account Sign-Up claims provider -->
<ClaimsProvider>
<DisplayName>Local Account</DisplayName>
<TechnicalProfiles>
<!-- SIGN-IN -->
<TechnicalProfile Id="SelfAsserted-LocalAccountSignin-Email">
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="needToMigrate" />
</OutputClaims>
<ValidationTechnicalProfiles>
<!--Demo: Add user migration validation technical profile before login-NonInteractive -->
<ValidationTechnicalProfile ReferenceId="REST-UserMigration-LocalAccount-SignIn" ContinueOnError="false" />
<!--Demo: Run this validation technical profile only if user doesn't need to migrate -->
<ValidationTechnicalProfile ReferenceId="login-NonInteractive">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>needToMigrate</Value>
<Value>local</Value>
<Action>SkipThisValidationTechnicalProfile</Action>
</Precondition>
</Preconditions>
</ValidationTechnicalProfile>
<!--Demo: Run this validation technical profile only if user needs to migrate -->
<ValidationTechnicalProfile ReferenceId="AAD-MigrateUserUsingLogonEmail">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="false">
<Value>needToMigrate</Value>
<Value>local</Value>
<Action>SkipThisValidationTechnicalProfile</Action>
</Precondition>
</Preconditions>
</ValidationTechnicalProfile>
</ValidationTechnicalProfiles>
</TechnicalProfile>
<!-- SIGN-UP -->
<TechnicalProfile Id="LocalAccountSignUpWithLogonEmail">
<Metadata>
<Item Key="EnforceEmailVerification">False</Item>
</Metadata>
<ValidationTechnicalProfiles>
<!--Demo: Add user migration validation technical profile before AAD-UserWriteUsingLogonEmail -->
<ValidationTechnicalProfile ReferenceId="REST-UserMigration-LocalAccount-SignUp" ContinueOnError="false" />
<ValidationTechnicalProfile ReferenceId="AAD-UserWriteUsingLogonEmail" ContinueOnError="false" />
</ValidationTechnicalProfiles>
</TechnicalProfile>
<!-- PASSWORD RESET first page -->
<TechnicalProfile Id="LocalAccountDiscoveryUsingEmailAddress">
<Metadata>
<Item Key="EnforceEmailVerification">False</Item>
</Metadata>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AAD-UserReadUsingEmailAddress" ContinueOnError="true" />
<ValidationTechnicalProfile ReferenceId="REST-UserMigration-LocalAccount-PasswordReset1" ContinueOnError="false" />
</ValidationTechnicalProfiles>
</TechnicalProfile>
<!-- PASSWORD RESET second page -->
<TechnicalProfile Id="LocalAccountWritePasswordUsingObjectId">
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AAD-UserWritePasswordUsingObjectId">
<!--Don't run this validation technical profile if objectId is not exists (migrated acccount)-->
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>objectId</Value>
<Action>SkipThisValidationTechnicalProfile</Action>
</Precondition>
</Preconditions>
</ValidationTechnicalProfile>
<ValidationTechnicalProfile ReferenceId="REST-UserMigration-LocalAccount-PasswordReset2">
<!--Don't run this validation technical profile if objectId is exists (existing acccount)-->
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisValidationTechnicalProfile</Action>
</Precondition>
</Preconditions>
</ValidationTechnicalProfile>
<ValidationTechnicalProfile ReferenceId="AAD-MigrateUserUsingLogonEmail">
<!--Don't run this validation technical profile if objectId is exists (existing acccount)-->
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisValidationTechnicalProfile</Action>
</Precondition>
</Preconditions>
</ValidationTechnicalProfile>
</ValidationTechnicalProfiles>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>REST APIs</DisplayName>
<TechnicalProfiles>
<!--Demo: Checks if user exists in the migration table. If yes, validate the credentials and migrate the account -->
<TechnicalProfile Id="REST-UserMigration-LocalAccount-SignIn">
<DisplayName>Migrate user sign-in flow</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="ServiceUrl">{Settings:LikvidoWebAppApiBaseUrl}/users/azure-ad-b2c/migrate</Item>
<Item Key="AuthenticationType">None</Item>
<Item Key="SendClaimsIn">Body</Item>
<Item Key="AllowInsecureAuthInProduction">True</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInName" />
<InputClaim ClaimTypeReferenceId="password" />
<InputClaim ClaimTypeReferenceId="useInputPassword" DefaultValue="false" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="needToMigrate" />
<OutputClaim ClaimTypeReferenceId="email" />
<OutputClaim ClaimTypeReferenceId="newPassword" />
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="firstName" />
<OutputClaim ClaimTypeReferenceId="surName" PartnerClaimType="lastName" />
<OutputClaim ClaimTypeReferenceId="extension_creditorId" PartnerClaimType="creditorId" />
<OutputClaim ClaimTypeReferenceId="extension_likvidoUserId" PartnerClaimType="likvidoUserId" />
<OutputClaim ClaimTypeReferenceId="extension_likvidoRole" PartnerClaimType="likvidoRole" />
<OutputClaim ClaimTypeReferenceId="extension_title" PartnerClaimType="title" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
<!--Demo: Checks if user exists in the migration table. If yes, raises an error -->
<TechnicalProfile Id="REST-UserMigration-LocalAccount-SignUp">
<DisplayName>Migrate user sign-in flow</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="ServiceUrl">{Settings:LikvidoWebAppApiBaseUrl}/users/azure-ad-b2c/raise-error-if-exists</Item>
<Item Key="AuthenticationType">None</Item>
<Item Key="SendClaimsIn">Body</Item>
<Item Key="AllowInsecureAuthInProduction">True</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" PartnerClaimType="signInName" />
</InputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
<!--Demo: Checks if user exists in Azure AD B2C or the migration table. If not, raises an error -->
<TechnicalProfile Id="REST-UserMigration-LocalAccount-PasswordReset1">
<DisplayName>Migrate user sign-in flow</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="ServiceUrl">{Settings:LikvidoWebAppApiBaseUrl}/users/azure-ad-b2c/raise-error-if-not-exists</Item>
<Item Key="AuthenticationType">None</Item>
<Item Key="SendClaimsIn">Body</Item>
<Item Key="AllowInsecureAuthInProduction">True</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" PartnerClaimType="signInName" />
<InputClaim ClaimTypeReferenceId="objectId" />
</InputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
<TechnicalProfile Id="REST-UserMigration-LocalAccount-PasswordReset2">
<DisplayName>Migrate user sign-in flow</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="ServiceUrl">{Settings:LikvidoWebAppApiBaseUrl}/users/azure-ad-b2c/migrate</Item>
<Item Key="AuthenticationType">None</Item>
<Item Key="SendClaimsIn">Body</Item>
<Item Key="AllowInsecureAuthInProduction">True</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" PartnerClaimType="signInName" />
<!-- <InputClaim ClaimTypeReferenceId="password" /> -->
<InputClaim ClaimTypeReferenceId="useInputPassword" DefaultValue="true" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="needToMigrate" />
<OutputClaim ClaimTypeReferenceId="email" />
<!-- Don't return the new password <OutputClaim ClaimTypeReferenceId="newPassword" /> -->
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surName" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<!-- Local account Sign-In claims provider -->
<ClaimsProvider>
<DisplayName>Local Account SignIn</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="login-NonInteractive">
<DisplayName>Local Account SignIn</DisplayName>
<Protocol Name="OpenIdConnect" />
<Metadata>
<Item Key="UserMessageIfClaimsPrincipalDoesNotExist">We can't seem to find your account</Item>
<Item Key="UserMessageIfInvalidPassword">Your password is incorrect</Item>
<Item Key="UserMessageIfOldPasswordUsed">Looks like you used an old password</Item>
<Item Key="ProviderName">https://sts.windows.net/</Item>
<Item Key="METADATA">https://login.microsoftonline.com/{tenant}/.well-known/openid-configuration</Item>
<Item Key="authorization_endpoint">https://login.microsoftonline.com/{tenant}/oauth2/token</Item>
<Item Key="response_types">id_token</Item>
<Item Key="response_mode">query</Item>
<Item Key="scope">email openid</Item>
<Item Key="grant_type">password</Item>
<!-- Policy Engine Clients -->
<Item Key="UsePolicyInRedirectUri">false</Item>
<Item Key="HttpBinding">POST</Item>
<Item Key="client_id">{Settings:ProxyIdentityExperienceFrameworkAppId}</Item>
<Item Key="IdTokenAudience">{Settings:IdentityExperienceFramework}</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInName" PartnerClaimType="username" Required="true" />
<InputClaim ClaimTypeReferenceId="password" Required="true" />
<InputClaim ClaimTypeReferenceId="grant_type" DefaultValue="password" />
<InputClaim ClaimTypeReferenceId="scope" DefaultValue="openid" />
<InputClaim ClaimTypeReferenceId="nca" PartnerClaimType="nca" DefaultValue="1" />
<InputClaim ClaimTypeReferenceId="client_id" DefaultValue="{Settings:ProxyIdentityExperienceFrameworkAppId}" />
<InputClaim ClaimTypeReferenceId="resource_id" PartnerClaimType="resource" DefaultValue="{Settings:IdentityExperienceFramework}" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" 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="userPrincipalName" PartnerClaimType="upn" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="localAccountAuthentication" />
<OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" />
<OutputClaim ClaimTypeReferenceId="extension_creditorId" />
<OutputClaim ClaimTypeReferenceId="extension_likvidoUserId" />
<OutputClaim ClaimTypeReferenceId="extension_likvidoRole" />
<OutputClaim ClaimTypeReferenceId="extension_title" />
</OutputClaims>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>Azure Active Directory</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="AAD-MigrateUserUsingLogonEmail">
<Metadata>
<Item Key="Operation">Write</Item>
<Item Key="RaiseErrorIfClaimsPrincipalAlreadyExists">true</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<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,DisableStrongPassword" AlwaysUseDefaultValue="true"/>
<!-- Optional claims. -->
<PersistedClaim ClaimTypeReferenceId="givenName" />
<PersistedClaim ClaimTypeReferenceId="surname" />
<PersistedClaim ClaimTypeReferenceId="extension_creditorId" />
<PersistedClaim ClaimTypeReferenceId="extension_likvidoUserId" />
<PersistedClaim ClaimTypeReferenceId="extension_likvidoRole" />
<PersistedClaim ClaimTypeReferenceId="extension_title" />
</PersistedClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="newUser" PartnerClaimType="newClaimsPrincipalCreated" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="localAccountAuthentication" />
<OutputClaim ClaimTypeReferenceId="userPrincipalName" />
<OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" />
<OutputClaim ClaimTypeReferenceId="extension_creditorId" />
<OutputClaim ClaimTypeReferenceId="extension_likvidoUserId" />
<OutputClaim ClaimTypeReferenceId="extension_likvidoRole" />
<OutputClaim ClaimTypeReferenceId="extension_title" />
</OutputClaims>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<!-- Facebook claims provider -->
<ClaimsProvider>
<DisplayName>Facebook</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="Facebook-OAUTH">
<Metadata>
<!--Demo action required: Change to your Facebook App Id-->
<Item Key="client_id">TODO</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>
</ClaimsProviders>
<!--<UserJourneys>
</UserJourneys>-->
</TrustFrameworkPolicy>
Uploading this custom profile works fine, but when I also add these claims to the SignUpOrSignIn.xml file, then I receive validation errors. This is my complete SignUpOrSignIn.xml file:
<?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="{Settings:Tenant}" PolicyId="B2C_1A_JITMigraion_signup_signin" PublicPolicyUri="http://{Settings:Tenant}/B2C_1A_JITMigraion_signup_signin">
<BasePolicy>
<TenantId>{Settings:Tenant}</TenantId>
<PolicyId>B2C_1A_JITMigraion_TrustFrameworkExtensions</PolicyId>
</BasePolicy>
<RelyingParty>
<DefaultUserJourney ReferenceId="SignUpOrSignIn" />
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="OpenIdConnect" />
<InputClaims>
<InputClaim ClaimTypeReferenceId="passwordPolicies" DefaultValue="DisablePasswordExpiration, DisableStrongPassword"/>
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surname" />
<OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub"/>
<OutputClaim ClaimTypeReferenceId="identityProvider" />
<OutputClaim ClaimTypeReferenceId="needToMigrate" DefaultValue="false" />
<OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" PartnerClaimType="email" />
<OutputClaim ClaimTypeReferenceId="extension_creditorId" PartnerClaimType="creditorId" />
<OutputClaim ClaimTypeReferenceId="extension_likvidoUserId" PartnerClaimType="likvidoUserId" />
<OutputClaim ClaimTypeReferenceId="extension_likvidoRole" PartnerClaimType="likvidoRole" />
<OutputClaim ClaimTypeReferenceId="extension_title" PartnerClaimType="title" />
</OutputClaims>
<SubjectNamingInfo ClaimType="sub" />
</TechnicalProfile>
</RelyingParty>
</TrustFrameworkPolicy>
I receive these error messages when I try to upload this policy:
Validation failed: 4 validation error(s) found in policy "B2C_1A_JITMIGRAION_SIGNUP_SIGNIN" of tenant "likvidostaging.onmicrosoft.com".
Claim type "extension_creditorId" is the output claim of the relying party's technical profile, but it is not an output claim in any of the steps of user journey "SignUpOrSignIn".
Claim type "extension_likvidoUserId" is the output claim of the relying party's technical profile, but it is not an output claim in any of the steps of user journey "SignUpOrSignIn".
Claim type "extension_likvidoRole" is the output claim of the relying party's technical profile, but it is not an output claim in any of the steps of user journey "SignUpOrSignIn".
Claim type "extension_title" is the output claim of the relying party's technical profile, but it is not an output claim in any of the steps of user journey "SignUpOrSignIn".
I don't understand what I am doing wrong here, so any help is very much appreciated!
The scope of the output claims of a validation technical profile is limited to the self-asserted technical profile that invokes the validation technical profile, and its validation technical profiles. If you want to use the output claims in the next orchestration step, add the output claims to the self-asserted technical profile that invokes the validation technical profile.
https://learn.microsoft.com/en-us/azure/active-directory-b2c/validation-technical-profile
I figured out how to make it work now. Apparently, it is required to also add these output claims to the SelfAsserted-LocalAccountSignin-Email technical profile (no idea why).
So, my resulting TrustedFrameworkExtensions.xml looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<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="{Settings:Tenant}" PolicyId="B2C_1A_JITMigraion_TrustFrameworkExtensions" PublicPolicyUri="http://{Settings:Tenant}/B2C_1A_JITMigraion_TrustFrameworkExtensions">
<BasePolicy>
<TenantId>{Settings:Tenant}</TenantId>
<PolicyId>B2C_1A_JITMigraion_TrustFrameworkBase</PolicyId>
</BasePolicy>
<BuildingBlocks>
<ClaimsSchema>
<!--Demo: This claim indicates whether the user need to migrate-->
<ClaimType Id="needToMigrate">
<DisplayName>needToMigrate</DisplayName>
<DataType>string</DataType>
<AdminHelpText>Indicates whether the user need to migrate</AdminHelpText>
<UserHelpText>Indicates whether the user need to migrate</UserHelpText>
</ClaimType>
<ClaimType Id="useInputPassword">
<DisplayName>useInputPassword</DisplayName>
<DataType>boolean</DataType>
</ClaimType>
<ClaimType Id="extension_creditorId">
<DisplayName>Creditor ID</DisplayName>
<DataType>int</DataType>
<AdminHelpText>The ID of the creditor this user belongs to</AdminHelpText>
<UserHelpText>The ID of the creditor this user belongs to</UserHelpText>
</ClaimType>
<ClaimType Id="extension_likvidoUserId">
<DisplayName>Likvido user ID</DisplayName>
<DataType>string</DataType>
<AdminHelpText>The ID of the user in the Likvido DB</AdminHelpText>
<UserHelpText>The ID of the user in the Likvido DB</UserHelpText>
</ClaimType>
<ClaimType Id="extension_likvidoRole">
<DisplayName>Likvido user role</DisplayName>
<DataType>string</DataType>
<AdminHelpText>The role of the user in Likvido</AdminHelpText>
<UserHelpText>The role of the user in Likvido</UserHelpText>
</ClaimType>
<ClaimType Id="extension_title">
<DisplayName>Likvido user title</DisplayName>
<DataType>string</DataType>
<AdminHelpText>The title of the user</AdminHelpText>
<UserHelpText>The title of the user</UserHelpText>
</ClaimType>
</ClaimsSchema>
</BuildingBlocks>
<ClaimsProviders>
<ClaimsProvider>
<DisplayName>Azure Active Directory</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="AAD-Common">
<Metadata>
<!--Insert b2c-extensions-app application ID here, for example: 11111111-1111-1111-1111-111111111111-->
<Item Key="ClientId">{Settings:B2CExtensionsAppClientId}</Item>
<!--Insert b2c-extensions-app application ObjectId here, for example: 22222222-2222-2222-2222-222222222222-->
<Item Key="ApplicationObjectId">{Settings:B2CExtensionsAppObjectId}</Item>
</Metadata>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<!-- Local account Sign-Up claims provider -->
<ClaimsProvider>
<DisplayName>Local Account</DisplayName>
<TechnicalProfiles>
<!-- SIGN-IN -->
<TechnicalProfile Id="SelfAsserted-LocalAccountSignin-Email">
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="needToMigrate" />
<OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" />
<OutputClaim ClaimTypeReferenceId="extension_creditorId" />
<OutputClaim ClaimTypeReferenceId="extension_likvidoUserId" />
<OutputClaim ClaimTypeReferenceId="extension_likvidoRole" />
<OutputClaim ClaimTypeReferenceId="extension_title" />
</OutputClaims>
<ValidationTechnicalProfiles>
<!--Demo: Add user migration validation technical profile before login-NonInteractive -->
<ValidationTechnicalProfile ReferenceId="REST-UserMigration-LocalAccount-SignIn" ContinueOnError="false" />
<!--Demo: Run this validation technical profile only if user doesn't need to migrate -->
<ValidationTechnicalProfile ReferenceId="login-NonInteractive">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>needToMigrate</Value>
<Value>local</Value>
<Action>SkipThisValidationTechnicalProfile</Action>
</Precondition>
</Preconditions>
</ValidationTechnicalProfile>
<!--Demo: Run this validation technical profile only if user needs to migrate -->
<ValidationTechnicalProfile ReferenceId="AAD-MigrateUserUsingLogonEmail">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="false">
<Value>needToMigrate</Value>
<Value>local</Value>
<Action>SkipThisValidationTechnicalProfile</Action>
</Precondition>
</Preconditions>
</ValidationTechnicalProfile>
</ValidationTechnicalProfiles>
</TechnicalProfile>
<!-- SIGN-UP -->
<TechnicalProfile Id="LocalAccountSignUpWithLogonEmail">
<Metadata>
<Item Key="EnforceEmailVerification">False</Item>
</Metadata>
<ValidationTechnicalProfiles>
<!--Demo: Add user migration validation technical profile before AAD-UserWriteUsingLogonEmail -->
<ValidationTechnicalProfile ReferenceId="REST-UserMigration-LocalAccount-SignUp" ContinueOnError="false" />
<ValidationTechnicalProfile ReferenceId="AAD-UserWriteUsingLogonEmail" ContinueOnError="false" />
</ValidationTechnicalProfiles>
</TechnicalProfile>
<!-- PASSWORD RESET first page -->
<TechnicalProfile Id="LocalAccountDiscoveryUsingEmailAddress">
<Metadata>
<Item Key="EnforceEmailVerification">False</Item>
</Metadata>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AAD-UserReadUsingEmailAddress" ContinueOnError="true" />
<ValidationTechnicalProfile ReferenceId="REST-UserMigration-LocalAccount-PasswordReset1" ContinueOnError="false" />
</ValidationTechnicalProfiles>
</TechnicalProfile>
<!-- PASSWORD RESET second page -->
<TechnicalProfile Id="LocalAccountWritePasswordUsingObjectId">
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AAD-UserWritePasswordUsingObjectId">
<!--Don't run this validation technical profile if objectId is not exists (migrated acccount)-->
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>objectId</Value>
<Action>SkipThisValidationTechnicalProfile</Action>
</Precondition>
</Preconditions>
</ValidationTechnicalProfile>
<ValidationTechnicalProfile ReferenceId="REST-UserMigration-LocalAccount-PasswordReset2">
<!--Don't run this validation technical profile if objectId is exists (existing acccount)-->
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisValidationTechnicalProfile</Action>
</Precondition>
</Preconditions>
</ValidationTechnicalProfile>
<ValidationTechnicalProfile ReferenceId="AAD-MigrateUserUsingLogonEmail">
<!--Don't run this validation technical profile if objectId is exists (existing acccount)-->
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisValidationTechnicalProfile</Action>
</Precondition>
</Preconditions>
</ValidationTechnicalProfile>
</ValidationTechnicalProfiles>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>REST APIs</DisplayName>
<TechnicalProfiles>
<!--Demo: Checks if user exists in the migration table. If yes, validate the credentials and migrate the account -->
<TechnicalProfile Id="REST-UserMigration-LocalAccount-SignIn">
<DisplayName>Migrate user sign-in flow</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="ServiceUrl">{Settings:LikvidoWebAppApiBaseUrl}/users/azure-ad-b2c/migrate</Item>
<Item Key="AuthenticationType">None</Item>
<Item Key="SendClaimsIn">Body</Item>
<Item Key="AllowInsecureAuthInProduction">True</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInName" />
<InputClaim ClaimTypeReferenceId="password" />
<InputClaim ClaimTypeReferenceId="useInputPassword" DefaultValue="false" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="needToMigrate" />
<OutputClaim ClaimTypeReferenceId="email" />
<OutputClaim ClaimTypeReferenceId="newPassword" />
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="firstName" />
<OutputClaim ClaimTypeReferenceId="surName" PartnerClaimType="lastName" />
<OutputClaim ClaimTypeReferenceId="extension_creditorId" PartnerClaimType="creditorId" />
<OutputClaim ClaimTypeReferenceId="extension_likvidoUserId" PartnerClaimType="likvidoUserId" />
<OutputClaim ClaimTypeReferenceId="extension_likvidoRole" PartnerClaimType="likvidoRole" />
<OutputClaim ClaimTypeReferenceId="extension_title" PartnerClaimType="title" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
<!--Demo: Checks if user exists in the migration table. If yes, raises an error -->
<TechnicalProfile Id="REST-UserMigration-LocalAccount-SignUp">
<DisplayName>Migrate user sign-in flow</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="ServiceUrl">{Settings:LikvidoWebAppApiBaseUrl}/users/azure-ad-b2c/raise-error-if-exists</Item>
<Item Key="AuthenticationType">None</Item>
<Item Key="SendClaimsIn">Body</Item>
<Item Key="AllowInsecureAuthInProduction">True</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" PartnerClaimType="signInName" />
</InputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
<!--Demo: Checks if user exists in Azure AD B2C or the migration table. If not, raises an error -->
<TechnicalProfile Id="REST-UserMigration-LocalAccount-PasswordReset1">
<DisplayName>Migrate user sign-in flow</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="ServiceUrl">{Settings:LikvidoWebAppApiBaseUrl}/users/azure-ad-b2c/raise-error-if-not-exists</Item>
<Item Key="AuthenticationType">None</Item>
<Item Key="SendClaimsIn">Body</Item>
<Item Key="AllowInsecureAuthInProduction">True</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" PartnerClaimType="signInName" />
<InputClaim ClaimTypeReferenceId="objectId" />
</InputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
<TechnicalProfile Id="REST-UserMigration-LocalAccount-PasswordReset2">
<DisplayName>Migrate user sign-in flow</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="ServiceUrl">{Settings:LikvidoWebAppApiBaseUrl}/users/azure-ad-b2c/migrate</Item>
<Item Key="AuthenticationType">None</Item>
<Item Key="SendClaimsIn">Body</Item>
<Item Key="AllowInsecureAuthInProduction">True</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" PartnerClaimType="signInName" />
<!-- <InputClaim ClaimTypeReferenceId="password" /> -->
<InputClaim ClaimTypeReferenceId="useInputPassword" DefaultValue="true" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="needToMigrate" />
<OutputClaim ClaimTypeReferenceId="email" />
<!-- Don't return the new password <OutputClaim ClaimTypeReferenceId="newPassword" /> -->
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surName" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<!-- Local account Sign-In claims provider -->
<ClaimsProvider>
<DisplayName>Local Account SignIn</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="login-NonInteractive">
<DisplayName>Local Account SignIn</DisplayName>
<Protocol Name="OpenIdConnect" />
<Metadata>
<Item Key="UserMessageIfClaimsPrincipalDoesNotExist">We can't seem to find your account</Item>
<Item Key="UserMessageIfInvalidPassword">Your password is incorrect</Item>
<Item Key="UserMessageIfOldPasswordUsed">Looks like you used an old password</Item>
<Item Key="ProviderName">https://sts.windows.net/</Item>
<Item Key="METADATA">https://login.microsoftonline.com/{tenant}/.well-known/openid-configuration</Item>
<Item Key="authorization_endpoint">https://login.microsoftonline.com/{tenant}/oauth2/token</Item>
<Item Key="response_types">id_token</Item>
<Item Key="response_mode">query</Item>
<Item Key="scope">email openid</Item>
<Item Key="grant_type">password</Item>
<!-- Policy Engine Clients -->
<Item Key="UsePolicyInRedirectUri">false</Item>
<Item Key="HttpBinding">POST</Item>
<Item Key="client_id">{Settings:ProxyIdentityExperienceFrameworkAppId}</Item>
<Item Key="IdTokenAudience">{Settings:IdentityExperienceFramework}</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInName" PartnerClaimType="username" Required="true" />
<InputClaim ClaimTypeReferenceId="password" Required="true" />
<InputClaim ClaimTypeReferenceId="grant_type" DefaultValue="password" />
<InputClaim ClaimTypeReferenceId="scope" DefaultValue="openid" />
<InputClaim ClaimTypeReferenceId="nca" PartnerClaimType="nca" DefaultValue="1" />
<InputClaim ClaimTypeReferenceId="client_id" DefaultValue="{Settings:ProxyIdentityExperienceFrameworkAppId}" />
<InputClaim ClaimTypeReferenceId="resource_id" PartnerClaimType="resource" DefaultValue="{Settings:IdentityExperienceFramework}" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" 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="userPrincipalName" PartnerClaimType="upn" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="localAccountAuthentication" />
<OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" />
<OutputClaim ClaimTypeReferenceId="extension_creditorId" />
<OutputClaim ClaimTypeReferenceId="extension_likvidoUserId" />
<OutputClaim ClaimTypeReferenceId="extension_likvidoRole" />
<OutputClaim ClaimTypeReferenceId="extension_title" />
</OutputClaims>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>Azure Active Directory</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="AAD-MigrateUserUsingLogonEmail">
<Metadata>
<Item Key="Operation">Write</Item>
<Item Key="RaiseErrorIfClaimsPrincipalAlreadyExists">true</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<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,DisableStrongPassword" AlwaysUseDefaultValue="true"/>
<!-- Optional claims. -->
<PersistedClaim ClaimTypeReferenceId="givenName" />
<PersistedClaim ClaimTypeReferenceId="surname" />
<PersistedClaim ClaimTypeReferenceId="extension_creditorId" />
<PersistedClaim ClaimTypeReferenceId="extension_likvidoUserId" />
<PersistedClaim ClaimTypeReferenceId="extension_likvidoRole" />
<PersistedClaim ClaimTypeReferenceId="extension_title" />
</PersistedClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="newUser" PartnerClaimType="newClaimsPrincipalCreated" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="localAccountAuthentication" />
<OutputClaim ClaimTypeReferenceId="userPrincipalName" />
<OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" />
<OutputClaim ClaimTypeReferenceId="extension_creditorId" />
<OutputClaim ClaimTypeReferenceId="extension_likvidoUserId" />
<OutputClaim ClaimTypeReferenceId="extension_likvidoRole" />
<OutputClaim ClaimTypeReferenceId="extension_title" />
</OutputClaims>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<!-- Facebook claims provider -->
<ClaimsProvider>
<DisplayName>Facebook</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="Facebook-OAUTH">
<Metadata>
<!--Demo action required: Change to your Facebook App Id-->
<Item Key="client_id">TODO</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>
</ClaimsProviders>
<!--<UserJourneys>
</UserJourneys>-->
</TrustFrameworkPolicy>
When I run my signup_signin custom policy I receive "Sign in with your sign in name" in the UI, however I'd like for this to be an email address sign in. From what I can tell the TrustFrameworkBase has the operatingMode set to email, however I'm not certain why I'm not getting the correct outcome. Do I need to adjust the SelfAsserted-LocalAccountSignin-Email technical profile in some way?
TrustFrameworkBase unmodified from this:
https://github.com/Azure-Samples/active-directory-b2c-custom-policy-starterpack/blob/master/LocalAccounts/TrustFrameworkBase.xml
TrustFrameworkExtensions.xml
<?xml version="1.0" encoding="utf-8" ?>
<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="{Settings:Tenant}" PolicyId="B2C_1A_TrustFrameworkExtensions" PublicPolicyUri="http://{Settings:Tenant}/B2C_1A_TrustFrameworkExtensions">
<BasePolicy>
<TenantId>{Settings:Tenant}</TenantId>
<PolicyId>B2C_1A_TrustFrameworkBase</PolicyId>
</BasePolicy>
<BuildingBlocks>
<ClaimsSchema>
<!-- Custom Attributes -->
<!-- Seamless Migration -->
<ClaimType Id="extension_RequiresMigration">
<DisplayName>extension_RequiresMigration</DisplayName>
<DataType>boolean</DataType>
<AdminHelpText>Migration status for the user's account. If true, reach out to legacy IdP to migrate user.</AdminHelpText>
</ClaimType>
<ClaimType Id="TokenSuccess">
<DisplayName>TokenSuccess</DisplayName>
<DataType>boolean</DataType>
<AdminHelpText>Add help text here</AdminHelpText>
</ClaimType>
<ClaimType Id="MigrationRequired">
<DisplayName>MigrationRequired</DisplayName>
<DataType>boolean</DataType>
<AdminHelpText>Holds the value of false when the legacy IdP authentication succeeded</AdminHelpText>
</ClaimType>
</ClaimsSchema>
<ContentDefinitions>
<!-- This content definition is to render an error page that displays unhandled errors. -->
<ContentDefinition Id="api.error">
<LoadUri>~/tenant/templates/AzureBlue/exception.cshtml</LoadUri>
<RecoveryUri>~/common/default_page_error.html</RecoveryUri>
<DataUri>urn:com:microsoft:aad:b2c:elements:contract:globalexception:1.2.0</DataUri>
<Metadata>
<Item Key="DisplayName">Error page</Item>
</Metadata>
</ContentDefinition>
<ContentDefinition Id="api.idpselections">
<LoadUri>~/tenant/templates/AzureBlue/idpSelector.cshtml</LoadUri>
<RecoveryUri>~/common/default_page_error.html</RecoveryUri>
<DataUri>urn:com:microsoft:aad:b2c:elements:contract:providerselection:1.2.0</DataUri>
<Metadata>
<Item Key="DisplayName">Idp selection page</Item>
<Item Key="language.intro">Sign in</Item>
</Metadata>
</ContentDefinition>
<ContentDefinition Id="api.idpselections.signup">
<LoadUri>~/tenant/templates/AzureBlue/idpSelector.cshtml</LoadUri>
<RecoveryUri>~/common/default_page_error.html</RecoveryUri>
<DataUri>urn:com:microsoft:aad:b2c:elements:contract:providerselection:1.2.0</DataUri>
<Metadata>
<Item Key="DisplayName">Idp selection page</Item>
<Item Key="language.intro">Sign up</Item>
</Metadata>
</ContentDefinition>
<ContentDefinition Id="api.signuporsignin">
<LoadUri>https://{Settings:BlobStorageAccount}.blob.core.windows.net/{Settings:BlobContainer}/html/sign_in.html</LoadUri>
<RecoveryUri>~/common/default_page_error.html</RecoveryUri>
<DataUri>urn:com:microsoft:aad:b2c:elements:contract:unifiedssp:2.1.0</DataUri>
<Metadata>
<Item Key="DisplayName">Signin and Signup</Item>
</Metadata>
</ContentDefinition>
<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.1.0</DataUri>
<Metadata>
<Item Key="DisplayName">Collect information from user page</Item>
</Metadata>
</ContentDefinition>
<ContentDefinition Id="api.selfasserted.profileupdate">
<LoadUri>~/tenant/templates/AzureBlue/selfAsserted.cshtml</LoadUri>
<RecoveryUri>~/common/default_page_error.html</RecoveryUri>
<DataUri>urn:com:microsoft:aad:b2c:elements:contract:selfasserted:2.1.0</DataUri>
<Metadata>
<Item Key="DisplayName">Collect information from user page</Item>
</Metadata>
</ContentDefinition>
<ContentDefinition Id="api.localaccountsignup">
<LoadUri>~/tenant/templates/AzureBlue/selfAsserted.cshtml</LoadUri>
<RecoveryUri>~/common/default_page_error.html</RecoveryUri>
<DataUri>urn:com:microsoft:aad:b2c:elements:contract:selfasserted:2.1.0</DataUri>
<Metadata>
<Item Key="DisplayName">Local account sign up page</Item>
</Metadata>
</ContentDefinition>
<ContentDefinition Id="api.localaccountpasswordreset">
<LoadUri>~/tenant/templates/AzureBlue/selfAsserted.cshtml</LoadUri>
<RecoveryUri>~/common/default_page_error.html</RecoveryUri>
<DataUri>urn:com:microsoft:aad:b2c:elements:contract:selfasserted:2.1.0</DataUri>
<Metadata>
<Item Key="DisplayName">Local account change password page</Item>
</Metadata>
</ContentDefinition>
</ContentDefinitions>
</BuildingBlocks>
<ClaimsProviders>
<!-- Allows for referencing custom attributes in custom policies -->
<!-- https://learn.microsoft.com/en-us/azure/active-directory-b2c/custom-policy-custom-attributes#modify-your-custom-policy -->
<ClaimsProvider>
<DisplayName>Azure Active Directory</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="AAD-Common">
<Metadata>
<Item Key="ClientId">{Settings:B2CExtensionsAppId}</Item>
<Item Key="ApplicationObjectId">{Settings:B2CExtensionsObjectId}</Item>
</Metadata>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<!-- Seamless Migration -->
<ClaimsProvider>
<DisplayName>Local Account SignIn - Read Migration Status</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="Get-RequiresMigration-Status-SignIn">
<Metadata>
<Item Key="Operation">Read</Item>
<Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">true</Item>
<Item Key="UserMessageIfClaimsPrincipalDoesNotExist">An account could not be found for the provided user ID.</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInName" PartnerClaimType="signInNames.emailAddress" Required="true"/>
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId"/>
<OutputClaim ClaimTypeReferenceId="extension_RequiresMigration" DefaultValue="false"/>
</OutputClaims>
<IncludeTechnicalProfile ReferenceId="AAD-Common"/>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>REST API to communicate with Legacy IdP</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="UserMigrationViaLegacyIdP">
<DisplayName>REST API call to communicate with Legacy IdP</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://{Settings:TIAPIBaseUrl}/SSOAuth</Item>
<Item Key="AuthenticationType">None</Item>
<Item Key="SendClaimsIn">Body</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInName" PartnerClaimType="email"/>
<InputClaim ClaimTypeReferenceId="password"/>
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="TokenSuccess" DefaultValue="false"/>
<OutputClaim ClaimTypeReferenceId="MigrationRequired"/>
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop"/>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>Local Account SignIn - Write new password and unmark for migration</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="AAD-WritePasswordAndFlipMigratedFlag">
<Metadata>
<Item Key="Operation">Write</Item>
<Item Key="RaiseErrorIfClaimsPrincipalAlreadyExists">false</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="objectId" Required="true"/>
</InputClaims>
<PersistedClaims>
<PersistedClaim ClaimTypeReferenceId="objectId"/>
<PersistedClaim ClaimTypeReferenceId="userPrincipalName" />
<PersistedClaim ClaimTypeReferenceId="displayName" />
<PersistedClaim ClaimTypeReferenceId="password" PartnerClaimType="password"/>
<PersistedClaim ClaimTypeReferenceId="passwordPolicies" DefaultValue="DisablePasswordExpiration, DisableStrongPassword" AlwaysUseDefaultValue="true"/>
<PersistedClaim ClaimTypeReferenceId="MigrationRequired" PartnerClaimType="extension_RequiresMigration"/>
</PersistedClaims>
<IncludeTechnicalProfile ReferenceId="AAD-Common"/>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD"/>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<!-- End Seamless Migration -->
<ClaimsProvider>
<DisplayName>Local Account SignIn</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="login-NonInteractive">
<Metadata>
<Item Key="client_id">{Settings:ProxyIdentityExperienceFrameworkAppId}</Item>
<Item Key="IdTokenAudience">{Settings:IdentityExperienceFrameworkAppId}</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="client_id" DefaultValue="{Settings:ProxyIdentityExperienceFrameworkAppId}" />
<InputClaim ClaimTypeReferenceId="resource_id" PartnerClaimType="resource" DefaultValue="{Settings:IdentityExperienceFrameworkAppId}" />
</InputClaims>
</TechnicalProfile>
<!-- Seamless Migration -->
<TechnicalProfile Id="SelfAsserted-LocalAccountSignin-Email">
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="extension_RequiresMigration"/>
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="Get-RequiresMigration-Status-SignIn" ContinueOnError="false"/>
<ValidationTechnicalProfile ReferenceId="UserMigrationViaLegacyIdP" ContinueOnError="false">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>extension_RequiresMigration</Value>
<Value>False</Value>
<Action>SkipThisValidationTechnicalProfile</Action>
</Precondition>
</Preconditions>
</ValidationTechnicalProfile>
<ValidationTechnicalProfile ReferenceId="AAD-WritePasswordAndFlipMigratedFlag" ContinueOnError="false">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>TokenSuccess</Value>
<Action>SkipThisValidationTechnicalProfile</Action>
</Precondition>
</Preconditions>
</ValidationTechnicalProfile>
<ValidationTechnicalProfile ReferenceId="login-NonInteractive"/>
</ValidationTechnicalProfiles>
</TechnicalProfile>
<!-- End Seamless Migration-->
</TechnicalProfiles>
</ClaimsProvider>
</ClaimsProviders>
<!--UserJourneys>
</UserJourneys-->
</TrustFrameworkPolicy>
SignUpOrSignin.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="{Settings:Tenant}" PolicyId="B2C_1A_SignUp_Signin" PublicPolicyUri="http://{Settings:Tenant}/B2C_1A_SignUp_Signin" UserJourneyRecorderEndpoint="urn:journeyrecorder:applicationinsights" DeploymentMode="{Settings:ApplicationInsightsDeploymentMode}">
<BasePolicy>
<TenantId>{Settings:Tenant}</TenantId>
<PolicyId>B2C_1A_TrustFrameworkExtensions</PolicyId>
</BasePolicy>
<RelyingParty>
<DefaultUserJourney ReferenceId="SignUpOrSignIn" />
<UserJourneyBehaviors>
<JourneyInsights TelemetryEngine="ApplicationInsights" InstrumentationKey="{Settings:ApplicationInsightsInstrumentationKey}" DeveloperMode="{Settings:ApplicationInsightsDeveloperMode}" ClientEnabled="false" ServerEnabled="true" TelemetryVersion="1.0.0" />
<ScriptExecution>Allow</ScriptExecution>
</UserJourneyBehaviors>
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="OpenIdConnect" />
<InputClaims>
<InputClaim ClaimTypeReferenceId="passwordPolicies" DefaultValue="DisablePasswordExpiration, DisableStrongPassword" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surname" />
<OutputClaim ClaimTypeReferenceId="email" />
<OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub"/>
<OutputClaim ClaimTypeReferenceId="tenantId" AlwaysUseDefaultValue="true" DefaultValue="{Policy:TenantObjectId}" />
</OutputClaims>
<SubjectNamingInfo ClaimType="sub" />
</TechnicalProfile>
</RelyingParty>
</TrustFrameworkPolicy>
To fix email field type, use <UserInputType>EmailBox</UserInputType>.
<ClaimsSchema>
<ClaimType Id="signInName">
<DisplayName>Email Address</DisplayName>
<DataType>string</DataType>
<AdminHelpText>Email address that the user can use to sign in.</AdminHelpText>
<UserHelpText>Email address to use for signing in.</UserHelpText>
<UserInputType>EmailBox</UserInputType>
</ClaimType>
</ClaimsSchema>
We will try make this default in the starter pack.
https://learn.microsoft.com/en-us/azure/active-directory-b2c/claimsschema#userinputtype
This seems to be an issue with Page Layout Version 2.1.x as indicated by JasSuri. My current workaround working solution:
<LocalizedString ElementType="UxElement"
StringId="local_intro_generic">Sign in with your email address</LocalizedString>
<LocalizedString ElementType="ClaimType"
ElementId="signInName"
StringId="DisplayName">Email Address</LocalizedString>
Its not perfect, but fine for the moment for me. The main problem is that the field is no longer of type email, which causes some issues (e.g. mobile keyboards, password managers)
edit: Adding the code by JasSuri in order to make this one "complete" answer:
To fix email field type, use <UserInputType>EmailBox</UserInputType>.
<ClaimsSchema>
<ClaimType Id="signInName">
<DisplayName>Email Address</DisplayName>
<DataType>string</DataType>
<AdminHelpText>Email address that the user can use to sign in.</AdminHelpText>
<UserHelpText>Email address to use for signing in.</UserHelpText>
<UserInputType>EmailBox</UserInputType>
</ClaimType>
</ClaimsSchema>