Azure B2C自定义策略:禁止向已验证邮箱发验证邮件(用户名登录)
Azure B2C自定义策略:用户名登录下实现验证邮箱唯一
核心问题分析
你当前的错误在于用strongAuthenticationEmailAddress做校验——这个属性是用户后续配置MFA时添加的邮箱,并非注册流程中用于验证的邮箱。在用户名登录的自定义策略里,注册时的验证邮箱默认存储在otherMails属性(数组类型),同时还要考虑到其他用户可能把该邮箱作为登录邮箱(signInNames.emailAddress)或MFA邮箱的情况,需要同时校验这三个属性来确保邮箱唯一性。
解决方案配置修改
1. 新增邮箱唯一性查询技术配置文件
替换你原有的AAD-UserReadUsingStrongAuthenticationEmailAddress,创建一个能同时检查三类邮箱属性的查询配置:
<TechnicalProfile Id="AAD-UserReadByEmailExists"> <Metadata> <Item Key="Operation">Read</Item> <Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">false</Item> <!-- 过滤条件:检查邮箱是否存在于登录邮箱、验证邮箱、MFA邮箱中的任意一个 --> <Item Key="Filter">signInNames/any(x:x/value eq '{email}') or otherMails/any(x:x eq '{email}') or strongAuthenticationEmailAddress eq '{email}'</Item> </Metadata> <IncludeInSso>false</IncludeInSso> <InputClaims> <InputClaim ClaimTypeReferenceId="email" Required="true" /> </InputClaims> <OutputClaims> <OutputClaim ClaimTypeReferenceId="objectId" /> </OutputClaims> <IncludeTechnicalProfile ReferenceId="AAD-Common" /> </TechnicalProfile>
2. 修改验证控件逻辑
在发送验证码前先执行邮箱唯一性检查,若邮箱已被使用则抛出错误:
<DisplayControls> <DisplayControl Id="signupEmailVerificationControl" UserInterfaceControlType="VerificationControl"> <InputClaims> <InputClaim ClaimTypeReferenceId="email" /> </InputClaims> <DisplayClaims> <DisplayClaim ClaimTypeReferenceId="email" ControlClaimType="Email" Required="true" /> <DisplayClaim ClaimTypeReferenceId="verificationCode" ControlClaimType="VerificationCode" Required="true" /> </DisplayClaims> <Actions> <Action Id="SendCode"> <ValidationClaimsExchange> <!-- 第一步:检查邮箱是否已被任何账户使用 --> <ValidationClaimsExchangeTechnicalProfile TechnicalProfileReferenceId="AAD-UserReadByEmailExists" ContinueOnError="false" /> <!-- 若查询到用户存在(objectId不为空),抛出邮箱已存在错误 --> <ValidationClaimsExchangeTechnicalProfile TechnicalProfileReferenceId="Throw-EmailAlreadyExistsError"> <Preconditions> <Precondition Type="ClaimsExist" ExecuteActionsIf="false"> <Value>objectId</Value> <Action>SkipThisValidationTechnicalProfile</Action> </Precondition> </Preconditions> </ValidationClaimsExchangeTechnicalProfile> <!-- 邮箱未被使用,执行发送验证码 --> <ValidationClaimsExchangeTechnicalProfile TechnicalProfileReferenceId="AadSspr-SendCode" /> </ValidationClaimsExchange> </Action> <Action Id="VerifyCode"> <ValidationClaimsExchange> <ValidationClaimsExchangeTechnicalProfile TechnicalProfileReferenceId="AadSspr-VerifyCode" /> </ValidationClaimsExchange> </Action> </Actions> </DisplayControl> </DisplayControls>
3. 添加错误抛出技术配置文件
用于在邮箱重复时显示自定义错误提示:
<!-- 抛出邮箱已存在错误 --> <TechnicalProfile Id="Throw-EmailAlreadyExistsError"> <DisplayName>Throw error if email already exists</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> </Metadata> <InputClaims> <InputClaim ClaimTypeReferenceId="email" /> </InputClaims> <OutputClaims> <OutputClaim ClaimTypeReferenceId="email" /> </OutputClaims> <ValidationTechnicalProfiles> <ValidationTechnicalProfile ReferenceId="SelfAsserted-ThrowError" /> </ValidationTechnicalProfiles> </TechnicalProfile> <TechnicalProfile Id="SelfAsserted-ThrowError"> <DisplayName>Throw Error</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="UserMessageIfClaimsPrincipalAlreadyExists">该邮箱地址 {email} 已被其他账户绑定,请使用其他邮箱。</Item> </Metadata> <InputClaims> <InputClaim ClaimTypeReferenceId="email" /> </InputClaims> <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" /> </TechnicalProfile>
4. 确保注册时邮箱正确存储
在用户名注册的技术配置文件AAD-UserWriteUsingLogonName中,确认邮箱被映射到otherMails:
<TechnicalProfile Id="AAD-UserWriteUsingLogonName"> <Metadata> <Item Key="Operation">Write</Item> <Item Key="RaiseErrorIfClaimsPrincipalAlreadyExists">true</Item> <Item Key="UserMessageIfClaimsPrincipalAlreadyExists">该用户名已被使用,请更换。</Item> </Metadata> <InputClaims> <InputClaim ClaimTypeReferenceId="signInName" PartnerClaimType="signInNames.userName" Required="true" /> <InputClaim ClaimTypeReferenceId="email" PartnerClaimType="otherMails" Required="true" /> <InputClaim ClaimTypeReferenceId="newPassword" PartnerClaimType="password" Required="true" /> <InputClaim ClaimTypeReferenceId="displayName" Required="true" /> </InputClaims> <OutputClaims> <OutputClaim ClaimTypeReferenceId="objectId" /> <OutputClaim ClaimTypeReferenceId="newUser" PartnerClaimType="newClaimsPrincipalCreated" /> <OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="localAccountAuthentication" /> <OutputClaim ClaimTypeReferenceId="userPrincipalName" /> <OutputClaim ClaimTypeReferenceId="signInName" /> </OutputClaims> <IncludeTechnicalProfile ReferenceId="AAD-Common" /> <UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" /> </TechnicalProfile>
内容的提问来源于stack exchange,提问作者Daniele Perilli
相关产品推荐
相关产品推荐

