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
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
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
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
I've followed the B2C - ADFS article.
I connect to ADFS, authenticate successfully but then B2C throws the above error.
I am using the latest SocialandLocalAccounts template.
My XML in the Extensions XML.
<ClaimsProvider>
<Domain>ABC.com</Domain>
<DisplayName>ABC ADFS</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="ABC-SAML2">
<DisplayName>ABC staff</DisplayName>
<Description>Login with your ADFS account</Description>
<Protocol Name="SAML2"/>
<Metadata>
<Item Key="WantsEncryptedAssertions">false</Item>
<Item Key="PartnerEntity">https://my-adfs.southeastasia.cloudapp.azure.com/federationmetadata/2007-06/federationmetadata.xml</Item>
<Item Key="XmlSignatureAlgorithm">Sha256</Item>
</Metadata>
<CryptographicKeys>
<Key Id="SamlAssertionSigning" StorageReferenceId="B2C_1A_SamlCert"/>
<Key Id="SamlMessageSigning" StorageReferenceId="B2C_1A_SamlCert"/>
</CryptographicKeys>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="issuerUserId" PartnerClaimType="userPrincipalName" />
<OutputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="given_name"/>
<OutputClaim ClaimTypeReferenceId="surname" PartnerClaimType="family_name"/>
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="email"/>
<OutputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="name"/>
<OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="ABC.com" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="socialIdpAuthentication"/>
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="CreateRandomUPNUserName"/>
<OutputClaimsTransformation ReferenceId="CreateUserPrincipalName"/>
<OutputClaimsTransformation ReferenceId="CreateAlternativeSecurityId"/>
<OutputClaimsTransformation ReferenceId="CreateSubjectClaimFromAlternativeSecurityId"/>
</OutputClaimsTransformations>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop"/>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
</ClaimsProviders>
<UserJourneys>
<UserJourney Id="SignUpSignInADFS">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin">
<ClaimsProviderSelections>
<ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninEmailExchange" />
<ClaimsProviderSelection TargetClaimsExchangeId="ABCExchange" />
</ClaimsProviderSelections>
<ClaimsExchanges>
<ClaimsExchange Id="LocalAccountSigninEmailExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Email" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="SignUpWithLogonEmailExchange" TechnicalProfileReferenceId="LocalAccountSignUpWithLogonEmail" />
<ClaimsExchange Id="ABCExchange" TechnicalProfileReferenceId="ABC-SAML2" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="3" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="4" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>
</UserJourneys>
This is what is returned in the SAML token from ADFS:
<AttributeStatement>
<Attribute Name="userPrincipalName">
<AttributeValue>user1#test.dev.com</AttributeValue>
</Attribute>
<Attribute Name="family_name">
<AttributeValue>Test</AttributeValue>
</Attribute>
<Attribute Name="given_name">
<AttributeValue>User1</AttributeValue>
</Attribute>
<Attribute Name="email">
<AttributeValue>user1#company.com</AttributeValue>
</Attribute>
<Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name">
<AttributeValue>User1 Test</AttributeValue>
</Attribute>
<Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress">
<AttributeValue>user1#company.com</AttributeValue>
</Attribute>
</AttributeStatement>
As above, make sure you are using "SocialAndLocalAccounts".