B2C Custom Policy Nested SubJourney - azure-ad-b2c

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?

Related

AAD-FindLocalAccountWithSocialEmail produces No suitable claims providers were found

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

AADB2C Embedded Password Reset: Local account discover is not being fired

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.

B2C Custom Policy - Are SubJourney OutputClaims accessible in parent UserJourney

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>

Is it possible to run a password reset user flow, right after using sign in user flow in Azure B2C?

In Azure B2C, I have a sign in user flow to sign in the user. Since they still don't have a password reset on first sign in, I used the following method.
The user has a custom attribute called 'IsUserNew'. This is used to
check whether the user is new or not.
If the user is new, I wanna
redirect the user to the B2C, with a password reset user flow.
The problem is, after sign in, the user is not redirecting to B2C. Is it because the user as already signed in with a user flow?
I believe you are using Custom Policy and your extension attribute is defined as boolean and value is true on registration, if this is the case you can do following:
OrchestrationStep for Authentication.
OrchestrationStep for Read User Details, in the technical profile add "extension_IsUserNew" so that this attribute can be read from User.
Add below in the next OrchestrationStep. This will check the attribute and call the Password Technical Profile.
<OrchestrationStep Order="3" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>extension_IsUserNew</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
<Precondition Type="ClaimEquals" ExecuteActionsIf="false">
<Value>extension_IsUserNew</Value>
<Value>True</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="NewCredentials" TechnicalProfileReferenceId="LocalAccountWritePasswordUsingObjectId" />
</ClaimsExchanges>
</OrchestrationStep>
Add Step to modify the "extension_IsUserNew" value or delete the attribute so that Password Reset will not ask in the next login.
In 1 Policy only you can handle the Password Reset in 1st login, no need to create separate policies.

How to disable storing of claims principal records in Azure B2C?

Azure B2C stores information about every claims principal logged in.
We do not need this information.
Users should be just passed through B2C from IdPs to service provider.
How to disable storing users information in B2C?
Here is the solution I found.
The base policy in SignIn userjourney
<UserJourney Id="SignIn">
contains an orchestration step that calls a technical profile AAD-UserWriteUsingAlternativeSecurityId
<OrchestrationStep Order="4" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AAD-UserWriteUsingAlternativeSecurityId" TechnicalProfileReferenceId="AAD-UserWriteUsingAlternativeSecurityId" />
</ClaimsExchanges>
</OrchestrationStep>
This step creates user registrations in Azure B2C.
If this user journey is overloaded in an extension policy and this step skipped there then users will not be created.
Another step that checks users registration with id AAD-UserReadUsingAlternativeSecurityId-NoError
TechnicalProfileReferenceId="AAD-UserReadUsingAlternativeSecurityId-NoError"
can also be skipped.
The authentication process with an external identity providers goes on flawlessly without these steps.
Azure AD B2C does not store anything if you are using an external identity provider. But if you are using Azure AD B2C's idedntity provider, It will store the claims within it.
Hope the information helps.

Resources