2
votes

Can you pass password claims between steps in Azure AD B2C Custom Policies?

My symptom is that after signing up using a multiple page custom policy, a user cannot sign in until they reset their password.

I am asking this as I spent many hours debugging a problem that it turns out could not be fixed. I found the answer under another question (Azure AD B2C Multi steps custom policy) that was differently worded but had similar symptoms.

I am posting here in the hope it is more easily found and helpful to others. Apologies if you think this is a duplicate.

4

4 Answers

1
votes

In short, no. See Azure AD B2C Multi steps custom policy.

A password claim is "scoped" to a given step. This means the orchestration step that collects the password claim from the end user must be the same step that writes it to the User object.

1
votes

I was looking for the same and have found a way to do multi step sign up. I tried several methods, including a outputclaimstransformation to store the password in another claim, but that did not work out. Because I already needed to do some input validation against an API I found a way to also copy the password to a new claim (plaintextPassword) that is not scoped to one orchestration step. This claim can be used in a later step to create the user account with the password provided by the plaintextPassword claim.

Create a self asserted technical profile that has an inputclaim (the password) and a validation technical profile. In the validation technical profile you can copy the password to a claim of type string with an inputclaimstransformation. Then add the new claim as outputclaim to the validation profile and to the technical profile. See code below for an example:

  <ClaimType Id="plaintextPassword">
    <DisplayName>password</DisplayName>
    <DataType>string</DataType>
    <UserInputType>TextBox</UserInputType>
  </ClaimType>

  <ClaimType Id="password">
    <DisplayName>Your password</DisplayName>
    <DataType>string</DataType>
    <UserHelpText>Your password</UserHelpText>
    <UserInputType>Password</UserInputType>
  </ClaimType>

  <ClaimsTransformation Id="CopyPassword" TransformationMethod="FormatStringClaim">
    <InputClaims>
      <InputClaim ClaimTypeReferenceId="password" TransformationClaimType="inputClaim" />
    </InputClaims>
    <InputParameters>
      <InputParameter Id="stringFormat" DataType="string" Value="{0}" />
    </InputParameters>
    <OutputClaims>
      <OutputClaim ClaimTypeReferenceId="plaintextPassword" TransformationClaimType="outputClaim" />
    </OutputClaims>
  </ClaimsTransformation>

  <TechnicalProfile Id="SignUp-PasswordValidation">
    <DisplayName>Email signup</DisplayName>
    <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
    <Metadata>
      <Item Key="ServiceUrl">url</Item>
      <Item Key="AuthenticationType">ClientCertificate</Item>
      <Item Key="SendClaimsIn">Body</Item>
    </Metadata>
    <CryptographicKeys>
      <Key Id="ClientCertificate" StorageReferenceId="B2C_1A_ClientCertificate" />
    </CryptographicKeys>
    <InputClaimsTransformations>
      <InputClaimsTransformation ReferenceId="CopyPassword" />
    </InputClaimsTransformations>
    <InputClaims>
      <InputClaim ClaimTypeReferenceId="claim_to_validate" PartnerClaimType="claim_to_validate" />
    </InputClaims>
    <OutputClaims>
      <OutputClaim ClaimTypeReferenceId="plaintextPassword" />
    </OutputClaims>
    <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
  </TechnicalProfile>

  <TechnicalProfile Id="SignUp">
    <DisplayName>Email signup</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.localaccountsignup</Item>
      <Item Key="setting.retryLimit">3</Item>
    </Metadata>
    <CryptographicKeys>
      <Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
    </CryptographicKeys>
    <InputClaims>
      <InputClaim ClaimTypeReferenceId="email" />
    </InputClaims>
    <OutputClaims>
      <OutputClaim ClaimTypeReferenceId="email" Required="true" />
      <OutputClaim ClaimTypeReferenceId="claim_to_validate" Required="true" />
      <OutputClaim ClaimTypeReferenceId="password" Required="true" />
      <OutputClaim ClaimTypeReferenceId="executed-SelfAsserted-Input" DefaultValue="true" />
      <OutputClaim ClaimTypeReferenceId="plaintextPassword" />
    </OutputClaims>
    <ValidationTechnicalProfiles>
      <ValidationTechnicalProfile ReferenceId="SignUp-Validation" />
    </ValidationTechnicalProfiles>
  </TechnicalProfile>

In the technical profile where you create the user in AAD add this line:

  <PersistedClaim ClaimTypeReferenceId="plaintextPassword" PartnerClaimType="password"/>
0
votes

I was able to solve the problem. When we have to add the user information (LocalAccountSignUpWithLogonEmail) I removed the validation that called the technical profile that is written in AAD, I changed it to a validation that calls another technical profile. This will copy our password through a ClaimsTransformation that will save our password in another InputClaim to be visible in any other step of our flow.

<ClaimType Id="plaintextPassword">
    <DisplayName>password</DisplayName>
    <DataType>string</DataType>
</ClaimType>

<ClaimType Id="passwordTransformation">
    <DisplayName>requestSocialRestSecondCall</DisplayName>
    <DataType>string</DataType>
</ClaimType>

<ValidationTechnicalProfiles>
    <ValidationTechnicalProfile ReferenceId="TransformationPassword" />
</ValidationTechnicalProfiles>


<ClaimsProvider> <!-- Copy Password-->
    <DisplayName>Copy Password</DisplayName>
    <TechnicalProfiles> 

        <TechnicalProfile Id="TransformationPassword">
            <DisplayName>Copy Pass</DisplayName>
            <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.ClaimsTransformationProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
            <InputClaims>
                <InputClaim ClaimTypeReferenceId="passwordTransformation" />
            </InputClaims>
            <OutputClaims>
                <OutputClaim ClaimTypeReferenceId="passwordTransformation" />
            </OutputClaims>
            <OutputClaimsTransformations>
                <OutputClaimsTransformation ReferenceId="Generate_TranformationPassword" />
            </OutputClaimsTransformations>
        </TechnicalProfile>

    </TechnicalProfiles>
</ClaimsProvider>



 <ClaimsTransformation Id="Generate_TranformationPassword" TransformationMethod="CopyClaim">
    <InputClaims>
        <InputClaim ClaimTypeReferenceId="newPassword" TransformationClaimType="inputClaim" />
    </InputClaims>
    <OutputClaims>
        <OutputClaim ClaimTypeReferenceId="plaintextPassword" TransformationClaimType="outputClaim" />
    </OutputClaims>
</ClaimsTransformation>
0
votes

I removed the validation that called the technical profile that is written in AAD and changed it to a validation that calls another technical profile.

This will copy the password through a ClaimsTransformation that will save our password in another InputClaim to be visible in any other step of our flow.

<TechnicalProfile Id="SignUp-PasswordValidation">
 <Metadata>
      <Item Key="ServiceUrl">url</Item>
      <Item Key="AuthenticationType">ClientCertificate</Item>
      <Item Key="SendClaimsIn">Body</Item>
 </Metadata>

What is the url you used?

<CryptographicKeys>
    <Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>

What was you application to create the Secret?