This was suggested by Hari Krishna on another thread to open a new SO thread for this discussion. How do I programmatically clear or update a phone number for Azure AD B2C MFA?
We are using B2C custom policies with a step to write back the user's MFA profile to the B2C profile. The B2C technical profile name is AAD-UserWritePhoneNumberUsingObjectId.
<!-- Save MFA phone number: The precondition verifies whether the user provided a new number in the
previous step. If so, then the phone number is stored in the directory for future authentication requests. -->
<OrchestrationStep Order="12" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>newPhoneNumberEntered</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AADUserWriteWithObjectId" TechnicalProfileReferenceId="AAD-UserWritePhoneNumberUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
When this value is written back to B2C under Users->Authentication Methods->Phone, it does so in the format +12223334444 with no space in between the country code and the area code. B2C accepts this value and subsequent MFA requests work just fine.
However, the bug appears when you go to administer the MFA phone number using the Graph API methods under https://graph.microsoft.com/beta/users/{userId}/authentication/phoneMethods. When calling the GET method, any phone number not in the format +1 2223334444 (note the spaces b/w country code and area code this time) is ignored and the result is an empty array.
{
"#odata.context": "https://graph.microsoft.com/beta/$metadata#users('{userId}')/authentication/phoneMethods",
"value": []
}
Furthermore, the value cannot be deleted. Calling DELETE https://graph.microsoft.com/beta/users/{userId}/authentication/phoneMethods/3179e48a-750b-4051-897c-87b9720928f7 results in a 404 with the response:
{
"error": {
"code": "resourceNotFound",
"message": "Unable to delete authentication method of the requested type with an id of [3179e48a-750b-4051-897c-87b9720928f7] because it was not found for the user.",
"innerError": {
"message": "Unable to delete authentication method of the requested type with an id of [3179e48a-750b-4051-897c-87b9720928f7] because it was not found for the user.",
"date": "2020-12-08T14:02:53",
"request-id": "eba02037-1884-4dce-9faf-ceb1e377975b",
"client-request-id": "eba02037-1884-4dce-9faf-ceb1e377975b"
}
}
}
The one thing that does work is to perform a "ghost update" with a PATCH request and then a subsequent DELETE requests that will result in a blank phone number and then the user will be re-prompted to enter their new phone number on the next B2C sign-in attempt. See below.
Step 1 - Issue a "ghost update" to set the MFA phone number to a dummy value.
POST https://graph.microsoft.com/beta/users/{userId}/authentication/phoneMethods
{
"phoneNumber": "+1 2223334444",
"phoneType": "mobile"
}
Step 2 - Delete the dummy phone number, which is now allowed since it's in the correct format
DELETE https://graph.microsoft.com/beta/users/{userId}/authentication/phoneMethods/3179e48a-750b-4051-897c-87b9720928f7
This ultimately allows for a semi-suitable workaround because it allows an administrator to clear the old phone number so the user is re-prompted, but it is definitely a bug and prevents the administrator from viewing the existing phone number for verification purposes, which results in reduced security.
Related
We are using Azure AD B2C login in our web application. We want to be able to set MFA for a B2C user based on a setting in the application. From the documentation, I can see that custom policies are able to be applied. But can the custom policy hook in to the application to read a setting (eg whether MFA should be applied in this case)?
Another approach we are considering is to set MFA programmatically for the user, based on the application setting. I have read that MFA can be set on a per user basis, through the Azure Portal. Is there a way to do this programmatically? I have looked at the Graph API but have not seen anything obvious.
I have tried setting the MFA setting for a user via the Azure portal, but this is not working for me yet. Despite setting the user's MFA to Enforced, the user can still sign in without being challenged. I also find the portal confusing, and for many users there is no clear way of identifying them (the portal only shows display name as Unknown and email address as an internal identity rather than an external identity).
I have seen these following posts Can I apply MFA to each user in Azure ADB2C and Azure AD B2C MFA on a function which do provide some useful information, but I am still unclear on this.
Is this possible, and which approach (custom policy hooking in to the application to read setting, or setting the MFA for the specific user from the application) would be better?
The way I normally do this is by having an extension attribute in B2C that determines whether the user has MFA or not.
Using the starter pack MFA sample e.g.:
<OrchestrationStep Order="7" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>extension_MFA</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-InputOrVerify"/>
</ClaimsExchanges>
</OrchestrationStep>
You would need to add the extension attribute as an output claim in a "AAD-UserRead" step.
I'm trying to implement a password change custom policy for Azure AD B2C.
I've used this file as a base, changed the tenantid, and uploaded it to Azure.
When I try a demo from the Azure portal, it asks me to log in, then asks for an old password and a new password to change it. And it works perfectly fine. I can change my password there.
Then I've implemented a button, that redirects to the password change URL.
private redirectPasswordChange(appConfigService: AppConfigService): void {
const passwordChangeUrl = `${appConfigService.config.AD_AUTHORITY_CHANGE}?client_id=${appConfigService.config.AD_CLIENT_ID}
&nonce=defaultNonce
&redirect_uri=${appConfigService.config.BASE_URL}
&scope=openid
&response_type=id_token`;
if (isPlatformBrowser(PLATFORM_ID)) {
window.location.href = passwordChangeUrl;
} else {
this.window.location.href = passwordChangeUrl;
}
}
I've removed prompt=login from the URL because the button that is going to redirect to the password change URL is on the "My profile" section on our website. So users are already logged in, and I don't want them to log in again.
Then I tested it on our website, when I click to button, it redirects me to the password change page, it asks for my old password, new password, and confirmation of it, which is perfect for me. But the problem is, it doesn't accept my old password, it says "The username or password provided in the request are invalid".
I've searched for the problem online, including StackOverflow, but I found that there are lots of different answers for this problem. One of them is saying that:
Its because these input claims are overwriting the input claim names for login-noninteractive.
<InputClaim ClaimTypeReferenceId="grant_type" DefaultValue="client_credentials" />
<InputClaim ClaimTypeReferenceId="scope" DefaultValue="https://{{tenantID}}.onmicrosoft.com/{{registeredApiAppName}}/.default" />
</InputClaims>
use different claim names, and use a partnerclaimtype to send the value with the original claim name.
https://stackoverflow.com/a/65931145/8831824
When I take a look, I can see that those orchestrationsteps on PasswordChange.xml are the same in TrustFrameworkBase.xml
<OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin"> <ClaimsProviderSelections> <ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninEmailExchange" /> </ClaimsProviderSelections> <ClaimsExchanges> <ClaimsExchange Id="LocalAccountSigninEmailExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Email" /> </ClaimsExchanges> </OrchestrationStep>
<OrchestrationStep Order="3" Type="ClaimsExchange"> <ClaimsExchanges> <ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId" /> </ClaimsExchanges> </OrchestrationStep>
Is there some kind of overwriting here? I mean, they are exactly the same, but in two different files, In TrustFrameworkBase.xml, for SignUpOrSignIn journey, and PasswordChange.xml, for PasswordChange journey.
Here are the policies I have. I did put great effort to solve the problem, but I couldn't even identify it. Hope someone can give me an idea to work on it. Thanks for reading.
TrustFrameworkBase.xml: https://pastebin.com/RXXhcwpN
TrustFrameworkExtensions.xml: https://pastebin.com/UUfVNaJ7
PasswordChange.xml : https://pastebin.com/sPjZjNYT
PasswordReset.xml : https://pastebin.com/CxE3pMH3
Your password change xml should have the base policy node reference the trust framework extensions policy, not the trust framework base policy, as per my sample.
Ultimately the issue is because trust framework base file only contains part of the login-noninteractive technical profile. It is completed in the extension file, hence you must reference a file from which all dependencies are satisfied.
Got the same problem, but different from the question author my issue wasn't mixing up user flows with custom policies.
I was 100% sure that the root cause was not misconfiguration of login-noninteractive nor IdentityExperienceFramework to ProxyIdentityExperienceFrameworkAppId wrong setup as I followed copybook style ms docs on it.
The error cause turns out to be that the property accessTokenAcceptedVersion on IdentityExperienceFramework's manifest file was set to 2 instead of null.
Setting it to null solves the issue.
Considerations
How come this property was set to 2? And why is this a problem
Most likely because I've created the app initially configured to
support Accounts in any organizational directory (Any Azure AD
directory - Multitenant) but changed my mind and updated it to
support Accounts in this organizational directory only (your-tenant
only - Single tenant) changing the manifest property to look like so:
"signInAudience": "AzureADMyOrg",
as the docs states:
If signInAudience is azureADandPersonalMicrosoftAccount, the value (of accessTokenAcceptedVersion) must be 2.
How do I spot that issue?
By automating the environment configuration of an auxiliary B2C tenant by using the https://b2ciefsetupapp.azurewebsites.net/ and comparing each of the app's configs.
If you've setup IdentityExperienceFramework and ProxyIdentityExperienceFrameworkAppId manually by following ms docs and still facing this obscure issue:
I highly recommend you delete your apps and rely on the automated tool. (I've learned my lesson)
I need some advice on Customizing Azure B2c (Apple Identity Provider)
Is there a way to disable the sign up of a SignUpAndSignIn policy for an specific IDP? In that case apple?
I checked that post Azure B2C disable Sign up of a SignUpAndSignIn policy but is regarding to local signup, not for a social provider
You can do it only in custom policies. They are divided into steps (OrchestrationStep) which can be run under specified conditions (Precondition). One of those conditions can be identity provider selected. In your case it would be configuring the step which writes new external IdP user to directory to be skipped when particular provider is detected.
Something similar to this:
<OrchestrationStep Order="7" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>identityProvider</Value>
<Value>https://appleid.apple.com</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AADUserWrite" TechnicalProfileReferenceId="AAD-UserWriteUsingAlternativeSecurityId" />
</ClaimsExchanges>
However, this is just stopping the signup from happening. Some error handling, UX etc would have to be added as well.
Thank you very much for your time.
I'm receiving the following window, we don't want to see the following prompt
enter image description here
We want to use our Apple ID and jump to the app (see image bellow)
enter image description here
I've been working on implementing group-based access control for a B2C Tenant.
I've setup an API hook (POST) which accepts the user's ObjectId, validates if the user is in the permitted groups through Graph API and returns:
200 OK when successful
409 Conflict when not succesful
Below is the object returned with a 409:
{
"version": "1.0.0",
"status": 409,
"userMessage": "User is not authorized for this application"
}
For some reason, this message is not shown to the user, but rather a redirect occurs to
{{B2cUrl}}/#error=server_error&error_description=AADB2C%3a+User+is+not+authorized+for+this+application%0d%0aCorrelation+ID%3a+...8%0d%0aTimestamp%3a+...%3a03%3a39Z%0d%0a&state=...
In my user Journey, i have included the following orchestra
<OrchestrationStep Order="3" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="RESTValidateProfile" TechnicalProfileReferenceId="REST-ValidateProfile" />
</ClaimsExchanges>
</OrchestrationStep>
How can i get the user error message shown in the Login page for the user?
You need to call your rest api technical profile from a validation technical profile. The validation technical profile should be configured against the self asserted technical profile (login page) such that it can then return an error to it.
https://learn.microsoft.com/en-us/azure/active-directory-b2c/custom-policy-rest-api-claims-validation#validate-the-user-input
In its current form, the REST API is called after the page is submitted and a redirect starts. Therefore any error is just sent back to the app, as there is no page rendered at that time.
Azure B2C is gives a false impression that the user is in the directory when they try to reset their password.
Following is steps in reset password:
1) User clicks the Reset Password link
2) B2C presents a page with “Email Address” field and says “Verification is necessary. Please click Send button.”
3) User enters his email address and clicks “Send Verification Code”
4) B2C sends the verification code this that email address (Even if no user is associated with that email address. This is where the user thinks he is registered with the system)
5) Now the user enters the verification code he received and click “Verify Code”
6) B2C validated the code and says “E-mail address verified. You can now continue” (This is the step where they become confident that they exist in the system)
7) Now when the users click “Continue” they get the error “An account could not be found for the provided user ID.” As given in the screenshot.
Confirming an email that is not associated with a user completely confuses them.
i found this solution but not getting exactly how to use these policy along with my current policy
Based on this solution, you need to use the following XML snippet to implement it:
<Action Id="SendCode">
<ValidationClaimsExchange>
<ValidationClaimsExchangeTechnicalProfile TechnicalProfileReferenceId="AAD-UserReadUsingEmailAddress-emailAddress" />
<ValidationClaimsExchangeTechnicalProfile TechnicalProfileReferenceId="AadSspr-SendCode">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>objectId</Value>
<Action>SkipThisValidationTechnicalProfile</Action>
</Precondition>
</Preconditions>
</ValidationClaimsExchangeTechnicalProfile>
</ValidationClaimsExchange>
</Action>
So you should begin with adding "emailVerificationControl" into the "LocalAccountDiscoveryUsingEmailAddress" TechnicalProfile.
Just adding line 163-165 to the place behind line 890.
And you need to add DisplayControls into your TrustFrameworkExtensions.xml file so that it could be referenced.
If there are any other references, you should also add them to the corresponding position.