B2C password reset policy using signinname and verified email

Viewed 417

My custom pwd reset policy using signinName (not email) AND a verified email address is failing. From the portal I can see that my user was created with the correct userid and has the email in the Authentication Contact Info section. When running the reset policy I am able to verify the email with a one time code but when I press Continue I get 'Unable to validate the information provided'. I am assuming that my TP AAD-UserReadUsingSigninName, which is used as validation profile in the step collecting user id and email is failing. Why?

    <UserJourney Id="PasswordResetUsingUserId">
  <OrchestrationSteps>
    <OrchestrationStep Order="1" Type="ClaimsExchange">
      <ClaimsExchanges>
        <ClaimsExchange Id="PasswordResetUsingUserIdAddressExchange" TechnicalProfileReferenceId="LocalAccountDiscoveryUsingUserId" />
      </ClaimsExchanges>
    </OrchestrationStep>
    <OrchestrationStep Order="2" Type="ClaimsExchange">
      <ClaimsExchanges>
        <ClaimsExchange Id="NewCredentials" TechnicalProfileReferenceId="LocalAccountWritePasswordUsingObjectId" />
      </ClaimsExchanges>
    </OrchestrationStep>
    <OrchestrationStep Order="3" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
  </OrchestrationSteps>
  <ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>  

<TechnicalProfile Id="LocalAccountDiscoveryUsingUserId">
  <DisplayName>Reset password using user id and address</DisplayName>
  <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
  <Metadata>
    <Item Key="IpAddressClaimReferenceId">IpAddress</Item>
    <Item Key="ContentDefinitionReferenceId">api.localaccountpasswordreset</Item>
  </Metadata>
  <CryptographicKeys>
    <Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
  </CryptographicKeys>
  <IncludeInSso>false</IncludeInSso>
  <OutputClaims>
    <OutputClaim ClaimTypeReferenceId="signinName" Required="true" />
    <OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="Verified.Email" Required="true" />
    <OutputClaim ClaimTypeReferenceId="objectId" />
    <OutputClaim ClaimTypeReferenceId="userPrincipalName" />
    <OutputClaim ClaimTypeReferenceId="authenticationSource" />
  </OutputClaims>
  <ValidationTechnicalProfiles>
    <ValidationTechnicalProfile ReferenceId="AAD-UserReadUsingSigninName" />
  </ValidationTechnicalProfiles>
</TechnicalProfile>     

<TechnicalProfile Id="AAD-UserReadUsingSigninName">
  <Metadata>
    <Item Key="Operation">Read</Item>
    <Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">true</Item>
    <Item Key="UserMessageIfClaimsPrincipalDoesNotExist">An account could not be found for the provided user ID.</Item>
  </Metadata>
  <IncludeInSso>false</IncludeInSso>
  <InputClaims>
    <InputClaim ClaimTypeReferenceId="signinName" PartnerClaimType="signInNames.userName" Required="true" />
    <InputClaim ClaimTypeReferenceId="email" PartnerClaimType="strongAuthenticationEmailAddress" Required="true" />         
  </InputClaims>
  <OutputClaims>
    <!-- Required claims -->
    <OutputClaim ClaimTypeReferenceId="objectId" />
    <OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="localAccountAuthentication" />
    <!-- Optional claims -->
    <OutputClaim ClaimTypeReferenceId="userPrincipalName" />
    <OutputClaim ClaimTypeReferenceId="displayName" />
    <OutputClaim ClaimTypeReferenceId="otherMails" />
  </OutputClaims>
  <IncludeTechnicalProfile ReferenceId="AAD-Common" />
</TechnicalProfile> 
1 Answers

This is occurring because an AzureActiveDirectory technical profile can only accept one input claim.

You should modify the AAD-UserReadUsingSigninName technical profile to find the user object by sign-in name and then assert that the entered email address matches the saved one:

<ClaimsTransformation Id="AssertEmailAndStrongAuthenticationEmailAddressAreEqual" TransformationMethod="AssertStringClaimsAreEqual">
  <InputClaims>
    <InputClaim ClaimTypeReferenceId="strongAuthenticationEmailAddress" TransformationClaimType="inputClaim1" />
    <InputClaim ClaimTypeReferenceId="email" TransformationClaimType="inputClaim2" />
  </InputClaims>
  <InputParameters>
    <InputParameter Id="stringComparison" DataType="string" Value="ordinalIgnoreCase" />
  </InputParameters>
</ClaimsTransformation>

and:

<TechnicalProfile Id="AAD-UserReadUsingSigninName">
  <InputClaims>
    <InputClaim ClaimTypeReferenceId="signinName" PartnerClaimType="signInNames.userName" Required="true" />
  </InputClaims>
  <OutputClaims>
    <!-- Required claims -->
    <OutputClaim ClaimTypeReferenceId="objectId" />
    ...
    <!-- Optional claims -->
    ...
    <OutputClaim ClaimTypeReferenceId="strongAuthenticationEmailAddress" />         
  </OutputClaims>
  <OutputClaimsTransformations>
    <OutputClaimsTransformation ReferenceId="AssertEmailAndStrongAuthenticationEmailAddressAreEqual" />
  </OutputClaimsTransformations>
</TechnicalProfile>

You can then modify the LocalAccountDiscoveryUsingUserId technical profile to display an error message if the string comparison does fail:

<TechnicalProfile Id="LocalAccountDiscoveryUsingUserId">
  <Metadata>
    ...
    <Item Key="UserMessageIfClaimsTransformationStringsAreNotEqual">Custom error message the email addresses you provided are not the same.</Item>
  </Metadata>
</TechnicalProfile>

For more information about this specific pattern, see the AssertStringClaimsAreEqual section in the String Claims Transformations article.

Related