Introduction

I have been working with the Azure Active Directory B2C (AAD B2C) service since 2016, both integrating it into applications and helping people learn how to use it to add end-user authentication, registration, and management to their applications. Like most services in Azure, the functionality it offers has continued to grow since its release.

One of the more significant additions to the AAD B2C service has been the addition of custom policies. As the name implies, custom policies provide a way to include new behaviors in your service implementations beyond what is offered out-of-the box. However, working with Azure AD B2C custom policies can be a little daunting – policies are defined using an XML-based programming syntax that is a little unusual (I’m being nice.)

This series of posts will provide a walkthrough illustrating how to work with Azure Active Directory B2C custom policies by building one from the ground up. As the series progresses, the topics will serve to show how the different parts of the AAD B2C policy syntax and the underlying framework can be brought together to realize desired functionality. At the conclusion of the series, you should be ready to both understand existing AAD B2C policies and to create new ones for yourself.

Topics

The following table shows the topic areas being covered in this series. Topics in italics are coming soon.

Topic Focus Area
Introduction <– You are here
Hello World! Returning a simple message with a custom policy
Hello [Your Name Here]! Custom policy elements and syntax, how to collect and work with user input
Hello! (But Only If You Have an Access Code) How to perform basic input validation
Hello! (But Only If You Have an Access Code – NOW WITH REST!) How to make REST calls to external services for validation or information exchange
Hello! Let’s Add a User How to use the backing Azure Active Directory database to store and retrieve user information
Hello! Please Either Sign In or Sign Up How to either sign in an existing user or allow a new user to sign up
Hello! It’s Time To Clean Up a Bit How to use multiple files and a policy file hierarchy to manage and reuse policy content

NOTE
This walkthrough is intended for developers who want to learn how to leverage AAD B2C Custom Policies. The content assumes you understand the basic AAD B2C concepts such as how to make use of the built-in policies to implement basic user management and authentication tasks. Basic functional tutorials for AAD B2C can be found in the online documentation here.

Custom Policy Considerations

The out-of-the-box AAD B2C functionality offers a pretty wide range of options for customizing the service to your particular application needs. Some of these options include:

  • Customizing data collection by choosing what information to collect when users sign up for an account or what attributes users should be able to edit when updating their profile online. This includes selecting from predefined attributes or even creating new ones.
  • Configuring the display names used for attributes, such as changing the word “Surname” to “Last Name” instead.
  • Selecting the data collection controls for claims – for example choosing between free-text entry, drop-down, or radio list selections.
  • Changing the layout and/or styling of the HTML page that is displayed to the end-user to make the AAD B2C site mimic the calling application’s user interface.
  • Choosing what information is returned (in the form of claims) as the result of sign-in or similar operations.

CustomizedAADB2CExample
An example of a customized AAD B2C sign-in page

Even with this level of flexibility, it is pretty common to find that your specific business identity needs or business rules will require more than what is offered by the default AAD B2C built-in policies. Some examples might include:

  • Restricting new users to only those people to whom you have sent a predefined access/invitation code.
  • Calling a REST API to verify a user’s registration information or to fetch additional information about a user.
  • Having both new and existing users to accept a license agreement before they can use your application.
  • Allowing users to use their phones for a passwordless login experience.
  • Integrating a CAPTCHA or some other additional challenge as part of a login path.

Most of the time, the needs that require the use of AAD B2C custom policies are less about altering the look of the user experience and are more about altering the behavior of the user experience.

Custom Policies and the Identity Experience Framework

There are three key components to the AAD B2C service:

  • A user directory. This directory stores information about both local and federated users and can be accessed with the Microsoft Graph.
  • A security token service (STS) whose job it is to both issue tokens and other authentication currencies, as well as to validate token authenticity.
  • A framework known as the Identity Experience Framework (IEF) that serves as a runtime platform for coordinating interactions with the authentication endpoints exposed by an AAD B2C service instance.

In AAD B2C, user interactions are defined by policies (also known as User Journeys or User Flows). Policies – both built-in and custom – that are hosted by the IEF are defined using an XML-based pseudo-programming syntax.

Getting Started with Custom Policies – Abandon Hope Ye Who Enter Here

When you arrive at the “Getting Started” page in the AAD B2C documentation (or for that matter, pretty much any page in the AAD B2C documentation that speaks about custom policies), the first thing you might notice is the following rather prominent warning:

NOTE
In Azure Active Directory B2C, custom policies are designed primarily to address complex scenarios. For most scenarios, we recommend that you use built-in user flows.

In fact, if you open the Identity Experience Framework section of the AAD B2C panel in the Azure Portal, you will see a very similar warning:

Custom policies are designed for complex scenarios and configured by identity pros. Carefully assess if they are the right fit for your use case.

Who says that warnings only come after the spells? Admittedly, there is a bit of a learning curve that comes with working with these policies. But putting this kind of warning on every corner and turn within the AAD B2C Custom Policy experience has the unfortunate effect of scaring away people who very legitimately may need to use this technology. Hopefully, the content of this walkthrough will help put these warnings in their proper context.

Getting Started with Custom Policies – For Real this Time

There is a section in the online documentation that discusses how you can get started with custom policies. For the sake of this series, please ignore it.

A lot of the samples, tutorials, walkthroughs, quickstarts, etc. take the approach of starting with policies that are either fully or close-to-fully built-out and suggest adjustments and tweaks in strategic places without explaining the rest of the gunk in the file(s). The thinking is perhaps that if you make enough of these small adjustments, eventually you will figure out for yourself what all that stuff happens to be, or maybe you will decide to read through the entirety of the SDK documentation in order to figure things out. We are not going to take that approach here. Instead, we’re going to take a more gradual approach, and we’re not going to use Facebook.

NOTE
There is a whole library of custom policies located at the Azure Active Directory B2C: Custom CIAM User Journeys github repository. This repository contains a fantastic collection of custom policies that have been developed to address many common business requirements. Hopefully this series will help you understand the policies defined there and how to go about integrating them into your own solutions.

Create the AAD B2C Tenant

So, the first thing you need to do is to create an Azure AD B2C tenant and link it to your Azure subscription, using these instructions. Make sure you remember the name that you give your B2C tenant.

AAD B2C Resource
The AAD B2C tenant resource in the Azure portal

Open the AAD B2C tenant settings page in the Azure Portal and click Identity Experience Framework in the AAD B2C menu on the left.

IEF Menu Item
Select the Identity Experience Framework menu item

Build the Initial Policy

Let’s build and upload the simplest possible Custom Policy. Using your preferred XML editor (I am using Visual Studio for reasons that I will explain in the next post in this series), create a new XML file and that contains the following markup, substituting the name you gave your AAD B2C tenant for the two YOUR_TENANT_NAME entries (TenantId and PublicPolicyUri):

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<TrustFrameworkPolicy
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns="http://schemas.microsoft.com/online/cpim/schemas/2013/06"
  PolicySchemaVersion="0.3.0.0"
  TenantId="YOUR_TENANT_NAME.onmicrosoft.com"
  PolicyId="B2C_1A_Example"
  PublicPolicyUri="http://YOUR_TENANT_NAME.onmicrosoft.com/B2C_1A_Example">

</TrustFrameworkPolicy>

The root TrustFrameworkPolicy element indicates some meta-information about the XML you are using, the schema version for the policy file (0.3.0.0), the ID of the AAD B2C tenant that your policy file will reside in (YOUR_TENANT_NAME.onmicrosoft.com), the ID that will be used to refer to your policy (B2C_1A_Example), and the URI that will be used to reference this policy when it is deployed (http://YOUR_TENANT_NAME.onmicrosoft.com/B2C_1A_Example) – which is a concatenation of the TenantId and the PolicyId.

NOTE
I am not a fan of having to include the Tenant ID and Public Policy URI in the policy files. Having to do so complicates DevOps scenarios because policies need to be modified between environments with meta-information that the framework could (should) figure out for itself and/or track elsewhere.

You might notice that the Policy ID includes the peculiar prefix B2C_1A_. When you upload your policy file this will automatically be prepended to the Policy ID if it is not already present, and external references to your policy endpoints will be expected to use this now-prefixed ID. In order to avoid confusion resulting from mismatching the policy ID you specify and the one that B2C uses, it is general convention to just include the B2C_1A_ prefix yourself.

Upload the Initial Policy

Upload the file into your AAD B2C tenant by clicking the Upload custom policy button. Select the policy XML file and click Upload.

Upload custom policy
Upload a custom policy file

Notice that uploading the policy results in an error:

Validation failed: 1 validation error(s) found in policy “B2C_1A_EXAMPLE” of tenant “custompolicies.onmicrosoft.com”. A policy must have a technical profile “TpEngine_c3bd4fe2-1775-4013-b91d-35f16d377d13” with a metadata setting “url” that specifies the URL of the TPEngine service. Policy “B2C_1A_Example” in tenant “custompolicies.onmicrosoft.com” either does not contain such a technical profile or the technical profile does not contain the setting.

As you upload your policy files, AAD B2C will attempt to parse and validate their contents as best it can. If the XML in a policy can be considered a programming language, uploading the file can be considered to be similar to compiling the program.

Fix the Policy

Update the policy file by including the following elements inside of the TrustFrameworkPolicy element that you previously defined.

<ClaimsProviders>
  <ClaimsProvider>
    <!-- The technical profile(s) defined in this section is required by the framework to be included in all policies. -->
    <DisplayName>Trustframework Policy Engine TechnicalProfiles</DisplayName>
    <TechnicalProfiles>
      <TechnicalProfile Id="TpEngine_c3bd4fe2-1775-4013-b91d-35f16d377d13">
        <DisplayName>Trustframework Policy Engine Default Technical Profile</DisplayName>
        <Protocol Name="None" />
        <Metadata>
          <Item Key="url">{service:te}</Item>
        </Metadata>
      </TechnicalProfile>
    </TechnicalProfiles>
  </ClaimsProvider>
</ClaimsProviders>

This is an element that must be included in all IEF policies. (See my previous note about things that the framework should be figuring out for itself.) Don’t worry too much about the details of the TechnicalProfile element or its parent elements – they will be examined in more detail later in this series.

This seems to be the simplest possible policy file that can be successfully uploaded and parsed. Again considering the previous programming language comparison, this might be the equivalent of a C# .NET console application with just a static void Main(string[] args){} method, with no code within the method. We’re not quite at “Hello World” yet.

Upload the Fixed Policy

Upload the updated policy file. When the upload completes successfully, the policy ID will be listed in the Custom policies section within the Identity Experience Framework panel.

Uploaded Policy
The uploaded policy

If you click on the policy ID for this (basically) empty policy, the portal will display a panel that contains the following message:

A custom policy can be run from here only if the policy or any of its parent custom policies has a relying party section. The relying party section also needs to have OpenID Connect protocol and a default interactive user journey.

In short, there’s some more work to be done. In the next section of this series we will update our policy and get it to display the requisite “Hello World” message.