“Inheritance” in Azure AD B2C custom policies

Rory Braybrook
The new control plane
3 min readMar 22, 2021

Typically you’ll have a RelyingParty (RP) custom policy that inherits from an extension policy that inherits from a base policy.

This is the way the starter pack is built.

The “PasswordReset”, “ProfileEdit” and “SignUpOrSignIn” (SUSI) policies are all RP policies and then you have the extension and the base.

Flows could be e.g.:

  • SUSI
  • SUSI with Facebook federation
  • SUSI with Azure AD federation
  • SUSI with forced password reset

One way to deal with this is to have four SUSI policies in the base file.

BUT, every now and then, Microsoft updates the starter pack.

If you’ve added all the new stuff into the base, you have a merge ahead of you 😢

Enter inheritance!

Lets change the policy flow to:

RP → Custom extension file → Extension file → Base file

There is ideally a custom extension for each flow.

Taking federation as an example, the functionality is in the first two SUSI steps:

<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>
</OrchestrationStep>

This example is for Facebook.

For e.g. Azure AD, it would look like:

<OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin">
<ClaimsProviderSelections>
<ClaimsProviderSelection TargetClaimsExchangeId="AAD-Exchange"/>
<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="AAD-Exchange" TechnicalProfileReferenceId="AAD-OAUTH"/>
<ClaimsExchange Id="SignUpWithLogonEmailExchange" TechnicalProfileReferenceId="LocalAccountSignUpWithLogonEmail"/>
</ClaimsExchanges>
</OrchestrationStep>

In the custom extension file for Azure AD, we would have:

<UserJourney Id="SignUpOrSignIn">
<OrchestrationSteps>
<OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin">
<ClaimsProviderSelections>
<ClaimsProviderSelection TargetClaimsExchangeId="AAD-Exchange"/>
<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="AAD-Exchange" TechnicalProfileReferenceId="AAD-OAUTH"/>
<ClaimsExchange Id="SignUpWithLogonEmailExchange" TechnicalProfileReferenceId="LocalAccountSignUpWithLogonEmail"/>
</ClaimsExchanges>
</OrchestrationStep>
</OrchestrationSteps>
</UserJourney>

We now have a SUSI user journey of two steps.

When B2C uploads the files, it sees that the TP in the custom extension file is also in the base file and it “merges” the two.

This way we get the AAD / Facebook federation in two different flows without changing the base file.

Or another example:

Let’s assume that you need to add an extension attribute for a particular flow to “UserWriteUsingLogonEmail”.

So you add:

<OutputClaim ClaimTypeReferenceId="extension_1234"/>

to the Technical Profile (TP) in the base.

Then for another flow, you need to add “extension_5678” and for another flow you need to add “extension_9012”.

The TP in the base file is now catering for three different flows.

A better way to do this is to add this in three different extension files; one for each flow e.g.

<TechnicalProfile Id="AAD-UserWriteUsingLogonEmail">
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="extension_1234"/>
</OutputClaims>
</TechnicalProfile>

You see the same thing when you want to remove email verification.

<ClaimsProvider>
<DisplayName>Local Account</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="LocalAccountSignUpWithLogonEmail">
<Metadata>
<Item Key="EnforceEmailVerification">false</Item>
</Metadata>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>

So you could have two policies; one that requires this and one that doesn’t. By putting this code in an extension files, you get the two different functionalities without having to change the base file.

There is one exception to this rule i.e. when you want to delete something. There is no way to do this in an extension file. You have to do this in the base.

All good!

--

--

Rory Braybrook
The new control plane

NZ Microsoft Identity dude and MVP. Azure AD/B2C/ADFS/Auth0/identityserver. StackOverflow: https://bit.ly/2XU4yvJ Presentations: http://bit.ly/334ZPt5