Migrating From Azure AD B2C to Keycloak

Guilliano Molaire Guilliano Molaire 11 min read

Last updated: September 2026

Migrating from Azure AD B2C to Keycloak means exporting your B2C users via the Microsoft Graph API, recreating your B2C user flows and custom policies as Keycloak authentication flows and identity providers, importing users into a Keycloak realm, and repointing your apps to Keycloak’s OIDC endpoints. The main constraint, as with most IdP migrations, is password hashes — Azure AD B2C does not export them, so plan either a lazy migration (validate against B2C on first login, then store credentials in Keycloak) or a forced password reset. Factor that decision into your rollout timeline early.

Microsoft has begun steering Azure AD B2C customers toward Entra External ID, its next-generation CIAM platform. The service is closed to new customers, and Microsoft is communicating a longer-term end-of-support path. Check current Microsoft timelines at aka.ms/b2c-eosp for the most authoritative dates, as the specifics continue to evolve.

For teams already running on B2C, this creates a concrete decision: migrate to Entra External ID (staying within the Microsoft ecosystem but facing its own pricing and lock-in concerns) or migrate to an open-standards identity platform like Keycloak. This guide focuses on the second path.

Why Teams Move From Azure AD B2C to Keycloak

Common motivations: vendor lock-in (B2C IEF/TrustFramework XML is proprietary; teams want portable standards-based flows); cost at scale (B2C MAU pricing compounds quickly; Keycloak’s open-source model removes the per-MAU charge); customization depth (Keycloak’s SPI system gives Java developers a familiar extension model); data residency (self-hosting gives full control over where user data lives); and sunset pressure (Microsoft’s direction has accelerated timelines for teams planning a long stay on B2C).

The structural patterns for this migration parallel other IdP moves; see the Okta to Keycloak migration guide and the Keycloak vs Okta enterprise IAM comparison for reference. B2C adds two specific wrinkles: custom policies with no direct Keycloak equivalent, and the password hash problem covered below.

Phase 1: Inventory Your Azure AD B2C Tenant

Before touching Keycloak, document everything in your B2C tenant. A migration you start without a complete inventory will surface surprises during cutover.

Users, Custom Attributes, and Flows

List your user count and custom attribute schema. B2C stores custom attributes as extension attributes (typically extension_{appId}_{attributeName}); you will recreate these as Keycloak user profile attributes. The Microsoft Graph API documentation for B2C users covers the full set of queryable properties — pay attention to the identities field, which holds local account (email/username) and federated identity entries.

Enumerate every user flow and custom policy in your tenant:

Artifact B2C Location What to Document
Built-in user flows Azure Portal > User flows Flow type (sign-up/sign-in, profile edit, password reset), claims issued, MFA settings
Custom policies (IEF) Azure Portal > Identity Experience Framework All XML files in the TrustFramework, technical profiles, claims providers
Identity providers Azure Portal > Identity providers Social IdPs (Google, Facebook, etc.), enterprise IdPs (SAML/OIDC federation)
App registrations Azure Portal > App registrations Client IDs, redirect URIs, scopes, token lifetimes

Custom policies are the hardest part of a B2C inventory. If your tenant uses IEF (Identity Experience Framework) XML policies, plan significant mapping work — there is no automated translation from IEF to Keycloak flows.

Document every API permission your apps request from B2C. These will become Keycloak client scopes and resource server configurations.

Phase 2: Map B2C Concepts to Keycloak

The table below maps B2C terminology to Keycloak equivalents. This is your translation dictionary for the rest of the migration.

Azure AD B2C Concept Keycloak Equivalent Notes
Tenant Realm One realm per B2C tenant is the standard starting point
User flow (built-in) Authentication flow Recreate in Keycloak Admin Console > Authentication
Custom policy (IEF) Authentication flow + Authenticator SPI Complex policies may need a custom Java authenticator
Claims provider (social IdP) Identity provider (social) Keycloak has built-in Google, Facebook, GitHub, etc.
Claims provider (enterprise) Identity provider (OIDC/SAML) Configure as identity brokering in Keycloak
App registration Keycloak client One client per app registration
Custom attributes User profile attributes Defined in Realm Settings > User Profile
Token customization (claims) Protocol mapper Mapper per claim transformation
User journey Authentication flow steps Map each orchestration step to a Keycloak flow step
Password reset flow Required action (Update Password) Built into Keycloak; triggered by flow or admin API
MFA with SMS/email OTP Authentication flow with OTP authenticator Keycloak TOTP/email OTP, or custom SPI for SMS
Conditional access Conditional flow steps or custom authenticator Implement via Keycloak’s conditional authentication
B2C OIDC endpoint /realms/{realm}/.well-known/openid-configuration All Keycloak endpoints follow this base path

User Flow to Authentication Flow Mapping

Keycloak’s built-in flows cover the standard B2C user flow types: sign-up/sign-in becomes a browser flow with registration enabled (Realm Settings > Login > User registration: On); profile editing is handled by the Account Console at /realms/{realm}/account; password reset uses the built-in Reset credentials flow; and MFA is added as an OTP form or WebAuthn step in the browser flow.

For custom policies that orchestrate complex multi-step journeys (step-up auth, progressive profiling, external API enrichment), the correct Keycloak path is a custom Java Authenticator SPI — write a class implementing Authenticator, package it as a JAR, and deploy to Keycloak’s providers/ directory. Script authenticators are deprecated in current Keycloak versions. Most teams find the SPI model cleaner to maintain than IEF XML despite the initial investment.

Phase 3: Export Users From Azure AD B2C

This is where most migration projects spend the most time. Microsoft Graph provides comprehensive user export; the challenge is transforming the output into a format Keycloak can import.

Export via Microsoft Graph

Microsoft Graph’s /users endpoint returns all directory users. Page through large tenants using $top with the @odata.nextLink continuation token. For custom attributes, include the extension attribute names explicitly in $select — they follow the pattern extension_{appId}_{attributeName} where {appId} is the object ID of the b2c-extensions-app in App registrations:

az rest 
  --method GET 
  --url "https://graph.microsoft.com/v1.0/users?$select=id,displayName,givenName,surname,mail,identities,extension_abc123_subscriptionTier&$top=999"

Use a service principal with User.Read.All or Directory.Read.All permission for production exports rather than a user credential.

The Password Hash Problem

Azure AD B2C does not export password hashes. This is a hard constraint, not a configuration option. The hashes are stored in Microsoft’s directory infrastructure and are not accessible via any API.

Your options are:

Option A: Lazy (just-in-time) migration
Keep B2C running in parallel. On first login to Keycloak, a custom authenticator validates the credentials against B2C (via its OIDC token endpoint or ROPC grant if enabled). On success, Keycloak sets the user’s password locally and subsequent logins use Keycloak directly. This is the lowest-disruption approach and the one most migration teams choose.

Option B: Forced password reset
Import users without passwords. Set the UPDATE_PASSWORD required action on all imported accounts. On first login, users are prompted to set a new password. Simple to implement; higher user-visible friction.

Option C: Use B2C as a temporary identity provider
Configure B2C as an OIDC identity provider in Keycloak. Users who log in are federated through B2C until they have authenticated at least once in Keycloak. After a defined window, retire the B2C IdP. This avoids writing custom authenticators but requires keeping B2C operational longer.

Most teams with a large user base choose Option A. See the Okta migration guide’s lazy migration pattern for a reference implementation — the authenticator logic is nearly identical regardless of the source IdP.

Transform the Export for Keycloak Import

Write a script to transform the Graph API output into Keycloak’s partial import JSON shape: a top-level users array where each object has username, email, firstName, lastName, enabled, emailVerified, an attributes map, and a requiredActions array. Store the B2C object ID as a user attribute (b2c_object_id) — useful for cross-referencing during the transition and for any external system that references B2C user identifiers.

Phase 4: Configure Keycloak

Realm Setup

Create a dedicated realm for your migrated users. For most B2C tenants, a single realm is the right structure. B2B SaaS products where each customer had their own B2C user flow should consider Keycloak’s Organizations feature for per-customer isolation — the Keycloak Organizations guide covers that pattern.

In Realm Settings, configure login options (user registration, forgot password, remember me), SMTP for email verification and password reset, token lifetimes to match B2C, and the user profile schema.

For each B2C extension attribute, add a corresponding attribute under Realm Settings > User Profile in Keycloak. Use a clean name — drop the extension_ prefix.

For each B2C app registration, create a Keycloak client:

Clients > Create client
- Client type: OpenID Connect
- Client ID: (match or replace the B2C application ID)
- Client authentication: On (for confidential clients), Off (for SPAs/public clients)
- Valid redirect URIs: (copy from B2C app registration)
- Web origins: (add CORS origins for browser-based clients)

For the OIDC well-known endpoint, point your apps to:

https://{keycloak-host}/realms/{realm-name}/.well-known/openid-configuration

This replaces the B2C endpoint format of:

https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/v2.0/.well-known/openid-configuration?p={policy}

Note the structural difference: B2C encodes the user flow/policy in the endpoint URL. Keycloak does not — each realm has a single OIDC discovery endpoint, and flow selection happens at the client configuration level, not the URL level.

Recreate Identity Providers

For each social or enterprise IdP in B2C, configure the equivalent Keycloak identity provider under Identity Providers in your realm. Social providers (Google, Facebook, GitHub) are built in — select the type, enter the client ID and secret. Enterprise IdPs use Add provider > OpenID Connect v1.0 or SAML v2.0 and accept metadata XML import. Claims transformations from B2C’s technical profiles become identity provider mappers (Identity Providers > {provider} > Mappers).

For a SAML brokering walkthrough, see configuring Keycloak as a SAML service provider.

Protocol Mappers for Custom Claims

B2C’s token customization maps to Keycloak protocol mappers. Navigate to Clients > {client} > Client scopes > {client}-dedicated > Add mapper > By configuration. The User Attribute mapper type covers most B2C claims transformations — it maps a user profile attribute directly to a token claim.

Phase 5: Import Users

Keycloak Partial Import

For user sets under roughly 50,000, use Keycloak’s built-in partial import: Realm Settings > Action > Partial import. Upload the transformed JSON and choose Fail or Skip on existing records depending on whether this is an initial load or a refresh run.

Bulk Import via Admin API

For larger datasets, use POST /admin/realms/{realm}/users in the Keycloak Admin REST API. First obtain an admin token via POST /realms/master/protocol/openid-connect/token with admin-cli client credentials, then stream user objects using the same JSON shape from the partial import. Batch the calls and add retry logic — the Admin API rate-limits under high load. A throughput of 100-200 users/second is typical for most Keycloak deployments.

Phase 6: Phased Cutover

A hard cutover (flip all apps at once, decommission B2C immediately) is high risk. Run in phases:

  1. Weeks 1-2 — Keycloak running with users imported; all apps still point to B2C; smoke-test every flow against Keycloak with test accounts
  2. Weeks 3-4 — Cut internal/employee-facing apps over first; lower blast radius, real feedback from a controlled audience
  3. Weeks 5-6 — Migrate a segment of customer-facing apps; monitor error rates and support tickets; run the lazy migration for password validation
  4. Week 7+ — Full cutover; keep B2C available as the lazy-migration validation endpoint until the hit rate drops below your threshold (typically 60-90 days)
  5. Decommission — Disable the B2C validation authenticator; archive tenant config; remove app registrations and custom policies

For rollback, store the B2C authority, client_id, and policy name in your deployment config rather than hardcoding them — reverting any individual app is then a config change, not a code deploy. Keep the B2C tenant active (do not delete users) until the migration window closes.

For SSO implementation patterns across your migrated apps, the SSO implementation guide for developers covers the OIDC configuration side in detail.

Validating the Migration

Before each cutover step, verify:

  • Local account sign-in (email + password) completes end-to-end
  • Sign-up populates all expected custom attributes
  • Password reset email is delivered and the flow completes
  • Each social IdP federates and maps attributes correctly
  • Each enterprise IdP (SAML, OIDC) federates; groups and roles map as expected
  • Access tokens contain all expected custom claims with correct lifetimes
  • Single logout clears both the Keycloak session and the app session

For B2B scenarios with per-customer IdP federation, the multitenancy Organizations pattern is particularly relevant if you have isolated per-customer user flows.

Frequently Asked Questions

Can I export password hashes from Azure AD B2C?

No. Azure AD B2C does not expose password hashes through any API, including Microsoft Graph. This is a deliberate security boundary. Your options are a lazy just-in-time migration (validate credentials against B2C on first login, then store them in Keycloak), a forced password reset for all users, or routing users through B2C as a temporary Keycloak identity provider until they have authenticated at least once.

Is Azure AD B2C being discontinued?

Microsoft has closed Azure AD B2C to new customers and is steering existing tenants toward Entra External ID. The product is in maintenance mode and not receiving new features. Existing tenants continue to operate, but Microsoft has communicated an end-of-support timeline — check aka.ms/b2c-eosp for current dates, as specifics continue to be updated.

How long does an Azure AD B2C to Keycloak migration take?

It depends on complexity. A simple tenant with one or two user flows and no custom policies can be migrated and cut over in three to four weeks. A complex tenant with IEF custom policies, multiple enterprise IdP federations, and custom claims transformations typically takes two to four months. The user import is fast; the time goes into mapping flows, validating apps, and running the parallel-operation window.

Do Keycloak’s OIDC endpoints replace B2C policy-specific endpoints one-for-one?

Not directly. B2C encodes the user flow in the discovery endpoint URL (?p=B2C_1_signup_signin). Keycloak uses a single well-known endpoint per realm (/realms/{realm}/.well-known/openid-configuration). Apps that construct different authority URLs per B2C policy will need updating to use a single Keycloak authority; flow differences are handled at the client configuration level rather than in the URL.

Can I use Keycloak’s Organizations feature to replicate B2C’s multi-policy isolation?

Yes, with caveats. Keycloak Organizations (Keycloak 26+) supports per-organization identity providers and domain-based routing — the right pattern for B2B SaaS where enterprise customers authenticate via their own IdP. For CIAM scenarios where you need different branding or registration fields per user segment, realm-level theming and conditional flows are typically sufficient without Organizations. See the Keycloak Organizations guide for implementation details.

Summary

Migrating from Azure AD B2C to Keycloak is a structured project with a predictable set of challenges. The password hash gap is the most consequential technical constraint; custom IEF policies are the most labor-intensive artifact to port. Everything else — users, social IdPs, enterprise federations, app registrations — maps cleanly to Keycloak equivalents via the table in Phase 2.

The four decisions to settle before you start: password migration strategy (lazy migration, forced reset, or temporary B2C IdP); realm structure (single realm or Organizations-based multi-tenant); custom policy strategy (built-in flows first, SPI only where necessary); and cutover pace (phased by app, not a big-bang switch).

If you want a managed Keycloak environment that removes the infrastructure work so you can focus on the migration itself, Skycloak’s managed Keycloak hosting gives you a production-ready Keycloak 26.x instance with monitoring, backups, and support — so your team spends cycles on the migration logic, not cluster operations.

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