Dual-Run Cutover from Azure AD B2C: Passwords, Apps, and Rollback

Guilliano Molaire Guilliano Molaire 11 min read

Last updated: September 2026

TL;DR

An Azure B2C migration has one failure mode that matters: customers cannot sign in. Dual-run removes it by keeping B2C and Keycloak both live, brokering B2C from Keycloak so passwords keep working without a reset, cutting applications over in waves, and giving every wave a rollback that is a config change rather than a code deploy.

The five decisions that make or break the cutover:

  • Password strategy first. B2C does not export password hashes. Pick lazy migration, forced reset, or temporary brokering before you plan anything else, because it determines the shape of the whole rollout.
  • Both IdPs stay live. B2C remains the credential authority until enough users have authenticated through Keycloak. Support runs until at least May 2030, so there is no external pressure forcing a short overlap.
  • Waves, ranked not guessed. Sequence applications by blast radius, protocol, and token coupling. Internal and low-traffic first, revenue-critical last.
  • Rollback is a design input. Store the authority URL in config, never in code. Reverting a wave should be a deploy-free flip.
  • Exit criteria are numeric. You stop dual-running when the B2C validation hit rate crosses a threshold you set in advance, not when someone feels ready.

The general pattern is covered in Dual-Run IAM Migration: Switching Identity Providers Without Downtime. This post is the B2C-specific version. For the underlying technical steps, see Migrating From Azure AD B2C to Keycloak.

Why big-bang fails specifically for B2C

Every IdP migration benefits from phasing. B2C makes it non-negotiable for one reason: the credential does not move.

Azure AD B2C does not expose password hashes through Microsoft Graph or any other API. That is a deliberate security boundary, not a missing feature. So on cutover day, in a big-bang plan, every customer with a local account arrives at a Keycloak that has their username, their email, their custom attributes, and no way to verify their password.

Your options at that moment are to force a password reset on your entire user base simultaneously, or to fail. Neither is a plan. A consumer-facing product that emails every customer “reset your password to continue” on a Tuesday morning gets a support queue, a spike in churn, and a security team fielding calls about whether the email is phishing (which, to a customer, it is indistinguishable from).

Dual-run avoids the whole scenario by letting the credential migrate one user at a time, invisibly, as people sign in normally.

There is also no urgency argument for rushing. B2C reached end of sale for new customers on May 1, 2025, and Microsoft has said it will support the service until at least May 2030. You have room for a long, boring overlap, and boring is the goal.

Decision 1: the password strategy

Three approaches. Pick one before anything else.

Approach How it works User impact Engineering cost Best for
Lazy (just-in-time) migration A custom Keycloak authenticator validates credentials against B2C on first login, then stores the password locally None visible Custom Authenticator SPI Large consumer bases, most teams
Forced password reset Import users without credentials, set the UPDATE_PASSWORD required action Every user resets once Minimal Small user bases, or where a reset is wanted anyway
B2C as temporary identity provider Configure B2C as an OIDC identity provider in Keycloak; users federate through until retired One extra redirect Configuration only Teams avoiding custom code

Lazy migration, the default choice

Lazy migration is what most teams with real user volume land on. Sequence per user:

  1. User arrives at Keycloak and enters email and password.
  2. Keycloak finds the imported user record, which has no credential.
  3. A custom authenticator validates the submitted credentials against B2C.
  4. On success, Keycloak writes the password to its own credential store and completes the login.
  5. Every subsequent login for that user is Keycloak-only. B2C is never called again for them.

The user notices nothing. Migration completes for the active population over one natural login cycle.

The catch worth planning for: the validation call needs a clear failure mode. If B2C is unreachable, do you fail the login or fall back? Set a timeout, log every validation attempt with its outcome, and alert on the error rate. This authenticator sits in the critical path of every unmigrated user’s login, so it deserves the same operational care as a payment call.

Brokering, the no-code variant

If you would rather not write Java, configure B2C as an OIDC identity provider in Keycloak. Users hit Keycloak, get redirected to B2C, authenticate there, and come back as a federated identity. Keycloak creates the local user on first broker login.

It is simpler to build and it keeps B2C in the path longer, with an extra redirect visible to users. It is also a perfectly good bridge if your timeline is generous, which, given the support horizon, it probably is.

Forced reset, and when it is honest

For a few thousand users, or for a product where a credential refresh is independently desirable (an old hash policy, a past incident), a forced reset is legitimate. Be deliberate about it: communicate in advance, stagger the cohorts rather than hitting everyone at once, and make sure your password reset email deliverability is verified before you depend on it for your entire user base.

Decision 2: ranking the application waves

Do not migrate applications in the order they appear in your app registration list. Rank them.

Scoring inputs

For each application, score:

  • Blast radius. How many users and how much revenue does a broken login block?
  • Protocol. Standards-compliant OIDC clients move easily. SAML relying parties and anything using a policy-specific authority URL need code changes.
  • Token coupling. Does the app read custom claims, rely on specific token lifetimes, or parse the issuer string? Tight coupling means more validation.
  • Deploy cadence. An app that ships weekly can roll back quickly. An app that ships quarterly through a change board cannot.
  • Ownership. Is there an active team who will answer a page, or is it an inherited service?

A typical wave plan

Wave Applications Why here Rollback
0 Test clients only Validate every flow against Keycloak with synthetic users N/A
1 Internal and admin tools Small, known audience who can report problems directly Config flip
2 Low-traffic customer apps First real customer traffic, limited exposure Config flip
3 Mid-tier customer apps Volume rises, patterns are proven Config flip
4 Revenue-critical apps Last, with the most evidence behind the move Config flip plus a standing incident bridge
5 B2C retirement Validation hit rate below threshold None (point of no return)

Wave 0 is not optional and it is not a formality. Every flow (sign-up, sign-in, password reset, each social provider, each enterprise federation, MFA enrollment, MFA challenge, logout) gets exercised end to end against Keycloak with test accounts before any real user touches it.

Applications that need code, not config

Flag these during inventory, because they set your critical path:

  • Anything constructing a B2C authority URL per policy (?p=B2C_1A_...). Keycloak has one discovery endpoint per realm, so this is a code change.
  • Mobile applications. App store review adds a release cycle you cannot compress, so start them early and ship a version that reads the authority from remote config.
  • Anything with a pinned issuer or a hardcoded JWKS URL.
  • Server-to-server integrations using B2C-specific grant behavior.

Decision 3: designing the rollback

A rollback plan that requires a code deploy is not a rollback plan. Under incident pressure, with customers unable to log in, you need a revert measured in minutes.

Externalize the identity configuration

Every application should read its identity configuration from environment variables, a config service, or feature flags. At minimum:

OIDC_ISSUER=https://auth.example.com/realms/customers
OIDC_CLIENT_ID=web-app
OIDC_DISCOVERY_URL=https://auth.example.com/realms/customers/.well-known/openid-configuration

Reverting a wave then means restoring the previous values. No build, no review, no deploy pipeline.

Keep B2C intact until the end

Do not delete users, app registrations, or custom policies during the migration. Do not let anyone “clean up” the tenant because it looks unused. The B2C tenant is your rollback target and your lazy-migration validation endpoint at the same time, and it costs almost nothing to leave running.

Define trigger metrics in advance

Decide before the wave what makes you roll back, so the call is mechanical rather than political at 2 a.m.:

Metric Watch for Suggested trigger
Login success rate Drop versus the pre-cutover baseline More than 2 percentage points below baseline for 10 minutes
Authentication latency (p95) Added redirect or validation cost More than 2x baseline sustained
B2C validation error rate Lazy migration authenticator failing More than 1 percent of validation attempts
Support ticket volume Login-related tickets More than 3x the hourly baseline
Token validation errors in apps Claim or issuer mismatch Any sustained non-zero rate post-cutover

Instrument these before Wave 1, not after. Migrating without a login success rate dashboard means your first signal is a customer complaint.

Decision 4: what to monitor during the overlap

The overlap period has its own operational profile, and there are two numbers that matter most.

The B2C validation hit rate is your migration progress meter. Every time the lazy-migration authenticator calls B2C, that is one user who has not yet been migrated. Chart it daily. It falls fast at first as your active population signs in, then develops a long tail of infrequent users.

The split of logins by path (Keycloak-native versus B2C-validated versus federated) tells you whether the shape of your traffic matches your model. A surprise here usually means an application you did not know about is still pointing somewhere unexpected.

Also watch: failed logins by error type (to distinguish wrong-password from broken-integration), new user registrations landing in the right place, and password reset completion rates, which are a leading indicator of email deliverability problems that only become visible at scale.

Decision 5: when to stop dual-running

Set the exit criteria before you start, and make them numeric.

A reasonable set:

  • B2C validation hit rate below 1 percent of daily logins for 14 consecutive days.
  • All applications on Keycloak for at least 30 days with no login-related incidents.
  • Remaining unmigrated accounts identified and handled explicitly (a reset campaign for the tail, or accepting that dormant accounts will reset on return).
  • Rollback formally retired, with a sign-off that everyone understands B2C retirement is one-way.

The long tail is the part people underestimate. Users who sign in once a year are still real customers, and you should decide consciously what happens to them rather than discovering the answer through a support ticket. A common landing spot: after the main window closes, convert remaining unmigrated accounts to a password reset on next login and communicate it as a security refresh.

Typical overlap windows run 60 to 90 days for products with frequent logins, and longer for seasonal or annual-use products. Given that Microsoft supports B2C until at least May 2030, there is no reason to compress this artificially.

A realistic sequence

Pulling the decisions together into an order of operations:

  1. Inventory the tenant: users, attributes, flows, policies, identity providers, app registrations, and every app that builds a policy-specific authority URL.
  2. Build the Keycloak realm: flows, identity providers, clients, user profile schema, protocol mappers, theme. Flow mapping detail is in Azure AD B2C Custom Policies (IEF) to Keycloak Flows.
  3. Import users without credentials, storing the B2C object ID as an attribute for cross-referencing.
  4. Deploy the lazy-migration authenticator and test it against B2C with real (non-production) accounts.
  5. Instrument login success rate, latency, validation hit rate, and error types.
  6. Run Wave 0 against test clients, exercising every flow.
  7. Run Waves 1 through 4, one at a time, each with a soak period and a documented rollback.
  8. Monitor the overlap until exit criteria are met.
  9. Retire B2C: disable the validation authenticator, handle the tail, archive tenant configuration, then decommission.

Frequently asked questions

Can I migrate Azure AD B2C password hashes to Keycloak?

No. Azure AD B2C does not expose password hashes through Microsoft Graph or any other API. This is a security boundary rather than a configuration gap, and it applies regardless of your destination platform. Your three options are lazy just-in-time migration, a forced password reset, or brokering through B2C temporarily.

How long should a dual-run overlap last?

Long enough for your active population to complete one natural login cycle, plus a tail. For products with frequent logins, 60 to 90 days is typical. For seasonal or annual-use products, longer. Drive the decision with the B2C validation hit rate rather than the calendar, and remember there is no external deadline forcing speed.

Does dual-run mean users see two login prompts?

No, and if they do, something is misconfigured. In lazy migration the B2C call is server-side and invisible. In the brokering approach, users see one additional redirect but authenticate once. A double prompt means the session handoff is broken, not that dual-run inherently causes it.

What happens to users who never log in during the overlap?

They keep their imported account with no local credential. When the overlap closes, set the UPDATE_PASSWORD required action on the remaining accounts so a returning user resets on next login. Size this cohort before you close the window; for consumer products it can be a large fraction of total accounts and a small fraction of active users.

Can I dual-run into Entra External ID instead of Keycloak?

The pattern applies to any destination. The password constraint is identical because it is a property of B2C, not of where you are going. Wave sequencing, externalized configuration, and numeric rollback triggers are destination-agnostic. Part 2 compares the destinations: Azure AD B2C vs Entra External ID vs Keycloak.

Do I need custom code for dual-run?

For lazy migration, yes: a Keycloak Authenticator SPI that validates against B2C. For the brokering approach, no, it is identity provider configuration. Teams without Java capacity in-house usually start with brokering and accept the longer B2C dependency.

Where to go next

The cutover is where migrations are actually won or lost, and the winning version is unglamorous: both systems live, small waves, real rollback, numeric exit criteria, and a long overlap nobody is rushing.

If the blocker is having a production-grade Keycloak to run waves against, Skycloak provides managed upstream Keycloak with a 7-day free trial and no credit card. Lazy-migration authenticators are custom provider JARs; uploading your own JAR on Skycloak requires the Enterprise Custom Extensions plan (marketplace extensions remain available on all plans).

Azure AD B2C exit series

This post is Part 4 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
  4. Dual-Run Cutover from Azure AD B2C: Passwords, Apps, and Rollback (this post)
  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. For the general dual-run pattern beyond B2C, see Dual-Run IAM Migration: Switching Identity Providers Without Downtime.

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