Azure AD B2C Custom Policy: REST technical profile validation doesn't work - azure

I have created a custom policy for signup on Azure AD B2C, with the goal of only allowing users whose email is whitelisted to be able to register into the system.
For that, I have configured the custom policy with a REST technical profile.
I followed this guide: https://learn.microsoft.com/en-us/azure/active-directory-b2c/custom-policy-rest-api-claims-exchange?pivots=b2c-custom-policy
The associated web service receives the email of the user that wants to signup and verifies whether that user's email address is part of a list of emails that have been whitelisted. And if it's not, I return a message of the following format:
{
"userMessage": "Sorry, this email is not whitelisted",
"status": 409,
"version": "1.0.0"
}
If the email is part of the whitelist, I return:
{
"emailValue": "myemail#email.com",
"isWhiteListed": true
}
Here's what my user journey looks like (in Signup.xml file):
<UserJourneys>
<UserJourney Id="SignUp">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="SignUpWithLogonEmailExchange" TechnicalProfileReferenceId="LocalAccountSignUpWithLogonEmail" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- This step reads any user attributes that we may not have received when in the token. -->
<OrchestrationStep Order="2" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="3" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="RESTEmailWhitelist" TechnicalProfileReferenceId="REST-EmailWhitelist" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="4" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>
</UserJourneys>
The problem is that even after returning the 409 error in case the email is not whitelisted, it still successfully gets created in Azure AD B2C.
How do I prevent the user's account from being successfully created?

You’re probably running your “AAD-UserwriteUsingEmail” validation technical profile twice, once before the REST call, once after it. Delete the validation tech profile in your base file in the LocalAccountSignUpWithLogonEmail technical profile. Then put the validation tech profiles (AAD-userWriteUsingEmail and REST API) in your extension file, in the LocalAccountSignUpWithLogonEmail technical profile, in the correct order (REST then Write).

Related

What does a PasswordChange with TOTP for Azure B2C look like?

I have an existing Azure B2C solution using custom policies and provides sign up / sign in via AAD, MS Account, Google and Local. I'm now adding TOTP MFA as provided by https://learn.microsoft.com/en-us/azure/active-directory-b2c/multi-factor-authentication?pivots=b2c-custom-policy.
I have SignUp/SignIn and Password Reset policies using TOTP working for the Local Account. I don't need TOTP for the social accounts. However, I'm struggling with a ChangePassword policy, where a user who is already logged in can change their password. For this step, I would like the user to verify themselves via TOTP before getting to change the password.
In my non-TOTP solution, I'm using the PasswordChange UserJourney as provided by https://learn.microsoft.com/en-us/azure/active-directory-b2c/add-password-change-policy?pivots=b2c-custom-policy
For completeness, it looks like this:
<UserJourney Id="PasswordChange">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="ClaimsProviderSelection" ContentDefinitionReferenceId="api.signuporsignin">
<ClaimsProviderSelections>
<ClaimsProviderSelection TargetClaimsExchangeId="LocalAccountSigninEmailExchange" />
</ClaimsProviderSelections>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="LocalAccountSigninEmailExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Email" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="3" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="NewCredentials" TechnicalProfileReferenceId="LocalAccountWritePasswordChangeUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="4" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="5" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>
I was hoping to get away with something similar for PasswordChangeWithTOTP, by inserting the TotpFactor-Input and TotpFactor-Verify steps between steps 2 and 3 of the old policy.
<UserJourney Id="PasswordChangeWithTOTP">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="ClaimsProviderSelection" ContentDefinitionReferenceId="api.signuporsignin">
<ClaimsProviderSelections>
<ClaimsProviderSelection TargetClaimsExchangeId="LocalAccountSigninEmailExchange" />
</ClaimsProviderSelections>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="LocalAccountSigninEmailExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Email" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- Call the TOTP enrollment ub journey. If user already enrolled the sub journey will not ask the user to enroll -->
<OrchestrationStep Order="3" Type="InvokeSubJourney">
<JourneyList>
<Candidate SubJourneyReferenceId="TotpFactor-Input" />
</JourneyList>
</OrchestrationStep>
<!-- Call the TOTP validation sub journey-->
<OrchestrationStep Order="4" Type="InvokeSubJourney">
<JourneyList>
<Candidate SubJourneyReferenceId="TotpFactor-Verify" />
</JourneyList>
</OrchestrationStep>
<OrchestrationStep Order="5" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="NewCredentials" TechnicalProfileReferenceId="LocalAccountWritePasswordChangeUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="6" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="7" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>
minor talking points: I went with a new policy due to the release cycle of the application using it, and I'm not totally sold that I should offer TOTP registration as a part of this particular step, even though it's something we do for SignUpSignIn and PasswordReset.
The error that I get "The service received a bad request" when performing the "Get available strong authentication devices" activity. The error message has a target type User and Id consistent with similar operations used during sign in. I can't see anything else particularly jumping out at me.
I did try inserting a <ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" /> OrchestrationStep before the TotpFactor-Input step, but thought that may have been defeating the purpose of TOTP. I also didn't get prompted to provide a verification code before getting to change the password.
What should a PasswordChange with TOTP policy look like?
Do you have:
<OutputClaim ClaimTypeReferenceId="userPrincipalName" />
as an output claim from SelfAsserted-LocalAccountSignin-Email?
As far as I remember from docs/samples - userPrincipalName is required for TOTP SubJourneys to work.

Can I apply MFA to each user in Azure ADB2C?

I used a custom policy to create the login screen.
And we want to run MFA on a per user basis.
For example, I have two user account.(user1, user2)
User 1 wants to log in without using MFA.
User 2 applies MFA and wants to log in.
Both users then access the same URL for login.
Where can we set up an MFA for each user in this way?
I've been able to implement self-service per user TOTP MFA enrollment/unenrollment via custom policies (one policy for enrollment, second one for unenrollment).
The idea in my scenario was that it is up to the end user to decide if he would like to use TOTP MFA or not.
So far this has been only a PoC implementation but it was working fine during my tests.
It was based on a TOTP MFA sample available on GitHub - https://github.com/azure-ad-b2c/samples/tree/master/policies/totp.
Relevant parts of my UserJourneys:
Enable MFA UserJourney
<UserJourney Id="EnableMFA" DefaultCpimIssuerTechnicalProfileReferenceId="JwtIssuer">
<OrchestrationSteps>
//Default steps for signing in user with Local/Social account
[...]
<OrchestrationStep Order="5" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="CheckAvailableDevices" TechnicalProfileReferenceId="AzureMfa-GetAvailableDevices" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="6" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>numberOfAvailableDevices</Value>
<Value>0</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="CreateErrorResponse-MfaAlreadyEnabled" TechnicalProfileReferenceId="CreateErrorResponse-MfaAlreadyEnabled" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="7" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="ReturnOAuth2Error">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>numberOfAvailableDevices</Value>
<Value>0</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
</OrchestrationStep>
<OrchestrationStep Order="8" Type="InvokeSubJourney">
<JourneyList>
<Candidate SubJourneyReferenceId="TotpFactor-Input" />
</JourneyList>
</OrchestrationStep>
<OrchestrationStep Order="9" Type="InvokeSubJourney">
<JourneyList>
<Candidate SubJourneyReferenceId="TotpFactor-Verify" />
</JourneyList>
</OrchestrationStep>
<OrchestrationStep Order="10" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>
Step 5 reads numberOfAvailableDevices which indicates wheter user is already enrolled (numberOfAvailableDevices=1) or not (numberOfAvailableDevices=0).
If user is already enrolled, steps 6 & 7 are creating and returning an error to notify user he cannot enable MFA as it is already enabled.
Steps 8 & 9 are enrolling user. These steps are taken from GitHub sample.
Disable MFA UserJourney
Here, the AzureMfaProtocolProvider technical provider doesn't support unenrollment of TOTP MFA. List of available operations can be found here - https://learn.microsoft.com/en-us/azure/active-directory-b2c/multi-factor-auth-technical-profile#totp-mode.
In order to unenroll user you can use Microsoft Graph (custom REST technical profile to your API is needed).
https://learn.microsoft.com/en-us/graph/api/resources/softwareoathauthenticationmethod?view=graph-rest-beta
Using these endpoints, you can list softwareOathMethods for specific user and delete them if needed. If user has TOTP MFA enabled, then MS Graph should return an array with 1 softwareOathMethod.
UserJourney:
<UserJourney Id="DisableMFA" DefaultCpimIssuerTechnicalProfileReferenceId="JwtIssuer">
<OrchestrationSteps>
//Default steps for signing in user with Local/Social account
[...]
<OrchestrationStep Order="5" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="CheckAvailableDevices" TechnicalProfileReferenceId="AzureMfa-GetAvailableDevices" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="6" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="false">
<Value>numberOfAvailableDevices</Value>
<Value>0</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="CreateErrorResponse-MfaNotEnabled" TechnicalProfileReferenceId="CreateErrorResponse-MfaNotEnabled" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="7" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="ReturnOAuth2Error">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="false">
<Value>numberOfAvailableDevices</Value>
<Value>0</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
</OrchestrationStep>
<OrchestrationStep Order="8" Type="InvokeSubJourney">
<JourneyList>
<Candidate SubJourneyReferenceId="TotpFactor-Verify" />
</JourneyList>
</OrchestrationStep>
<OrchestrationStep Order="9" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="REST-DisableMfa" TechnicalProfileReferenceId="REST-DisableMfa" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="10" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>
Steps 5,6 & 7 are similar to corresponding steps in previously mentioned EnableMFA UserJourney.
Step 8 forces user to input TOTP code from his authenticator app before disabling MFA so we are sure he is the owner of the account.
In step 9, REST-DisableMfa technical profile is my custom REST api which then calls Microsoft Graph api.
Using the build-in user flows, it's not possible. You'll need to use a custom policy and allow the MFA based on some custom property (e.g. role admin / regular user).
Here's a link that can help you with some guidance related to implement custom MFA:
https://www.kallemarjokorpi.fi/blog/azure-b2c-custom-mfa-implementation.html

how to reset MFA and get a new QR when using azure b2c, MFA with REST API

i've followed https://github.com/azure-ad-b2c/samples/tree/master/policies/custom-mfa-totp to build MFA in azure B2C and it's working fine. just one question, after user scanned the QR code and register their phone, if they want to use another phone or reset their MFA, how can they get the QR code agian?
Any type of account management should existing on a 'Profile Edit' policy. This sample does not include it. You would need to pull the components out of the TrustFrameworkExtensions.xml like the AppFactor-Register, AppFactor-Challenge, ect., technical profile and add to your new user journey for ProfileEdit - potentially created a whole new experience. Of course, adding a write technical profile back into the directory will be needed to 'update' any information that is stored.
Most of the components to achieve your scenario is already there with this sample - just create a new user journey called "ProfileEditwithQR" or something and copy the starter pack profileEdit user journey to your Extension file (if you haven't already done this) - then add the steps from this sample to it.
This will require you to have a minimum of 300 level knowledge on custom policies to put this together.
I created a policy which allows to re-create the QR code.
Prerequisites: You have been able to run this sample in you tenant.
Add this to the TrustFrameworkExtension.xml:
<UserJourney Id="EditQR" DefaultCpimIssuerTechnicalProfileReferenceId="JwtIssuer">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="ClaimsProviderSelection" ContentDefinitionReferenceId="api.idpselections">
<ClaimsProviderSelections>
<ClaimsProviderSelection TargetClaimsExchangeId="LocalAccountSigninEmailExchange" />
</ClaimsProviderSelections>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="LocalAccountSigninEmailExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Email" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="3" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- Demo: The following orchestration step is alwasy executed.
It generates a verification app secret key for the user (none interactive step). -->
<OrchestrationStep Order="4" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AppFactorGenerateTotpWebHook" TechnicalProfileReferenceId="AppFactor-GenerateTotpWebHook" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- Demo: The following orchestration step is alwasy executed.
It registers a verification app through QR code that mobile authentication app should scan. -->
<OrchestrationStep Order="5" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AppFactorRegisterExchange" TechnicalProfileReferenceId="AppFactor-Register" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- Demo: The following orchestration step is always executed.
It updates the verification app time step matched for a given user in the Azure Active Directory.
An error is raised if the user does not exist. -->
<OrchestrationStep Order="6" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADWriteUserAppCodeByObjectId" TechnicalProfileReferenceId="AAD-WriteUserAppCodeByObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="7" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>
and this is the new user flow, I called it EditQR.xml:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<TrustFrameworkPolicy
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://schemas.microsoft.com/online/cpim/schemas/2013/06"
PolicySchemaVersion="0.3.0.0"
TenantId="yourtenant.onmicrosoft.com"
PolicyId="B2C_1A_editQr"
PublicPolicyUri="http://yourtenant.onmicrosoft.com/B2C_1A_editQr">
<BasePolicy>
<TenantId>yourtenant.onmicrosoft.com</TenantId>
<PolicyId>B2C_1A_TrustFrameworkExtensions</PolicyId>
</BasePolicy>
<RelyingParty>
<DefaultUserJourney ReferenceId="EditQR" />
<UserJourneyBehaviors>
<ScriptExecution>Allow</ScriptExecution>
</UserJourneyBehaviors>
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="OpenIdConnect" />
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub"/>
<OutputClaim ClaimTypeReferenceId="tenantId" AlwaysUseDefaultValue="true" DefaultValue="{Policy:TenantObjectId}" />
</OutputClaims>
<SubjectNamingInfo ClaimType="sub" />
</TechnicalProfile>
</RelyingParty>
</TrustFrameworkPolicy>
Test it using http://jwt.ms, you will be promted to login and then redirected to the QR page.
Cheers!

Azure AD B2C Custom SignIn Policy Displays SignUpSignIn

I have a custom SignIn policy, because I need to fetch something from an application database (via REST API) during the sign in process.
So I have this user journey
<UserJourney Id="SignIn">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.localaccountsignin">
<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="GetCustomDbObj" TechnicalProfileReferenceId="GetCustomDbObj" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="4" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>
You can see that in step #3, I have a reference to a custom technical profile that fetches some data from a custom REST API.
In the first step, I display (supposedly) the sign in page, it references api.localaccountsignin
<ContentDefinition Id="api.localaccountsignin">
<LoadUri>https://mytenant.b2clogin.com/static/tenant/default/signin.cshtml</LoadUri>
<RecoveryUri>https://mytenant.b2clogin.com/static/tenant/default/signin.cshtml</RecoveryUri>
<DataUri>urn:com:microsoft:aad:b2c:elements:contract:unifiedssp:1.0.0</DataUri>
<Metadata>
<Item Key="TemplateId">azureBlue</Item>
</Metadata>
</ContentDefinition>
The problem is that when I use the policy, I see a page that resembles the signup+signin unified policy
Instead of just the sign in policy, which is what I desire.
I suspect that it has something to do with
<OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.localaccountsignin">
More specifically, CombinedSignInAndSignUp
But I don't know what to put instead. I tried using ClaimsExchange but it didn't work.
Is there a solution?
As per this:
You can achieve this by setting SignUp to “False”.
<Item Key=”setting.showSignupLink”>False</Item>

Azure AD B2C Custom Policy with Sign Up Page Only

I want to create a custom policy in Azure AD B2C. I can't find such an example in the starterpack.
I checked this post Azure AD B2C Link to Sign Up Page (Not Sign In) and, if I understand it correctly, there is no URL I can use for an existing custom signup_signin policy to end up directly in the Sign Up page. Creating "Sign up v2" flow works, but I need a custom one, and copying code from the build in one and placing it to the LocalAccounts example from the starterpack doesn't work.
This is the UserJourney code downloaded from "Sign up v2" flow
<UserJourney Id="B2CSignUp_V2">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="ClaimsProviderSelection" ContentDefinitionReferenceId="api.idpselections.signup">
<ClaimsProviderSelections>
<ClaimsProviderSelection TargetClaimsExchangeId="SignUpWithLogonEmailExchange" />
</ClaimsProviderSelections>
</OrchestrationStep>
</OrchestrationSteps>
</UserJourney>
I tried to replace the OrchestrationStep 1 and 2 from the starter pack with the one here but it doesn't work.
In your Custom policies, In the B2C_1A_TrustFrameworkBase.xml file replace the 1st Orchestration Step for the User Journey Id="SignUpOrSignIn" with the following Orchestration Step and you will be directly landed to Signup page -
<OrchestrationStep Order="1" Type="ClaimsProviderSelection" ContentDefinitionReferenceId="api.idpselections.signup">
<ClaimsProviderSelections>
<ClaimsProviderSelection TargetClaimsExchangeId="SignUpWithLogonEmailExchange" />
</ClaimsProviderSelections>
</OrchestrationStep>
Alternatively, you can:
Remove orchestration step 1
Remove the objectId precondition from orchestration step 2
Renumber the following orchestration steps
Such as:
<OrchestrationStep Order="1" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="SignUpWithLogonEmailExchange" TechnicalProfileReferenceId="LocalAccountSignUpWithLogonEmail" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="3" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />

Resources