Azure B2C | Password Reset sending to wrong UserJourney

Facundo Barbera 41 Reputation points
2021-09-10T16:06:50.253+00:00

I followed the following tutorial: Password reset custom policy

I followed each step and did not get any errors when uploading the files to the b2c.

What the policy does is add a password reset option in the SignUpSignIn Policy. The button appears and it's clickable, but when clicked I'm redirected to the SignUp part of the policy instead of the password reset.

At first I thought it might be a template for the password reset, but when trying to use it to "create" an account the "create" button won't do anything.

The code for the Journeys is the following:

SignUpSignIn Journey:  
  
<UserJourney Id="SignUpOrSignIn">  
      <OrchestrationSteps>  
        
        <OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin">  
          <ClaimsProviderSelections>  
            <ClaimsProviderSelection TargetClaimsExchangeId="ForgotPasswordExchange" />  
            <ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninEmailExchange" />  
          </ClaimsProviderSelections>  
          <ClaimsExchanges>  
            <ClaimsExchange Id="ForgotPasswordExchange" TechnicalProfileReferenceId="ForgotPassword" />  
            <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>  
  
        <OrchestrationStep Order="3" Type="InvokeSubJourney">  
            <Preconditions>  
              <Precondition Type="ClaimsExist" ExecuteActionsIf="false">  
                <Value>isForgotPassword</Value>  
                <Action>SkipThisOrchestrationStep</Action>  
              </Precondition>  
            </Preconditions>  
            <JourneyList>  
              <Candidate SubJourneyReferenceId="PasswordReset" />  
            </JourneyList>  
          </OrchestrationStep>  
  
        <!-- For social IDP authentication, attempt to find the user account in the directory. -->  
        <OrchestrationStep Order="4" Type="ClaimsExchange">  
          <Preconditions>  
            <Precondition Type="ClaimEquals" ExecuteActionsIf="true">  
              <Value>authenticationSource</Value>  
              <Value>localAccountAuthentication</Value>  
              <Action>SkipThisOrchestrationStep</Action>  
            </Precondition>  
          </Preconditions>  
          <ClaimsExchanges>  
            <ClaimsExchange Id="AADUserReadUsingAlternativeSecurityId" TechnicalProfileReferenceId="AAD-UserReadUsingAlternativeSecurityId-NoError" />  
          </ClaimsExchanges>  
        </OrchestrationStep>  
  
        <!-- Show self-asserted page only if the directory does not have the user account already (i.e. we do not have an objectId).   
          This can only happen when authentication happened using a social IDP. If local account was created or authentication done  
          using ESTS in step 2, then an user account must exist in the directory by this time. -->  
        <OrchestrationStep Order="5" Type="ClaimsExchange">  
          <Preconditions>  
            <Precondition Type="ClaimsExist" ExecuteActionsIf="true">  
              <Value>objectId</Value>  
              <Action>SkipThisOrchestrationStep</Action>  
            </Precondition>  
          </Preconditions>  
          <ClaimsExchanges>  
            <ClaimsExchange Id="SelfAsserted-Social" TechnicalProfileReferenceId="SelfAsserted-Social" />  
          </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="6" Type="ClaimsExchange">  
          <Preconditions>  
            <Precondition Type="ClaimEquals" ExecuteActionsIf="true">  
              <Value>authenticationSource</Value>  
              <Value>socialIdpAuthentication</Value>  
              <Action>SkipThisOrchestrationStep</Action>  
            </Precondition>  
          </Preconditions>  
          <ClaimsExchanges>  
            <ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />  
          </ClaimsExchanges>  
        </OrchestrationStep>  
        <!-- The previous step (SelfAsserted-Social) could have been skipped if there were no attributes to collect   
             from the user. So, in that case, create the user in the directory if one does not already exist   
             (verified using objectId which would be set from the last step if account was created in the directory. -->  
        <OrchestrationStep Order="7" 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="8" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />  
   
      </OrchestrationSteps>  
      <ClientDefinition ReferenceId="DefaultWeb" />  
    </UserJourney>  

PasswordReset subjourney:  
    <SubJourneys>  
        <SubJourney Id="PasswordReset" Type="Call">  
          <OrchestrationSteps>  
      
            <OrchestrationStep Order="1" Type="ClaimsExchange">  
              <ClaimsExchanges>  
                <ClaimsExchange Id="PasswordResetUsingEmailAddressExchange" TechnicalProfileReferenceId="LocalAccountDiscoveryUsingEmailAddress" />  
              </ClaimsExchanges>  
            </OrchestrationStep>  
      
            <OrchestrationStep Order="2" Type="ClaimsExchange">  
              <ClaimsExchanges>  
                <ClaimsExchange Id="NewCredentials" TechnicalProfileReferenceId="LocalAccountWritePasswordUsingObjectId" />  
              </ClaimsExchanges>  
            </OrchestrationStep>  
      
            <OrchestrationStep Order="3" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />  
      
          </OrchestrationSteps>  
        </SubJourney>  
      </SubJourneys>  
Microsoft Entra External ID
Microsoft Entra External ID
A modern identity solution for securing access to customer, citizen and partner-facing apps and services. It is the converged platform of Azure AD External Identities B2B and B2C. Replaces Azure Active Directory External Identities.
2,639 questions
Microsoft Entra ID
Microsoft Entra ID
A Microsoft Entra identity service that provides identity management and access control capabilities. Replaces Azure Active Directory.
19,459 questions
{count} votes

2 answers

Sort by: Most helpful
  1. Jitendra Rai 231 Reputation points
    2021-09-28T10:17:17.357+00:00

    Thanks and could you make sure the precondition of step 3 is failed to call subjourney - passwordReset otherwise it will skip this step 3 and it will goto step 4? Please use Application Insight for the steps troubleshoot. .

    Please find the document to use Application Insight with B2C:-
    https://learn.microsoft.com/en-us/azure/active-directory-b2c/troubleshoot-with-application-insights

    0 comments No comments

  2. singhh-msft 2,431 Reputation points
    2021-09-29T09:36:55.957+00:00

    @Facundo Barbera , thank you for reaching out to us. I suspect that orchestration step 3 (SendClaims) might not be required. Can you have a look at Sign-up and Sign-in with embedded password reset sample once? And use below policy files instead:

    SignUpOrSigninCustomSelfServicePasswordReset.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="yourtenant.onmicrosoft.com" PolicyId="B2C_1A_signup_signin_Custom_SelfServicePasswordReset" PublicPolicyUri="http://yourtenant.onmicrosoft.com/B2C_1A_signup_signin_Custom_SelfServicePasswordReset">  
      
      <BasePolicy>  
        <TenantId>yourtenant.onmicrosoft.com</TenantId>  
        <PolicyId>B2C_1A_TrustFrameworkExtensions_custom_SelfServicePasswordReset</PolicyId>  
      </BasePolicy>  
      
      <RelyingParty>  
        <DefaultUserJourney ReferenceId="SignUpOrSignIn-Custom" />  
        <TechnicalProfile Id="PolicyProfile">  
          <DisplayName>PolicyProfile</DisplayName>  
          <Protocol Name="OpenIdConnect" />  
          <OutputClaims>  
            <OutputClaim ClaimTypeReferenceId="displayName" />  
            <OutputClaim ClaimTypeReferenceId="givenName" />  
            <OutputClaim ClaimTypeReferenceId="surname" />  
            <OutputClaim ClaimTypeReferenceId="email" />  
            <OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub" />  
            <OutputClaim ClaimTypeReferenceId="identityProvider" />  
            <OutputClaim ClaimTypeReferenceId="tenantId" AlwaysUseDefaultValue="true" DefaultValue="{Policy:TenantObjectId}" />  
          </OutputClaims>  
          <SubjectNamingInfo ClaimType="sub" />  
        </TechnicalProfile>  
      </RelyingParty>  
    </TrustFrameworkPolicy>  
    

    TrustFrameworkExtensionsCustomSelfServicePasswordReset.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="yourtenant.onmicrosoft.com" PolicyId="B2C_1A_TrustFrameworkExtensions_custom_SelfServicePasswordReset" PublicPolicyUri="http://yourtenant.onmicrosoft.com/B2C_1A_TrustFrameworkExtensions_custom_SelfServicePasswordReset">  
      
      <BasePolicy>  
        <TenantId>yourtenant.onmicrosoft.com</TenantId>  
        <PolicyId>B2C_1A_TrustFrameworkExtensions</PolicyId>  
      </BasePolicy>  
      <BuildingBlocks>  
        <ClaimsSchema>  
          <ClaimType Id="isForgotPassword">  
            <DisplayName>isForgotPassword</DisplayName>  
            <DataType>boolean</DataType>  
            <AdminHelpText>Whether the user has clicked Forgot Password</AdminHelpText>  
          </ClaimType>  
        </ClaimsSchema>  
        <ContentDefinitions>  
          <ContentDefinition Id="api.signuporsigninwithpasswordreset">  
            <LoadUri>~/tenant/default/unified.cshtml</LoadUri>  
            <RecoveryUri>~/common/default_page_error.html</RecoveryUri>  
            <DataUri>urn:com:microsoft:aad:b2c:elements:contract:unifiedssp:2.1.2</DataUri>  
            <Metadata>  
              <Item Key="DisplayName">Signin and Signup</Item>  
            </Metadata>  
          </ContentDefinition>  
        </ContentDefinitions>  
      </BuildingBlocks>  
      
      <ClaimsProviders>  
        <ClaimsProvider>  
          <DisplayName>Local Account</DisplayName>  
          <TechnicalProfiles>  
            <TechnicalProfile Id="ForgotPassword">  
              <DisplayName>Forgot your password?</DisplayName>  
              <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.ClaimsTransformationProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>  
              <OutputClaims>  
                <OutputClaim ClaimTypeReferenceId="isForgotPassword" DefaultValue="true" AlwaysUseDefaultValue="true"/>  
              </OutputClaims>  
            </TechnicalProfile>  
            <TechnicalProfile Id="SelfAsserted-LocalAccountSignin-Email">  
              <Metadata>  
                <Item Key="setting.forgotPasswordLinkOverride">ForgotPasswordExchange</Item>  
              </Metadata>  
            </TechnicalProfile>  
          </TechnicalProfiles>  
        </ClaimsProvider>  
      </ClaimsProviders>  
      
      <UserJourneys>  
      
        <UserJourney Id="SignUpOrSignIn-Custom">  
          <OrchestrationSteps>  
      
            <OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsigninwithpasswordreset">  
              <ClaimsProviderSelections>  
                <ClaimsProviderSelection TargetClaimsExchangeId="FacebookExchange"/>  
                <ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninEmailExchange"/>  
                <ClaimsProviderSelection TargetClaimsExchangeId="ForgotPasswordExchange" />  
              </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"/>  
                <ClaimsExchange Id="SignUpWithLogonEmailExchange" TechnicalProfileReferenceId="LocalAccountSignUpWithLogonEmail"/>  
                <ClaimsExchange Id="ForgotPasswordExchange" TechnicalProfileReferenceId="ForgotPassword" />  
              </ClaimsExchanges>  
            </OrchestrationStep>  
      
            <OrchestrationStep Order="3" Type="InvokeSubJourney">  
              <Preconditions>  
                <Precondition Type="ClaimsExist" ExecuteActionsIf="false">  
                  <Value>isForgotPassword</Value>  
                  <Action>SkipThisOrchestrationStep</Action>  
                </Precondition>  
              </Preconditions>  
              <JourneyList>  
                <Candidate SubJourneyReferenceId="PasswordReset" />  
              </JourneyList>  
            </OrchestrationStep>  
      
            <!-- For social IDP authentication, attempt to find the user account in the directory. -->  
            <OrchestrationStep Order="4" Type="ClaimsExchange">  
              <Preconditions>  
                <Precondition Type="ClaimEquals" ExecuteActionsIf="true">  
                  <Value>authenticationSource</Value>  
                  <Value>localAccountAuthentication</Value>  
                  <Action>SkipThisOrchestrationStep</Action>  
                </Precondition>  
              </Preconditions>  
              <ClaimsExchanges>  
                <ClaimsExchange Id="AADUserReadUsingAlternativeSecurityId" TechnicalProfileReferenceId="AAD-UserReadUsingAlternativeSecurityId-NoError"/>  
              </ClaimsExchanges>  
            </OrchestrationStep>  
      
            <!-- Show self-asserted page only if the directory does not have the user account already (i.e. we do not have an objectId).   
              This can only happen when authentication happened using a social IDP. If local account was created or authentication done  
              using ESTS in step 2, then an user account must exist in the directory by this time. -->  
            <OrchestrationStep Order="5" Type="ClaimsExchange">  
              <Preconditions>  
                <Precondition Type="ClaimsExist" ExecuteActionsIf="true">  
                  <Value>objectId</Value>  
                  <Action>SkipThisOrchestrationStep</Action>  
                </Precondition>  
              </Preconditions>  
              <ClaimsExchanges>  
                <ClaimsExchange Id="SelfAsserted-Social" TechnicalProfileReferenceId="SelfAsserted-Social"/>  
              </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="6" Type="ClaimsExchange">  
              <Preconditions>  
                <Precondition Type="ClaimEquals" ExecuteActionsIf="true">  
                  <Value>authenticationSource</Value>  
                  <Value>socialIdpAuthentication</Value>  
                  <Action>SkipThisOrchestrationStep</Action>  
                </Precondition>  
              </Preconditions>  
              <ClaimsExchanges>  
                <ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId"/>  
              </ClaimsExchanges>  
            </OrchestrationStep>  
            <!-- The previous step (SelfAsserted-Social) could have been skipped if there were no attributes to collect   
                 from the user. So, in that case, create the user in the directory if one does not already exist   
                 (verified using objectId which would be set from the last step if account was created in the directory. -->  
            <OrchestrationStep Order="7" 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="8" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer"/>  
      
          </OrchestrationSteps>  
          <ClientDefinition ReferenceId="DefaultWeb"/>  
        </UserJourney>  
      </UserJourneys>  
      <SubJourneys>  
        <SubJourney Id="PasswordReset" Type="Call">  
          <OrchestrationSteps>  
            <!--Sample: Validate user's email address. Run this step only when user resets the password-->  
            <OrchestrationStep Order="1" Type="ClaimsExchange">  
              <ClaimsExchanges>  
                <ClaimsExchange Id="PasswordResetUsingEmailAddressExchange" TechnicalProfileReferenceId="LocalAccountDiscoveryUsingEmailAddress" />  
              </ClaimsExchanges>  
            </OrchestrationStep>  
      
            <!--Sample: Collect and persist a new password. Run this step only when user resets the password-->  
            <OrchestrationStep Order="2" Type="ClaimsExchange">  
              <ClaimsExchanges>  
                <ClaimsExchange Id="NewCredentials" TechnicalProfileReferenceId="LocalAccountWritePasswordUsingObjectId" />  
              </ClaimsExchanges>  
            </OrchestrationStep>  
          </OrchestrationSteps>  
        </SubJourney>  
      </SubJourneys>  
    </TrustFrameworkPolicy>  
    

    Note: This sample policy is based on SocialAndLocalAccounts starter pack. All changes are marked with Demo: comment inside the policy XML files. Make the necessary changes in the Demo action required sections.

    Let me know if you have any follow-up questions.

    -----------------------------------------------------------------------------------------------------------

    Please "Accept the answer" and upvote if the information helped you. This will help us and others in the community as well.

    0 comments No comments