Azure-AD B2C 可以使用手机号码作为用户名吗?

Posted

技术标签:

【中文标题】Azure-AD B2C 可以使用手机号码作为用户名吗?【英文标题】:Can Azure-AD B2C use a mobile telephone number as a username? 【发布时间】:2019-05-28 01:36:04 【问题描述】:

我们有一个移动应用程序和网站。我们想使用 Azure AD-B2C 进行身份验证。我们不会允许任何第三方身份验证,而是仅在单独的域中使用 Azure AD。

我们更愿意使用用户的电话号码。我的研究表明,此功能是 requested,但目前不存在。

是否有任何解决方法,或者是否已实现此功能但我错过了?

【问题讨论】:

嗨,布莱恩。您可以使用任何内容作为本地帐户的登录名,但您可能必须为此使用自定义策略。您要在注册使用之前验证电话号码吗? 克里斯,确切地说,我们想向手机号码发送一条短信,并让用户验证他们确实拥有手机。我会为那部分使用自定义策略吗? 嗨 Bryan:是的,为此需要自定义策略,请参阅下面的示例策略。 【参考方案1】:

这可以实现为 a custom policy,来自 the SocialAndLocalAccountsWithMfa starter pack,其中最终用户的电话号码存储为登录名,并进行了以下更改。

1) 创建名为 PhoneVerified 且类型为 Boolean 的 a custom attribute,以表示最终用户的电话号码是否已经过验证。

2) 在the TrustFrameworkBase.xml file 中,将以下声明类型添加到声明架构中:

我。 phone 声明类型,表示最终用户的电话号码是如何输入的。 E.164 是此声明类型的必需格式:

<ClaimType Id="phone">
  <DisplayName>Phone Number</DisplayName>
  <DataType>string</DataType>
  <UserInputType>TextBox</UserInputType>
  <Restriction>
    <Pattern RegularExpression="^\+[0-9]7,15$" HelpText="Please enter a valid phone number." />
  </Restriction>
</ClaimType>

二。 signInNames.phoneNumber 声明类型,表示最终用户电话号码的保存方式:

<ClaimType Id="signInNames.phoneNumber">
  <DisplayName>Phone Number</DisplayName>
  <DataType>string</DataType>
  <UserInputType>TextBox</UserInputType>
</ClaimType>

三。 extension_PhoneVerified 声明类型,表示最终用户的电话号码是否已经过验证:

<ClaimType Id="extension_PhoneVerified">
  <DisplayName>Phone Number Verified</DisplayName>
  <DataType>boolean</DataType>
</ClaimType>

3) 在 TrustFrameworkBase.xml 文件中,将 LocalAccountSignUpWithLogonPhone 技术配置文件添加到 Local Account 声明提供程序和 AAD- Azure Active Directory 声明提供商的 UserWriteUsingLogonPhone 技术配置文件,用于使用电话号码注册新的最终用户:

<TechnicalProfile Id="LocalAccountSignUpWithLogonPhone">
  <DisplayName>Phone signup</DisplayName>
  <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
  <Metadata>
    <Item Key="IpAddressClaimReferenceId">IpAddress</Item>
    <Item Key="ContentDefinitionReferenceId">api.localaccountsignup</Item>
    <Item Key="language.button_continue">Create</Item>
  </Metadata>
  <CryptographicKeys>
    <Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
  </CryptographicKeys>
  <OutputClaims>
    <OutputClaim ClaimTypeReferenceId="objectId" />
    <OutputClaim ClaimTypeReferenceId="phone" Required="true" />
    <OutputClaim ClaimTypeReferenceId="newPassword" Required="true" />
    <OutputClaim ClaimTypeReferenceId="reenterPassword" Required="true" />
    <OutputClaim ClaimTypeReferenceId="executed-SelfAsserted-Input" DefaultValue="true" />
    <OutputClaim ClaimTypeReferenceId="authenticationSource" />
    <OutputClaim ClaimTypeReferenceId="newUser" />
    <!-- Optional claims, to be collected from the user -->
    <OutputClaim ClaimTypeReferenceId="displayName" />
    <OutputClaim ClaimTypeReferenceId="givenName" />
    <OutputClaim ClaimTypeReferenceId="surname" />
  </OutputClaims>
  <ValidationTechnicalProfiles>
    <ValidationTechnicalProfile ReferenceId="AAD-UserWriteUsingLogonPhone" />
  </ValidationTechnicalProfiles>
  <UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>
<TechnicalProfile Id="AAD-UserWriteUsingLogonPhone">
  <Metadata>
    <Item Key="Operation">Write</Item>
    <Item Key="RaiseErrorIfClaimsPrincipalAlreadyExists">true</Item>
  </Metadata>
  <IncludeInSso>false</IncludeInSso>
  <InputClaims>
    <InputClaim ClaimTypeReferenceId="phone" PartnerClaimType="signInNames.phoneNumber" Required="true" />
  </InputClaims>
  <PersistedClaims>
    <!-- Required claims -->
    <PersistedClaim ClaimTypeReferenceId="phone" PartnerClaimType="signInNames.phoneNumber" />
    <PersistedClaim ClaimTypeReferenceId="newPassword" PartnerClaimType="password"/>
    <PersistedClaim ClaimTypeReferenceId="displayName" DefaultValue="unknown" />
    <PersistedClaim ClaimTypeReferenceId="passwordPolicies" DefaultValue="DisablePasswordExpiration" />
    <PersistedClaim ClaimTypeReferenceId="extension_PhoneVerified" DefaultValue="false" AlwaysUseDefaultValue="true" />
    <!-- Optional claims. -->
    <PersistedClaim ClaimTypeReferenceId="givenName" />
    <PersistedClaim ClaimTypeReferenceId="surname" />
  </PersistedClaims>
  <OutputClaims>
    <OutputClaim ClaimTypeReferenceId="objectId" />
    <OutputClaim ClaimTypeReferenceId="newUser" PartnerClaimType="newClaimsPrincipalCreated" />
    <OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="localAccountAuthentication" />
    <OutputClaim ClaimTypeReferenceId="userPrincipalName" />
    <OutputClaim ClaimTypeReferenceId="signInNames.phoneNumber" />
  </OutputClaims>
  <IncludeTechnicalProfile ReferenceId="AAD-Common" />
  <UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>

最终用户的电话号码保存为 phoneNumber 类型的登录名,并且将最终用户的电话号码是否经过验证设置为 false

4) 在 TrustFrameworkBase.xml 文件中,将 SelfAsserted-LocalAccountSignin-Phone 技术配置文件添加到 Local Account 声明提供程序,用于使用电话号码登录现有的最终用户:

<TechnicalProfile Id="SelfAsserted-LocalAccountSignin-Phone">
  <DisplayName>Local Account Signin</DisplayName>
  <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
  <Metadata>
    <Item Key="SignUpTarget">SignUpWithLogonPhoneExchange</Item>
    <Item Key="setting.operatingMode">Username</Item>
    <Item Key="ContentDefinitionReferenceId">api.selfasserted</Item>
  </Metadata>
  <IncludeInSso>false</IncludeInSso>
  <InputClaims>
    <InputClaim ClaimTypeReferenceId="signInName" />
  </InputClaims>
  <OutputClaims>
    <OutputClaim ClaimTypeReferenceId="signInName" Required="true" />
    <OutputClaim ClaimTypeReferenceId="password" Required="true" />
    <OutputClaim ClaimTypeReferenceId="objectId" />
    <OutputClaim ClaimTypeReferenceId="authenticationSource" />
  </OutputClaims>
  <ValidationTechnicalProfiles>
    <ValidationTechnicalProfile ReferenceId="login-NonInteractive" />
  </ValidationTechnicalProfiles>
  <UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>

setting.operatingMode 设置设置为 Username,因此登录标识符字段没有电子邮件地址所需的格式。

5) 在 TrustFrameworkBase.xml 文件中,将 AAD-UserReadForPhoneUsingObjectId 技术添加到 Azure Active Directory 声明提供程序,以获取最终用户的对象,包括电话配置文件:

<TechnicalProfile Id="AAD-UserReadForPhoneUsingObjectId">
  <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="signInNames.phoneNumber" />
    <OutputClaim ClaimTypeReferenceId="displayName" />
    <OutputClaim ClaimTypeReferenceId="givenName" />
    <OutputClaim ClaimTypeReferenceId="surname" />
    <OutputClaim ClaimTypeReferenceId="extension_PhoneVerified" />
  </OutputClaims>
  <IncludeTechnicalProfile ReferenceId="AAD-Common" />
</TechnicalProfile>

6) 在 TrustFrameworkBase.xml 文件中,将 PhoneFactor-Verify 技术配置文件添加到 Phone Factor 声明提供程序,以验证最终用户的电话号码:

<TechnicalProfile Id="PhoneFactor-Verify">
  <DisplayName>PhoneFactor</DisplayName>
  <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.PhoneFactorProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
  <Metadata>
    <Item Key="ContentDefinitionReferenceId">api.phonefactor</Item>
    <Item Key="ManualPhoneNumberEntryAllowed">false</Item>
  </Metadata>
  <CryptographicKeys>
    <Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
  </CryptographicKeys>
  <InputClaimsTransformations>
    <InputClaimsTransformation ReferenceId="CreateUserIdForMFA" />
  </InputClaimsTransformations>
  <InputClaims>
    <InputClaim ClaimTypeReferenceId="userIdForMFA" PartnerClaimType="userId" />
    <InputClaim ClaimTypeReferenceId="signInNames.phoneNumber" PartnerClaimType="strongAuthenticationPhoneNumber" />
  </InputClaims>
  <OutputClaims>
    <OutputClaim ClaimTypeReferenceId="signInNames.phoneNumber" PartnerClaimType="Verified.strongAuthenticationPhoneNumber" />
  </OutputClaims>
  <UseTechnicalProfileForSessionManagement ReferenceId="SM-MFA" />
</TechnicalProfile>

7) 在 TrustFrameworkBase.xml 文件中,将 UserWritePhoneVerifiedUsingObjectId 技术配置文件添加到 Azure Active Directory 声明提供程序,用于设置是否最终用户的电话号码已被验证为true

<TechnicalProfile Id="AAD-UserWritePhoneNumberUsingObjectId">
  <Metadata>
    <Item Key="Operation">Write</Item>
    <Item Key="RaiseErrorIfClaimsPrincipalAlreadyExists">false</Item>
    <Item Key="RaiseErrorIfClaimsPrincipalDoesNotExist">true</Item>
  </Metadata>
  <IncludeInSso>false</IncludeInSso>
  <InputClaims>
    <InputClaim ClaimTypeReferenceId="objectId" Required="true" />
  </InputClaims>
  <PersistedClaims>
    <PersistedClaim ClaimTypeReferenceId="objectId" />
    <PersistedClaim ClaimTypeReferenceId="extension_PhoneVerified" DefaultValue="true" AlwaysUseDefaultValue="true" />
  </PersistedClaims>
  <IncludeTechnicalProfile ReferenceId="AAD-Common" />
</TechnicalProfile>

注意:必须在 TrustFrameworkBase.xml 文件中添加其他技术配置文件,以允许现有最终用户使用电话号码重置其当前密码,但这有留给读者作为练习。

8) 在 TrustFrameworkBase.xml 文件中,添加 SignUpOrSignInForPhone 用户旅程,允许新最终用户使用电话号码或现有终端注册-user 使用电话号码登录,然后验证最终用户的电话号码。

<UserJourney Id="SignUpOrSignInForPhone">
  <OrchestrationSteps>

    <!-- Display the sign-up or sign-in interaction so an existing end-user can sign in with a phone number -->      
    <OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin">
      <ClaimsProviderSelections>
        <ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninPhoneExchange" />
      </ClaimsProviderSelections>
      <ClaimsExchanges>
        <ClaimsExchange Id="LocalAccountSigninPhoneExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Phone" />
      </ClaimsExchanges>
    </OrchestrationStep>

    <!-- A new end-user has selected to sign up with a phone number -->
    <OrchestrationStep Order="2" Type="ClaimsExchange">
      <Preconditions>
        <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
          <Value>objectId</Value>
          <Action>SkipThisOrchestrationStep</Action>
        </Precondition>
      </Preconditions>
      <ClaimsExchanges>
        <ClaimsExchange Id="SignUpWithLogonPhoneExchange" TechnicalProfileReferenceId="LocalAccountSignUpWithLogonPhone" />
      </ClaimsExchanges>
    </OrchestrationStep>

    <!-- Read the user object -->
    <OrchestrationStep Order="3" Type="ClaimsExchange">
      <ClaimsExchanges>
        <ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadForPhoneUsingObjectId" />
      </ClaimsExchanges>
    </OrchestrationStep>

    <!-- If the end-user's phone number hasn't been verified, then verify it during sign-up or following the first sign-in with an unverified phone number -->
    <OrchestrationStep Order="4" Type="ClaimsExchange">
      <Preconditions>
        <Precondition Type="ClaimEquals" ExecuteActionsIf="true">
          <Value>extension_PhoneVerified</Value>
          <Value>True</Value>
          <Action>SkipThisOrchestrationStep</Action>
        </Precondition>
        <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
          <Value>isActiveMFASession</Value>
          <Action>SkipThisOrchestrationStep</Action>
        </Precondition>
      </Preconditions>
      <ClaimsExchanges>
        <ClaimsExchange Id="PhoneFactor-Verify" TechnicalProfileReferenceId="PhoneFactor-Verify" />
      </ClaimsExchanges>
    </OrchestrationStep>

    <!-- Set whether the end-user's phone number has been verified to true -->
    <OrchestrationStep Order="5" Type="ClaimsExchange">
      <Preconditions>
        <Precondition Type="ClaimEquals" ExecuteActionsIf="true">
          <Value>extension_PhoneVerified</Value>
          <Value>True</Value>
          <Action>SkipThisOrchestrationStep</Action>
        </Precondition>
        <Precondition Type="ClaimsExist" ExecuteActionsIf="true">
          <Value>isActiveMFASession</Value>
          <Action>SkipThisOrchestrationStep</Action>
        </Precondition>
      </Preconditions>
      <ClaimsExchanges>
        <ClaimsExchange Id="AADUserWriteWithObjectId" TechnicalProfileReferenceId="AAD-UserWritePhoneVerifiedUsingObjectId" />
      </ClaimsExchanges>
    </OrchestrationStep>

    <OrchestrationStep Order="6" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />

  </OrchestrationSteps>
</UserJourney>

9) 创建名为 SignUpOrSignInForPhone.xml(或类似名称)的 a relying party file 并引用 SignUpOrSignInForPhone 用户旅程:

<?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="yourtenant.onmicrosoft.com"
    PolicyId="B2C_1A_signup_signin_phone"
    PublicPolicyUri="http://yourtenant.onmicrosoft.com/B2C_1A_signup_signin_phone">
  <BasePolicy>
    <TenantId>yourtenant.onmicrosoft.com</TenantId>
    <PolicyId>B2C_1A_TrustFrameworkExtensions</PolicyId>
  </BasePolicy>
  <RelyingParty>
    <DefaultUserJourney ReferenceId="SignUpOrSignInForPhone" />
    <TechnicalProfile Id="PolicyProfile">
      <DisplayName>PolicyProfile</DisplayName>
      <Protocol Name="OpenIdConnect" />
      <OutputClaims>
        <OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub" />
        <OutputClaim ClaimTypeReferenceId="displayName" />
        <OutputClaim ClaimTypeReferenceId="givenName" />
        <OutputClaim ClaimTypeReferenceId="surname" />
        <OutputClaim ClaimTypeReferenceId="signInNames.phoneNumber" PartnerClaimType="phone_number" />
        <OutputClaim ClaimTypeReferenceId="extension_PhoneNumber" PartnerClaimType="phone_number_verified" />
      </OutputClaims>
      <SubjectNamingInfo ClaimType="sub" />
    </TechnicalProfile>
  </RelyingParty>
</TrustFrameworkPolicy>

输出给依赖方的令牌声明包括:

我。 phone_number 声明,代表最终用户的电话号码。

二。 phone_number_verified 声明表示最终用户的电话号码是否已经过验证。

【讨论】:

克里斯,非常感谢你。太棒了!! 嗨,克里斯,您能帮我为上述实施添加自定义密码重置策略吗(手机号登录注册) 克里斯,这引用了似乎不存在的“AAD-UserWritePhoneVerifiedUsingObjectId”。这应该是“AAD-UserWritePhoneNumberUsingObjectId”吗? 嗨,克里斯,帮了大忙。要确认是否必须添加额外的验证来检查移动电话号码?据我所知,这适用于任何电话号码,那么验证是否会尝试发送到非手机号码? 仅供参考:Microsoft 建议不要修改 TrustFrameworkBase.xml。您应该始终在基础之上创建一个扩展文件。【参考方案2】:

终于正式支持了:

Azure AD B2C now supports phone-based sign-in and sign-up for apps using B2C custom policy

【讨论】:

【参考方案3】:

我可以使用手机号码在内置登录或注册策略中注册为本地用户名。流程如下:

结果是:

或者您也可以像 Chris Padgett 所说的那样使用自定义策略来实现。

【讨论】:

我们还希望能够通过向用户发送文本并让他们采取一些操作(例如点击 URL)来验证电话号码。自定义政策可以做到这一点吗? @BryanSchmiedeler,您可以在内置策略中启用 MFA。详情可参考here。 太棒了。现在,如果我想添加重置密码策略怎么办。您能否为此与我分享步骤和自定义政策。

以上是关于Azure-AD B2C 可以使用手机号码作为用户名吗?的主要内容,如果未能解决你的问题,请参考以下文章

用于Azure B2C acces令牌的Exchange Facebook SDK acces令牌

如何使用 Azure AD B2C 获取 Facebook 个人资料图片

B2C ID 令牌中缺少电话号码声明

使用 Azure AD B2C 作为服务进行身份验证

如何从 Azure B2C 获取 ASPCore 中的 OID 声明

蔚蓝 B2C。使用 Azure 门户编辑自定义属性