Created
March 4, 2025 02:04
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://medium.com/the-new-control-plane/integrating-both-sms-and-totp-mfa-in-a-single-custom-policy-in-azure-ad-b2c-and-giving-users-the-5bd1471e0d6a