Azure AD B2C Custom Policies (IEF) to Keycloak Flows: Mapping Guide

Guilliano Molaire Guilliano Molaire 12 min read

Last updated: September 2026

TL;DR

Azure AD B2C custom policies do not convert to Keycloak. There is no translator, no importer, and no XML-to-flow tool, because the two platforms model orchestration differently. What does exist is a reliable mapping: user journeys become Keycloak authentication flows, technical profiles become authenticators or identity providers, claims transformations become protocol mappers or a small amount of Java, and validation technical profiles that call REST APIs become custom Authenticator SPIs.

What to expect going in:

  • Most IEF policies are simpler than their XML suggests. A large TrustFramework file is often three real decisions buried in inheritance and boilerplate.
  • Keycloak flows are a tree, not an orchestration engine. Steps execute in order with Required, Alternative, and Conditional semantics. Most journeys fit; genuinely branchy ones need an SPI.
  • Claims transformations split three ways. Most become protocol mappers, some become identity provider mappers, and a few need code.
  • Script authenticators are deprecated. Write a Java Authenticator SPI. It is more work up front and considerably less work to maintain than IEF XML.
  • Budget the mapping, not the import. User import is fast. Policy mapping is the project.

This post is the deep dive on the single hardest artifact in a B2C migration. For the end-to-end walkthrough, see the canonical guide: Migrating From Azure AD B2C to Keycloak.

Why there is no automated conversion

Azure AD B2C’s Identity Experience Framework is a declarative orchestration engine. You describe claims, claims providers, technical profiles, and user journeys in TrustFramework XML, and the engine executes the journey step by step, evaluating preconditions and passing a claims bag between steps.

Keycloak models authentication as an ordered tree of executions inside a flow. Each execution is an authenticator with a requirement setting, and sub-flows group related steps. There is no claims bag traversing the journey and no precondition language. Context lives in the authentication session, and branching is expressed through conditional sub-flows or code.

Those are different abstractions, which is why nobody has shipped a converter. The good news is that the mapping is consistent once you know it, and most tenants use a small fraction of what IEF can express.

The concept mapping table

Your translation dictionary. Print it, put it next to the inventory.

Azure AD B2C (IEF) Keycloak equivalent Notes
Trust Framework Base / Extensions / Relying Party Realm configuration plus authentication flows Keycloak has no inheritance chain; flatten before you map
User journey Authentication flow One journey per flow, bound in Authentication > Flows
Orchestration step Execution within a flow Order matters; requirement setting controls branching
Precondition (ClaimsExist, ClaimEquals) Conditional sub-flow Condition – User Attribute, Condition – User Role, or a custom condition
Self-asserted technical profile Registration form or Update Profile required action Attribute-level config lives in User Profile
Azure AD technical profile (read/write user) Built-in user storage Keycloak owns the user store; no explicit read/write step
OAuth2 / OIDC technical profile Identity provider (OpenID Connect v1.0) Configure under Identity Providers
SAML technical profile Identity provider (SAML v2.0) Metadata import supported
Validation technical profile calling REST Custom Authenticator SPI The main reason teams write Java
RESTful technical profile (claims enrichment) Custom Authenticator SPI or protocol mapper Depends on whether it runs at login time or token time
Claims transformation Protocol mapper, IdP mapper, or SPI Most are mappers; string manipulation may need code
Output claims Protocol mapper on the client scope Per-claim configuration
Content definition (page customization) Keycloak theme FreeMarker templates or Keycloakify
Relying party policy Keycloak client plus flow binding One client per B2C application
Policy in the endpoint URL (?p=B2C_1_...) Single realm discovery endpoint Structural change; see below

The endpoint change your app teams need to know about

B2C encodes the policy in the authority URL. Keycloak does not. One realm has one discovery endpoint, and flow selection is a client-side configuration concern rather than part of the URL.

B2C:      https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/v2.0/.well-known/openid-configuration?p=B2C_1A_signup_signin
Keycloak: https://{host}/realms/{realm}/.well-known/openid-configuration

Any application that constructs different authority URLs per policy needs code changes, not just config changes. Find those early. They are usually the longest-lead item in the whole migration and they are easy to miss during inventory because they look like configuration.

Step 1: Flatten the policy set

IEF policies inherit. A relying party policy extends an extensions policy which extends a base policy, and the effective behavior is the merge of all three. You cannot map what you cannot see, so flatten first.

For each relying party policy, produce a single document listing:

  • The user journey it invokes, with every orchestration step in order.
  • Every technical profile referenced by those steps, with its input claims, output claims, and validation profiles.
  • Every claims transformation invoked, with input and output claims.
  • Every precondition, with the claim it tests and the step it skips to.
  • The output claims in the relying party section, which define your token shape.

This is tedious and it is the highest-value hour of the migration. Teams that skip it discover missing behavior during cutover, which is the worst possible time.

A useful sanity check: count the orchestration steps that do something other than read or write the directory or emit a token. That count, not the XML line count, is the size of your project.

Step 2: Map user journeys to authentication flows

In Keycloak, go to Authentication > Flows and create a new flow per B2C user journey. Add executions in the order the journey’s orchestration steps run.

Requirement semantics

Keycloak gives each execution a requirement setting, and this is where journey branching gets expressed:

  • Required runs and must succeed. The direct equivalent of an unconditional orchestration step.
  • Alternative offers a choice among siblings. This is how you express “log in locally or with a social provider,” which in B2C is a combined sign-in technical profile.
  • Conditional runs a sub-flow only when its conditions pass. This is your precondition equivalent.
  • Disabled is off.

The standard sign-up / sign-in journey

The most common B2C journey maps almost mechanically:

B2C orchestration step Keycloak execution
Show combined sign-in page with IdP buttons Browser flow: Identity Provider Redirector (Alternative) plus Username Password Form (Alternative)
Federate to social IdP Configured identity provider, reached via the redirector
Read user from directory by objectId or email Implicit; Keycloak resolves the user
Self-asserted sign-up form Registration flow with User Profile attributes
Email verification Verify Email required action
MFA challenge OTP Form or WebAuthn Authenticator in a conditional sub-flow
Issue token with output claims Protocol mappers on the client scope

Conditional MFA, the most common branch

B2C tenants frequently have a precondition like “require MFA only when the user has a phone number” or “only for a particular application.” In Keycloak:

  1. Add a sub-flow to your browser flow and set it to Conditional.
  2. Inside it, add a condition execution (Condition – User Attribute, Condition – User Role, or Condition – User Configured).
  3. Add the OTP or WebAuthn authenticator below the condition, set to Required.

The sub-flow runs only when the condition passes. For conditions Keycloak does not ship (risk score from an external service, customer tier from your billing system), implement a ConditionalAuthenticator rather than bending a built-in condition into something it is not.

Step 3: Map technical profiles

Technical profiles are the workhorses of IEF, and they fall into four buckets. Sort yours before you map anything, because the buckets have wildly different costs.

Bucket 1: Directory read and write (no work)

Azure AD technical profiles that read or write the user object have no Keycloak equivalent because Keycloak simply owns its user store. AAD-UserWriteUsingAlternativeSecurityId, AAD-UserReadUsingObjectId, and friends disappear from your flow entirely. This is usually the largest bucket and it costs nothing.

Bucket 2: Self-asserted forms (configuration)

Self-asserted technical profiles collect input from the user. In Keycloak these become the registration form or the Update Profile required action, and the field-level configuration lives in Realm Settings > User Profile.

For each B2C input claim, define the attribute in User Profile with its display name, required flag, permissions, and validators. Keycloak’s declarative user profile covers most of what B2C’s input claim configuration expressed, including regex validation and length constraints.

Custom attributes in B2C carry the extension_{appId}_{name} naming convention. Drop the prefix when you define them in Keycloak, and keep a mapping table so your data transformation script knows which source field feeds which target attribute.

Bucket 3: Federation (configuration)

OIDC and SAML technical profiles become Keycloak identity providers, one per external IdP.

  • OIDC: Identity Providers > OpenID Connect v1.0. Supply the discovery URL, client ID, and secret. Keycloak fills in the endpoints.
  • SAML: Identity Providers > SAML v2.0, then import the metadata XML or URL.
  • Claims mapping: Identity Providers > {provider} > Mappers, using Attribute Importer for incoming assertions or claims.

B2C output claims transformations that ran inside the technical profile become IdP mappers here. The pattern is close enough that this bucket is mostly mechanical translation work.

For a walkthrough, see Federated SSO vs a Single IdP and the Skycloak identity providers documentation. If you are federating Microsoft Entra ID, How to Set Entra ID SAML in Keycloak as an IdP covers that specific case.

Bucket 4: Validation and REST calls (code)

This is the bucket that costs money. Validation technical profiles and RESTful technical profiles call an external API mid-journey, then feed the response back into the claims bag. Typical uses: check an entitlement service before allowing sign-up, enrich the token with a customer tier from your CRM, validate an invitation code, or run a fraud check.

In Keycloak, a mid-login external call is a custom Authenticator SPI. Implement org.keycloak.authentication.Authenticator, package it as a JAR, and drop it into the providers/ directory.

The skeleton, so the shape is concrete:

public class EntitlementCheckAuthenticator implements Authenticator {

    @Override
    public void authenticate(AuthenticationFlowContext context) {
        UserModel user = context.getUser();
        EntitlementResponse response = entitlementClient.check(user.getEmail());

        if (!response.isAllowed()) {
            context.failure(AuthenticationFlowError.ACCESS_DENIED);
            return;
        }

        user.setSingleAttribute("customer_tier", response.getTier());
        context.success();
    }

    @Override
    public boolean requiresUser() {
        return true;
    }
}

Two points that save pain later. First, script authenticators are deprecated in current Keycloak versions, so do not plan around them. Second, anything that calls an external service in the login path needs a timeout and a documented failure mode. Decide explicitly whether a down entitlement service fails the login or lets it through, because IEF made that decision for you and Keycloak will not.

If the enrichment only needs to happen at token issuance rather than during authentication, you may not need an authenticator at all. A protocol mapper is the cheaper answer.

Step 4: Map claims transformations

B2C claims transformations manipulate the claims bag: concatenate strings, compare booleans, convert case, generate GUIDs, format dates. Sort yours into three groups.

Transformation type Keycloak equivalent Effort
Copy a directory value into a token claim User Attribute protocol mapper Configuration
Rename or remap an incoming federated claim Identity provider Attribute Importer mapper Configuration
Static or computed value in the token Hardcoded Claim mapper, or Script mapper where enabled Configuration
String manipulation, conditionals, formatting Custom protocol mapper (Java) Small code
Multi-claim logic feeding a journey decision Custom Authenticator SPI Code

Most tenants find the majority of their transformations land in the first two rows. The tail is small, and the tail is where the Java goes.

For token-time claims, the path is Clients > {client} > Client scopes > {client}-dedicated > Add mapper > By configuration. The User Attribute mapper covers the common case: attribute in, claim out, with control over whether it appears in the ID token, access token, or userinfo response.

Step 5: Rebuild the UI layer

B2C content definitions and custom HTML templates become Keycloak themes. You have two options:

  • FreeMarker themes, the built-in Keycloak mechanism. Extend the base theme and override the templates you need.
  • Keycloakify, which lets you build login pages as a React application. For teams whose B2C pages were already heavily customized front-end work, this is usually the faster route. See Customizing Keycloak Themes with Keycloakify in Skycloak.

Budget real time here if your B2C login was pixel-matched to your product. It is front-end work, it is visible to every customer, and it tends to get scheduled last and discovered late.

A worked example

Take a moderately common B2C journey: sign-up and sign-in with local accounts plus Google, requiring email verification, calling an invitation-code API during sign-up, and emitting a subscription_tier claim.

Journey element Keycloak implementation Bucket
Combined sign-in page Browser flow with Identity Provider Redirector and Username Password Form as Alternatives Configuration
Google federation Identity provider (Google) with attribute mappers Configuration
Local sign-up form Registration flow plus User Profile attributes Configuration
Email verification Verify Email required action Configuration
Invitation code validation (REST) Custom Authenticator SPI in the registration flow Code
Read subscriptionTier from directory Already a user attribute None
Emit subscription_tier claim User Attribute protocol mapper Configuration
Branded login page Keycloak theme Front-end

One Java class. Everything else is configuration or theming. That ratio is typical, and it is why the flattening exercise in Step 1 pays for itself: it tells you whether you are staring at one authenticator or eight.

Frequently asked questions

Is there a tool that converts B2C custom policies to Keycloak?

No. The two platforms model authentication differently (declarative orchestration with a claims bag versus an ordered execution tree), so no automated converter exists or is likely to. The mapping is manual but consistent, and the table above covers the constructs most tenants actually use.

How long does IEF policy mapping take?

It scales with the number of orchestration steps that do real work, not with XML size. A journey with federation, a self-asserted form, and conditional MFA is typically a few days of configuration. Each validation technical profile that calls an external API adds a custom authenticator, which is a development and testing task rather than a configuration one. Tenants with a dozen or more REST-calling profiles should plan for months rather than weeks.

Can I use script authenticators instead of writing Java?

Script authenticators are deprecated in current Keycloak versions and should not be the foundation of a migration. Write a proper Authenticator SPI. It is a JAR in the providers/ directory, it is testable, and it survives upgrades.

What happens to B2C policy-specific endpoint URLs?

They do not carry over. B2C encodes the policy in the discovery URL; Keycloak has one discovery endpoint per realm and resolves flows through client configuration. Applications that build authority URLs per policy require code changes. Audit for this pattern during inventory rather than during cutover.

Do I need one Keycloak realm per B2C policy?

No. A realm is the equivalent of a tenant, not of a policy. Multiple flows live inside one realm. Use separate realms only for hard isolation between user populations, and consider the Organizations feature for per-customer B2B federation instead. Today that pattern uses realm-level identity providers linked to each Organization; a dedicated per-organization IdP dashboard is still a gap (tracked in GitHub issue #1355). See Multitenancy in Keycloak Using the Organizations Feature.

Does Entra External ID import custom policies?

No. IEF TrustFramework XML does not lift and shift into Entra External ID either. The rebuild cost applies on both exit paths, which is why custom policy complexity rarely decides between destinations. Part 2 covers that comparison: Azure AD B2C vs Entra External ID vs Keycloak.

Where to go next

Once your flows are mapped and your authenticators are written, the remaining risk is entirely in the cutover: password migration, application waves, and rollback. That is Part 4.

If you want somewhere to build and test these flows without standing up a cluster first, Skycloak runs upstream Keycloak with a 7-day free trial and no credit card. Custom Authenticator SPIs are ordinary Keycloak provider JARs; on Skycloak, uploading your own JAR is an Enterprise Custom Extensions capability (the extension marketplace is available on all plans). Validate the authenticator against a real instance before deciding where it eventually runs.

Azure AD B2C exit series

This post is Part 3 of a five-part series on leaving Azure AD B2C.

  1. Azure AD B2C End of Sale and Support Timeline (What Microsoft Actually Said)
  2. Azure AD B2C vs Entra External ID vs Keycloak: Three Exit Paths
  3. Azure AD B2C Custom Policies (IEF) to Keycloak Flows: Mapping Guide (this post)
  4. Dual-Run Cutover from Azure AD B2C: Passwords, Apps, and Rollback
  5. Skycloak Shorter Path: Managed Upstream Keycloak for Azure AD B2C Teams

For the step-by-step technical walkthrough, see Migrating From Azure AD B2C to Keycloak.

Migrating onto managed Keycloak

Skycloak imports your existing realms, users and clients in Keycloak's own format, because it runs real upstream Keycloak rather than a fork. You drive the import and our team verifies the first realm before it carries live traffic.

Guilliano Molaire
Written by
Founder

Guilliano is the founder of Skycloak and a cloud infrastructure specialist with deep expertise in product development and scaling SaaS products. He discovered Keycloak while consulting on enterprise IAM and built Skycloak to make managed Keycloak accessible to teams of every size.

Start Free Trial Talk to Sales
© 2026 Skycloak. All Rights Reserved. Design by Yasser Soliman