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.
Related
I am trying to get an access token using the password grant type in a POST request from Azure AD B2C. Once sent it gets the below error
"error": "server_error",
"error_description": "AADB2C90108: The orchestration step '1' does not specify a CpimIssuerTechnicalProfileReferenceId when one was expected.\r\nCorrelation ID: .............\r\n"
I have tried with other authentication types like client credintials which is not working azure ad b2c but getting below error
"error": "unsupported_grant_type",
"error_description": "AADB2C90086: The supplied grant_type [client_credentials] is not supported.\r\nCorrelation ID:
How can I resolve this?
The usual reason you get this is that it's missing e.g.
<OrchestrationStep Order="3"
Type="SendClaims"
CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
Are you using a custom policy? What type of policy are you using?
As the error message says, "client credentials" is not yet supported in B2C.
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)
There appears to be limited documentation around sub journeys. https://learn.microsoft.com/en-us/azure/active-directory-b2c/subjourneys
I have a problem where in my SubJourney, I read the user and get the object Id. In the main UserJourney, I later use that object Id to read the user again, but it complains.
Although objectId is an output claim in the first step of the SubJourney, the main User Journey cannot use that output.
<SubJourneys>
<SubJourney Id="ResetPhoneNumberOnAccount" Type="Call">
<OrchestrationSteps>
<!-- Look to see if the user exists if its a phone recovery -->
<OrchestrationStep Order="1" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="CheckIfUserExists" TechnicalProfileReferenceId="AAD-UserDiscoveryUsingLogonPhoneNumber-FullProfile" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- Other Step -->
</SubJourney>
</SubJourneys>
AAD-UserDiscoveryUsingLogonPhoneNumber-FullProfile is defined: https://github.com/Azure-Samples/active-directory-b2c-custom-policy-starterpack/blob/master/scenarios/phone-number-passwordless/Phone_Email_Base.xml#L905
Are SubJourneys not able to send output claims up the chain?
Yes, output claims from orchestration steps in sub journeys are accessible from the parent user journey. It seems like when an orchestration step depends on an output claim returned from a sub journey, the step must itself be encapsulated in its own sub journey.
If AAD-UserReadUsingObjectId exists in the main journey, but objectId is only output in a sub journey, the policy will fail validation. This appears to me to be bug with the XML schema validator.
I have examined trace logs in app insights and, after applying this workaround, can confirm that claims output in sub journeys do persist until the main journey has ended.
For a more in depth look at this problem, check out my issue on GitHub.
Seems like its not possible, can you try using the transfer sub-journey here?
<SubJourneys>
<SubJourney Id="B" Type="Transfer">
<OrchestrationSteps>
...
<OrchestrationStep Order="5" Type="SendClaims">
</OrchestrationSteps>
</SubJourney>
</SubJourneys>
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.
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.