'Unable to get ValidationTechnicalProfile to valid input

I am trying to setup the use of a one time pass code (OTP) on an Azure B2C Custom Policy. I have a working set of Orchestration steps that extract an email from a claims token supplied in the URL and then mail a randomly generated code to that email. I know there are "VerifyCode" and "GenerateCode" technical profiles available, but they rely on the user entering the email into a display field first, which I want to avoid.

I am unable get a ValidationTechnicalProfile to fire so that it can execute a ClaimsTransformation to the two claims values. These are the generated OTP mailed to the user and the input collected from a TechnicalProvider that uses a SelfAssertedAttributeProvider to display the input field with a ContentDefinition.

I am basing my code on this article and also a walkthrough with regard to ValidationTechnicalProfiles

Please could someone explain why the ValidationTechnicalProfile appears to either be skipped or is failing to work?

Claims

<ClaimType Id="Otp">
  <DisplayName>One-time password</DisplayName>
  <DataType>string</DataType>
</ClaimType>
<ClaimType Id="VerificationCode">
  <DisplayName>Secondary Verification Code</DisplayName>
  <DataType>string</DataType>
  <UserHelpText>Enter your verification code</UserHelpText>
  <UserInputType>TextBox</UserInputType>
</ClaimType>

Orchestration Step

<OrchestrationStep Order="4" Type="ClaimsExchange">
   <ClaimsExchanges>
      <ClaimsExchange Id="SelfAsserted-OTP-Exchange" TechnicalProfileReferenceId="SelfAsserted-EnterOTP" />
   </ClaimsExchanges>
</OrchestrationStep>

Technical Profile

Uses the api.page.codeinput content definition to collect the value of the VerificationCode claim in an input box. Once collected, it should fire the Self-AssertedOTPCompare TechnicalProfile as a ValidationTechnicalProfile

<TechnicalProfile Id="SelfAsserted-EnterOTP">
 <DisplayName>Enter OTP</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.page.codeinput</Item>
   <Item Key="UserMessageIfClaimsTransformationStringsAreNotEqual">Invalid OTP Code</Item>
 </Metadata>
 <OutputClaims>
   <OutputClaim ClaimTypeReferenceId="VerificationCode" Required="true" />
 </OutputClaims>
 <ValidationTechnicalProfiles>
    <ValidationTechnicalProfile ReferenceId="Self-AssertedOTPCompare" />
 </ValidationTechnicalProfiles>
</TechnicalProfile>

Validation Technical Profile

Executes the AssertSuppliedAndGeneratedOTPAreEqual ClaimsTransformation

<TechnicalProfile Id="Self-AssertedOTPCompare">
  <DisplayName>Returns the result from comparing the generated OTP with the supplied on</DisplayName>
  <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.ClaimsTransformationProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
  <OutputClaims>
    <OutputClaim ClaimTypeReferenceId="VerificationCode" />
  </OutputClaims>
  <OutputClaimsTransformations>
    <OutputClaimsTransformation ReferenceId="AssertSuppliedAndGeneratedOTPAreEqual" />
  </OutputClaimsTransformations>
</TechnicalProfile>

Claims Transformation

Should compare the two claims and raise an exception to prevent the user continuing if the codes are different.

<ClaimsTransformation Id="AssertSuppliedAndGeneratedOTPAreEqual" TransformationMethod="AssertStringClaimsAreEqual">
  <InputClaims>
    <InputClaim ClaimTypeReferenceId="Otp" TransformationClaimType="inputClaim1" />
    <InputClaim ClaimTypeReferenceId="VerificationCode" TransformationClaimType="inputClaim2" />
  </InputClaims>
  <InputParameters>
    <InputParameter Id="stringComparison" DataType="string" Value="ordinalIgnoreCase" />
  </InputParameters>
</ClaimsTransformation>

Logging with AppInsights I think it's hitting the technical profile but control appears to be returning to the orchestration. If I remove the ValidationTechnicalProfiles section from the SelfAsserted-EnterOTP technical profile, the flow stops at the page where the user would enter their code.

 {
    "Kind": "HandlerResult",
    "Content": {
      "Result": true,
      "RecorderRecord": {
        "Values": [
          {
            "Key": "InitiatingClaimsExchange",
            "Value": {
              "ProtocolType": "Identity Experience Engine API",
              "TargetEntity": "Generate-OTP",
              "TechnicalProfileId": "SelfAsserted-EnterOTP",
              "ProtocolProviderType": "SelfAssertedAttributeProvider"
            }
          }
        ]
      },
      "PredicateResult": "True"
    }
  }, 
  {
    "Kind": "Action",
    "Content": "Web.TPEngine.StateMachineHandlers.SwitchToApiOrchestrationHandler"
  },
  {
    "Kind": "HandlerResult",
    "Content": {
      "Result": true
    }
  },
  {
    "Kind": "Transition",
    "Content": {
      "EventName": "SELFASSERTED",
      "StateName": "AwaitingNextStep"
    }
  },

Additional

The original implementation above caused the process to never show the input field to collect the OTP from the user - jas-suri-msft suggests this is because a ValidationTechnicalProvider does not bubble up exceptions.

If this is the case, the documentation appears to be wrong:

The AssertStringClaimsAreEqual claims transformation is always executed from a validation technical profile that is called by a self-asserted technical profile, or a DisplayControl. The UserMessageIfClaimsTransformationStringsAreNotEqual metadata of a self-asserted technical profile controls the error message that is presented to the user.

I have tried changing the ValidationTechnicalProfiles to a OutputClaimsTransformations node as suggested.

I replaced this:

 <ValidationTechnicalProfiles>
    <ValidationTechnicalProfile ReferenceId="Self-AssertedOTPCompare" />
 </ValidationTechnicalProfiles>

with this:

<OutputClaimsTransformations>
  <OutputClaimsTransformation 
 ReferenceId="AssertSuppliedAndGeneratedOTPAreEqual" />
</OutputClaimsTransformations>

This caused the OTP collection screen to show but the page to show no error message if the code was wrong and go no further. This is despite setting UserMessageIfClaimsTransformationStringsAreNotEqual in the metadata However, entering the correct code allowed the steps to progress.

So how to do I get the page to show the exception message?

SOLUTION

As mentioned in the comments, I am unable to remove the OutputClaims from the ValidationTechnicalProfile as the policy will not validate. However, changing the OutputClaim to a DisplayClaim resolves the issue.

<TechnicalProfile Id="SelfAsserted-EnterOTP">
  <DisplayName>Enter OTP</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.page.codeinput</Item>
    <Item Key="UserMessageIfClaimsTransformationStringsAreNotEqual">Invalid OTP Code</Item>
  </Metadata>
  <DisplayClaims>
    <DisplayClaim ClaimTypeReferenceId="suppliedOTP" Required="true" />
  </DisplayClaims>
  <ValidationTechnicalProfiles>
    <ValidationTechnicalProfile ReferenceId="Self-AssertedOTPCompare" />
  </ValidationTechnicalProfiles>
</TechnicalProfile>


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source