samples icon indicating copy to clipboard operation
samples copied to clipboard

Add forgot password link in Dynamic Sigin/Signup sample

Open sh-jthorpe opened this issue 2 years ago • 5 comments

I'm trying to use the https://github.com/azure-ad-b2c/samples/tree/master/policies/dynamic-sign-up-sign-in sample, however I need to add a forgot password link on step 2 (reminder: step 1 asks the user for their email, and checks if a user exists, if it does, step 2 asks the user for their password).

Step 1: image

Step 2: image

What's missing: image

I'm hoping someone else has run into the same problem, as surely nobody is rolling out login flows that don't allow you to reset your password :)

I've got self-service password reset working in other flows, however applying that to this flow hasn't worked (the flow works, but the forgot password link isn't shown).

The problem here I think is that there needs to be a ClaimsProviderSelection OrchestrationStep once the user gets to step 2, but i'm not sure how to do that, and the B2C custom policy docs are... confusing.

I noted a solution was provided in https://github.com/azure-ad-b2c/samples/issues/390, but that solution doesn't work, I think that's because they used a different sample, which doesn't have multiple steps like the dynamic signin/signup one does.

Any help would be greatly appreciated!

Here's my user journey. It's pretty much the same as the sample linked above, just with the forgot password stuff as per the docs (also above). The technical profiles and relying party is the same as the sample.

<UserJourney Id="DynamicSignUpOrSignIn">
  <OrchestrationSteps>
    <OrchestrationStep Order="1" Type="ClaimsExchange"
      ContentDefinitionReferenceId="api.signuporsignin">
      <ClaimsExchanges>
        <ClaimsExchange Id="LocalAccountSigninEmailExchange"
          TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Dynamic" />
      </ClaimsExchanges>
    </OrchestrationStep>

    <!-- Run this step when a user select the "Sign-up now" link -->
    <OrchestrationStep Order="2" Type="ClaimsExchange">
      <Preconditions>
        <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
          <Value>objectId</Value>
          <Action>SkipThisOrchestrationStep</Action>
        </Precondition>
        <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
          <Value>readOnlyEmail</Value>
          <Action>SkipThisOrchestrationStep</Action>
        </Precondition>
      </Preconditions>
      <ClaimsExchanges>
        <ClaimsExchange Id="SignUpWithLogonEmailExchange"
          TechnicalProfileReferenceId="LocalAccountSignUpWithLogonEmail" />
      </ClaimsExchanges>
    </OrchestrationStep>
    <!-- Run this step when a user tries to sign-in with unregistered account -->
    <OrchestrationStep Order="3" Type="ClaimsExchange">
      <Preconditions>
        <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
          <Value>objectId</Value>
          <Action>SkipThisOrchestrationStep</Action>
        </Precondition>
        <Precondition Type="ClaimsExist" ExecuteActionsIf="false">
          <Value>readOnlyEmail</Value>
          <Action>SkipThisOrchestrationStep</Action>
        </Precondition>
      </Preconditions>
      <ClaimsExchanges>
        <ClaimsExchange Id="DynamicSignUp"
          TechnicalProfileReferenceId="LocalAccountSignUpWithLogonEmail_Implicit" />
      </ClaimsExchanges>
    </OrchestrationStep>
    <!-- If objectId was returned in step 2, then this must be a sign in -->
    <OrchestrationStep Order="4" Type="ClaimsExchange">
      <Preconditions>
        <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
          <Value>newUser</Value>
          <Action>SkipThisOrchestrationStep</Action>
        </Precondition>
      </Preconditions>
      <ClaimsExchanges>
        <ClaimsExchange Id="SignInPasswordPage"
          TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Password" />
      </ClaimsExchanges>
    </OrchestrationStep>
    <OrchestrationStep Order="5" Type="InvokeSubJourney">
      <Preconditions>
        <Precondition Type="ClaimsExist" ExecuteActionsIf="false">
          <Value>isForgotPassword</Value>
          <Action>SkipThisOrchestrationStep</Action>
        </Precondition>
      </Preconditions>
      <JourneyList>
        <Candidate SubJourneyReferenceId="PasswordReset" />
      </JourneyList>
    </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-UserWriteUsingLogonEmail" />
      </ClaimsExchanges>
    </OrchestrationStep>
    <OrchestrationStep Order="8" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
  </OrchestrationSteps>
</UserJourney>

sh-jthorpe avatar Aug 16 '23 05:08 sh-jthorpe

You would have to make the 2nd screen (password collection), a Combined Sign In and Sign Up page, rather than a SelfAsserted page.

JasSuri avatar Aug 18 '23 13:08 JasSuri

can you please provide a sample policy for that @JasSuri

I am also having similar issue as @sh-jthorpe

christianangel15 avatar Sep 19 '23 17:09 christianangel15

Hi @sh-jthorpe did you seem to find a solution?

I am also facing similar issue, I also made 2nd screen (password collection), a Combined Sign In and Sign Up page, but seems to cause problems @JasSuri

Hi @sh-jthorpe did you seem to find a solution?

I am also facing similar issue, I also made 2nd screen (password collection), a Combined Sign In and Sign Up page, but seems to cause problems @JasSuri

Hi, no I haven't. When I have time I'll probably go through paid azure support.

For now we're making do with a more standard custom policy, even though it's a worse experience.

sh-jthorpe avatar Sep 21 '23 07:09 sh-jthorpe

I seem to have figured out the solution here -- Basic idea is instead of showing password page I am triggering basic sign in screen and made it as Combined sign in and sign up and also used content definition as 'api.signuporsignin'

Main thing to change here is we need to use a base technical profile - 'SelfAsserted-LocalAccountSignin-Email' instead of 'SelfAsserted-LocalAccountSignin-Password' in step 4 - so step 4 will look like as below

    <OrchestrationStep Order="4" Type="CombinedSignInAndSignUp"
      ContentDefinitionReferenceId="api.signuporsignin">
      <!-- skip this step if sign up flow was executed via step 2 or 3 -->
      <Preconditions>
        <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
          <Value>executed-SelfAsserted-Input</Value>
          <Action>SkipThisOrchestrationStep</Action>
        </Precondition>
      </Preconditions>
      <ClaimsProviderSelections>
        <ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninEmailExchange2" />
        <ClaimsProviderSelection TargetClaimsExchangeId="ForgotPasswordExchange" />
      </ClaimsProviderSelections>
      <ClaimsExchanges>
        <ClaimsExchange Id="LocalAccountSigninEmailExchange2"
          TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Email" />
      </ClaimsExchanges>
    </OrchestrationStep>

This will make the forgot password link appear. To link this link to actual password reset journey and get rid of Error - AADB2C90118 - we can refer to this documentation - https://learn.microsoft.com/en-us/azure/active-directory-b2c/add-password-reset-policy?pivots=b2c-custom-policy

I followed this and it worked! Hope this helps.

christianangel15 avatar Oct 26 '23 22:10 christianangel15