Reputation: 16567
I am struggling with the Custom Policies and trying to get a specific flow to work.
I already set up custom policies for email invitations and have modified them to allow two custom claims that are attached to the invitation and saved into the B2C extensions app registration correctly, and then returned in claims when the user signs in.
This all works great, but it is for local accounts only. I have been tasked with adding sign up support to the invitation process to allow the user to register via Google or Microsoft social accounts.
My process is that first I loaded the default custom policies. Then I used the Email Invitations example, renamed the policies, and customized them with my two new claims. Then, I created a new set of policies where I am trying to combine the Email Invitations with Account Linkage Unified
Issue number 1 was that I do not want sign in, as this page is signup only and should require a valid invitation token. After a lot of fighting with the page, I have reached the point where it is now only showing the social options:
I am not sure how to also get it to show the local account option that used to show:
Ideally it would show this input form with the two social accounts as options below that.
You'll see below in my user journey I tried a couple options to make them show, but those always resulted in an error. When they were the SignInOrSignUp type, it showed signin boxes with a link to sign up that failed when clicked - but that is not the behavior I want anyway. This screen should be sign up only. For sign In, I am using a UserFlow
that is sign in only that is already configured to allow all three auth methods. I couldn't do the same for signup, because it always needs to happen via email invitation only.
Here is my current user journey that is just showing the two social accounts. I am unsure of what changes I need to make to include both local and social account creation on this screen.
<UserJourneys>
<!-- UserJourney to redeem the email invite. It uses id_token_hint to validate the id_token passed -->
<UserJourney Id="SignInWithIdTokenHint">
<OrchestrationSteps>
<!-- Read the input claims from the id_token_hint-->
<OrchestrationStep Order="1" Type="GetClaims" CpimIssuerTechnicalProfileReferenceId="IdTokenHint_ExtractClaims" />
<!-- Check if user tries to run the policy without invitation -->
<OrchestrationStep Order="2" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>email</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="SelfAsserted-Unsolicited" TechnicalProfileReferenceId="SelfAsserted-Unsolicited" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- Read the user properties from the directory - should not exist -->
<OrchestrationStep Order="3" Type="ClaimsExchange">
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadUsingEmailAddress" TechnicalProfileReferenceId="AAD-UserReadUsingEmailAddress-NoError" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- Check whether the user not existed in the directory and throw error in that case -->
<OrchestrationStep Order="4" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="false">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="SelfAssertedUserAlreadyExists" TechnicalProfileReferenceId="SelfAsserted-UserAlreadyExists" />
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="5" Type="ClaimsProviderSelection" ContentDefinitionReferenceId="api.idpselections.signup">
<ClaimsProviderSelections>
<ClaimsProviderSelection TargetClaimsExchangeId="MicrosoftAccountExchange"/>
<ClaimsProviderSelection TargetClaimsExchangeId="GoogleExchange"/>
<!-- <ClaimsProviderSelection TargetClaimsExchangeId="LocalAccountSigninEmailExchange"/> -->
<!-- <ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninEmailExchange"/> -->
</ClaimsProviderSelections>
<ClaimsExchanges>
<!-- <ClaimsExchange Id="LocalAccountSigninEmailExchange" TechnicalProfileReferenceId="LocalAccountSignUpFromEmailInvite"/> -->
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="6" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="MicrosoftAccountExchange" TechnicalProfileReferenceId="MSA-OIDC-SignIn"/>
<ClaimsExchange Id="GoogleExchange" TechnicalProfileReferenceId="Google-OAUTH-SignIn"/>
<ClaimsExchange Id="SignUpWithLogonEmailExchange" TechnicalProfileReferenceId="LocalAccountSignUpFromEmailInvite"/>
</ClaimsExchanges>
</OrchestrationStep>
<!-- For social IDP authentication, attempt to find the user account in the directory. -->
<OrchestrationStep Order="7" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>authenticationSource</Value>
<Value>localAccountAuthentication</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadUsingUserIdentity" TechnicalProfileReferenceId="AAD-UserReadUsingUserIdentity-NoError" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- Show self-asserted page only if the directory does not have the user account already (i.e. we do not have an objectId).
This can only happen when authentication happened using a social IDP. If local account was created or authentication done
using ESTS in step 2, then an user account must exist in the directory by this time. -->
<OrchestrationStep Order="8" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="SelfAsserted-Social" TechnicalProfileReferenceId="SelfAsserted-Social-v2"/>
</ClaimsExchanges>
</OrchestrationStep>
<!-- This step reads any user attributes that we may not have received when authenticating using ESTS so they can be sent
in the token. -->
<OrchestrationStep Order="9" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimEquals" ExecuteActionsIf="true">
<Value>authenticationSource</Value>
<Value>socialIdpAuthentication</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="AADUserReadWithObjectId" TechnicalProfileReferenceId="AAD-UserReadUsingObjectId"/>
</ClaimsExchanges>
</OrchestrationStep>
<!-- Let the user signup (this is the old signup step from when we just allowed local accounts) -->
<!-- <OrchestrationStep Order="5" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="SelfAssertedUserNotFound" TechnicalProfileReferenceId="LocalAccountSignUpFromEmailInvite" />
</ClaimsExchanges>
</OrchestrationStep> -->
<OrchestrationStep Order="10" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />
</OrchestrationSteps>
<ClientDefinition ReferenceId="DefaultWeb" />
</UserJourney>
</UserJourneys>
EDIT: Adding a little more context. Here is the claims provider that is used by the old email invitation redemption policy that works, but only allows local accounts. It also has the output for my two custom claims that go through a transformation.
<ClaimsProvider>
<DisplayName>Local Account</DisplayName>
<TechnicalProfiles>
<!-- Self Asserted page for Signup from an email invite and id_token_hint -->
<TechnicalProfile Id="LocalAccountSignUpFromEmailInvite">
<DisplayName>Email signup</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.SelfAssertedAttributeProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
<Metadata>
<Item Key="SignUpTarget">SignUpFromEmailInviteExchange</Item>
<Item Key="IpAddressClaimReferenceId">IpAddress</Item>
<Item Key="ContentDefinitionReferenceId">api.localaccountsignup</Item>
<Item Key="language.button_continue">Signup</Item>
<Item Key="setting.showCancelButton">false</Item>
</Metadata>
<CryptographicKeys>
<Key Id="issuer_secret" StorageReferenceId="B2C_1A_TokenSigningKeyContainer" />
</CryptographicKeys>
<InputClaimsTransformations>
<InputClaimsTransformation ReferenceId="CopyEmailAddress" />
</InputClaimsTransformations>
<InputClaims>
<InputClaim ClaimTypeReferenceId="ReadOnlyEmail" />
<InputClaim ClaimTypeReferenceId="displayName" />
</InputClaims>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="objectId" />
<OutputClaim ClaimTypeReferenceId="ReadOnlyEmail" Required="true" />
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="newPassword" Required="true" />
<OutputClaim ClaimTypeReferenceId="reenterPassword" Required="true" />
<OutputClaim ClaimTypeReferenceId="executed-SelfAsserted-Input" DefaultValue="true" />
<OutputClaim ClaimTypeReferenceId="extension_advisorId" />
<OutputClaim ClaimTypeReferenceId="extension_clientId" />
</OutputClaims>
<ValidationTechnicalProfiles>
<ValidationTechnicalProfile ReferenceId="ConvertAdvisorId" />
<ValidationTechnicalProfile ReferenceId="ConvertClientId" />
<ValidationTechnicalProfile ReferenceId="AAD-UserWriteUsingLogonEmail" />
<!-- write the user object to the directory -->
<!-- <ValidationTechnicalProfile ReferenceId="SendSignupEvent" /> -->
<!-- notify backend system of user creation -->
</ValidationTechnicalProfiles>
<IncludeTechnicalProfile ReferenceId="AAD-Common" />
<UseTechnicalProfileForSessionManagement ReferenceId="SM-AAD" />
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
EDIT 2:
Here are my two steps where I try to add the SignUp provider. When I try to upload, however, I get an error (yes it repeats the same error 4 times):
Validation failed: 2 validation error(s) found in policy xxx of tenant yyy. ClaimsExchange with Id "SignUpFromEmailInviteExchange" is referneced in UerJourney with id zzz in policy xxx of tenant yyy, but it was not found. Claims Exchange with Id "SignUpFromEmailInviteExchange" is referneced in UserJourney with id zzz in policy xxx of tenant yyy, but it was not found. Claims Exchange with Id "SignUpFromEmailInviteExchange" is referneced in UserJourney with id zzz in policy xxx of tenant yyy, but it was not found. Claims Exchange with Id "SignUpFromEmailInviteExchange" is referneced in UserJourney with id zzz in policy xxx of tenant yyy, but it was not found.
<OrchestrationStep Order="5" Type="ClaimsProviderSelection" ContentDefinitionReferenceId="api.idpselections.signup">
<ClaimsProviderSelections>
<ClaimsProviderSelection TargetClaimsExchangeId="MicrosoftAccountExchange"/>
<ClaimsProviderSelection TargetClaimsExchangeId="GoogleExchange"/>
<ClaimsProviderSelection TargetClaimsExchangeId="SignUpFromEmailInviteExchange"/>
</ClaimsProviderSelections>
<ClaimsExchanges>
<ClaimsExchange Id="SignUpFromEmailInviteExchange" TechnicalProfileReferenceId="LocalAccountSignUpFromEmailInvite"/>
</ClaimsExchanges>
</OrchestrationStep>
<OrchestrationStep Order="6" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="MicrosoftAccountExchange" TechnicalProfileReferenceId="MSA-OIDC-SignIn"/>
<ClaimsExchange Id="GoogleExchange" TechnicalProfileReferenceId="Google-OAUTH-SignIn"/>
<ClaimsExchange Id="SelfAssertedUserNotFound" TechnicalProfileReferenceId="LocalAccountSignUpFromEmailInvite"/>
</ClaimsExchanges>
</OrchestrationStep>
Upvotes: 0
Views: 29
Reputation: 46773
If you want both local and social to show up, it needs to be as per the startup pack:
<OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin">
<ClaimsProviderSelections>
<ClaimsProviderSelection TargetClaimsExchangeId="FacebookExchange" />
<ClaimsProviderSelection ValidationClaimsExchangeId="LocalAccountSigninEmailExchange" />
</ClaimsProviderSelections>
<ClaimsExchanges>
<ClaimsExchange Id="LocalAccountSigninEmailExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Email" />
</ClaimsExchanges>
</OrchestrationStep>
<!-- Check if the user has selected to sign in using one of the social providers -->
<OrchestrationStep Order="2" Type="ClaimsExchange">
<Preconditions>
<Precondition Type="ClaimsExist" ExecuteActionsIf="true">
<Value>objectId</Value>
<Action>SkipThisOrchestrationStep</Action>
</Precondition>
</Preconditions>
<ClaimsExchanges>
<ClaimsExchange Id="FacebookExchange" TechnicalProfileReferenceId="Facebook-OAUTH" />
<ClaimsExchange Id="SignUpWithLogonEmailExchange" TechnicalProfileReferenceId="LocalAccountSignUpWithLogonEmail" />
</ClaimsExchanges>
</OrchestrationS
You need both "ClaimsProviderSelection" and "ClaimsExchange".
It looks like you have commented this out?
Upvotes: 0