How to check if the account exists with the given email in Azure AD B2C Custom policies?

Rahul 20 Reputation points
2024-01-03T09:37:11.22+00:00

I am using Mailjet custom sample policy files(Link) to implement email verification with Mailjet. When I run the signup flow, it sends the verification code to the email without verifying if an account with the given email already exists in the directory. similarly, when I run the password reset flow it sends the verification code even if an account with a given email does not exist in the directory.

What I need is if the user is running the signup flow and if the user enters an email that has an existing account then instead of sending opt it should show an error message, also for reset password flow if the user enters an email which is not associated with an existing account then it should show an error message.

I found a reset password policy(Link) that checks if an account exists with a given email, if the account does not exist it shows an error message. I tried modifying my DisplayControl_TrustFrameworkExtensions.xml of mailjet policy files according to the reset password policy file but it does not work.

Microsoft Security | Microsoft Entra | Microsoft Entra ID
0 comments No comments
{count} votes

Accepted answer
  1. James Hamil 27,221 Reputation points Microsoft Employee Moderator
    2024-01-03T16:58:57.41+00:00

    Hi @Rahul , to check if an account exists with the given email in Azure AD B2C Custom policies, you can use the AAD-UserReadUsingEmailAddress technical profile. This technical profile reads the user's account information using the email address provided by the user. If the account exists, it returns the user's object ID. If the account does not exist, it returns an error.

    To use this technical profile in your custom policy, you can add it to your SignUp or PasswordReset user journey. Here is an example of how to add it to the SignUp user journey:

    1. In your SignUp user journey, add a new orchestration step before the SendCode step. This step will call the AAD-UserReadUsingEmailAddress technical profile.
    <OrchestrationStep Order="1" Type="ClaimsExchange">
      <Preconditions>
        <Precondition Type="ClaimEquals" ExecuteActionsIf="false">
          <Value>isExistingUser</Value>
          <Value>False</Value>
          <Action>SkipThisOrchestrationStep</Action>
        </Precondition>
      </Preconditions>
      <ClaimsExchanges>
        <ClaimsExchange Id="CheckExistingUser" TechnicalProfileReferenceId="AAD-UserReadUsingEmailAddress" />
      </ClaimsExchanges>
    </OrchestrationStep>
    
    1. Add a precondition to the SendCode step that checks if the objectId claim is present. If the claim is present, it means that the user already exists, and you can skip the SendCode step.
    <OrchestrationStep Order="2" Type="ClaimsExchange">
      <Preconditions>
        <Precondition Type="ClaimEquals" ExecuteActionsIf="false">
          <Value>isExistingUser</Value>
          <Value>True</Value>
          <Action>SkipThisOrchestrationStep</Action>
        </Precondition>
      </Preconditions>
      <ClaimsExchanges>
        <ClaimsExchange Id="SendCode" TechnicalProfileReferenceId="SendCode" />
      </ClaimsExchanges>
    </OrchestrationStep>
    
    1. Add a validation technical profile that checks if the objectId claim is present. If the claim is not present, it means that the user does not exist, and you can show an error message.
    <TechnicalProfile Id="CheckExistingUserValidation">
      <DisplayName>Check if user exists</DisplayName>
      <Protocol Name="None" />
      <OutputClaims>
        <OutputClaim ClaimTypeReferenceId="isExistingUser" DefaultValue="False" />
      </OutputClaims>
      <ValidationTechnicalProfiles>
        <ValidationTechnicalProfile ReferenceId="AAD-UserReadUsingEmailAddress" ContinueOnError="false" />
      </ValidationTechnicalProfiles>
    </TechnicalProfile>
    
    1. Add the CheckExistingUserValidation technical profile to the SignUp user journey after the LocalAccountSignUpWithLogonEmail technical profile.
    <TechnicalProfile Id="LocalAccountSignUpWithLogonEmail">
      ...
      <OutputClaims>
        ...
        <OutputClaim ClaimTypeReferenceId="isExistingUser" DefaultValue="False" />
      </OutputClaims>
      <ValidationTechnicalProfiles>
        <ValidationTechnicalProfile ReferenceId="CheckExistingUserValidation" />
      </ValidationTechnicalProfiles>
    </TechnicalProfile>
    

    With these changes, when a user enters an email address during the SignUp user journey, the AAD-UserReadUsingEmailAddress technical profile will check if an account exists with that email address. If an account exists, the objectId claim will be set, and the SendCode step will be skipped. If an account does not exist, the objectId claim will not be set, and the CheckExistingUserValidation technical profile will show an error message. Please let me know if you have any questions and I can help you further.

    If this answer helps you please mark "Accept Answer" so other users can reference it.

    Thank you,

    James


1 additional answer

Sort by: Most helpful
  1. Rahul 20 Reputation points
    2024-01-04T14:53:19.0133333+00:00

    My user journey does not have orchestration steps for sending OTP, it has orchestration steps for sign in, signup, and resetting passwords.so, I have made the changes in the sendcode action and LocalAccountSignUpWithLogonEmail technical profile.

    Inside the send code Action, I have added the AAD-UserReadUsingEmailAddress technical profile and the precondition on the Sendcode technical profile as mentioned in steps 1 and 2. The following code is for sendcode action.

    <Action Id="SendCode">
                <ValidationClaimsExchange>
    
                  <!-- calling the AAD-UserReadUsingEmailAddress technical profile as mentioned in step
                  1-->
                  <ValidationClaimsExchangeTechnicalProfile
                    TechnicalProfileReferenceId="AAD-UserReadUsingEmailAddress">
                    <Preconditions>
                      <Precondition Type="ClaimEquals" ExecuteActionsIf="false">
                        <Value>isExistingUser</Value>
                        <Value>False</Value>
                        <Action>SkipThisOrchestrationStep</Action>
                      </Precondition>
                    </Preconditions>
                  </ValidationClaimsExchangeTechnicalProfile>
    
                  <ValidationClaimsExchangeTechnicalProfile
                    TechnicalProfileReferenceId="AAD-CheckEmailAddressExists" />
    
                  <!-- Adding precondition to send code as mentioned in step 2-->
                  <ValidationClaimsExchangeTechnicalProfile TechnicalProfileReferenceId="GenerateOtp">
                    <Preconditions>
                      <Precondition Type="ClaimEquals" ExecuteActionsIf="false">
                        <Value>isExistingUser</Value>
                        <Value>True</Value>
                        <Action>SkipThisOrchestrationStep</Action>
                      </Precondition>
                    </Preconditions>
                  </ValidationClaimsExchangeTechnicalProfile>
                  <ValidationClaimsExchangeTechnicalProfile TechnicalProfileReferenceId="SendOtp" />
                </ValidationClaimsExchange>
              </Action>
    

    Also, I changed my LocalAccountSignUpWithLogonEmail according to steps 3 and 4 but was unable to make it work.it is still sending verification codes to registered emails.

    Here is the user journey I am using

    <OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin">
              <ClaimsProviderSelections>
                <ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninEmailExchange" />
                <ClaimsProviderSelection TargetClaimsExchangeId="ForgotPasswordExchange" />
              </ClaimsProviderSelections>
              <ClaimsExchanges>
                <ClaimsExchange Id="LocalAccountSigninEmailExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Email" />
              </ClaimsExchanges>
            </OrchestrationStep>
    <OrchestrationStep Order="2" Type="ClaimsExchange">
              <Preconditions>
                <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
                  <Value>objectId</Value>
                  <Action>SkipThisOrchestrationStep</Action>
                </Precondition>
              </Preconditions>
              <ClaimsExchanges>
                <ClaimsExchange Id="SignUpWithLogonEmailExchange" TechnicalProfileReferenceId="LocalAccountSignUpWithLogonEmail" />
                <ClaimsExchange Id="ForgotPasswordExchange" TechnicalProfileReferenceId="ForgotPassword" />
              </ClaimsExchanges>
            </OrchestrationStep>
    

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.