0

I'm using the guide here to perform Just-In-Time migration of a user from a legacy Idp to azure ad b2c: https://github.com/azure-ad-b2c/user-migration/tree/master/jit-migration-v2. I have this working properly on its own with a service I am using to query the legacy IdP and returning expected claims.

I also tried out the custom TOTP sample and have it working with a QR code for new user sign ups using this sample and policies: https://github.com/azure-ad-b2c/samples/tree/master/policies/custom-mfa-totp. I have a separate service for generating QR code which is presented to the user when they sign up.

However, I'm not sure how to incorporate the user migration above with the TOTP. When a user that exists in legacy IdP tries to sign in, and if they are validated and their account is created in AD, I would like them to be directed to the QR code page as per the above policy where they must sign up their newly migrated account with microsoft auth app. More specifically, I would like some guidance around how to setup the custom policies above to accomplish this.

Riz
  • 6,486
  • 19
  • 66
  • 106

1 Answers1

0
  1. Inherit the features into a single policy: Chain your files like this by configuing the basePolicy element. Base <- Extensions <- Extensions_TOTP <- Extensions_Migration <- SignInSignUp
  2. Remove or rename all the UserJourneys
  3. Create a new UserJourney which creates the logic you want to call the specific functionality (technical profiles) you desire, with preconditions to only call the TOTP technical profiles based on specific claim values. Essentially you can take the JIT migration user journey, and bring in the specific steps from the TOTP user journey to give the TOTP functionality.
  4. Update the defaultUserJourney id in the SignInSignUp file.

Your journey should look like

      <UserJourney Id="SignUpOrSignIn">
      <OrchestrationSteps>
      
        <OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin">
          <ClaimsProviderSelections>
            <ClaimsProviderSelection TargetClaimsExchangeId="FacebookExchange" />
            <ClaimsProviderSelection TargetClaimsExchangeId="GoogleExchange" />
           <ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninEmailExchange" />
          </ClaimsProviderSelections>
          <ClaimsExchanges>
            <ClaimsExchange Id="LocalAccountSigninEmailExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Email" />
          </ClaimsExchanges>
        </OrchestrationStep>

        <!-- Check if the user has selected to sign in using one of the social providers -->
        <OrchestrationStep Order="2" Type="ClaimsExchange">
          <Preconditions>
            <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
              <Value>objectId</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
          </Preconditions>
          <ClaimsExchanges>
            <ClaimsExchange Id="FacebookExchange" TechnicalProfileReferenceId="Facebook-OAUTH" />
            <ClaimsExchange Id="GoogleExchange" TechnicalProfileReferenceId="Google-OAUTH" />
           <ClaimsExchange Id="SignUpWithLogonEmailExchange" TechnicalProfileReferenceId="LocalAccountSignUpWithLogonEmail" />
          </ClaimsExchanges>
        </OrchestrationStep>

        <!-- For social IDP authentication, attempt to find the user account in the directory. -->
        <OrchestrationStep Order="3" Type="ClaimsExchange">
          <Preconditions>
            <Precondition Type="ClaimEquals" ExecuteActionsIf="true">
              <Value>authenticationSource</Value>
              <Value>localAccountAuthentication</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
          </Preconditions>
          <ClaimsExchanges>
            <ClaimsExchange Id="AADUserReadUsingAlternativeSecurityId" TechnicalProfileReferenceId="AAD-UserReadUsingAlternativeSecurityId-NoError" />
          </ClaimsExchanges>
        </OrchestrationStep>

        <!-- Show self-asserted page only if the directory does not have the user account already (i.e. we do not have an objectId). 
          This can only happen when authentication happened using a social IDP. If local account was created or authentication done
          using ESTS in step 2, then an user account must exist in the directory by this time. -->
        <OrchestrationStep Order="4" Type="ClaimsExchange">
          <Preconditions>
            <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
              <Value>objectId</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
          </Preconditions>
          <ClaimsExchanges>
            <ClaimsExchange Id="SelfAsserted-Social" TechnicalProfileReferenceId="SelfAsserted-Social" />
          </ClaimsExchanges>
        </OrchestrationStep>

        <!-- This step reads any user attributes that we may not have received when authenticating using ESTS so they can be sent 
          in the token. -->
        <OrchestrationStep Order="5" Type="ClaimsExchange">
          <Preconditions>
            <Precondition Type="ClaimEquals" ExecuteActionsIf="true">
              <Value>authenticationSource</Value>
              <Value>socialIdpAuthentication</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
          </Preconditions>
          <ClaimsExchanges>
            <ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
          </ClaimsExchanges>
        </OrchestrationStep>
        <!-- The previous step (SelfAsserted-Social) could have been skipped if there were no attributes to collect 
             from the user. So, in that case, create the user in the directory if one does not already exist 
             (verified using objectId which would be set from the last step if account was created in the directory. -->
        <OrchestrationStep Order="6" Type="ClaimsExchange">
          <Preconditions>
            <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
              <Value>objectId</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
          </Preconditions>
          <ClaimsExchanges>
            <ClaimsExchange Id="AADUserWrite" TechnicalProfileReferenceId="AAD-UserWriteUsingAlternativeSecurityId" />
          </ClaimsExchanges>
        </OrchestrationStep>


       <!-- Demo: The following orchestration step is executed only for unregistered 
        accounts (new created account or if user cancel the sign-up process). 
        It generates a verification app secret key for the user (none interactive step). -->
        <OrchestrationStep Order="7" Type="ClaimsExchange">
          <Preconditions>
            <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
              <Value>extension_StrongAuthenticationAppSecretKey</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
          </Preconditions>
          <ClaimsExchanges>
            <ClaimsExchange Id="AppFactorGenerateTotpWebHook" TechnicalProfileReferenceId="AppFactor-GenerateTotpWebHook" />
          </ClaimsExchanges>
        </OrchestrationStep>

        <!-- Demo: The following orchestration step is executed only for unregistered 
        accounts (new created account or if user cancel the sign-up process). 
        It registers a verification app through QR code that mobile authentication app should scan. -->
        <OrchestrationStep Order="8" Type="ClaimsExchange">
          <Preconditions>
            <Precondition Type="ClaimsExist" ExecuteActionsIf="false">
              <Value>strongAuthenticationAppQRCodeBitmap</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
            <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
              <Value>isActiveMFASession</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
          </Preconditions>
          <ClaimsExchanges>
            <ClaimsExchange Id="AppFactorRegisterExchange" TechnicalProfileReferenceId="AppFactor-Register" />
          </ClaimsExchanges>
        </OrchestrationStep>

        <!-- Demo: The following orchestration step is executed only for registered accounts.
        It asks the user to provide the TOTP code and verifies the provided code (using validation technical profile). -->
        <OrchestrationStep Order="9" Type="ClaimsExchange">
          <Preconditions>
            <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
              <Value>strongAuthenticationAppQRCodeBitmap</Value>
              <Action>SkipThisOrchestrationStep</Action>
            </Precondition>
          </Preconditions>
          <ClaimsExchanges>
            <ClaimsExchange Id="AppFactorChallengeExchange" TechnicalProfileReferenceId="AppFactor-Challenge" />
          </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="10" Type="ClaimsExchange">
          <ClaimsExchanges>
            <ClaimsExchange Id="AADWriteUserAppCodeByObjectId" TechnicalProfileReferenceId="AAD-WriteUserAppCodeByObjectId" />
          </ClaimsExchanges>
        </OrchestrationStep>


 
        <OrchestrationStep Order="11" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
 
      </OrchestrationSteps>
      <ClientDefinition ReferenceId="DefaultWeb" />
    </UserJourney>

You will want to add preconditions to Step 7/8/9 such that it triggers only for these migrated accounts. You can use the claims emitted in step 1 from your REST API to indicate whether this is a JIT migration. You can use extension attributes to indicate whether this is a migrated user. These claims can then be used to drive the logic for these steps as you desire.

Jas Suri - MSFT
  • 10,605
  • 2
  • 10
  • 20
  • Thanks very much for taking the time to write this. I've been digging deep into the AD B2C technical documentation and I see that you have contributed quite a bit to the github projects, the vs code extension, docs, etc. Thanks for your contributions. – Riz Feb 20 '21 at 11:06