Can Azure-AD B2C use a mobile telephone number as a username?
This can be implemented as a custom policy, from the SocialAndLocalAccountsWithMfa starter pack where the end-user's phone number is stored as a sign-in name, with the following changes.
1) Create a custom attribute called PhoneVerified of type Boolean to represent whether the end-user's phone number has been verified.
2) In the TrustFrameworkBase.xml file, add the following claim types to the claims schema:
i. The phone claim type to represent how the end-user's phone number is entered. E.164 is the required format of this claim type:
<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>
ii. The signInNames.phoneNumber claim type to represent how the end-user's phone number is saved:
<ClaimType Id="signInNames.phoneNumber"> <DisplayName>Phone Number</DisplayName> <DataType>string</DataType> <UserInputType>TextBox</UserInputType></ClaimType>
iii. The extension_PhoneVerified claim type to represent whether the end-user's phone number has been verified:
<ClaimType Id="extension_PhoneVerified"> <DisplayName>Phone Number Verified</DisplayName> <DataType>boolean</DataType></ClaimType>
3) In the TrustFrameworkBase.xml file, add the LocalAccountSignUpWithLogonPhone tehcnical profile to the Local Account claims provider and AAD-UserWriteUsingLogonPhone technical profile to the Azure Active Directory claims provider, for registering a new end-user with a phone number:
<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>
The end-user's phone number is saved as a sign-in name of type phoneNumber and whether the end-user's phone number has been verified is set to false.
4) In the TrustFrameworkBase.xml file, add a SelfAsserted-LocalAccountSignin-Phone technical profile to the Local Account claims provider, for logging in an existing end-user with a phone number:
<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>
The setting.operatingMode setting is set to Username so that the logon identifier field doesn't have the required format of an email address.
5) In the TrustFrameworkBase.xml file, add a AAD-UserReadForPhoneUsingObjectId technical to the Azure Active Directory claims provider, for getting the end-user's object including the phone profile:
<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) In the TrustFrameworkBase.xml file, add a PhoneFactor-Verify technical profile to the Phone Factor claims provider, for verifying the end-user's phone number:
<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) In the the TrustFrameworkBase.xml file, add an UserWritePhoneVerifiedUsingObjectId technical profile to the Azure Active Directory claims provider, for setting whether the end-user's phone number has been verified to 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>
Note: Additional technical profiles must be added in the TrustFrameworkBase.xml file to allow an existing end-user to reset their current password using a phone number but this has been left as an exercise for the reader.
8) In the TrustFrameworkBase.xml file, add a SignUpOrSignInForPhone user journey, which allows either a new end-user to register with a phone number or an existing end-user to log in with a phone number and then verifies the end-user's phone number.
<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) Create a relying party file called SignUpOrSignInForPhone.xml (or similar) and reference the SignUpOrSignInForPhone user journey:
<?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>
The token claims that are output to a relying party include:
i. The phone_number claim that represents the end-user's phone number.
ii. The phone_number_verified claims that represents whether the end-user's phone number has been verified.
It is finally supported officially:
Azure AD B2C now supports phone-based sign-in and sign-up for apps using B2C custom policy
I could use a mobile telephone number to sign up as the username for the local in the built-in sign-in or sign-up policy. The process like the following:
And the result is:
Or you could also use the custom policy to make this just like Chris Padgett said.