In this series of articles, we'll dive into questions like:
What was our experience with Azure Active Directory B2C identity as service?
Why we won’t be using it any time soon.
Issues with custom frontend and Microsoft flows for authentication.
Horrific complexity of custom policies in XML.
User unfriendly release of custom policies to Azure.
Developer Experience in AD B2C?
What about Microsoft’s developer tools, coding language, and general approach?
And now we get to the crème de la crème of this blog post: User Flows and Custom Policies are tools, systems, and frontend for users to sign up, sign in, or manage their profile –– like reset password or change information.
User Flows
User flows are very basic and barebones building blocks for creating user experience. You can follow Microsoft’s wizard to create a user flow, which works pretty well.
But the issues creep up when you realize you are pretty much stuck with Microsoft’s frontend design, which sometimes makes no sense or is not user-friendly. Therefore, our opinion is that it’s hard to make complex and custom User Flows. This means you can’t really use User Flows for production deployment. User Flows might only be valid for prototyping or for very simple solutions that require no exotic information from the user (like national identity numbers, driver’s license numbers, etc.).
You can choose from a few designs Microsoft made. You can customize your Azure AD B2C pages with a banner logo, background image, and background color by using Microsoft Entra ID Company Branding. But in the end, you don't have much of a choice:
To see how this looks for the end user, you can go to part 4 of this series.
Custom Policies
When your task is to create something more tangible, you have to use custom policies. Right off the bat, you are greeted by a confusing and unreadable XML file that contains… all basic Microsoft policies. This is a snippet from an almost 1300-lines-long file:
You are asked to use these files to create your custom policies:
TrustFrameworkBase.xml
TrustFrameworkExtensions.xml
TrustFrameworkLocalization.xml
You don’t necessarily need them, but developing your custom policy is next to impossible without Microsoft’s base code.
Afterward, you can start developing your custom policies. This is Microsoft’s example of password reset policy:
Not only are you asked to write the policy in XML, but you are also asked to know many custom and very specific XML tags that initially look confusing. Which, frankly, sucks even with sophisticated XML editors or VS Code extensions – fortunately, Microsoft provides Azure AD B2C extension for VS Code, which helps a lot. After a while, it boils down to UserJorneys
, TechnicalProfiles
, and a whole bunch of name configuration copy-pasting. Other tags are usually just reused when you create new policy or one-off settings that doesn’t need to change that much.
You can learn more about the XML tags in part 3 of this series.
How do you query and manage users created by this process, and how can you access custom data?
While the application is doing its thing, you might want to access the registered users into your Active Directory to help them with registration or get a statistic about usage. The problem is that accessing meaningful data from the Azure Portal – Active Directory is impossible. You can delete or create users in the active directory. You can also access or export basic data about the user. But nothing else. If you want to see your custom claims, you are out of luck.
The only meaningful way to do more complex tasks with your users is to use Microsoft Graph API. We can’t do MS Graph API justice here because it would need its own blog post, but it works splendidly and an immaculate architecture. It essentially allows queries similar to OData System Query Options like $filter or $select.
GET <https://graph.microsoft.com/v1.0/me/messages?filter=emailAddress> eq '[email protected]'
And it works very well for accessing AD B2C data. Unfortunately, it is a little constrained in some aspects. As of now, AD B2C does not support advanced query capabilities on directory objects. This means that there is no support for $count
, $search
query parametersm and Not (not
), Not equals (ne
), and Ends with (endsWith
) operators in $filter
query parameter. Currently, there exist two major versions of Microsoft Graph (v1.0 and beta), and we had to use the beta version because v1.0 didn’t allow some more complex filters.
Getting user data in .NET
Microsoft made excellent libraries for .NET that can help you contact Azure resources. The usage is very simple and straightforward:
One of the most significant drawbacks is that user claims/data from external applications (like when using Custom Policies) save their data into weird properties on the user objects, and they can look something like this:
"extension_1cdaac2e207c4dbf9e7d2d9666c2df76_phoneNumber": "123456789"
"extension_{registered application id}_phoneNumber"
This is okay if you use only one AD B2C, but we needed one for every environment. Therefore, the code had to be more complex since typical deserialization libraries wouldn’t work here.
Getting all users at once in .NET
Even though it’s not recommended, Microsoft built a way to get all users in the Active Directory with a straightforward query.
This simplifies exports quite a bit and is very useful when you need to perform an operation on the users that cannot be done via MS Graph API, like search by property that you need to parse before you check for equality (like you would do in LINQ). It’s reasonably fast.