Azure Active Directory (AAD) Business-to-Customer (B2C) provides identity as a service for custom applications. It’s built on the same underlying technology as AAD, with additional critical features required for customer-facing applications, including standard identity management operations such as signing up for an application, signing in, resetting a password, and updating basic user profile information.
B2C enables application developers to manage local logons and use popular identity providers such as Microsoft, Facebook, and Google. B2C allows the developer to have as much control as necessary over the end user’s experience when they sign up or sign in to their application. These experiences are defined by user flows or custom policies.
User flows are templates provided by Microsoft that you can configure in the Azure portal to implement the identity operations mentioned above. Custom policies provide much deeper customization of these operations.
User Flows or Custom Policies
In general, if you need basic B2C functionality along with the need to customize the UI, you can safely go with standard user flows. If you know your application’s integration with B2C will be very complex and will require a lot of customization, you’ll want to use custom policies. Microsoft is continually working to provide the most requested features in user flows that were previously only available in custom polices.
User flows have become quite powerful and offer enough flexibility for many applications. I would caution developers to not immediately decide to use custom policies simply because they give you more control and influence over the user experience. You can fully customize the UI of user flows with your company’s branding. User flows also allow you to collect and store custom attributes for users. You can call REST APIs to validate information that a user provided when signing up. User flows also enable you to let users log on with other identity providers such as Facebook and Google.
Custom policies are XML configuration files built on the Identity Experience Framework (IEF). Whereas user flow templates are predefined in the Azure AD B2C portal for the most common identity management operations, custom policies can be created and updated by an identity developer to complete a wide variety of tasks. Custom policies allow you to federate with any identity provider that supports OpenID Connect (OIDC) or Security Assertion Markup Language (SAML). They can support multi-factor authentication (MFA) with both Microsoft and third-party providers. Custom policies also allow you to call REST APIs anywhere in the user experience and they support the ability to leverage OAuth bearer tokens to authenticate a REST API, whereas user flows only support basic and certificate authentication for REST APIs.
Organizing Custom Policies
After choosing to use custom policies, it’s important to understand the different types of policies and the overall structure of how they relate to one another.
Custom Policies Hierarchy
Custom policies are composed in a hierarchical system. Child policies inherit from parent policies. B2C elements in child policies override existing elements in the base or are added as new elements. This is like the idea of inheritance in object-oriented programming. You inherit from a base class to leverage all its features and then override the methods that are unique to your requirements.
It’s highly recommended to use Microsoft’s B2C Starter Packs on GitHub as a starting point for your solution. The default hierarchy in the local accounts starter pack can be seen in the diagram below:
The base of the hierarchy is the B2C_1A_TrustFrameworkBase. The B2C_1A_TrustFrameworkExtensions policy inherits from the base. The SignUpSignIn, ProfileEdit, and PasswordReset relying party policies inherit from the extensions policy.
The hierarchy is specified in a parent policy XML tag. The following is an example of the BasePolicy element in my TrustFrameworkExtensions policy:
<BasePolicy>
<TenantId>andylandexternal.onmicrosoft.com</TenantId>
<PolicyId>B2C_1A_TrustFrameworkBase</PolicyId>
</BasePolicy>
Code language: HTML, XML (xml)
Using the BasePolicy element in policies, it’s possible to chain together as many policies as you like.
Base Policy
The base policy provided by Microsoft in the starter packs contain the basic elements required to get up and running with B2C, including:
- Claim Type Definitions: Claims provide temporary storage of data when a B2C policy is executed. They are used as variables in custom policies.
- Claim Transformations: A claim transformation can make a change to a claim value, such as changing a string to all uppercase.
- Claims Providers: A claims provider is a collection of technical profiles.
- Technical Profiles: A technical profile provides a framework with built-in mechanisms to interface with different systems. A technical profile can be used to read and write to the Azure AD B2C directory. Another technical profile can be used to make calls to a REST API.
- Content Definitions: A content definition is used to specify the URLs of HTML templates that are used in a user journey. You can use content definitions to customize the UI of your user journeys.
- User Journeys: User journeys specify the steps that a user goes through when a custom policy is called.
The base policy should rarely, if ever, be modified. If this policy is modified, comments should be added so it is clear why the changes were made. Microsoft releases new base policies on a regular basis; fewer customizations made to the base will make it easier to swap out the base and be able to take advantage of new features.
Extensions Policy
The extensions policy is where an identity developer should make customizations and override the base policy. The extensions policy inherits from the base policy. The extensions policy in the starter pack has no B2C elements. It only has placeholders where you can add your customizations and overrides to the elements in the base policy. Depending on the complexity and number of customizations being made, multiple extensions policies can be used.
Relying Party Policies
An application interfaces with B2C by calling a relying party policy’s endpoint. Examples of relying party policies from the starter packs are SignUpSignIn, ProfileEdit, and PasswordReset. An application would redirect to the SignUpSignIn policy when a user chooses to sign up for an account. An example endpoint for sign up and sign in is:
Relying party policies are usually quite small. They simply call a user journey and specify what claims should be provided in the JSON Web Token (JWT) that gets sent back to the calling application. These relying party policies are configured to inherit from the extensions policy mentioned above.
Here is an example of a SignUpSignIn relying party policy.
<RelyingParty>
<DefaultUserJourney ReferenceId="SignUpOrSignIn" />
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="OpenIdConnect" />
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surname" />
<OutputClaim ClaimTypeReferenceId="email" />
<OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub"/>
<OutputClaim ClaimTypeReferenceId="tenantId" AlwaysUseDefaultValue="true" DefaultValue="{Policy:TenantObjectId}" />
</OutputClaims>
<SubjectNamingInfo ClaimType="sub" />
</TechnicalProfile>
</RelyingParty>
Code language: HTML, XML (xml)
Expanding the Extensions Policy
The extensions policy is where most customizations will be made. These customizations can be leveraged by all relying party policies to provide consistency. Customizations enable developers to adhere to the Don’t Repeat Yourself (DRY) programming principle. Customizations can also be made at the relying party policy level, but they will only affect that policy.
If a solution includes a lot of customizations and the extensions policy is growing in unmanageable complexity, you’ll want to expand the extensions policy into two or more policies. Multiple types of elements can be added to the extensions policy. As you build out your custom extensions, it’s important to note which elements depend on which. For example, if you have a technical profile that uses a custom claim, that custom claim needs to be defined in a parent policy.
Below is an example of a profile hierarchy with multiple extensions policies. The extensions policies are named according to what types of elements they contain.
Adding and Overriding Elements
The following section assumes the reader is familiar with the basic elements and structure of custom policies. Microsoft has a good overview of the different elements that can be used in custom policies.
Custom Claims
Claims are the most fundamental element in a B2C policy. Using the example hierarchy above, custom claims should be defined in the ClaimsProviderExtension policy. A basic claim can be created using the ClaimType element.
<ClaimType Id="shoeSize">
<DisplayName>Shoe Size</DisplayName>
<DataType>Int</DataType>
</ClaimType>
Code language: HTML, XML (xml)
Along with the claims, there may be predicates and input validations for something like a password claim. It’s reasonable to put these elements in the same file as the claims they are being used by. The point of using multiple extension files is so you’ll know where to add or modify different types of elements.
Claims Providers and Technical Profiles
A very common task when using custom policies is overriding existing technical profiles. One such example is to be able to read more user attributes from B2C than what you get by default. This can be accomplished by adding more output claims to the AAD-UserReadUsingObjectID technical profile. It’s very tempting to simply edit this technical profile in the base policy; however, it’s best to make these types of modifications in the extensions policy, specifically in the ClaimsProviderExtensionPolicy in the hierarchy above. It isn’t necessary to copy the entire technical profile to your extensions policy. You only need to specify the TechnicalProfile ID, and then you can add your customizations to it. To add a new output claim, such as city, to this technical profile, you would add a snippet like the following to the ClaimsProviderExtensionPolicy.
<ClaimsProvider>
<DisplayName>Azure Active Directory</DisplayName>
<TechnicalProfiles>
<TechnicalProfile Id="AAD-UserReadUsingObjectId">
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="city" />
</OutputClaims>
</TechnicalProfile>
</TechnicalProfiles>
</ClaimsProvider>
Code language: HTML, XML (xml)
User Journeys
When using custom policies, you’ll most likely be overriding existing user journeys or writing new ones from scratch. Using the custom hierarchy above, these customizations should be added to the User Journeys extension. This can be especially useful if some of the sub-journeys are used by multiple relying party policies.
Conclusion
Azure AD B2C offers a lot of flexibility and opportunities for authentication as a service. Knowing and understanding the real-world trade-offs between using out-of-the-box user flows and custom policies can be a huge advantage as you embark on a B2C project. Ravenswood Technology Group can help design and build a robust B2C platform for your application. Contact our team to get started.