ad b2c allow user to change MFA setting - azure-ad-b2c

how would I allow the user to change their MFA setting like their phone number? I don't see any option for them to be able to do that easily.

Try this. B2C MFA reset was the reason I wrote it.
http://gordon.byers.me/azure/resetting-a-users-azure-ad-multi-factor-mfa-requirement/
As it's powershell you could put it inside an Azure function and call it via HTTP to allow the user to self serve.

Currently it is not possible to change an Azure AD B2C users' MFA settings.
There's already an ask for this ask in the Azure AD B2C forum that you should vote for:
https://feedback.azure.com/forums/169401-azure-active-directory/suggestions/15334329-change-security-info

The MFA phone number can be changed with custom policies. When you create a UserJourney that invokes a TechnicalProfile that does not take the strongAuthenticationPhoneNumber as InputClaim, IEF acts as if the user registers for MFA for the first time.
Of course you need to think about security measures, since it is a second factor that the user changes (e.g. ask the user to input some data, that can be validated, before allowing the user to change the phonenumber). Otherwise the use of MFA makes no sense.

Apparently, user's can't do that as stated by Saca 😢
Admins 👮 can do this on the user's behalf via the Azure Portal though:
Users -> All users -> Pick the user you're interested in -> Update Phone under Authentication contact info

Looks like this is now possible with custom policies.
There is a full example here: https://github.com/azure-ad-b2c/samples/tree/master/policies/edit-mfa-phone-number
In case the link breaks, the key part appears to be this:
<TechnicalProfiles>
<TechnicalProfile Id="PhoneFactor-EditAndVerify">
<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">true</Item>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="CreateUserIdForMFA" />
</InputClaimsTransformations>
<InputClaims>
<InputClaim ClaimTypeReferenceId="userIdForMFA" PartnerClaimType="UserId" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="Verified.strongAuthenticationPhoneNumber" PartnerClaimType="Verified.OfficePhone" />
<OutputClaim ClaimTypeReferenceId="newPhoneNumberEntered" PartnerClaimType="newPhoneNumberEntered" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>

Related

How to avoid hardcoding ServiceUrl in AD B2C custom policy API connector?

I have a REST API connector in my B2C custom policy. All examples I have seen hard code the value of the ServiceUrl metadata item in the REST technical profile. For example:
<ClaimsProvider>
<DisplayName>REST APIs</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="REST-MyApiConnector">
<DisplayName>This an example of my API connector</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="ServiceUrl">https://someapi.com/api/endpoint</Item>
<Item Key="SendClaimsIn">Url</Item>
<Item Key="AuthenticationType">Basic</Item>
<Item Key="AllowInsecureAuthInProduction">false</Item>
</Metadata>
<CryptographicKeys>
<Key Id="BasicAuthenticationUsername" StorageReferenceId="B2C_1A_RestApiUsername" />
<Key Id="BasicAuthenticationPassword" StorageReferenceId="B2C_1A_RestApiPassword" />
</CryptographicKeys>
<InputClaims>
</InputClaims>
<OutputClaims>
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
I have seen a snippet on a GitHub issue (https://github.com/MicrosoftDocs/azure-docs/issues/58267) where the poster seems to have pulled the ServiceUrl from some external settings:
<Item Key="ServiceUrl">{Settings:CID-ApiUrl}/{user_id}</Item>
I can't seem to find any further information on this. Is there a way to store things like the ServiceUrl in some external settings that can then be accessed within the custom policy to avoid having to hardcode the URL?
Loading the ServiceUrl dynamically is not possible once it is deployed to B2C. But there is a very useful VS Code extension which should help during development. Go to the policy settings section from the following documentation: Azure AD B2C Tools for VSCode
In short, it helps to define variables, and replace them based on values stored in an appsettings.json file. You can have multiple sets of settings for different environments. Then use a single command to replace all variables and get the final policies to another folder.
You can then deploy them to your b2c tenant. Another option is to replace the variables from a CI/CD pipelines and deploy them to the tenant.
It’s not dynamic. They would be using CICD pipeline to populate them before uploading into the tenant.

Share session between two different (but very similar) TechnicalProfiles

Scenario is that during combined SignIn/SignUp flow I'm using following TechnicalProfile for signing in the user with local account:
Base.xml
<TechnicalProfile Id="SelfAsserted-LocalAccountSignin-Email">
<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">SignUpWithLogonEmailExchange</Item>
<Item Key="setting.operatingMode">Email</Item>
<Item Key="ContentDefinitionReferenceId">api.signuporsignin</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>
Extensions:
<TechnicalProfile Id="SelfAsserted-LocalAccountSignin-Email">
<Metadata>
<Item Key="setting.forgotPasswordLinkOverride">ForgotPasswordExchange</Item>
</Metadata>
</TechnicalProfile>
Base.xml is taken from one of the starter-packs available on GitHub.
This is working fine and when I'm logged in and go to combined SignUp/SignIn policy again, I'm authenticated automatically without needing to provide any credentials or to select external IdP.
Now, for some other flows (ProfileEdit, PasswordChange) I've slightly modified SelfAsserted-LocalAccountSignin-Email technical profile to not show SignUp/ForgotPassword links.
Technical profile definition:
<TechnicalProfile Id="SelfAsserted-LocalAccountSigninOnly-Email">
<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="setting.showSignupLink">False</Item>
<Item Key="setting.forgotPasswordLinkLocation">none</Item>
</Metadata>
<IncludeTechnicalProfile ReferenceId="SelfAsserted-LocalAccountSignin-Email" />
</TechnicalProfile>
As you can see, it includes the SelfAsserted-LocalAccountSignin-Email and just overrides two setting values.
However, now when the user is authenticated after executing combined SignUp/SignIn policy and he goes to my ProfileEdit policy, he is prompted to authenticate again.
This happens only if user was authenticated using local account, as in my case SignUp/SignIn & ProfileEdit/PasswordChange policies are using different (but very similar) SelfAsserted technical profiles. If user was authenticated using any social idp, ProfileEdit/PasswordChange policies work fine without prompting user for reauthentication.
Any ideas how to solve the issue for the local account?
One solution would be to use the same technical profile for combined SignUp/SignIn and ProfileEdit/PasswordChange and then hide links where necessary using JavaScript, however, if possible I would like to minimize changes made by JS.

In the PhoneFactor-InputOrVerify Technical Profile (B2C custom user flow), is it possible to allow the user to change their default mobile number?

In Azure B2C custom policies, our business process requirements mean we need control over whether:
The user may not replace the mobile number we have on record for them (i.e. first-time password set experience), or
We present the number we hold on record for them by default, but allow the user to overtype and save this number with a new number (e.g. logged-in edit profile experience).
The question: Is there a way I can allow the user to replace the number we hold on record for them with a new number, perhaps by the addition of a new button [Provide New Number] in this series:
[Send Code] [Call Me] [New Button To Provide New Number] [Cancel]
PhoneFactor-InputOrVerify sample screen (image)
The code below is a slight modification of the similarly-named policy from the B2C Custom Policy Starterpack.
Note how in this example we're providing the mobile phone number value stored in the mobile property of the user object as strongAuthenticationNumber. This is because there is currently no way of setting the MFA number programmatically. (Thanks for your help, Chris Padgett.)
Other than this, the policy is as per starterpack.
<ClaimsProvider>
<DisplayName>PhoneFactor</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="PhoneFactor-InputOrVerify">
<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">true</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="strongAuthenticationPhoneNumber" /-->
<InputClaim ClaimTypeReferenceId="mobile" PartnerClaimType="strongAuthenticationPhoneNumber" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="Verified.strongAuthenticationPhoneNumber" PartnerClaimType="Verified.OfficePhone" />
<OutputClaim ClaimTypeReferenceId="newPhoneNumberEntered" PartnerClaimType="newPhoneNumberEntered" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-MFA" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
I suspect there may be some metadata setting to enable a button or text input box as described, but to my knowledge there is no documentation for this yet.
You must create a custom flow for this that:
Prompts the end user to sign in with a local or external account.
Reads the current phone number for the end user from Azure AD.
Prompts the end user to verify their current phone number.
Prompts the end user to enter and verify their new phone number.
Writes the new phone number for the end user to Azure AD.
See this sample policy.

How can I read the mobile value from a B2C user record and transform it to a strongAuthenticationPhoneNumber?

I have the need to create Azure B2C user accounts programmatically. In a separate user data store I hold pertinent information about the users I need to set up in B2C including their mobile phone number, which we've already been communicating with them on.
My business requirement is that this mobile phone number is used as a secondary factor during the user's first-time login/password reset experience. I have an initial login experience which uses an externally-created JWT token to take the user to a custom User Journey where they can set a password for the first time.
I understand that it is not yet possible to set the Azure MFA mobile number via Graph API or PowerShell. (Is this still true?). Therefore B2C asks the user to enter their mobile number in the exemplar PhoneFactor-InputOrVerify Technical Profile. This is a security hole as you can just enter any mobile number in there and verify that number.
I can easily programmatically add the user's number to some other field - e.g. the mobile field on the user record.
Question 1.
Is there a way to read the user account mobile value and present it to a Technical Profile as if it is the strongAuthenticationPhoneNumber value or Verified.strongAuthenticationPhoneNumber?
Question 2.
Is this even a good idea? I imagine there are good reasons not to do this, but I can't fathom what they might be.
I've tried creating new ClaimTypes, reading the 'mobile' field value, creating ClaimsTranfromations to try to make the mobile claim appear to be the strongAuthenticationPhoneNumber claim, and trying generally to 'spoof' B2C into thinking this is the actual number as stored in the MFA data store.
This is the the standard PhoneFactor-InputOrVerify Technical Profile from the starterpack:
<ClaimsProvider>
<DisplayName>PhoneFactor</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="PhoneFactor-InputOrVerify">
<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">true</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="strongAuthenticationPhoneNumber" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="Verified.strongAuthenticationPhoneNumber" PartnerClaimType="Verified.OfficePhone" />
<OutputClaim ClaimTypeReferenceId="newPhoneNumberEntered" PartnerClaimType="newPhoneNumberEntered" />
</OutputClaims>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-MFA" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
I can provide more code samples of the custom User Journey I mentioned earlier but I don't think this will help with this problem.
You have a few options:
You can add the strongAuthenticationPhoneNumber claim to the same JWT that is used for the onboarding flow and prompt for verification of this phone number during this onboarding flow.
You can map to the strongAuthenticationPhoneNumber claim for the PhoneFactor-InputOrVerify technical profile from the mobile property (or an extension property) of the user object.
For option 1, the onboarding user journey should write the verified phone number to the user object, without the newPhoneNumberEntered-based precondition:
<OrchestrationStep Order="8" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserWriteWithObjectId" TechnicalProfileReferenceId="AAD-UserWritePhoneNumberUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
For option 2, you can map to the strongAuthenticationPhoneNumber claim from the mobile property, as follows:
<InputClaims>
<InputClaim ClaimTypeReferenceId="userIdForMFA" PartnerClaimType="UserId" />
<InputClaim ClaimTypeReferenceId="mobile" PartnerClaimType="strongAuthenticationPhoneNumber" />
</InputClaims>

Differently-formatted issuerUserId when using Microsoft Account provider in Azure AD B2C

We're investigating a migration of our existing authentication system to Azure AD B2C. Our current system accepts both MSA and Google logins, and we use an in-house developed service based on IdentityServer, which stores the IDs from the third-party IdPs in Azure Table storage.
I've followed the instructions for setting up MSA as an ID provider and for migrating users with a social account, and have converted my existing MSA IDs to base64 when posting them as userIdentities to the AD Graph API (in line with the sample code from the second link above). But when I sign in with an MSA, instead of recognizing my account, it directs me to the sign-up page (as part of the Sign-in/Sign-up policy I have defined).
If I follow through with creating the user, and then inspect the newly created user with the /users/<new-user-id> endpoint in the Graph API, I get back a strange-looking issuerUserId when compared to what I have stored for my Microsoft users currently.
My existing users have ids that look something like 1234ab56789cde01, and the IDs being sent to me via B2C are formatted as something like AAAAAAAAAAAAAAAAAAAAAAbCdEF12GhIj_KlM34nOPQwhen base64 decoded. (Values altered to avoid any potential privacy issues.) The capital 'A's are always there at the beginning, and I get characters from the full range of alphanumeric values in the new format as compared to the original IDs that only have a range of hexadecimal characters.
I've managed to get the Google accounts working just fine. All that's necessary there is to convert them to base64 and they just work. But I'm struggling to figure out how to migrate the MSAs. Either I'm doing something wrong on the app registration side, or there's another step to generating the AAAAAAAA-prefixed IDs that I'm just missing. Any help is appreciated. Thanks!
The built-in identity provider for Microsoft Account maps from the sub (subject) claim for the Microsoft account to the issuerUserId property of the userIdentity object.
If you are wanting to migrate identities that are mapped from a different claim (such as the oid (object identifier) claim) of a Microsoft account, then you must use custom policies and then add a custom identity provider for Microsoft Account with the following modification:
<ClaimsProvider>
<Domain>live.com</Domain>
<DisplayName>Microsoft Account</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="MSA-OIDC">
<DisplayName>Microsoft Account</DisplayName>
<Protocol Name="OpenIdConnect" />
<Metadata>
<Item Key="ProviderName">https://login.live.com</Item>
<Item Key="METADATA">https://login.live.com/.well-known/openid-configuration</Item>
<Item Key="response_types">code</Item>
<Item Key="response_mode">form_post</Item>
<Item Key="scope">openid profile email</Item>
<Item Key="HttpBinding">POST</Item>
<Item Key="UsePolicyInRedirectUri">0</Item>
<Item Key="client_id">Your Microsoft application client id</Item>
</Metadata>
<CryptographicKeys>
<Key Id="client_secret" StorageReferenceId="B2C_1A_MSASecret" />
</CryptographicKeys>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="live.com" />
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="socialIdpAuthentication" />
<!-- ORIGINAL: The following output claims maps from the "sub" claim to the "issuerUserId" property. -->
<!--<OutputClaim ClaimTypeReferenceId="socialIdpUserId" PartnerClaimType="sub" />-->
<!-- MODIFICATION: The following output claims maps from the "oid" claim to the "issuerUserId" property. -->
<OutputClaim ClaimTypeReferenceId="socialIdpUserId" PartnerClaimType="oid" />
<OutputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="name" />
<OutputClaim ClaimTypeReferenceId="email" />
</OutputClaims>
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="CreateRandomUPNUserName" />
<OutputClaimsTransformation ReferenceId="CreateUserPrincipalName" />
<OutputClaimsTransformation ReferenceId="CreateAlternativeSecurityId" />
<OutputClaimsTransformation ReferenceId="CreateSubjectClaimFromAlternativeSecurityId" />
</OutputClaimsTransformations>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-SocialLogin" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
For information about getting started with custom policies, see Azure Active Directory B2C: Get started with custom policies.

Resources