Skip to content

Instantly share code, notes, and snippets.

@rbrayb
Last active August 26, 2024 02:47
Show Gist options
  • Save rbrayb/ad40c9e563615a40e941e05dce53c608 to your computer and use it in GitHub Desktop.
Save rbrayb/ad40c9e563615a40e941e05dce53c608 to your computer and use it in GitHub Desktop.
Using the TOTP MFA method in Azure AD B2C with an authenticator application
<?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_Demo_SignUp_SignIn_Create_TOTP"
PublicPolicyUri="http://tenant.onmicrosoft.com/B2C_1A_Demo_SignUp_SignIn_Create_TOTP">
<BasePolicy>
<TenantId>tenant.onmicrosoft.com</TenantId>
<PolicyId>B2C_1A_Demo_TrustFrameworkExtensions_Create_TOTP</PolicyId>
</BasePolicy>
<RelyingParty>
<DefaultUserJourney ReferenceId="SignUpOrSignInCreateTOTP" />
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="OpenIdConnect" />
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surname" />
<OutputClaim ClaimTypeReferenceId="email" />
<OutputClaim ClaimTypeReferenceId="totpIdentifier" />
<OutputClaim ClaimTypeReferenceId="numberOfAvailableDevices" />
<OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub" />
<OutputClaim ClaimTypeReferenceId="tenantId" AlwaysUseDefaultValue="true" DefaultValue="{Policy:TenantObjectId}" />
</OutputClaims>
<SubjectNamingInfo ClaimType="sub" />
</TechnicalProfile>
</RelyingParty>
</TrustFrameworkPolicy>
<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_Demo_TrustFrameworkExtensions_Create_TOTP" PublicPolicyUri="http://tenant.onmicrosoft.com/B2C_1A_Demo_TrustFrameworkExtensions_Create_TOTP">
<BasePolicy>
<TenantId>tenant.onmicrosoft.com</TenantId>
<PolicyId>B2C_1A_TrustFrameworkExtensions_DC</PolicyId>
</BasePolicy>
<BuildingBlocks>
<ClaimsSchema>
<ClaimType Id="totpIdentifier">
<DisplayName>UserId, Phone number or Email which can present the user's identity</DisplayName>
<DataType>string</DataType>
<AdminHelpText>UserId, Phone number or Email which can be used to create QR code identity.</AdminHelpText>
<UserInputType>Paragraph</UserInputType>
</ClaimType>
<ClaimType Id="numberOfAvailableDevices">
<DisplayName>Number of available devices:</DisplayName>
<DataType>string</DataType>
<UserInputType>Paragraph</UserInputType>
</ClaimType>
<ClaimType Id="QrCodeScanInstruction">
<DisplayName>Once you've downloaded the Authenticator app, you can use any of the methods below to continue with enrollment.</DisplayName>
<DataType>string</DataType>
<UserInputType>Paragraph</UserInputType>
</ClaimType>
<ClaimType Id="QrCodeVerifyInstruction">
<DisplayName>Enter the verification code from your authenticator app​.</DisplayName>
<DataType>string</DataType>
<UserInputType>Paragraph</UserInputType>
</ClaimType>
<ClaimType Id="qrCodeContent">
<DisplayName>QR Code Text</DisplayName>
<DataType>string</DataType>
<UserHelpText>QR code text</UserHelpText>
<UserInputType>Paragraph</UserInputType>
</ClaimType>
<ClaimType Id="secretKey">
<DisplayName>If you cant't scan the image, enter the Secret Key:</DisplayName>
<DataType>string</DataType>
<UserHelpText>QR code secret</UserHelpText>
<UserInputType>Paragraph</UserInputType>
</ClaimType>
<ClaimType Id="otpCode">
<DisplayName>Enter your code</DisplayName>
<DataType>string</DataType>
<UserHelpText>Enter the 6-digit verification code generated by the the Authenticator app in the box</UserHelpText>
<UserInputType>TextBox</UserInputType>
<Restriction>
<Pattern RegularExpression="^[0-9]{6}$" HelpText="Enter the 6-digit verification code generated by the the Authenticator app in the box" />
</Restriction>
</ClaimType>
<ClaimType Id="uriLabel">
<DisplayName>Uri Label</DisplayName>
<DataType>string</DataType>
</ClaimType>
<ClaimType Id="issuer">
<DisplayName>Issuer</DisplayName>
<DataType>string</DataType>
</ClaimType>
<!--End of TOTP Claims-->
</ClaimsSchema>
<ClaimsTransformations>
<!-- Create a TOPT secret key-->
<ClaimsTransformation Id="CreateSecret" TransformationMethod="CreateOtpSecret">
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="secretKey" TransformationClaimType="outputClaim" />
</OutputClaims>
</ClaimsTransformation>
<!-- Create the TOTP issuer name. The issuer name is your tenant name, such as "Contoso demo" -->
<ClaimsTransformation Id="CreateIssuer" TransformationMethod="CreateStringClaim">
<InputParameters>
<InputParameter Id="value" DataType="string" Value="{AuthenticatorIssuer}" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="issuer" TransformationClaimType="createdClaim" />
</OutputClaims>
</ClaimsTransformation>
<!-- Create the TOTP URI label. The label is a combination of the totpIdentifier and the issuer name:
For example, Contoso demo:emily@fabrikam.com-->
<ClaimsTransformation Id="CreateUriLabel" TransformationMethod="FormatStringMultipleClaims">
<InputClaims>
<InputClaim ClaimTypeReferenceId="issuer" TransformationClaimType="inputClaim1" />
<InputClaim ClaimTypeReferenceId="totpIdentifier" TransformationClaimType="inputClaim2" />
</InputClaims>
<InputParameters>
<InputParameter Id="stringFormat" DataType="string" Value="{0}:{1}" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="uriLabel" TransformationClaimType="outputClaim" />
</OutputClaims>
</ClaimsTransformation>
<!-- Create the TOTP URI. The label is a combination of the CreateUriLabel and the secret key:
For example, otpauth://totp/Contoso%20demo:emily@fabrikam.com?secret=fay2lj7ynpntjgqa&issuer=Contoso+demo
This URI is later converted into a QR code that is presented to the user-->
<ClaimsTransformation Id="CreateUriString" TransformationMethod="BuildUri">
<InputClaims>
<InputClaim ClaimTypeReferenceId="uriLabel" TransformationClaimType="path" />
<InputClaim ClaimTypeReferenceId="secretKey" TransformationClaimType="query.secret" />
</InputClaims>
<InputParameters>
<InputParameter Id="scheme" DataType="string" Value="otpauth" />
<InputParameter Id="host" DataType="string" Value="totp" />
<InputParameter Id="query.issuer" DataType="string" Value="{AuthenticatorIssuer}" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="qrCodeContent" TransformationClaimType="outputClaim" />
</OutputClaims>
</ClaimsTransformation>
<!-- Copy the signInName to the totpIdentifier -->
<ClaimsTransformation Id="SignInNameToTotpIdentifier" TransformationMethod="CopyClaim">
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInName" TransformationClaimType="inputClaim" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="totpIdentifier" TransformationClaimType="outputClaim" />
</OutputClaims>
</ClaimsTransformation>
<!-- end of TOTP Claims Transforms-->
</ClaimsTransformations>
<ContentDefinitions>
<ContentDefinition Id="api.selfasserted.totp">
<LoadUri>~/tenant/templates/AzureBlue/selfAsserted.cshtml</LoadUri>
<RecoveryUri>~/common/default_page_error.html</RecoveryUri>
<DataUri>urn:com:microsoft:aad:b2c:elements:contract:selfasserted:2.1.9</DataUri>
<Metadata>
<Item Key="DisplayName">Collect information from user page</Item>
</Metadata>
<LocalizedResourcesReferences>
<LocalizedResourcesReference Language="en" LocalizedResourcesReferenceId="api.selfasserted.totp.en" />
</LocalizedResourcesReferences>
</ContentDefinition>
</ContentDefinitions>
<Localization>
<!-- TOTP localized strings (English) -->
<LocalizedResources Id="api.selfasserted.totp.en">
<LocalizedStrings>
<LocalizedString ElementType="DisplayControl" ElementId="authenticatorAppIconControl" StringId="title_text">Download the Microsoft Authenticator using the download links for iOS and Android or use any other authenticator app of your choice.</LocalizedString>
<LocalizedString ElementType="DisplayControl" ElementId="authenticatorAppIconControl" StringId="instruction_text">Once you&#39;ve downloaded the Authenticator app, you can use any of the methods below to continue with enrollment.</LocalizedString>
<LocalizedString ElementType="ClaimType" ElementId="QrCodeScanInstruction" StringId="DisplayName">Once you've downloaded the Authenticator app, you can use any of the methods below to continue with enrollment.</LocalizedString>
<LocalizedString ElementType="DisplayControl" ElementId="totpQrCodeControl" StringId="title_text">Scan the QR code</LocalizedString>
<LocalizedString ElementType="DisplayControl" ElementId="totpQrCodeControl" StringId="info_msg">You can download the Microsoft Authenticator app or use any other authenticator app of your choice.</LocalizedString>
<LocalizedString ElementType="DisplayControl" ElementId="totpQrCodeControl" StringId="link_text">Can&#39;t scan? Try this</LocalizedString>
<LocalizedString ElementType="DisplayControl" ElementId="authenticatorInfoControl" StringId="title_text">Enter the account details manually</LocalizedString>
<LocalizedString ElementType="DisplayControl" ElementId="authenticatorInfoControl" StringId="account_name">Account Name:</LocalizedString>
<LocalizedString ElementType="DisplayControl" ElementId="authenticatorInfoControl" StringId="display_prefix">Secret</LocalizedString>
<LocalizedString ElementType="DisplayControl" ElementId="authenticatorInfoControl" StringId="collapse_text">Still having trouble?</LocalizedString>
<!-- Verification -->
<LocalizedString ElementType="ClaimType" ElementId="QrCodeVerifyInstruction" StringId="DisplayName">Enter the verification code from your authenticator app​.</LocalizedString>
<LocalizedString ElementType="ClaimType" ElementId="otpCode" StringId="DisplayName">Enter your code.</LocalizedString>
<!-- <LocalizedString ElementType="UxElement" StringId="button_continue">Verify</LocalizedString> -->
</LocalizedStrings>
</LocalizedResources>
</Localization>
<DisplayControls>
<!-- Render the authenticator apps icon. -->
<DisplayControl Id="authenticatorAppIconControl" UserInterfaceControlType="AuthenticatorAppIconControl" />
<!-- Render the QR code. It takes the URI (qrCodeContent) input claim and renders it as a QR code-->
<DisplayControl Id="totpQrCodeControl" UserInterfaceControlType="QrCodeControl">
<InputClaims>
<InputClaim ClaimTypeReferenceId="qrCodeContent" />
</InputClaims>
<DisplayClaims>
<DisplayClaim ClaimTypeReferenceId="qrCodeContent" ControlClaimType="QrCodeContent" />
</DisplayClaims>
</DisplayControl>
<!-- Render the TOTP information. It takes the totpIdentifier and the secretKey input claims and renders as a plan text-->
<DisplayControl Id="authenticatorInfoControl" UserInterfaceControlType="AuthenticatorInfoControl">
<InputClaims>
<InputClaim ClaimTypeReferenceId="totpIdentifier" />
<InputClaim ClaimTypeReferenceId="secretKey" />
</InputClaims>
<DisplayClaims>
<DisplayClaim ClaimTypeReferenceId="totpIdentifier" />
<DisplayClaim ClaimTypeReferenceId="secretKey" />
</DisplayClaims>
</DisplayControl>
</DisplayControls>
<!--End of TOTP contols-->
</BuildingBlocks>
<ClaimsProviders>
<!--Start TOTP-->
<ClaimsProvider>
<DisplayName>TOTP</DisplayName>
<TechnicalProfiles>
<!-- Begin the TOTP verification process.
This technical profile is called right before the AzureMfa-VerifyOTP technical profile-->
<TechnicalProfile Id="AzureMfa-BeginVerifyOTP">
<DisplayName>Create Device</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.AzureMfaProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="Operation">BeginVerifyOTP</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="secretKey" />
<InputClaim ClaimTypeReferenceId="objectId" />
<InputClaim ClaimTypeReferenceId="userPrincipalName" />
</InputClaims>
</TechnicalProfile>
<!-- Verify the TOTP verification process.
This technical profile is called immediately after the AzureMfa-BeginVerifyOTP technical profile-->
<TechnicalProfile Id="AzureMfa-VerifyOTP">
<DisplayName>Verify OTP</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.AzureMfaProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="Operation">VerifyOTP</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="otpCode" />
</InputClaims>
</TechnicalProfile>
<!-- Check the number of devices available for the user.
If the number of available devices is zero it indicates the user hasn't enrolled yet -->
<TechnicalProfile Id="AzureMfa-GetAvailableDevices">
<DisplayName>Get Available Devices</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.AzureMfaProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="Operation">GetAvailableDevices</Item>
</Metadata>
<InputClaims>
<InputClaim ClaimTypeReferenceId="userPrincipalName" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="numberOfAvailableDevices" />
</OutputClaims>
</TechnicalProfile>
<!-- TOTP session manager-->
<TechnicalProfile Id="SM-MFA-TOTP">
<DisplayName>Session Mananagement Provider</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.SSO.DefaultSSOSessionProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<PersistedClaims>
<PersistedClaim ClaimTypeReferenceId="totpIdentifier" />
<PersistedClaim ClaimTypeReferenceId="issuer" />
</PersistedClaims>
</TechnicalProfile>
<!-- Self-asserted technical profile that asks the user to enroll to the TOTP MFA. The technical profile:
1) Creates the TOTP secret
2) Sets the issuer name and the URI
3) Renders the QR code and the authentication URI
-->
<TechnicalProfile Id="EnableOTPAuthentication">
<DisplayName>Sign up with Authenticator app</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.totp</Item>
<Item Key="language.button_continue">Continue</Item>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="CreateSecret" />
<InputClaimsTransformation ReferenceId="CreateIssuer" />
<InputClaimsTransformation ReferenceId="CreateUriLabel" />
<InputClaimsTransformation ReferenceId="CreateUriString" />
</InputClaimsTransformations>
<InputClaims>
<InputClaim ClaimTypeReferenceId="qrCodeContent" />
<InputClaim ClaimTypeReferenceId="secretKey" />
</InputClaims>
<DisplayClaims>
<DisplayClaim DisplayControlReferenceId="authenticatorAppIconControl" />
<DisplayClaim ClaimTypeReferenceId="QrCodeScanInstruction" />
<DisplayClaim DisplayControlReferenceId="totpQrCodeControl" />
<DisplayClaim DisplayControlReferenceId="authenticatorInfoControl" />
</DisplayClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="secretKey" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-MFA-TOTP" />
</TechnicalProfile>
<!-- Self-asserted technical profile that verifies the TOTP-->
<TechnicalProfile Id="OTPVerification">
<DisplayName>Sign in with Authenticator app</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.totp</Item>
<Item Key="language.button_continue">Verify</Item>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<InputClaims></InputClaims>
<DisplayClaims>
<DisplayClaim ClaimTypeReferenceId="QrCodeVerifyInstruction" />
<DisplayClaim ClaimTypeReferenceId="otpCode" Required="true" />
</DisplayClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="otpCode" Required="true" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AzureMfa-BeginVerifyOTP" />
<ValidationTechnicalProfile ReferenceId="AzureMfa-VerifyOTP" />
</ValidationTechnicalProfiles>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-MFA-TOTP" />
</TechnicalProfile>
<!-- Copy the SignInName to the totpIdentifier -->
<TechnicalProfile Id="CreateTotpIdentifier-SignInName">
<DisplayName>Set Totp Default Values</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.ClaimsTransformationProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="totpIdentifier" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="SignInNameToTotpIdentifier" />
</OutputClaimsTransformations>
</TechnicalProfile>
<!--end of TOTP-->
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>Azure Active Directory</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="AAD-UserReadUsingObjectId">
<OutputClaims>
<!-- The userPrincipalName is required for the AzureMfaProtocolProvider technical profiles-->
<OutputClaim ClaimTypeReferenceId="userPrincipalName" />
</OutputClaims>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
<ClaimsProvider>
<DisplayName>Local Account</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="SelfAsserted-LocalAccountSignin-Email">
<OutputClaims>
<!-- The userPrincipalName is required for the AzureMfaProtocolProvider technical profiles-->
<OutputClaim ClaimTypeReferenceId="userPrincipalName" />
</OutputClaims>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
</ClaimsProviders>
<UserJourneys>
<UserJourney Id="SignUpOrSignInCreateTOTP">
<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>
<!-- Call the TOTP enrollment sub journey. If user already enrolled the sub journey will not ask the user to enroll -->
<OrchestrationStep Order="3" Type="InvokeSubJourney">
<JourneyList>
<Candidate SubJourneyReferenceId="TotpFactor-Input" />
</JourneyList>
</OrchestrationStep>
<OrchestrationStep Order="4" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
</UserJourney>
</UserJourneys>
<SubJourneys>
<!--TOTP subjourneys-->
<!-- Set the required claims numberOfAvailableDevices and totpIdentifier-->
<SubJourney Id="SetTotpInitialValue" Type="Call">
<OrchestrationSteps>
<!-- Get the identifier from local account sign-in name-->
<OrchestrationStep Order="1" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>totpIdentifier</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>signInName</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="SetTotpIdentifierAsSignInName" TechnicalProfileReferenceId="CreateTotpIdentifier-SignInName" />
</ClaimsExchanges>
</OrchestrationStep>
</OrchestrationSteps>
</SubJourney>
<!-- TOTP enrollment sub journey-->
<SubJourney Id="TotpFactor-Input" Type="Call">
<OrchestrationSteps>
<!-- Set the required claims numberOfAvailableDevices and totpIdentifier-->
<OrchestrationStep Order="1" Type="InvokeSubJourney">
<JourneyList>
<Candidate SubJourneyReferenceId="SetTotpInitialValue" />
</JourneyList>
</OrchestrationStep>
<!-- If current user is not a new one (this is a sign-in flow, and not sign-up),
check the number of available devices. -->
<OrchestrationStep Order="2" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="CheckAvailableDevices" TechnicalProfileReferenceId="AzureMfa-GetAvailableDevices" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- If the number of available devices is zero (user hasn't enrolled before),
render the TOTP enrollment page to scan the QR code that starts the enrollment process -->
<OrchestrationStep Order="3" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AuthenticatorForSignUp" TechnicalProfileReferenceId="EnableOTPAuthentication" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- If the number of available devices is zero (user hasn't enrolled before),
render the TOTP verification page. -->
<OrchestrationStep Order="4" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AuthenticatorForSignIn" TechnicalProfileReferenceId="OTPVerification" />
</ClaimsExchanges>
</OrchestrationStep>
</OrchestrationSteps>
</SubJourney>
</SubJourneys>
</TrustFrameworkPolicy>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment