Azure B2C - Radio Button with Sign In Options - azure-ad-b2c

I am looking to build a Custom Policy with Radio button selectors.
Right now the policy works by taking the email entered in orchestration step 1, and then doing a validation check on the email address. If the email is a registered email it will go to a Step where the user will be shown a radio button with the option to sign in with phone with a OTP or use the password on file associated with the mail address entered.
I have built the following in the Sign in Policy..
<OrchestrationStep Order="4" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>extension_mfaByPhoneOrEmail</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="SelfAsserted-Select-MFA-Method" TechnicalProfileReferenceId="SelfAsserted-Select-MFA-Method" />
</ClaimsExchanges>
</OrchestrationStep>
And then in extensions
<TechnicalProfile Id="SelfAsserted-Select-MFA-Method">
<DisplayName>{Claim:signInName}</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.MFAselfasserted</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInName" PartnerClaimType="signInNames.emailAddress" Required="true"/>
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="extension_mfaByPhoneOrEmail" Required="true" />
</OutputClaims>
</TechnicalProfile>
Claim type is
<ClaimType Id="extension_mfaByPhoneOrEmail">
<DisplayName>You can request a verification code to sign in without your password, or enter your password.</DisplayName>
<DataType>string</DataType>
<UserInputType>RadioSingleSelect</UserInputType>
<Restriction>
<Enumeration Text="Phone" Value="phone" SelectByDefault="true" />
<Enumeration Text="Email " Value="email" SelectByDefault="false" />
</Restriction>
</ClaimType>
This does generate the radio buttons. But I would like to be able to add the password box with the email option. Similar to this.
I cannot figure out how to generate the password box there in that selection.

In your Select-MFA-Method technical profile, move the radio button under DisplayClaims, and add the Password ClaimType under DisplayClaims as well. You'll have to adjust the CSS at your content definition to put the password field in between the two buttons, however.
<TechnicalProfile Id="SelfAsserted-Select-MFA-Method">
<DisplayName>{Claim:signInName}</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.MFAselfasserted</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInName" PartnerClaimType="signInNames.emailAddress" Required="true"/>
</InputClaims>
<DisplayClaims>
<DisplayClaim ClaimTypeReferenceId="extension_mfaByPhoneOrEmail" Required="true" />
<DisplayClaim ClaimTypeReferenceId="password" />
</DisplayClaims>
</TechnicalProfile>

Related

AD B2C compareClaims throws internal server error

I am trying to compare 2 claim (one from id_token_hint and one from AD B2C) . My requirement is to throw an Error Page that the 2 do not match and re-direct to login page.
For the same I have added the following steps :
Added ClaimType (boolean for comparison)
<ClaimType Id="agencyClaimMatch">
<DisplayName>Verify if input Agency and agency in AD B2C match</DisplayName>
<DataType>boolean</DataType>
<UserHelpText>Verify if input Agency and agency in AD B2C match</UserHelpText>
</ClaimType>
Create a ClaimTransformation (based on post to compare the 2 claims (extension_agency from AD B2C and agency from input claim of id_token_hint)
<ClaimsTransformation Id="checkSameAgency" TransformationMethod="CompareClaims">
<InputClaims>
<InputClaim ClaimTypeReferenceId="extension_agency" TransformationClaimType="inputClaim1"/>
<InputClaim ClaimTypeReferenceId="agency" TransformationClaimType="inputClaim2"/>
</InputClaims>
<InputParameters>
<InputParameter Id="operator" DataType="string" Value="EQUAL"/>
<InputParameter Id="ignoreCase" DataType="string" Value="true"/>
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="agencyClaimMatch" TransformationClaimType="outputClaim"/>
</OutputClaims>
3.Added a Technical Profile to invoke the transformation (I am expecting the agencyClaimMatch boolean to get a true or false value based on the transformation, if false bot do not match need to throw the error page else allow access)
<TechnicalProfile Id="CheckAgencyMatch">
<DisplayName>Check Agency Match</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">true</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="agency" Required="true" />
<InputClaim ClaimTypeReferenceId="extension_agency" Required="true" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="agency"/>
<OutputClaim ClaimTypeReferenceId="extension_agency" />
<OutputClaim ClaimTypeReferenceId="agencyClaimMatch"/>
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="checkSameAgency"/>
</OutputClaimsTransformations>
</TechnicalProfile>
In UserJourney I added a ClaimExchange to get the value of checkSameAgency.
<!--Verify claims match and get the boolean value-->
<OrchestrationStep Order="6" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="CheckAgencyMatch" TechnicalProfileReferenceId="CheckAgencyMatch"/>
</ClaimsExchanges>
</OrchestrationStep>
If boolean output checkSameAgency is not "True" that is both agencies do not match then throw an error else move to next step to issue jwt token .
<!-- Check if agencID Match-->
<OrchestrationStep Order="7" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="false">
<Value>agencyClaimMatch</Value>
<Value>True</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="SelfAssertedAgencyNotMatched" TechnicalProfileReferenceId="SelfAssertedAgencyNotMatched" />
</ClaimsExchanges>
</OrchestrationStep>
But I am getting an error "The page cannot be displayed because an internal server error has occurred." even if the agency matches or if they don't.
Any pointers would be very helpful.
===
Following solution as suggested by rbrayb helped resolve the issue .
I noted:
<TechnicalProfile Id="CheckAgencyMatch">
<DisplayName>Check Agency Match</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
A SelfAssertedAttributeProvider is only used to display a screen, but you are only comparing claims. It should be
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.ClaimsTransformationProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
https://learn.microsoft.com/en-us/azure/active-directory-b2c/claims-transformation-technical-profile
<TechnicalProfile Id="CheckAgencyMatch">
<DisplayName>Check Agency Match</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
A SelfAssertedAttributeProvider is only used to display a screen, but you are only comparing claims. It should be
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.ClaimsTransformationProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
Refer this.

How to Implement Azure AD B2C Identity Experience Framework Sign-In with Optional Email OTP

I have a business requirement but I've failed to figure out how to implement it.
The very first time a user signs-in, they are challenged with email OTP followed by a forced password reset.
All subsequence sign-in attempts will prompt the user only for email address and password (OTP is a one-time user flow).
My problem is that Orchestration step 1 collects the email address, then I query AAD by the email address provided and read an extension attribute called extension_EmailValidated. If the attribute is not TRUE, B2C will force the user to do email OTP verification, however, Orchestration step 2 won't let me pre-populate the previously entered email address input box AND show the OTP buttons. It will only let me do one or the other (hope that makes sense).
My UserJourney looks like this
<UserJourneys>
<UserJourney Id="B2CSignIn">
<OrchestrationSteps>
<!--User enters their email address-->
<OrchestrationStep Order="1" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADEmailDiscovery" TechnicalProfileReferenceId="AAD-EmailDiscovery" />
</ClaimsExchanges>
</OrchestrationStep>
<!--User enters their OTP-->
<OrchestrationStep Order="2" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>extension_EmailVerified</Value>
<Value>True</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AADEmailVerification" TechnicalProfileReferenceId="AAD-EmailVerification" />
</ClaimsExchanges>
</OrchestrationStep>
<!--Set extension_EmailVerified to TRUE-->
<OrchestrationStep Order="3" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>extension_EmailVerified</Value>
<Value>True</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AADUserWriteEmailVerifiedUsingEmail" TechnicalProfileReferenceId="AAD-UserWriteEmailVerifiedUsingEmail" />
</ClaimsExchanges>
</OrchestrationStep>
Here are the technical profiles for Step 1 and Step 2.
<TechnicalProfile Id="AAD-EmailDiscovery">
<DisplayName>Initiate Email Address Verification For Local Account</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.localaccountsignup</Item>
<Item Key="language.button_continue">Continue</Item>
<Item Key="setting.showCancelButton">False</Item>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="email" />
<OutputClaim ClaimTypeReferenceId="extension_EmailVerified" DefaultValue="false" Required="true" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AAD-UserReadUsingEmailAddress" />
</ValidationTechnicalProfiles>
</TechnicalProfile>
<TechnicalProfile Id="AAD-EmailVerification">
<DisplayName>Initiate Email Address Verification For Local Account</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.localaccountsignup</Item>
<Item Key="language.button_continue">Continue</Item>
<Item Key="setting.showCancelButton">False</Item>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="Verified.Email" Required="true" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AAD-UserReadUsingEmailAddress" />
</ValidationTechnicalProfiles>
</TechnicalProfile>
Another very confusing issue is that the only way I can get the OTP buttons to appear on Step 2 is to include "ParterClaimType=Verified.Email" for both the InputClaim and OutputClaims -- and that makes no sense.
If I omit "ParterClaimType=Verified.Email" from InputClaims and OutputClaims, I can get the email address to pre-populate from Step 1.
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" PartnerClaimType="Verified.Email" Required="true" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="Verified.Email" Required="true" />
</OutputClaims>
Advice and guidance is very much appreciated.
Thanks!
The solution here is to make the input text box readOnly.
Send the email collected at the sign in screen through a claim transform to copy it to a new claim, called readOnlyEmail. The claim should be defined as read only.
<ClaimType Id="readOnlyEmail">
<DisplayName>Email Address</DisplayName>
<DataType>string</DataType>
<UserHelpText/>
<UserInputType>Readonly</UserInputType>
</ClaimType>
Copy the email claim into the readOnly claim
<ClaimsTransformation Id="CopySignInNameToReadOnly" TransformationMethod="FormatStringClaim">
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInName" TransformationClaimType="inputClaim" />
</InputClaims>
<InputParameters>
<InputParameter Id="stringFormat" DataType="string" Value="{0}" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="readOnlyEmail" TransformationClaimType="outputClaim" />
</OutputClaims>
</ClaimsTransformation>
Then make a call to the claims transform from your sign up technical profile using an OutputClaimsTransformations node.
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="CopySignInNameToReadOnly" />
</OutputClaimsTransformations>
Finally on the email verification technical profile (selfAsserted technical profile), pass in the readOnly email to be validated:
<InputClaims>
<InputClaim ClaimTypeReferenceId="readOnlyEmail"/>
</InputClaims>
<OutputClaims>
<!-- Required claims -->
<OutputClaim ClaimTypeReferenceId="readOnlyEmail" PartnerClaimType="Verified.Email"/>
</OutputClaims>
The concept is demonstrated here:
https://github.com/azure-ad-b2c/samples/tree/master/policies/signin-email-verification
You just need to add your conditional logic on top of that sample.

AAD B2C SignUpOrSignIn with both verified Email AND verified Phone

Usually we have a signup/signin with either email or phone, but I have a requirement to do signup/signin requiring both verified email and verified phone number as well.
I tried to set this up using default 'User Flows', with MFA for phone/sms enabled, but the number is not accessible via the graph API call for phoneMethods, if it were, I could have also copied the number to the profile (which would mean that it was a verified phone number). Further on the same, enabling Authentication Method, OR even updating (separating phone number with a space char for code and number) the phone number on the profile page causes the phone number to appear via the phoneMethods graph API call, but not before then.
Can someone who's done this before share some insights on their approach or probably share the custom xml policy if possible? I did look into the github sample policies [https://github.com/azure-ad-b2c/samples/tree/master/policies], but I guess there is none with BOTH.
[Edit on the above]
Based on Wes' input, I ended up creating a technical profile as per below and used it into my UserJourney -> Orchestration step, post the PhoneFactor-InputOrVerify step as per below (apologies on the formatting):
<TechnicalProfile Id="AAD-UserWritePhoneNumberUsingObjectId">
<Metadata>
<Item Key="Operation">Write</Item>
<Item Key="RaiseErrorIfClaimsPrincipalAlreadyExists">false</Item>
<Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">true</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="objectId" Required="true" />
</InputClaims>
<PersistedClaims>
<PersistedClaim ClaimTypeReferenceId="objectId" />
<PersistedClaim ClaimTypeReferenceId="Verified.strongAuthenticationPhoneNumber" PartnerClaimType="extension_PhoneNumber" />
</PersistedClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="Verified.strongAuthenticationPhoneNumber" PartnerClaimType="extension_PhoneNumber"/>
</OutputClaims>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
</TechnicalProfile>
Ofcourse, the "extension_PhoneNumber" was setup in ClaimsSchema as per below:
<ClaimType Id="extension_PhoneNumber">
<DisplayName>Phone Number</DisplayName>
<DataType>string</DataType>
</ClaimType>
Hope this helps someone...
Cheers! ;)
PS: Appreciate any updates to any of the above comments to make it correct/better.
I have done something somewhat similar to this but in my case I verified a new MFA number as my flow resets the users MFA number, but hopefully this will give you an idea. As far as an approach, you could take the standard login flow from the samples -> add verify the email step -> add verify phone number step. I do not know your level of experience with custom policies, but I believe using the sample custom policy for sign in as an initial template will be your best starting place. Then add the two orchestration steps for verification in. Here are slightly modified excerpts from my implementation.
<OrchestrationSteps>
<OrchestrationStep ContentDefinitionReferenceId="api.signin" Order="1" Type="CombinedSignInAndSignUp">
<ClaimsProviderSelections>
<ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninEmailExchange" />
</ClaimsProviderSelections>
<ClaimsExchanges>
<ClaimsExchange Id="LocalAccountSigninEmailExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Email" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="3" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="EmailVerifyOnSignIn" TechnicalProfileReferenceId="EmailVerifyOnSignIn" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="4" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="NewPhoneFactor" TechnicalProfileReferenceId="PhoneFactor-Verify" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep CpimIssuerTechnicalProfileReferenceId="JwtIssuer" Order="5" Type="SendClaims" />
Steps 1,2 and 5 should be able to be copied directly from the samples with a small change of outputting the email as a read only claim to be used to verify(see below steps).
Your local account signin will need to add a transform that generates the readonly email from sign in name if you choose this route.
So in your local account signin you will have
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="CopySignInNameToReadOnly" />
</OutputClaimsTransformations>
just after your output claims and the transform that is being used will be as follows:
<ClaimsTransformation Id="CopySignInNameToReadOnly" TransformationMethod="FormatStringClaim">
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInName" TransformationClaimType="inputClaim" />
</InputClaims>
<InputParameters>
<InputParameter Id="stringFormat" DataType="string" Value="{0}" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="readOnlyEmail" TransformationClaimType="outputClaim" />
</OutputClaims>
</ClaimsTransformation>
Be sure to define your readonly claim in the claimstypes section:
<ClaimType Id="readOnlyEmail">
<DisplayName>Email Address</DisplayName>
<DataType>string</DataType>
<UserHelpText />
<UserInputType>Readonly</UserInputType>
</ClaimType>
This is just a simple claim copy transformation.
Steps 3 and 4, which do each of the verification, have the technical profiles similar to below:
<TechnicalProfile Id="EmailVerifyOnSignIn">
<DisplayName>EmailVerifyOnSignIn</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.selfasserted.EmailPage</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="readOnlyEmail" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="readOnlyEmail" PartnerClaimType="Verified.Email" />
</OutputClaims>
</TechnicalProfile>
This will verify the email that was used to sign in, then the following orchestration step will be to verify the MFA.
You will have to make changes to this next technical profile, but the basic idea will be the same as with email, here is a starting point :
<TechnicalProfile Id="PhoneFactor-Verify">
<DisplayName>PhoneFactor</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.PhoneFactorProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="Verified.strongAuthenticationPhoneNumber" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
The end result would be:
User logs in with email and password
User clicks button to verify the email used during login
User verifies phone number
Claims are sent and journey completes

Force user to login after ADB2C signup

When the user signup on the adb2c, I want him to type his login / password, and not being already connected.
I tried to edit the signin_signup policy but without results
In Custom Policies, you can use a self asserted page to show the user that they have successfully signed up, but not allow them to continue the journey from there. This is sometimes referred to as a "block page" in our samples. Since the user cannot continue the journey, a token will not be issued and a session will not be established.
You can instead use a Custom HTML page to allow the user to return to the home page from here.
The user then needs to login again to get an authenticated session.
The block page is shown to be used here:
https://github.com/azure-ad-b2c/samples/blob/4e43fab365e29f002e9e033a4e078bc2091a8494/policies/password-reset-only/policy/TrustFrameworkExtensions.xml#L132
<TechnicalProfile Id="SelfAsserted-BlockSignUpClaimsIssuance">
<DisplayName>BlockSignUpClaimsIssuance Page</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.selfasserted.blockSignUpPage</Item>
<!--Demo: hide the continue and cancel buttons -->
<Item Key="setting.showContinueButton">false</Item>
<Item Key="setting.showCancelButton">false</Item>
</Metadata>
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="GenerateAMessage" />
</InputClaimsTransformations>
<InputClaims>
<InputClaim ClaimTypeReferenceId="userMessage" />
</InputClaims>
<OutputClaims>
<!--Demo: Show the paragraph claim with the message to the user -->
<OutputClaim ClaimTypeReferenceId="userMessage" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
Apply a precondition to this step, such that if the newUser claim exists, that this step is not skipped:
<OrchestrationStep Order="YOUR STEP #" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>newUser</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="SelfAsserted-BlockSignUpClaimsIssuance" />
</ClaimsExchanges>
</OrchestrationStep>

Azure AD B2C Password Reset policy without email verification step

Is it possible to create custom policy to reset password for already known email?
I create user using Graph API and send invitation email to the specified email
address.
I want user to click on the link in that email and just set password for his account.
I can create signed token with this email claim and send as assertion to my custom policy. So policy gets email as input claim. I see it in the trace.
But I am not able to bypass email verification step in the password reset journey - when I remove it, I get 500 server error without additional detail.
I tried to send objectId for the user as input claim as well, but it does not help either.
Is there a way to skip email verification?
You have the following options that vary the user experience:
Display the email address as a read-only field and remove the email verification requirement.
Remove the email verification step.
Display the email address as a read-only field
1) Create a readOnlyEmail claim type:
<ClaimType Id="readOnlyEmail">
<DisplayName>Email Address</DisplayName>
<DataType>string</DataType>
<UserInputType>Readonly</UserInputType>
</ClaimType>
2) Create a claims transformation that copies from the email claim to the readOnlyEmail claim:
<ClaimsTransformation Id="CopyFromEmailToReadOnlyEmail" TransformationMethod="FormatStringClaim">
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" TransformationClaimType="inputClaim" />
</InputClaims>
<InputParameters>
<InputParameter Id="stringFormat" DataType="string" Value="{0}" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="readOnlyEmail" TransformationClaimType="outputClaim" />
</OutputClaims>
</ClaimsTransformation>
3) Add the CopyFromEmailToReadOnlyEmail claims transformation as an input claims transformation to the LocalAccountDiscoveryUsingEmailAddress technical profile and then replace the email claim type with readOnlyemail as the input and output claims for this technical profile:
<TechnicalProfile Id="LocalAccountDiscoveryUsingEmailAddress">
<DisplayName>Reset password using email 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>
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="CopyFromEmailToReadOnlyEmail" />
</InputClaimsTransformations>
<InputClaims>
<InputClaim ClaimTypeReferenceId="readOnlyEmail" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="readOnlyEmail" Required="true" />
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="userPrincipalName" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AAD-UserReadUsingEmailAddress" />
</ValidationTechnicalProfiles>
</TechnicalProfile>
Remove the email verification step
1) Change the first step for the PasswordReset journey from:
<OrchestrationStep Order="1" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="PasswordResetUsingEmailAddressExchange" TechnicalProfileReferenceId="LocalAccountDiscoveryUsingEmailAddress" />
</ClaimsExchanges>
</OrchestrationStep>
to:
<OrchestrationStep Order="1" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="UserReadUsingEmailAddressExchange" TechnicalProfileReferenceId="AAD-UserReadUsingEmailAddress" />
</ClaimsExchanges>
</OrchestrationStep>

Resources