Azure Active Directory netglade
Azure Active Directory netglade
Azure Active Directory netglade

Our experience with Azure AD B2C – 3. Inside XML

Backend

Backend

Backend

Peering into the architecture! Understand the intricacies of XML configuration that power Azure Active Directory B2C's functionality and customization.

Feb 19, 2024

Description of AD B2C Tags

What do the XML tags mean? What are they good for? How to use them?

<BasePolicy> is almost always just a reference to Microsoft’s ready-made XML base code. <DefaultUserJourney ReferenceId="PolicyName" /> refers to Microsoft’s base code Journey and is a base for the new policy you are trying to create.

<TechnicalProfile Id="PolicyProfile"> is your new profile that overrides Microsoft’s base code.

<Protocol> is more or less how to get the claims. For example, OpenID or Microsoft’s own proprietary way: <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.PhoneFactorProtocolProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />

Which is essentially a hard reference to Microsoft’s “logic” that resolves input data.

<OutputClaims> is what your technical profile returns when used in ClaimsExchange (more on that later).

<InputClaims> Even though this is not mentioned in the example above, you can have inputs. This tells you what is expected from the user input into the interface or what you’re expecting from other systems to input (for example, sig-in type).

And this should be enough to write a basic journey. But if you are looking to collect custom data from users or need to modify the data, you will need to look inside Microsoft's code and try to replicate the way the make Technical Profiles, Orchestration Steps, and Claims Exchange. And they can get real complicated, real fast.

The following example is User Journey for Profile edit inside Microsoft’s base code (TrustFrameworkBase.xml), which you are extending and/or overwriting with your policy.

<UserJourney Id="ProfileEdit">
      <OrchestrationSteps>

        <OrchestrationStep Order="1" Type="ClaimsProviderSelection" ContentDefinitionReferenceId="api.idpselections">
          <ClaimsProviderSelections>
            <ClaimsProviderSelection TargetClaimsExchangeId="FacebookExchange" />
            <ClaimsProviderSelection TargetClaimsExchangeId="LocalAccountSigninEmailExchange" />
          </ClaimsProviderSelections>
        </OrchestrationStep>
        <OrchestrationStep Order="2" Type="ClaimsExchange">
          <ClaimsExchanges>
            <ClaimsExchange Id="FacebookExchange" TechnicalProfileReferenceId="Facebook-OAUTH" />
            <ClaimsExchange Id="LocalAccountSigninEmailExchange" TechnicalProfileReferenceId="SelfAsserted-LocalAccountSignin-Email" />
          </ClaimsExchanges>
        </OrchestrationStep>

<UserJourney> and <OrchestrationStep> define the order of steps that users will go through in the policy.

<ClaimsProviderSelections> is basically the choice of claims source (Facebook, email, Google account).

<ClaimsExchange> tells you which claims you want to get from the claims provider.

You can create steps with preconditions and actions to check the claims or modify them.

The final Orchestration Step is often JWT Issuer, which issues a JWT token that contains claims you got throughout each step of the User Journey. Then, the journey will redirect you to your application with that JWT token correctly issued and signed.

But what about the problem that we were solving that made us write this blog post?

Well, we need a way to send a user data back to our backend mid-journey through Orchestration Steps, and we need the backend to return user ID from the database so that this ID is available in the JWT token. This is a slim-downed version of what that profile would look like:

<TechnicalProfile Id="GetInformationFromWeb">
  <DisplayName>Get Information from Web</DisplayName>
  <Protocol Name="Proprietary" Handler="Web.TPEngine.Providers.RestfulProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
  <Metadata>
    <Item Key="ServiceUrl">https://backend.com/api/getinfo</Item>
    <Item Key="SendClaimsIn">Body</Item>
    <Item Key="AuthenticationType">None</Item>
    <Item Key="AllowInsecureAuthInProduction">false</Item>
  </Metadata>
  <InputClaim ClaimTypeReferenceId="phone" PartnerClaimType="PhoneNumber" />
  <InputClaim ClaimTypeReferenceId="GivenName" />
  <InputClaim ClaimTypeReferenceId="Surname" />
</InputClaims>
<OutputClaims>
  <OutputClaim ClaimTypeReferenceId="database_id" PartnerClaimType="dbid" />
  <OutputClaim ClaimTypeReferenceId="reasonCode" PartnerClaimType="reason_code" />
</OutputClaims>
  <UseTechnicalProfileForSessionManagement ReferenceId="SM-Noop" />
</TechnicalProfile>

It’s nice that you can trust the Protocol Microsoft made to serialize and deserialize your data and call the web. It will use standard http to call your Backend and return proper data. It just works. You just need to properly configure the call with metadata.

It is a little constraining since you need to return a very specific HTTP body and HTTP status code from your backend; otherwise, the profile might fail, or the user might get an error message to retry the step. But it’s nothing too outlandish or complicated.

Afterwards you would call the JWT token issuer, and you get the token as well a new user in your Active Directory.

You might also like

You might also like

You might also like