Search code examples
azure-ad-b2cazure-ad-b2c-custom-policy

Azure B2C SMS OTP Verification with Custom Display Control


I'm currently working through a couple of situations here...

  1. Where the current OTP functionality for SMS that I have does not auto submit the verification code when using an IPhone (paste functionality). I'm trying to add a "Verify" button on that screen to enable the users to be able to submit, liberating this lack in functionality.

  2. using a custom display control to only show the US country code.

I am still seeing the default of all country codes and there is no button populating... I'm not sure what I'm missing here.

please find the components of the policy below...

    <ClaimType Id="countryCode">
            <DisplayName>Country</DisplayName>
            <DataType>string</DataType>
            <UserHelpText>Enter Country</UserHelpText>
            <UserInputType>DropdownSingleSelect</UserInputType>
            <Restriction>
                <Enumeration Text="United States (+1)" Value="US" />
            </Restriction>
        </ClaimType>

    <DisplayControl Id="phoneVerificationControl-Custom" UserInterfaceControlType="VerificationControl">
            <InputClaims>
                <InputClaim ClaimTypeReferenceId="Verified.strongAuthenticationPhoneNumber" />
                <InputClaim ClaimTypeReferenceId="countryCode" />
            </InputClaims>
            <DisplayClaims>
                <DisplayClaim ClaimTypeReferenceId="countryCode" ControlClaimType="CountryCode" Required="true" />
                <DisplayClaim ClaimTypeReferenceId="Verified.strongAuthenticationPhoneNumber" ControlClaimType="Phone" Required="true" />
                <DisplayClaim ClaimTypeReferenceId="verificationCode" ControlClaimType="VerificationCode" Required="true" />
            </DisplayClaims>
            <Actions>
                <Action Id="SendCode">
                    <ValidationClaimsExchange>
                        <ValidationClaimsExchangeTechnicalProfile TechnicalProfileReferenceId="CombineCountryCodeAndNationalNumber" />
                        <ValidationClaimsExchangeTechnicalProfile TechnicalProfileReferenceId="AzureMfa-SendSms" />
                    </ValidationClaimsExchange>
                </Action>
                <Action Id="VerifyCode">
                    <ValidationClaimsExchange>
                        <ValidationClaimsExchangeTechnicalProfile TechnicalProfileReferenceId="CombineCountryCodeAndNationalNumber" />
                        <ValidationClaimsExchangeTechnicalProfile TechnicalProfileReferenceId="AzureMfa-VerifySms" />
                    </ValidationClaimsExchange>
                </Action>
            </Actions>
        </DisplayControl>

    <TechnicalProfile Id="PhoneFactor-InputOrVerify">
                <DisplayName>PhoneFactor</DisplayName>
                <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.PhoneFactorProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
                <Metadata>
                    <Item Key="ContentDefinitionReferenceId">api.phonefactor</Item>
                    <Item Key="ManualPhoneNumberEntryAllowed">true</Item>
                <!-- This would be the verification button element -->
                <Item Key="Operation">VerifyCode</Item>
                </Metadata>
                <CryptographicKeys>
                    <Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
                </CryptographicKeys>
                <InputClaimsTransformations>
                    <InputClaimsTransformation ReferenceId="CreateUserIdForMFA" />
                </InputClaimsTransformations>
                <InputClaims>
                    <InputClaim ClaimTypeReferenceId="userIdForMFA" PartnerClaimType="UserId" />
                    <InputClaim ClaimTypeReferenceId="Verified.strongAuthenticationPhoneNumber" />
                </InputClaims>
                <DisplayClaims>
                    <DisplayClaim DisplayControlReferenceId="phoneVerificationControl-Custom" />
                </DisplayClaims>
                <OutputClaims>
                    <!-- Although 'Verified.OfficePhone' here makes no sense... it has to be that way. -->
                    <OutputClaim ClaimTypeReferenceId="Verified.strongAuthenticationPhoneNumber" PartnerClaimType="Verified.OfficePhone" />
                    <OutputClaim ClaimTypeReferenceId="newPhoneNumberEntered" PartnerClaimType="newPhoneNumberEntered" />
                    <OutputClaim ClaimTypeReferenceId="extension_mfaPhoneContactInformation" />
                </OutputClaims>
                <UseTechnicalProfileForSessionManagement ReferenceId="SM-MFA-Phone" />
            </TechnicalProfile>


   <SubJourney Id="phone-flow" Type="Call">
        <OrchestrationSteps>
            <OrchestrationStep Order="1" Type="ClaimsExchange">
                <ClaimsExchanges>
                    <ClaimsExchange Id="PhoneFactor-Verify" TechnicalProfileReferenceId="PhoneFactor-InputOrVerify" />
                </ClaimsExchanges>
            </OrchestrationStep>

            <OrchestrationStep Order="2" Type="ClaimsExchange">
                <ClaimsExchanges>
                    <ClaimsExchange Id="CopyVerifiedPhoneIntoExtension" TechnicalProfileReferenceId="CopyVerifiedPhoneIntoExtension" />
                </ClaimsExchanges>
            </OrchestrationStep>

            <OrchestrationStep Order="3" Type="ClaimsExchange">
                <ClaimsExchanges>
                    <ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
                </ClaimsExchanges>
            </OrchestrationStep>

            <!-- Save MFA phone number -->
            <OrchestrationStep Order="4" Type="ClaimsExchange">
                <Preconditions>
                    <Precondition Type="ClaimsExist" ExecuteActionsIf="false">
                        <Value>newPhoneNumberEntered</Value>
                        <Action>SkipThisOrchestrationStep</Action>
                    </Precondition>
                </Preconditions>
                <ClaimsExchanges>
                    <ClaimsExchange Id="AADUserWriteWithObjectId" TechnicalProfileReferenceId="AAD-UserWritePhoneNumberUsingObjectId" />
                </ClaimsExchanges>
            </OrchestrationStep>
        </OrchestrationSteps>
    </SubJourney>


Solution

    1. For the verify button to show up, update the version in DataUri property of ContentDefinition.
         <ContentDefinition Id="api.phonefactor">
           ... 
           <DataUri>urn:com:microsoft:aad:b2c:elements:contract:multifactor:1.2.5</DataUri>
           ...
         </ContentDefinition>
    
    1. If you are planning to use display controls instead, I believe you need to use a Self Asserted technical profile instead of PhoneFactor one. Ensure you use a new version of selfAsserted page (Update DataUri in ContentDefinition). eg: 2.1.6.