I am attempting to auto-link a social provider login to an existing local account.
I'm using the auto-linking sample, but doing so having replaced the sample Facebook ClaimsProviders/etc with ones for Google. Using my version of B2C_1A_ACCOUNTLINK_SUSI, I can create and login to a local account successfully. I can also add the user successfully with Google (if I delete the user beforehand - no linking). So, I know that these both work individually.
Yet, when I create the local account and attempt to login using Google as the idp and link that, I get an error that there are no suitable claims providers found.
Has anyone tried this auto-linking sample with Google and seen any tweaks I may need to Input/Output claims or other? I have made several over the course of a couple of weekends trying to get this to work. AppInsights shows that it happens in AAD-FindLocalAccountWithSocialEmail (AzureActiveDirectoryProvider) and Exception is: No suitable claims providers were found.
Any suggestions appreciated. I can post my Google ClaimsProvider blocks if that would help diagnose.
Probably because the IdP was not enabled using the issuers collection.
In all your Google IdP technical profiles, make sure to have:
<OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="google.com" AlwaysUseDefaultValue="true" />
In Google-OAUTH-Link technical profile, make sure to have this:
<Metadata>
<Item Key="ClaimTypeOnWhichToEnable">currentIssuers</Item>
<Item Key="ClaimValueOnWhichToEnable">google.com</Item>
</Metadata>
<!--snip-->
<OutputClaimsTransformations>
<OutputClaimsTransformation ReferenceId="CreateUserIdentityToLink" />
<OutputClaimsTransformation ReferenceId="AppendUserIdentityToLink" />
</OutputClaimsTransformations>
<EnabledForUserJourneys>OnItemExistenceInStringCollectionClaim</EnabledForUserJourneys>
In the HandleLinkLocalToSocial subjourney, make sure to add Google IdP in the linking steps:
<OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.idpselections">
<!--snip preconditions-->
<ClaimsProviderSelections DisplayOption="ShowSingleProvider">
<ClaimsProviderSelection TargetClaimsExchangeId="LinkGoogleExchange1"/>
</ClaimsProviderSelections>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<!--snip preconditions-->
<ClaimsExchanges>
<ClaimsExchange Id="LinkGoogleExchange1" TechnicalProfileReferenceId="Google-OAUTH-Link"/>
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="3" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin">
<!--snip preconditions-->
<ClaimsProviderSelections DisplayOption="ShowSingleProvider">
<ClaimsProviderSelection TargetClaimsExchangeId="LinkGoogleExchange2"/>
</OrchestrationStep>
<OrchestrationStep Order="4" Type="ClaimsExchange">
<!--snip preconditions-->
<ClaimsExchanges>
<ClaimsExchange Id="LinkGoogleExchange2" TechnicalProfileReferenceId="Google-OAUTH-Link"/>
</ClaimsExchanges>
</OrchestrationStep>
Had the same error using the auto-linking sample code. Check the output claims section of the technical profile AAD-FindLocalAccountWithSocialEmail. The claim currentUserIdentities is read from the directory there and is the input for the ExtractCurrentIssuers output claims transformation of this TP (which populates the currentIssuers claim and puts it into the claims bag).
The problem seems to be a wrong PartnerClaimType value.
Try
<OutputClaim ClaimTypeReferenceId="currentUserIdentities" PartnerClaimType="identities"/>
instead of
<OutputClaim ClaimTypeReferenceId="currentUserIdentities" PartnerClaimType="userIdentities"/>
If the PartnerClaimType is wrong the result is empty and an empty currentIssuers string collection leads to the error (in Google-OAUTH-Link TP) you described.
Would be nice if App-Insights could show the values of claims collections.
I have same problem relates to auto-account-linking sample, for my case I have only fix it by add output claim in AAD-FindLocalAccountWithSocialEmail Tehnical profile. Hopefully can help others
<OutputClaims>
<!-- sinp-->
<OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="localAccountAuthentication" />
</OutputClaims>
Issue is due to the hasPassword extension property. It doesn't exist by default for existing local B2C user profiles. Add true for that property to B2C user account, and it will start working. You will have to use Graph API to add value, so basically PATCH to user endpoint:
PATCH /v1.0/users/83bda93c-f782-431c-b969-12b5304c0668 HTTP/1.1
Host: graph.microsoft.com
Content-Type: application/json
Authorization: Bearer eyJ0...
{
"extension_83a963aa6ce74511923b85511f0f8dc7_haspassword" : true
}
See here for full discussion: https://bytemeta.vip/repo/azure-ad-b2c/samples/issues/430
Related
The Microsoft documentation on subjourneys state the following: A sub journey is called only from a user journey, it shouldn't call another sub journey. (https://learn.microsoft.com/en-us/azure/active-directory-b2c/subjourneys#user-journey-branching)
From a development point of view it would be good to split reusable steps into subjouneys and call as required which is exactly how MS structure their policies, this is an extract of base-v1
<SubJourney Id="TotpFactor-Verify" Type="Call">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="InvokeSubJourney">
<JourneyList>
<Candidate SubJourneyReferenceId="SetTotpInitialValue" />
</JourneyList>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
The base-v1 entire setup is small reusable subjourneys that are referenced from others.
Do we know if this statement in the documentation is out of date or it's not support for customer custom policies?
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)
We're implementing the embedded password reset, as is the new recommended practice. Once we click the Forgot your password? link the reset sub-journey is invoked as expected.
The reset sub-journy always skips the local account discovery step, where the user verifies their email to access their account information, and jumps directly to the screen to enter a new password - the new password entry then fails, because there is no account to write the new password into.
Our reset password journey is as follows:
<SubJourney Id="PasswordReset" Type="Call">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="ClaimsExchange">
<!-- This orchestration step never occurs. The user is never prompted for their email address. -->
<ClaimsExchanges>
<ClaimsExchange Id="PasswordResetUsingEmailAddressExchange" TechnicalProfileReferenceId="LocalAccountDiscoveryUsingEmailAddress" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="2" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="NewCredentials" nicalProfileReferenceId="LocalAccountWritePasswordUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
</OrchestrationSteps>
</SubJourney>
Our code, so far, is lifted directly from the tutorials and sample code. How can we fix this issue, and has anyone else encountered the same problem?
Your question has been solved, post it as the answer to the end of the thread:
This is a bug in the B2C system - the initial combined sign in and
sign up step seems to set the email claim, even if the user hits the
"forgot your password" link. The
LocalAccountDiscoveryUsingEmailAddress profile attempts to use the
(blank) claim without prompting the user to enter an address, jumping
to the password write step but not picking up an account to write the
password to. We worked around this by creating a new resetEmail claim
used only by the account discovery and password write profiles.
I am modifying an existing custom policy's behavior and have not yet been able to find guidance on what I a trying to do.
Backgound
I have a custom signup/signin policy with a terms & conditions page. The general workflow is:
Sign up / Sign in
Terms & conditions acceptance
Send the claims back to the web application, which signs the user in
Goal
I have been asked to modify the policy so that rather than redirecting back to the web application on sign in, it displays a custom HTML "pending verification page".
What I've done
Created and deployed the custom HTML content.
Created a ContentDefinition that points to that HTML page:
<ContentDefinition Id="api.selfasserted.pendingverification">
<LoadUri>[redacted]/pending-verification.html</LoadUri>
<RecoveryUri>~/common/default_page_error.html</RecoveryUri>
<DataUri>urn:com:microsoft:aad:b2c:elements:contract:selfasserted:1.2.0</DataUri>
<Metadata>
<Item Key="DisplayName">Pending Verification</Item>
</Metadata>
</ContentDefinition>
Created a TechnicalProfile that uses that content definition:
<TechnicalProfile Id="SelfAsserted-PendingVerification">
<DisplayName>Pending Verification</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="ContentDefinitionReferenceId">api.selfasserted.pendingverification</Item>
</Metadata>
<IncludeInSso>false</IncludeInSso>
</TechnicalProfile>
Added an OrchestrationStep that references the TechnicalProfile:
<OrchestrationStep Order="6" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="ShowPendingVerificationPage" TechnicalProfileReferenceId="SelfAsserted-PendingVerification" />
</ClaimsExchanges>
</OrchestrationStep>
Doublechecked that the order of the steps is what I'd expect and no steps are sharing the same order number in the orchestration.
Challenge
Despite these steps, the pending verification page seems to be skipped each time, and the next step (which returns the claims) appears to be executed.
Question
Is my understanding of how to create and refer to a static page above correct?
Is a technical profile with no input or output claims skipped by default?
What is the correct way to show a static HTML file in this manner without continuing on to the next step in the orchestration?
You are correct. A ClaimsExchange Orchestration Step will be skipped if there are no claims specified.
Of the examples I have seen, many people achieve this with a Claim that has a UserInputType of Readonly.
I am not aware of anything else, sans a more complicated Javascript to move content in and out.
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>