Skip to content

Instantly share code, notes, and snippets.

@rbrayb
Created March 4, 2025 02:04
Show Gist options
  • Save rbrayb/dd53300fa15c43386ba2adbd482ef72f to your computer and use it in GitHub Desktop.
Save rbrayb/dd53300fa15c43386ba2adbd482ef72f to your computer and use it in GitHub Desktop.
Integrating both SMS and TOTP MFA in a single custom policy in Azure AD B2C
<?xml version="1.0" encoding="utf-8"?>
<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="tenant.onmicrosoft.com" PolicyId="B2C_1A_Extensions_TOTPSMS"
PublicPolicyUri="http://tenant.onmicrosoft.com/B2C_1A_Extensions_TOTPSMS">
<!-- FYI
https://cloudfirstapproach.com/integrate-sms-and-totp-in-azure-ad-b2c-custom-policy/?form=MG0AV3
The TrustFrameworkBase.xml file contains the PhoneFactor-InputOrVerify technical profile. It uses
the PhoneFactorProtocolProvider
which provides a user interface to interact with the user to verify, or enroll a phone number. This
all works well until you want to update your phone number using the Graph API.
The problem is that the PhoneFactorProtocolProvider returns a verified phone number without space
between country prefix and phone
number (in Azure Portal it’s +11112223333, but should be +1 1112223333). The verified phone number
is then saved in Azure AD B2C.
If you ever want to update the phone number, the query for phone number using the Graph API returns
an empty array. And if you try to delete the phone number, the Graph API returns a 404.
You can work around this problem by using the AzureMfaProtocolProvider and save the phone number in
a custom claim. -->
<BasePolicy>
<TenantId>tenant.onmicrosoft.com</TenantId>
<PolicyId>B2C_1A_TrustFrameworkExtensions_TOTP</PolicyId>
</BasePolicy>
<BuildingBlocks>
<ClaimsSchema>
<ClaimType Id="extension_mfaScenario">
<DisplayName>Select MFA method.</DisplayName>
<DataType>string</DataType>
<UserInputType>RadioSingleSelect</UserInputType>
<Restriction>
<Enumeration Text="SMS" Value="sms" SelectByDefault="true" />
<Enumeration Text="TOTP" Value="totp" />
</Restriction>
</ClaimType>
<ClaimType Id="extension_mfaPhoneNumber">
<DataType>string</DataType>
<Mask Type="Simple">XXX-XXX-</Mask>
<UserInputType>Readonly</UserInputType>
</ClaimType>
<ClaimType Id="mfaSelected">
<DisplayName>mfaSelected</DisplayName>
<DataType>boolean</DataType>
</ClaimType>
<ClaimType Id="fullPhoneNumber">
<DisplayName>Phone number</DisplayName>
<DataType>phoneNumber</DataType>
<UserInputType>Readonly</UserInputType>
</ClaimType>
<ClaimType Id="inputPhoneNumber">
<DisplayName>Phone Number</DisplayName>
<DataType>string</DataType>
<UserInputType>TextBox</UserInputType>
</ClaimType>
<ClaimType Id="verificationCode">
<DisplayName>Verification code</DisplayName>
<DataType>string</DataType>
<UserInputType>TextBox</UserInputType>
</ClaimType>
<ClaimType Id="inputCountryCode">
<DisplayName>Country</DisplayName>
<DataType>string</DataType>
<UserHelpText>Country Code</UserHelpText>
<UserInputType>DropdownSingleSelect</UserInputType>
<Restriction>
<Enumeration Text="New Zealand(+64)" Value="NZ" />
</Restriction>
</ClaimType>
</ClaimsSchema>
<ClaimsTransformations>
<ClaimsTransformation Id="ConvertStringToPhoneNumber"
TransformationMethod="ConvertStringToPhoneNumberClaim">
<InputClaims>
<InputClaim ClaimTypeReferenceId="extension_mfaPhoneNumber"
TransformationClaimType="phoneNumberString" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="fullPhoneNumber" TransformationClaimType="outputClaim" />
</OutputClaims>
</ClaimsTransformation>
<ClaimsTransformation Id="ConvertStringToPhoneNumberWithCountryCode"
TransformationMethod="ConvertStringToPhoneNumberClaim">
<InputClaims>
<InputClaim ClaimTypeReferenceId="inputPhoneNumber"
TransformationClaimType="phoneNumberString" />
<InputClaim ClaimTypeReferenceId="inputCountryCode" TransformationClaimType="country" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="fullPhoneNumber" TransformationClaimType="outputClaim" />
</OutputClaims>
</ClaimsTransformation>
<ClaimsTransformation Id="ConvertPhoneNumberToString"
TransformationMethod="ConvertPhoneNumberClaimToString">
<InputClaims>
<InputClaim ClaimTypeReferenceId="fullPhoneNumber" TransformationClaimType="phoneNumber" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="extension_mfaPhoneNumber"
TransformationClaimType="phoneNumberString" />
</OutputClaims>
</ClaimsTransformation>
</ClaimsTransformations>
</BuildingBlocks>
<ClaimsProviders>
<ClaimsProvider>
<DisplayName>Facebook</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="Facebook-OAUTH">
<Metadata>
<Item Key="client_id">facebook_clientid</Item>
<Item Key="scope">email public_profile</Item>
<Item Key="ClaimsEndpoint">https://graph.facebook.com/me?fields=id,first_name,last_name,name,email</Item>
</Metadata>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>Local Account SignIn</DisplayName>
<TechnicalProfiles>
<!-- <TechnicalProfile Id="login-NonInteractive">
<Metadata>
<Item Key="client_id">ProxyIdentityExperienceFrameworkAppId</Item>
<Item Key="IdTokenAudience">IdentityExperienceFrameworkAppId</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="client_id" DefaultValue="ProxyIdentityExperienceFrameworkAppId"
/>
<InputClaim ClaimTypeReferenceId="resource_id" PartnerClaimType="resource"
DefaultValue="IdentityExperienceFrameworkAppId" />
</InputClaims>
</TechnicalProfile> -->
<TechnicalProfile Id="CreateFullPhoneNumber">
<DisplayName>Create Full Phone Number</DisplayName>
<Protocol Name="Proprietary"
Handler="Web.TPEngine.Providers.ClaimsTransformationProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="fullPhoneNumber" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="ConvertStringToPhoneNumber" />
</OutputClaimsTransformations>
</TechnicalProfile>
<TechnicalProfile Id="ValidatePhoneNumber">
<DisplayName>Validate Phone Number</DisplayName>
<Protocol Name="Proprietary"
Handler="Web.TPEngine.Providers.ClaimsTransformationProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="fullPhoneNumber" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="ConvertStringToPhoneNumberWithCountryCode" />
</OutputClaimsTransformations>
</TechnicalProfile>
<TechnicalProfile Id="AzureMfa-SendSms">
<DisplayName>Send Sms</DisplayName>
<Protocol Name="Proprietary"
Handler="Web.TPEngine.Providers.AzureMfaProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="Operation">OneWaySMS</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="userPrincipalName" />
<InputClaim ClaimTypeReferenceId="fullPhoneNumber" PartnerClaimType="phoneNumber" />
</InputClaims>
</TechnicalProfile>
<TechnicalProfile Id="AzureMfa-VerifySms">
<DisplayName>Verify Sms</DisplayName>
<Protocol Name="Proprietary"
Handler="Web.TPEngine.Providers.AzureMfaProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="Operation">Verify</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="fullPhoneNumber" PartnerClaimType="phoneNumber" />
<InputClaim ClaimTypeReferenceId="verificationCode" />
</InputClaims>
</TechnicalProfile>
<TechnicalProfile Id="AADUserWriteChosenMfa">
<Metadata>
<Item Key="Operation">Write</Item>
<Item Key="RaiseErrorIfClaimsPrincipalAlreadyExists">false</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="objectId" />
</InputClaims>
<PersistedClaims>
<PersistedClaim ClaimTypeReferenceId="objectId" />
<PersistedClaim ClaimTypeReferenceId="userPrincipalName" />
<PersistedClaim ClaimTypeReferenceId="displayName" />
<PersistedClaim ClaimTypeReferenceId="extension_mfaScenario" />
</PersistedClaims>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
</TechnicalProfile>
<TechnicalProfile Id="AADUserWriteChosenMfa_ProfileEdit">
<Metadata>
<Item Key="Operation">Write</Item>
<Item Key="RaiseErrorIfClaimsPrincipalAlreadyExists">false</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="objectId" />
</InputClaims>
<PersistedClaims>
<PersistedClaim ClaimTypeReferenceId="objectId" />
<PersistedClaim ClaimTypeReferenceId="userPrincipalName" />
<PersistedClaim ClaimTypeReferenceId="displayName" />
<PersistedClaim ClaimTypeReferenceId="extension_mfaScenario" />
</PersistedClaims>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
</TechnicalProfile>
<TechnicalProfile Id="AADUserWritePhoneNumberUsingObjectId">
<Metadata>
<Item Key="Operation">Write</Item>
<Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">true</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="ConvertPhoneNumberToString" />
</InputClaimsTransformations>
<InputClaims>
<InputClaim ClaimTypeReferenceId="objectId" Required="true" />
</InputClaims>
<PersistedClaims>
<PersistedClaim ClaimTypeReferenceId="objectId" />
<PersistedClaim ClaimTypeReferenceId="extension_mfaPhoneNumber" />
</PersistedClaims>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
</TechnicalProfile>
<TechnicalProfile Id="AADUserReadChosenMfaUsingObjectId">
<Metadata>
<Item Key="Operation">Read</Item>
<Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">true</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="objectId" Required="true" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="userPrincipalName" />
<OutputClaim ClaimTypeReferenceId="extension_mfaScenario" />
<OutputClaim ClaimTypeReferenceId="extension_mfaPhoneNumber" />
</OutputClaims>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
</TechnicalProfile>
<TechnicalProfile Id="SelfAssertedChooseMfa">
<DisplayName>Choose MFA Method</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</Item>
<Item Key="setting.showCancelButton">true</Item>
</Metadata>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="extension_mfaScenario" Required="true" />
<OutputClaim ClaimTypeReferenceId="mfaSelected" DefaultValue="true" Required="false" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
<TechnicalProfile Id="PhoneFactor-Configure">
<DisplayName>Configure Phone Factor</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</Item>
<Item Key="language.button_continue">Send Code</Item>
<Item Key="UserMessageIfClaimsTransformationInvalidPhoneNumber">The phone number is not valid.</Item>
<Item Key="UserMessageIfServerError">Internal error.</Item>
<Item Key="UserMessageIfThrottled">Please wait 30 seconds before trying to receive a new SMS code.</Item>
</Metadata>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="inputCountryCode" Required="true" />
<OutputClaim ClaimTypeReferenceId="inputPhoneNumber" Required="true" />
<OutputClaim ClaimTypeReferenceId="fullPhoneNumber" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="ConvertPhoneNumberToString" />
</OutputClaimsTransformations>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="ValidatePhoneNumber" />
<ValidationTechnicalProfile ReferenceId="AzureMfa-SendSms" />
</ValidationTechnicalProfiles>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
<TechnicalProfile Id="PhoneFactor-SendCode">
<DisplayName>PhoneFactor Send Code</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</Item>
<Item Key="language.button_continue">Send Code</Item>
<Item Key="UserMessageIfServerError">Internal error.</Item>
<Item Key="UserMessageIfThrottled">Please wait 30 seconds before trying to receive a new SMS code.</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="extension_mfaPhoneNumber" />
</InputClaims>
<DisplayClaims>
<DisplayClaim ClaimTypeReferenceId="extension_mfaPhoneNumber" />
</DisplayClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AzureMfa-SendSms" />
</ValidationTechnicalProfiles>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
<TechnicalProfile Id="PhoneFactor-EnterVerificationCode">
<DisplayName>Enter Verification Code</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</Item>
<Item Key="language.button_continue">Verify</Item>
<Item Key="UserMessageIfWrongCodeEntered">The verification code you entered does not match the code sent via sms.</Item>
<Item Key="UserMessageIfMaxAllowedCodeRetryReached">Max allowed code retry reached.</Item>
<Item Key="UserMessageIfServerError">Internal error.</Item>
<Item Key="UserMessageIfThrottled">Please wait 30 seconds before trying to receive a new SMS code.</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="extension_mfaPhoneNumber" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="extension_mfaPhoneNumber" />
<OutputClaim ClaimTypeReferenceId="verificationCode" Required="true" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AzureMfa-VerifySms" />
</ValidationTechnicalProfiles>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
</ClaimsProviders>
<UserJourneys>
<UserJourney Id="SignUpOrSignMultiMFA">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="CombinedSignInAndSignUp"
ContentDefinitionReferenceId="api.signuporsignin">
<ClaimsProviderSelections>
<ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninEmailExchange" />
</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" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="3" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithObjectId"
TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="4" Type="InvokeSubJourney">
<JourneyList>
<Candidate SubJourneyReferenceId="MultiFactorSubJourney" />
</JourneyList>
</OrchestrationStep>
<OrchestrationStep Order="5" Type="SendClaims"
CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
</UserJourney>
<UserJourney Id="ProfileEdit_TOTPSMS">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="CombinedSignInAndSignUp"
ContentDefinitionReferenceId="api.signuporsignin">
<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="AADUserReadChosenMfaUsingObjectId"
TechnicalProfileReferenceId="AADUserReadChosenMfaUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- First verify that they are the the actual user by using MFA with the current method -->
<OrchestrationStep Order="4" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>extension_mfaScenario</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimEquals" ExecuteActionsIf="false">
<Value>extension_mfaScenario</Value>
<Value>sms</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>extension_mfaPhoneNumber</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="CreateFullPhoneNumber"
TechnicalProfileReferenceId="CreateFullPhoneNumber" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="5" Type="InvokeSubJourney">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>extension_mfaScenario</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimEquals" ExecuteActionsIf="false">
<Value>extension_mfaScenario</Value>
<Value>sms</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<JourneyList>
<Candidate SubJourneyReferenceId="PhoneFactorJourney" />
</JourneyList>
</OrchestrationStep>
<OrchestrationStep Order="6" Type="InvokeSubJourney">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>extension_mfaScenario</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimEquals" ExecuteActionsIf="false">
<Value>extension_mfaScenario</Value>
<Value>totp</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<JourneyList>
<Candidate SubJourneyReferenceId="TotpFactor-Input" />
</JourneyList>
</OrchestrationStep>
<OrchestrationStep Order="7" Type="InvokeSubJourney">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>extension_mfaScenario</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimEquals" ExecuteActionsIf="false">
<Value>extension_mfaScenario</Value>
<Value>totp</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<JourneyList>
<Candidate SubJourneyReferenceId="TotpFactor-Verify" />
</JourneyList>
</OrchestrationStep>
<!-- MFA challenge passed. Update your MFA method -->
<OrchestrationStep Order="8" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="SelfAssertedChooseMfa" TechnicalProfileReferenceId="SelfAssertedChooseMfa" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- And then update the user's MFA method -->
<OrchestrationStep Order="9" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserWriteChosenMfa_ProfileEdit" TechnicalProfileReferenceId="AADUserWriteChosenMfa_ProfileEdit" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="10" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>
</UserJourneys>
<SubJourneys>
<SubJourney Id="PhoneFactorJourney" Type="Call">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>mfaSelected</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimEquals" ExecuteActionsIf="false">
<Value>extension_mfaScenario</Value>
<Value>sms</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="PhoneFactor-Configure"
TechnicalProfileReferenceId="PhoneFactor-Configure" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>mfaSelected</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimEquals" ExecuteActionsIf="false">
<Value>extension_mfaScenario</Value>
<Value>sms</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="PhoneFactor-SendCode"
TechnicalProfileReferenceId="PhoneFactor-SendCode" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="3" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>extension_mfaScenario</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimEquals" ExecuteActionsIf="false">
<Value>extension_mfaScenario</Value>
<Value>sms</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="PhoneFactor-EnterVerificationCode"
TechnicalProfileReferenceId="PhoneFactor-EnterVerificationCode" />
</ClaimsExchanges>
</OrchestrationStep>
</OrchestrationSteps>
</SubJourney>
<SubJourney Id="MultiFactorSubJourney" Type="Call">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadChosenMfaUsingObjectId"
TechnicalProfileReferenceId="AADUserReadChosenMfaUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>extension_mfaScenario</Value>
<Value>sms</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>extension_mfaScenario</Value>
<Value>totp</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="SelfAssertedChooseMfa"
TechnicalProfileReferenceId="SelfAssertedChooseMfa" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="3" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>extension_mfaScenario</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimEquals" ExecuteActionsIf="false">
<Value>extension_mfaScenario</Value>
<Value>sms</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>extension_mfaPhoneNumber</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="CreateFullPhoneNumber"
TechnicalProfileReferenceId="CreateFullPhoneNumber" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="4" Type="InvokeSubJourney">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>extension_mfaScenario</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimEquals" ExecuteActionsIf="false">
<Value>extension_mfaScenario</Value>
<Value>sms</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<JourneyList>
<Candidate SubJourneyReferenceId="PhoneFactorJourney" />
</JourneyList>
</OrchestrationStep>
<OrchestrationStep Order="5" Type="InvokeSubJourney">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>extension_mfaScenario</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimEquals" ExecuteActionsIf="false">
<Value>extension_mfaScenario</Value>
<Value>totp</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<JourneyList>
<Candidate SubJourneyReferenceId="TotpFactor-Input" />
</JourneyList>
</OrchestrationStep>
<OrchestrationStep Order="6" Type="InvokeSubJourney">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>extension_mfaScenario</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimEquals" ExecuteActionsIf="false">
<Value>extension_mfaScenario</Value>
<Value>totp</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<JourneyList>
<Candidate SubJourneyReferenceId="TotpFactor-Verify" />
</JourneyList>
</OrchestrationStep>
<OrchestrationStep Order="7" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>mfaSelected</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AADUserWriteChosenMfa"
TechnicalProfileReferenceId="AADUserWriteChosenMfa" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="8" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>mfaSelected</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimEquals" ExecuteActionsIf="false">
<Value>extension_mfaScenario</Value>
<Value>sms</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AADUserWritePhoneNumberUsingObjectId"
TechnicalProfileReferenceId="AADUserWritePhoneNumberUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
</OrchestrationSteps>
</SubJourney>
</SubJourneys>
</TrustFrameworkPolicy>
<?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="tenant.onmicrosoft.com" PolicyId="B2C_1A_ProfileEdit_TOTPSMS"
PublicPolicyUri="http://tenant.onmicrosoft.com/B2C_1A_ProfileEdit_TOTPSMS"
DeploymentMode="Development"
UserJourneyRecorderEndpoint="urn:journeyrecorder:applicationinsights">
<BasePolicy>
<TenantId>tenant.onmicrosoft.com</TenantId>
<PolicyId>B2C_1A_Extensions_TOTPSMS</PolicyId>
</BasePolicy>
<RelyingParty>
<DefaultUserJourney ReferenceId="ProfileEdit_TOTPSMS" />
<UserJourneyBehaviors>
<JourneyInsights TelemetryEngine="ApplicationInsights"
InstrumentationKey="410...5d0" DeveloperMode="true"
ClientEnabled="false" ServerEnabled="true" TelemetryVersion="1.0.0" />
</UserJourneyBehaviors>
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="OpenIdConnect" />
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="mfaSelected" />
<OutputClaim ClaimTypeReferenceId="extension_mfaScenario" />
<OutputClaim ClaimTypeReferenceId="extension_mfaPhoneNumber" />
<OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub" />
<OutputClaim ClaimTypeReferenceId="tenantId" AlwaysUseDefaultValue="true"
DefaultValue="{Policy:TenantObjectId}" />
</OutputClaims>
<SubjectNamingInfo ClaimType="sub" />
</TechnicalProfile>
</RelyingParty>
</TrustFrameworkPolicy>