如何使用可选的电子邮件 OTP 实现 Azure AD B2C 身份体验框架登录
How to Implement Azure AD B2C Identity Experience Framework Sign-In with Optional Email OTP
我有一个业务需求,但我没有弄清楚如何实现它。
- 用户第一次登录时,他们会收到电子邮件 OTP 的挑战,然后是强制密码重置。
- 所有后续登录尝试将仅提示用户输入电子邮件地址和密码(OTP 是一次性用户流程)。
我的问题是编排步骤 1 收集电子邮件地址,然后我通过提供的电子邮件地址查询 AAD 并读取名为 extension_EmailValidated[=37 的扩展属性=]。如果该属性不是 TRUE,B2C 将强制用户进行电子邮件 OTP 验证,但是,编排步骤 2 不会让我预填充先前输入的电子邮件地址输入框并显示 OTP 按钮。它只会让我做一个或另一个(希望有意义)。
我的 UserJourney 看起来像这样
<UserJourneys>
<UserJourney Id="B2CSignIn">
<OrchestrationSteps>
<!--User enters their email address-->
<OrchestrationStep Order="1" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADEmailDiscovery" TechnicalProfileReferenceId="AAD-EmailDiscovery" />
</ClaimsExchanges>
</OrchestrationStep>
<!--User enters their OTP-->
<OrchestrationStep Order="2" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>extension_EmailVerified</Value>
<Value>True</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AADEmailVerification" TechnicalProfileReferenceId="AAD-EmailVerification" />
</ClaimsExchanges>
</OrchestrationStep>
<!--Set extension_EmailVerified to TRUE-->
<OrchestrationStep Order="3" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>extension_EmailVerified</Value>
<Value>True</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AADUserWriteEmailVerifiedUsingEmail" TechnicalProfileReferenceId="AAD-UserWriteEmailVerifiedUsingEmail" />
</ClaimsExchanges>
</OrchestrationStep>
这是第 1 步和第 2 步的技术资料。
<TechnicalProfile Id="AAD-EmailDiscovery">
<DisplayName>Initiate Email Address Verification For Local Account</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.localaccountsignup</Item>
<Item Key="language.button_continue">Continue</Item>
<Item Key="setting.showCancelButton">False</Item>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="email" />
<OutputClaim ClaimTypeReferenceId="extension_EmailVerified" DefaultValue="false" Required="true" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AAD-UserReadUsingEmailAddress" />
</ValidationTechnicalProfiles>
</TechnicalProfile>
<TechnicalProfile Id="AAD-EmailVerification">
<DisplayName>Initiate Email Address Verification For Local Account</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.localaccountsignup</Item>
<Item Key="language.button_continue">Continue</Item>
<Item Key="setting.showCancelButton">False</Item>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="Verified.Email" Required="true" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AAD-UserReadUsingEmailAddress" />
</ValidationTechnicalProfiles>
</TechnicalProfile>
另一个非常令人困惑的问题是,我可以让 OTP 按钮出现在第 2 步的唯一方法是为 InputClaim 和 OutputClaims 添加“ParterClaimType=Verified.Email”——这毫无意义.
如果我从 InputClaims 和 OutputClaims 中省略“ParterClaimType=Verified.Email”,我可以从步骤 1 中获取要预填充的电子邮件地址。
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" PartnerClaimType="Verified.Email" Required="true" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="Verified.Email" Required="true" />
</OutputClaims>
非常感谢您的建议和指导。
谢谢!
这里的解决方法是让输入文本框readOnly
.
通过声明转换发送在登录屏幕上收集的电子邮件,以将其复制到名为 readOnlyEmail
的新声明中。声明应定义为只读。
<ClaimType Id="readOnlyEmail">
<DisplayName>Email Address</DisplayName>
<DataType>string</DataType>
<UserHelpText/>
<UserInputType>Readonly</UserInputType>
</ClaimType>
将电子邮件声明复制到只读声明中
<ClaimsTransformation Id="CopySignInNameToReadOnly" TransformationMethod="FormatStringClaim">
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInName" TransformationClaimType="inputClaim" />
</InputClaims>
<InputParameters>
<InputParameter Id="stringFormat" DataType="string" Value="{0}" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="readOnlyEmail" TransformationClaimType="outputClaim" />
</OutputClaims>
</ClaimsTransformation>
然后使用 OutputClaimsTransformations 节点从您注册的技术配置文件调用声明转换。
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="CopySignInNameToReadOnly" />
</OutputClaimsTransformations>
最后在邮箱验证技术配置文件(selfAsserted technical profile)上,传入待验证的readOnly邮箱:
<InputClaims>
<InputClaim ClaimTypeReferenceId="readOnlyEmail"/>
</InputClaims>
<OutputClaims>
<!-- Required claims -->
<OutputClaim ClaimTypeReferenceId="readOnlyEmail" PartnerClaimType="Verified.Email"/>
</OutputClaims>
这里展示了这个概念:
https://github.com/azure-ad-b2c/samples/tree/master/policies/signin-email-verification
您只需在该样本之上添加您的条件逻辑。
我有一个业务需求,但我没有弄清楚如何实现它。
- 用户第一次登录时,他们会收到电子邮件 OTP 的挑战,然后是强制密码重置。
- 所有后续登录尝试将仅提示用户输入电子邮件地址和密码(OTP 是一次性用户流程)。
我的问题是编排步骤 1 收集电子邮件地址,然后我通过提供的电子邮件地址查询 AAD 并读取名为 extension_EmailValidated[=37 的扩展属性=]。如果该属性不是 TRUE,B2C 将强制用户进行电子邮件 OTP 验证,但是,编排步骤 2 不会让我预填充先前输入的电子邮件地址输入框并显示 OTP 按钮。它只会让我做一个或另一个(希望有意义)。
我的 UserJourney 看起来像这样
<UserJourneys>
<UserJourney Id="B2CSignIn">
<OrchestrationSteps>
<!--User enters their email address-->
<OrchestrationStep Order="1" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADEmailDiscovery" TechnicalProfileReferenceId="AAD-EmailDiscovery" />
</ClaimsExchanges>
</OrchestrationStep>
<!--User enters their OTP-->
<OrchestrationStep Order="2" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>extension_EmailVerified</Value>
<Value>True</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AADEmailVerification" TechnicalProfileReferenceId="AAD-EmailVerification" />
</ClaimsExchanges>
</OrchestrationStep>
<!--Set extension_EmailVerified to TRUE-->
<OrchestrationStep Order="3" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>extension_EmailVerified</Value>
<Value>True</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AADUserWriteEmailVerifiedUsingEmail" TechnicalProfileReferenceId="AAD-UserWriteEmailVerifiedUsingEmail" />
</ClaimsExchanges>
</OrchestrationStep>
这是第 1 步和第 2 步的技术资料。
<TechnicalProfile Id="AAD-EmailDiscovery">
<DisplayName>Initiate Email Address Verification For Local Account</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.localaccountsignup</Item>
<Item Key="language.button_continue">Continue</Item>
<Item Key="setting.showCancelButton">False</Item>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="email" />
<OutputClaim ClaimTypeReferenceId="extension_EmailVerified" DefaultValue="false" Required="true" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AAD-UserReadUsingEmailAddress" />
</ValidationTechnicalProfiles>
</TechnicalProfile>
<TechnicalProfile Id="AAD-EmailVerification">
<DisplayName>Initiate Email Address Verification For Local Account</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.localaccountsignup</Item>
<Item Key="language.button_continue">Continue</Item>
<Item Key="setting.showCancelButton">False</Item>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<IncludeInSso>false</IncludeInSso>
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="Verified.Email" Required="true" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="AAD-UserReadUsingEmailAddress" />
</ValidationTechnicalProfiles>
</TechnicalProfile>
另一个非常令人困惑的问题是,我可以让 OTP 按钮出现在第 2 步的唯一方法是为 InputClaim 和 OutputClaims 添加“ParterClaimType=Verified.Email”——这毫无意义.
如果我从 InputClaims 和 OutputClaims 中省略“ParterClaimType=Verified.Email”,我可以从步骤 1 中获取要预填充的电子邮件地址。
<InputClaims>
<InputClaim ClaimTypeReferenceId="email" PartnerClaimType="Verified.Email" Required="true" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="Verified.Email" Required="true" />
</OutputClaims>
非常感谢您的建议和指导。
谢谢!
这里的解决方法是让输入文本框readOnly
.
通过声明转换发送在登录屏幕上收集的电子邮件,以将其复制到名为 readOnlyEmail
的新声明中。声明应定义为只读。
<ClaimType Id="readOnlyEmail">
<DisplayName>Email Address</DisplayName>
<DataType>string</DataType>
<UserHelpText/>
<UserInputType>Readonly</UserInputType>
</ClaimType>
将电子邮件声明复制到只读声明中
<ClaimsTransformation Id="CopySignInNameToReadOnly" TransformationMethod="FormatStringClaim">
<InputClaims>
<InputClaim ClaimTypeReferenceId="signInName" TransformationClaimType="inputClaim" />
</InputClaims>
<InputParameters>
<InputParameter Id="stringFormat" DataType="string" Value="{0}" />
</InputParameters>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="readOnlyEmail" TransformationClaimType="outputClaim" />
</OutputClaims>
</ClaimsTransformation>
然后使用 OutputClaimsTransformations 节点从您注册的技术配置文件调用声明转换。
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="CopySignInNameToReadOnly" />
</OutputClaimsTransformations>
最后在邮箱验证技术配置文件(selfAsserted technical profile)上,传入待验证的readOnly邮箱:
<InputClaims>
<InputClaim ClaimTypeReferenceId="readOnlyEmail"/>
</InputClaims>
<OutputClaims>
<!-- Required claims -->
<OutputClaim ClaimTypeReferenceId="readOnlyEmail" PartnerClaimType="Verified.Email"/>
</OutputClaims>
这里展示了这个概念: https://github.com/azure-ad-b2c/samples/tree/master/policies/signin-email-verification
您只需在该样本之上添加您的条件逻辑。