"ClaimsProviderSelection" is not working as expected - azure-ad-b2c

I have a user invitation custom policy, user will get an email with a link and that link will have a token. My requirement is I want to show two options to user:
Change password
Login with Other providers
Below is my user journey,
<UserJourneys>
<UserJourney Id="SignInWithInvitationLink">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="GetClaims" CpimIssuerTechnicalProfileReferenceId="IdTokenHint_ExtractClaims" />
<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>
<OrchestrationStep Order="3" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadUsingEmailAddress" TechnicalProfileReferenceId="AAD-UserInvitationStatusReadUsingEmailAddress"/>
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="4" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="Copy-UPN" TechnicalProfileReferenceId="CopyUserUPN"/>
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="5" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>userPrincipalName</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="SelfAssertedUserNotFound" TechnicalProfileReferenceId="SelfAsserted-UserNotFound" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="6" Type="ClaimsProviderSelection" ContentDefinitionReferenceId="api.idpselections">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>extension_isinvitationaccepted</Value>
<Value>True</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsProviderSelections>
<ClaimsProviderSelection TargetClaimsExchangeId="LocalAccountChangePassword"/>
<ClaimsProviderSelection TargetClaimsExchangeId="AzureADExchangeWithAADtoken"/>
</ClaimsProviderSelections>
</OrchestrationStep>
<OrchestrationStep Order="7" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>extension_isinvitationaccepted</Value>
<Value>True</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="LocalAccountChangePassword" TechnicalProfileReferenceId="LocalAccountWritePasswordUsingUPN" />
<ClaimsExchange Id="AzureADExchangeWithAADtoken" TechnicalProfileReferenceId="EmailValidator"/>
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="8" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>extension_isinvitationaccepted</Value>
<Value>True</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimEquals" ExecuteActionsIf="false">
<Value>provider</Value>
<Value>Microsoft</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AADUserLogin" TechnicalProfileReferenceId="AzureADProfile_issueAADtoken"/>
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="9" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>extension_isinvitationaccepted</Value>
<Value>True</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimEquals" ExecuteActionsIf="false">
<Value>provider</Value>
<Value>Google</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="GoogleUserLogin" TechnicalProfileReferenceId="Google-OAuth2"/>
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="10" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>extension_isinvitationaccepted</Value>
<Value>True</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimEquals" ExecuteActionsIf="false">
<Value>provider</Value>
<Value>Facebook</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="FacebookLogin" TechnicalProfileReferenceId="Facebook-OAUTH"/>
</ClaimsExchanges>
</OrchestrationStep>
<!-- For external IDP authentication, attempt to find the user account in the directory. -->
<OrchestrationStep Order="11" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>extension_isinvitationaccepted</Value>
<Value>True</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<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>
<OrchestrationStep Order="12" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>extension_isinvitationaccepted</Value>
<Value>True</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="SelfAsserted-Social" TechnicalProfileReferenceId="SelfAsserted-Social"/>
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="13" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>authenticationSource</Value>
<Value>socialIdpAuthentication</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithEmailAddress" TechnicalProfileReferenceId="AAD-UserReadUsingEmailAddress"/>
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="14" 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="15" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>extension_isinvitationaccepted</Value>
<Value>True</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimEquals" ExecuteActionsIf="false">
<Value>authenticationSource</Value>
<Value>socialIdpAuthentication</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="LocalAccountChangeInvitationStatus" TechnicalProfileReferenceId="LocalAccountWriteInvitationStatusUsingUPN"/>
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="16" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>email</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimEquals" ExecuteActionsIf="false">
<Value>authenticationSource</Value>
<Value>socialIdpAuthentication</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithSocialEmailAddress" TechnicalProfileReferenceId="AAD-UserReadUsingEmailAddress"/>
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="17" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>signInNames.emailAddress</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimEquals" ExecuteActionsIf="false">
<Value>authenticationSource</Value>
<Value>socialIdpAuthentication</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithSignInEmailAddress" TechnicalProfileReferenceId="AAD-UserReadUsingSignInEmailAddress"/>
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="18" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="CreateCustomerMapping" TechnicalProfileReferenceId="REST-CreateCustomerUserMappingInvitation"/>
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="19" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="GetCurrentUserTime" TechnicalProfileReferenceId="GetCurrentTime"/>
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="20" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserWriteLastLogin" TechnicalProfileReferenceId="AAD-UserWriteLastLogin"/>
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="21" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer"/>
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb"/>
</UserJourney>
Now, my problem is when I open the link in email B2C showing a generic error message like "something went wrong". Orchestration step 6 is allowing the user to choose whether password change or use other providers. I used application insights and got the following error:
{
"Kind": "FatalException",
"Content": {
"Time": "6:47 PM",
"Exception": {
"Kind": "Handled",
"HResult": "80131509",
"Message": "Claims exchange with id 'AADUserReadUsingEmailAddress' could not be found in orchestration step '7' and the step contains more than one claims exchange.",
"Data": {}
}
}
}

The reason for this error maybe because you might have written User Journey ID in two files both Base/Extension and Relying Party Policy.
Try to change the id to something else in one of the files if one of the id is same in both files(try to avoid duplicate user journey).
or try moving the userjourney to next step or after base files steps are over.
If the count of steps and ClaimsExchange ID is unique, then only it
will accept otherwise it will treat it as different ClaimsExchange and
error will occur .
References:
Azure AD B2C Custom Policy - Stack Overflow
Local and Social Account Sign policy
with split email verification and sign up - Stack Overflow
microsoft graph api - Azure AD B2C: Cannot login immediately after
going through a Password Reset Flow - Stack Overflow

Related

Add Forgot password link to email signin with passwordless template

I would like to add a Forgot Password link to the email signin page, like so:
I'm using the passwordless starter pack that splits Phone and Email signups/signins in different screens.
My problem is that I cannot find the right place to make the link appear in the email signin page. I followed the embedded password reset but I can only add it in the main page where it asks the user to type in his phone or email.
Here's the Relying party policy that I need to modify to add the password link SignUpSignInWithPhoneOrEmail:
<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="yourtenant.onmicrosoft.com"
PolicyId="B2C_1A_SignUpOrSignInWithPhoneOrEmail"
PublicPolicyUri="http://yourtenant.onmicrosoft.com/B2C_1A_SignUpOrSignInWithPhoneOrEmail"
DeploymentMode="Development"
UserJourneyRecorderEndpoint="urn:journeyrecorder:applicationinsights">
<BasePolicy>
<TenantId>yourtenant.onmicrosoft.com</TenantId>
<PolicyId>B2C_1A_Phone_Email_Base_Extension</PolicyId>
</BasePolicy>
<UserJourneys>
<UserJourney Id="SignUpOrSignInWithPhoneOrEmail-Custom">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="signuporsignin-phone-email">
<ClaimsProviderSelections>
<ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninPhoneEmailExchange" />
<ClaimsProviderSelection TargetClaimsExchangeId="SignUpWithEmail" />
<ClaimsProviderSelection TargetClaimsExchangeId="SignUpWithPhone" />
<ClaimsProviderSelection TargetClaimsExchangeId="ChangePhoneNumber" />
<ClaimsProviderSelection TargetClaimsExchangeId="GoogleExchange" />
<ClaimsProviderSelection TargetClaimsExchangeId="FacebookExchange" />
<ClaimsProviderSelection TargetClaimsExchangeId="AppleExchange" />
</ClaimsProviderSelections>
<ClaimsExchanges>
<ClaimsExchange Id="LocalAccountSigninPhoneEmailExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Phone-Email" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>isLocalAccountSignIn</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="GoogleExchange" TechnicalProfileReferenceId="Google-OAuth2" />
<ClaimsExchange Id="FacebookExchange" TechnicalProfileReferenceId="Facebook-OAUTH" />
<ClaimsExchange Id="AppleExchange" TechnicalProfileReferenceId="Apple-OIDC" />
<ClaimsExchange Id="SignUpWithPhone" TechnicalProfileReferenceId="LocalAccountSignUpWithLogonPhoneNumber" />
<ClaimsExchange Id="SignUpWithEmail" TechnicalProfileReferenceId="LocalAccountSignUpWithLogonEmail" />
<ClaimsExchange Id="ChangePhoneNumber" TechnicalProfileReferenceId="PhoneInputPage-ChangePhoneNumberClaimsProviderSelection" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- If the user comes from phone signup, collect recovery email address -->
<OrchestrationStep Order="3" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>isLocalAccountSignIn</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>isEmailSignUp</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>isChangePhoneNumber</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>authenticationSource</Value>
<Value>socialIdpAuthentication</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="SignUpWithPhone_CollectEmailAddress" TechnicalProfileReferenceId="LocalAccountSignUpWithLogonPhoneNumber_CollectEmailAddress" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- Debugging Step (useful for the app insight)-->
<OrchestrationStep Order="4" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="Debugging Step to see the claim bag" TechnicalProfileReferenceId="Debug-TechProfile" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- For social IDP authentication, attempt to find the user account in the directory. -->
<OrchestrationStep Order="5" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>authenticationSource</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimEquals" ExecuteActionsIf="false">
<Value>authenticationSource</Value>
<Value>socialIdpAuthentication</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadUsingAlternativeSecurityId" TechnicalProfileReferenceId="AAD-UserReadUsingAlternativeSecurityId-NoError" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- 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="6" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>authenticationSource</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimEquals" ExecuteActionsIf="false">
<Value>authenticationSource</Value>
<Value>socialIdpAuthentication</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AADUserWrite" TechnicalProfileReferenceId="AAD-UserWriteUsingAlternativeSecurityId" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- Leave the rest of the template -->
<OrchestrationStep Order="7" Type="InvokeSubJourney">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>isLocalAccountSignIn</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<JourneyList>
<Candidate SubJourneyReferenceId="SignInWithPhoneOrEmail" />
</JourneyList>
</OrchestrationStep>
<OrchestrationStep Order="8" Type="InvokeSubJourney">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>isChangePhoneNumber</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<JourneyList>
<Candidate SubJourneyReferenceId="ChangePhoneNumber" />
</JourneyList>
</OrchestrationStep>
<OrchestrationStep Order="9" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>hasFullProfile</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="10" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>
</UserJourneys>
<RelyingParty>
<DefaultUserJourney ReferenceId="SignUpOrSignInWithPhoneOrEmail-Custom" />
<UserJourneyBehaviors>
<JourneyInsights TelemetryEngine="ApplicationInsights" InstrumentationKey="urkey" DeveloperMode="true" ClientEnabled="false" ServerEnabled="true" TelemetryVersion="1.0.0" />
<ScriptExecution>Allow</ScriptExecution>
</UserJourneyBehaviors>
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="OpenIdConnect" />
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surname" />
<OutputClaim ClaimTypeReferenceId="email" />
<OutputClaim ClaimTypeReferenceId="signInNames.phoneNumber" />
<OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" />
<OutputClaim ClaimTypeReferenceId="strongAuthenticationEmailAddress" />
<OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub" />
<OutputClaim ClaimTypeReferenceId="identityProvider" />
<OutputClaim ClaimTypeReferenceId="tenantId" AlwaysUseDefaultValue="true" DefaultValue="{Policy:TenantObjectId}" />
</OutputClaims>
<SubjectNamingInfo ClaimType="sub" />
</TechnicalProfile>
</RelyingParty>
</TrustFrameworkPolicy>
I have no clue what I need to modify to add the forgot password link since this email signin page is a separate screen different from the examples.
I posted a complete explanation on the original GH issue that explains how to add the link to any email signin screen. GH issue answer here

Self service password change with TOTP confirmation

Based on samples available on Github (https://github.com/azure-ad-b2c/samples/tree/master/policies/totp) I have implemented policies for enabling TOTP MFA.
Now, when the user wants to change his password (not reset via "Forgot your password?") I would like to introduce TOTP verification step if he has TOTP enabled.
First attempt:
<UserJourney Id="PasswordChange">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin">
<ClaimsProviderSelections>
<ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninEmailExchange" />
</ClaimsProviderSelections>
<ClaimsExchanges>
<ClaimsExchange Id="LocalAccountSigninEmailExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSigninOnly-Email" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="InvokeSubJourney">
<JourneyList>
<Candidate SubJourneyReferenceId="TotpFactor-Verify" />
</JourneyList>
</OrchestrationStep>
<OrchestrationStep Order="3" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="NewCredentials" TechnicalProfileReferenceId="LocalAccountWritePasswordChangeUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="4" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="5" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>
</UserJourneys>
Fails immediately when I try to upload policy via Azure Portal with error:
Orchestration step order "2" in user journey "PasswordChange" in policy "B2C_1A_TrustFrameworkExtensions" of tenant "{tenant}" is followed by a claims provider selection step and must be a claims exchange, but it is of type "InvokeSubJourney"
Second attempt:
Commented AAD-UserWritePasswordUsingObjectId ValidationTechnicalProfile in LocalAccountWritePasswordChangeUsingObjectId:
<ClaimsProvider>
<DisplayName>Local Account Password Change</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="LocalAccountWritePasswordChangeUsingObjectId">
<DisplayName>Change password (username)</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="ContentDefinitionReferenceId">api.selfasserted</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="objectId" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="oldPassword" Required="true" />
<OutputClaim ClaimTypeReferenceId="newPassword" Required="true" />
<OutputClaim ClaimTypeReferenceId="reenterPassword" Required="true" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="login-NonInteractive-PasswordChange" />
<!-- <ValidationTechnicalProfile ReferenceId="AAD-UserWritePasswordUsingObjectId" /> -->
</ValidationTechnicalProfiles>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
UserJourney:
<UserJourney Id="PasswordChange">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin">
<ClaimsProviderSelections>
<ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninEmailExchange" />
</ClaimsProviderSelections>
<ClaimsExchanges>
<ClaimsExchange Id="LocalAccountSigninEmailExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSigninOnly-Email" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="NewCredentials" TechnicalProfileReferenceId="LocalAccountWritePasswordChangeUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="3" Type="InvokeSubJourney">
<JourneyList>
<Candidate SubJourneyReferenceId="TotpFactor-Verify" />
</JourneyList>
</OrchestrationStep>
<OrchestrationStep Order="4" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserWritePasswordUsingObjectId" TechnicalProfileReferenceId="AAD-UserWritePasswordUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="5" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="6" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>
As you can see I added OrchestrationStep 4 of type ClaimsExchange. It runs AAD-UserWritePasswordUsingObjectId.
I can successfully go through this journey without any errors, however password is not updated.
I managed to get this working by reverting changes made in LocalAccountWritePasswordChangeUsingObjectId TechnicalProfile (so basically uncommenting ValidationTechnical profile I have commented before) and with the following UserJourney.
<UserJourney Id="PasswordChange">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin">
<ClaimsProviderSelections>
<ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninEmailExchange" />
</ClaimsProviderSelections>
<ClaimsExchanges>
<ClaimsExchange Id="LocalAccountSigninEmailExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSigninOnly-Email" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges />
</OrchestrationStep>
<OrchestrationStep Order="3" Type="InvokeSubJourney">
<JourneyList>
<Candidate SubJourneyReferenceId="TotpFactor-Verify" />
</JourneyList>
</OrchestrationStep>
<OrchestrationStep Order="4" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="NewCredentials" TechnicalProfileReferenceId="LocalAccountWritePasswordChangeUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="5" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="6" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>
As you can see, here, as a second step, I had to add an empty step of type ClaimsExchange. It should be always skipped, as only available sign in method of step 1 is local account so objectId should always be present.
I can successfully upload such policy. It works without any errors and changes user password.
However, this empty step looks strange.

Sign Up and Sign In with 'Terms of Use' prompt - Internal Error Server

I've been debugging this internal server error using application insights it's been hours now and I believe it's time to ask for help. My custom policy is been enhanced with 'Terms of Use' consent and my goal is simply to adapt the existing sample on it to my policy scenario. Easier said than done!
The error happens after the "SelfAsserted-Input-ToU-SignIn" technical profile is called, which is, at the Sign-In with extension_termsOfUseConsentDateTime being prior to the date configured at IsTermsOfUseConsentRequiredForDateTime.
Here is a piece of the user journey:
...
<!-- 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="8" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="9" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="Check-TOU-Status" TechnicalProfileReferenceId="Check-TOU-Status" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- Display Terms of Use consent page for any SignIn scenario based on termsOfUseConsentRequired claim -->
<OrchestrationStep Order="10" Type="ClaimsExchange">
<Preconditions>
<!-- Add condition to not execute this step for sign up scenario based on newUser claim -->
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>newUser</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimEquals" ExecuteActionsIf="false">
<Value>termsOfUseConsentRequired</Value>
<Value>True</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="ShowToUConsentPageForNewUser" TechnicalProfileReferenceId="SelfAsserted-Input-ToU-SignIn" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="11" Type="ClaimsExchange">
<Preconditions>
<!-- Add condition to not execute this step for sign up scenario based on newUser claim -->
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>newUser</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="RESTAuthB2CSignIn" TechnicalProfileReferenceId="REST-AuthB2CSignIn" />
</ClaimsExchanges>
...
Application Insights does not show anything meaningful to me. No exceptions, no missing claims, etc., and the error message from the browser inner HTML doesn't help much as well, here one can see part of it:
<!DOCTYPE html>
--
  | <!-- Build: 1.0.2493.0 -->
  | <!-- StateVersion: 2.1.1 -->
  | <!-- DeploymentMode: Development -->
  | <!-- CorrelationId: ea9e5f4d-7e67-4253-8b1b-3f9ef32e43aa -->
  | <!-- DataCenter: SAN -->
  | <!-- Slice: 001-000 -->
  | <html lang="en-US"><head><link rel="icon" href="data:;base64,iVBORw0KGgo="><script data-container="true" nonce="uckM+NtdqgKmNou1EkScIg==">var GLOBALEX = {
  | "CorrelationId": "ea9e5f4d-7e67-4253-8b1b-3f9ef32e43aa",
  | "Timestamp": "2022-02-08 18:59:26Z",
  | "Detail": "AADB2C: An exception has occurred."
  | };
  |  
  |  
  | var CONTENT = {
  | "contact-none": "Your administrator hasn't provided any contact details.",
  | "contact-number-label": "Telephone",
  | "contact-email-label": "Email",
  | "error-title": "Sorry, but we're having trouble signing you in.",
  | "error-help": "We track these errors automatically, but if the problem persists feel free to contact us. In the meantime, please try again."
  | };
What might be happening here?
All the involved policies + a full log file from application insights from the user journey can be found here.
Really appreciate your input on it. Thank you!
It turns out that I was missing output claims on the Update-TOU-Status technical profile.
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="termsOfUseConsentRequired" DefaultValue="false" AlwaysUseDefaultValue="true" />
</OutputClaims>

Azure AD B2C: Invoke a subjouney whenever a user clicks to sign up

Is it possible to call a sub journey for users who intend to sign up during one of the main user journeys ClaimExchange?
For instance, how can I replace <ClaimsExchange Id="SignUpWithLogonEmailExchange" TechnicalProfileReferenceId="SelfAsserted-SignUp-Input"/>
for a "InvokeSubJourney" orchestration step?
My goal is to display the social providers' buttons once again whenever a user intends to sign up, but now with the buttons labeled as Sign up with <IdP>, and of course, a reference to TechnicalProfileReferenceId="SelfAsserted-SignUp-Input".
This is a piece of my user journey:
<UserJourneys>
<UserJourney Id="AccountLinkSignUpOrSignIn" DefaultCpimIssuerTechnicalProfileReferenceId="JwtIssuer">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin">
<ClaimsProviderSelections>
<ClaimsProviderSelection TargetClaimsExchangeId="FacebookExchange"/>
<ClaimsProviderSelection TargetClaimsExchangeId="LinkedInExchange" />
<ClaimsProviderSelection TargetClaimsExchangeId="GoogleExchange" />
<ClaimsProviderSelection TargetClaimsExchangeId="MicrosoftAccountExchange"/>
<ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninEmailExchange"/>
</ClaimsProviderSelections>
<ClaimsExchanges>
<ClaimsExchange Id="LocalAccountSigninEmailExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Email"/>
</ClaimsExchanges>
</OrchestrationStep>
<!-- Check if the user has selected to sign in using one of the social providers -->
<OrchestrationStep Order="2" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="FacebookExchange" TechnicalProfileReferenceId="Facebook-OAUTH-SignIn"/>
<ClaimsExchange Id="LinkedInExchange" TechnicalProfileReferenceId="LinkedIn-OAuth2-SignIn" />
<ClaimsExchange Id="GoogleExchange" TechnicalProfileReferenceId="Google-OAuth2-SignIn" />
<ClaimsExchange Id="MicrosoftAccountExchange" TechnicalProfileReferenceId="MSA-OIDC-SignIn"/>
<!-- Invoke subjourney at this point -->
<ClaimsExchange Id="SignUpWithLogonEmailExchange" TechnicalProfileReferenceId="SelfAsserted-SignUp-Input"/>
<!-- ### -->
</ClaimsExchanges>
</OrchestrationStep>
Finally, the subjourney:
<SubJourney Id="IdentityProviderSelection_SignUp" Type="Call">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="ClaimsProviderSelection" ContentDefinitionReferenceId="api.idpselections.signup">
<ClaimsProviderSelections>
<ClaimsProviderSelection TargetClaimsExchangeId="MicrosoftAccountExchange" />
<ClaimsProviderSelection TargetClaimsExchangeId="GoogleExchange" />
<ClaimsProviderSelection TargetClaimsExchangeId="FacebookExchange" />
<ClaimsProviderSelection TargetClaimsExchangeId="LinkedInExchange" />
<ClaimsProviderSelection TargetClaimsExchangeId="SignUpWithLogonEmailExchange" />
</ClaimsProviderSelections>
</OrchestrationStep>
<!-- Check if the user has selected to sign in using one of the social providers -->
<OrchestrationStep Order="2" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="FacebookExchange" TechnicalProfileReferenceId="Facebook-OAUTH-SignIn"/>
<ClaimsExchange Id="LinkedInExchange" TechnicalProfileReferenceId="LinkedIn-OAuth2-SignIn" />
<ClaimsExchange Id="GoogleExchange" TechnicalProfileReferenceId="Google-OAuth2-SignIn" />
<ClaimsExchange Id="MicrosoftAccountExchange" TechnicalProfileReferenceId="MSA-OIDC-SignIn"/>
<ClaimsExchange Id="SignUpWithLogonEmailExchange" TechnicalProfileReferenceId="SelfAsserted-SignUp-Input"/>
</ClaimsExchanges>
</OrchestrationStep>
</OrchestrationSteps>
</SubJourney>
</SubJourneys>```
As of now, it´s not possible to call any subjouney other than the Invokesubjourney type at the orchestration step.
More: https://learn.microsoft.com/en-us/answers/questions/671165/azure-ad-b2c-invoke-a-subjouney-whenever-a-user-cl.html#comment-686577

Custom Policy for Force Reset password on first login not working

I am trying to use https://github.com/azure-ad-b2c/samples/blob/master/policies/force-password-reset-first-logon to implement the Password reset on initial login for local accounts. I have followed all the steps in https://learn.microsoft.com/en-us/azure/active-directory-b2c/custom-policy-get-started except creating a Facebook secret key since I need to sign in using only local accounts. While testing the policy, when I sign-in using my email address, I am able to successfully login to my application instead of getting re-directed to the password reset page and the custom attribute is not cleared.
I am using Graph API to set the custom attribute "Extenstion_000000000000000000000000000000000_mustResetPassword" with user creation and ForceChangePasswordNextSignIn property is set to false. Can you please direct me where I am doing this wrong?
I would like the user to sign in for the first time and get redirected to reset the password and clear the custom attribute. Please Help!
I am creating user using Graph API
var result = await graphClient.Users
.Request()
.AddAsync(new User
{
GivenName = user.FirstName,
Surname = user.LastName,
DisplayName = user.UserName,
Identities = new List<ObjectIdentity>
{
new ObjectIdentity()
{
SignInType = SignInType.emailAddress.ToDescription(),
Issuer = config.TenantId,
IssuerAssignedId = user.Email
}
},
PasswordProfile = new PasswordProfile()
{
Password = password,
ForceChangePasswordNextSignIn =false
},
PasswordPolicies = "DisablePasswordExpiration",
AdditionalData = extensionInstance
});
**TrustFrameworkExtensions.xml code**
<?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="tenantId.onmicrosoft.com"
PolicyId="B2C_1A_TrustFrameworkExtensions"
PublicPolicyUri="http://tenantId.onmicrosoft.com/B2C_1A_TrustFrameworkExtensions">
<BasePolicy>
<TenantId>tenantId.onmicrosoft.com</TenantId>
<PolicyId>B2C_1A_TrustFrameworkBase</PolicyId>
</BasePolicy>
<BuildingBlocks>
<ClaimsSchema>
<!--Demo: Specifies whether user must reset the password-->
<ClaimType Id="extension_mustResetPassword">
<DisplayName>Must reset password</DisplayName>
<DataType>boolean</DataType>
<UserHelpText>Specifies whether user must reset the password</UserHelpText>
</ClaimType>
</ClaimsSchema>
</BuildingBlocks>
<ClaimsProviders>
<ClaimsProvider>
<DisplayName>Local Account SignIn</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="login-NonInteractive">
<Metadata>
<Item Key="client_id">00000000-0000-0000-0000-000000000000</Item>
<Item Key="IdTokenAudience">00000000-0000-0000-0000-000000000000</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="client_id" DefaultValue="00000000-0000-0000-0000-000000000000" />
<InputClaim ClaimTypeReferenceId="resource_id" PartnerClaimType="resource" DefaultValue="00000000-0000-0000-0000-000000000000" />
</InputClaims>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>Azure Active Directory</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="AAD-Common">
<DisplayName>Azure Active Directory</DisplayName>
<!-- Demo action required: Provide objectId and appId before using extension properties.
For more information: https://learn.microsoft.com/en-us/azure/active-directory-b2c/active-directory-b2c-create-custom-attributes-profile-edit-custom
Action required: Insert objectId and appId here -->
<Metadata>
<Item Key="ApplicationObjectId">00000000-0000-0000-0000-000000000000</Item>
<Item Key="ClientId">00000000-0000-0000-0000-000000000000</Item>
</Metadata>
</TechnicalProfile>
<TechnicalProfile Id="AAD-UserReadUsingObjectId">
<OutputClaims>
<!--Demo: Read the 'must reset password' extension attribute -->
<OutputClaim ClaimTypeReferenceId="extension_mustResetPassword" />
</OutputClaims>
</TechnicalProfile>
<TechnicalProfile Id="AAD-UserRemoveMustResetPasswordUsingObjectId">
<Metadata>
<Item Key="Operation">DeleteClaims</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="objectId" Required="true" />
</InputClaims>
<PersistedClaims>
<PersistedClaim ClaimTypeReferenceId="objectId" />
<PersistedClaim ClaimTypeReferenceId="extension_mustResetPassword" />
</PersistedClaims>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
</TechnicalProfile>
<!--Demo: to create the extension attribute extension_mustResetPassword, you should upload the policy
and create one account. Then ***comment out this technical profile***.
-->
<TechnicalProfile Id="AAD-UserWriteUsingLogonEmail">
<PersistedClaims>
<PersistedClaim ClaimTypeReferenceId="extension_mustResetPassword" DefaultValue="true" />
</PersistedClaims>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
</ClaimsProviders>
<UserJourneys>
<UserJourney Id="SignUpOrSignInWithForcePasswordReset">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin">
<ClaimsProviderSelections>
<ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninEmailExchange" />
</ClaimsProviderSelections>
<ClaimsExchanges>
<ClaimsExchange Id="LocalAccountSigninEmailExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Email" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- Check if the user has selected to sign in using one of the social providers -->
<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>
<!-- 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="3" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>authenticationSource</Value>
<Value>localAccountAuthentication</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<!--Demo: check if change password is required. If yes, ask the user to reset the password-->
<OrchestrationStep Order="4" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>extension_mustResetPassword</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimEquals" ExecuteActionsIf="false">
<Value>extension_mustResetPassword</Value>
<Value>True</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="NewCredentials" TechnicalProfileReferenceId="LocalAccountWritePasswordUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<!--Demo: check if change password is required. If yes remove the value of the extension attribute.
So, on the next time user dons' t need to update the password-->
<OrchestrationStep Order="5" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>extension_mustResetPassword</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimEquals" ExecuteActionsIf="false">
<Value>extension_mustResetPassword</Value>
<Value>True</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AADUserRemoveMustResetPasswordUsingObjectId" TechnicalProfileReferenceId="AAD-UserRemoveMustResetPasswordUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="6" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>
</UserJourneys>
</TrustFrameworkPolicy>
Object details in Azure
[
{
"accountEnabled": true,
"assignedLicenses": [],
"assignedPlans": [],
"businessPhones": [],
"createdDateTime": "2020-12-17T08:22:17+00:00",
"creationType": "LocalAccount",
"deviceKeys": [],
"displayName": "DevM8",
"givenName": "Dev",
"identities": [
{
"signInType": "emailAddress",
"issuer": "tenantId.onmicrosoft.com",
"issuerAssignedId": "Dev.M8#test.com",
"#odata.type": "microsoft.graph.objectIdentity"
},
{
"signInType": "userPrincipalName",
"issuer": "tenantId.onmicrosoft.com",
"issuerAssignedId": "a2a5dbe2-7ba7-42a4-bd9a-67eb41c05d7e#tenantId.onmicrosoft.com",
"#odata.type": "microsoft.graph.objectIdentity"
}
],
"imAddresses": [],
"mailNickname": "a2a5dbe2-7ba7-42a4-bd9a-67eb41c05d7e",
"onPremisesExtensionAttributes": {
"#odata.type": "microsoft.graph.onPremisesExtensionAttributes",
"extensionAttribute1": null,
"extensionAttribute2": null,
"extensionAttribute3": null,
"extensionAttribute4": null,
"extensionAttribute5": null,
"extensionAttribute6": null,
"extensionAttribute7": null,
"extensionAttribute8": null,
"extensionAttribute9": null,
"extensionAttribute10": null,
"extensionAttribute11": null,
"extensionAttribute12": null,
"extensionAttribute13": null,
"extensionAttribute14": null,
"extensionAttribute15": null
},
"onPremisesProvisioningErrors": [],
"otherMails": [],
"passwordPolicies": "DisablePasswordExpiration",
"provisionedPlans": [],
"proxyAddresses": [],
"refreshTokensValidFromDateTime": "2020-12-17T08:22:16+00:00",
"signInSessionsValidFromDateTime": "2020-12-17T08:22:16+00:00",
"surname": "M",
"userPrincipalName": "a2a5dbe2-7ba7-42a4-bd9a-67eb41c05d7e#tenantId.onmicrosoft.com",
"userType": "Member",
"id": "a2a5dbe2-7ba7-42a4-bd9a-67eb41c05d7e",
"#odata.type": "microsoft.graph.user",
"deletedDateTime": null,
"ageGroup": null,
"city": null,
"companyName": null,
"consentProvidedForMinor": null,
"country": null,
"department": null,
"employeeId": null,
"employeeHireDate": null,
"employeeOrgData": null,
"employeeType": null,
"faxNumber": null,
"infoCatalogs": [],
"isManagementRestricted": null,
"isResourceAccount": null,
"jobTitle": null,
"legalAgeGroupClassification": null,
"mail": null,
"mobilePhone": null,
"onPremisesDistinguishedName": null,
"officeLocation": null,
"onPremisesDomainName": null,
"onPremisesImmutableId": null,
"onPremisesLastSyncDateTime": null,
"onPremisesSecurityIdentifier": null,
"onPremisesSamAccountName": null,
"onPremisesSyncEnabled": null,
"onPremisesUserPrincipalName": null,
"passwordProfile": null,
"postalCode": null,
"preferredDataLocation": null,
"preferredLanguage": null,
"showInAddressList": null,
"state": null,
"streetAddress": null,
"usageLocation": null,
"externalUserState": null,
"externalUserStateChangeDateTime": null,
"extension_185724b7875d4374904106f92b4b951e_FavouriteSeason": "summer",
"extension_185724b7875d4374904106f92b4b951e_mustResetPassword": true,
"extension_185724b7875d4374904106f92b4b951e_LovesPets": true
}
]
AAD-Common Technical profile
<TechnicalProfile Id="AAD-Common">
<DisplayName>Azure Active Directory</DisplayName>
<!-- Demo action required: Provide objectId and appId before using extension properties.
For more information: https://learn.microsoft.com/en-us/azure/active-directory-b2c/active-directory-b2c-create-custom-attributes-profile-edit-custom
Action required: Insert objectId and appId here -->
<Metadata>
<Item Key="ApplicationObjectId">90aab09b-721e-4c95-b1e5-52266eb727a4</Item>
<Item Key="ClientId">96e21f60-871b-48a0-867c-404c4ebfa6de</Item>
</Metadata>
</TechnicalProfile>
Thank you for your question.
As of now, the only possible way to require users to reset their passwords at first logon is by using custom policy: https://github.com/azure-ad-b2c/samples/tree/master/policies/force-password-reset-first-logon. So you are following the Correct way.
While creating local accounts in B2C via Graph API, the forceChangePasswordNextSignIn property must be set to false.
Please see this DOC .
Could you please Check again.
I had this exact issue and just figured it out, to use the AAD methods you have to include
<Metadata>
<Item Key="ApplicationObjectId">00000000-0000-0000-0000-000000000000</Item>
<Item Key="ClientId">00000000-0000-0000-0000-000000000000</Item>
</Metadata>
On each TechnicalProfile, so you need to add it to:
AAD-UserReadUsingObjectId
AAD-UserRemoveMustResetPasswordUsingObjectId
I found if not included the AAD request fails, took me awhile debugging this with application insights hooked up, there is a really nice extension for VS code Azure AD B2C tools that lets you directly hook up to an app insights instance attached to your user flow, and it shows where an exception occurs in the flow. Without this you get almost 0 feedback on the internal errors

Resources