logo
Technology

Refactoring Auth, Not Breaking Prod: A Case Study in S2S Migration

Dev Pathak
May 19, 2025
3 minutes
What it takes to migrate from one identity manager to another without downtime — and how we pulled it off at CARS24.

At CARS24, scale isn’t just a goal — it’s a reality. And with scale comes complexity. As our internal ecosystem of services grew, so did the challenges in our authentication layer. This is how we moved from FusionAuth to a unified, in-house identity system built over Keycloak to power secure, scalable, and flexible service-to-service (S2S) communication. The ask was simple:

  • Zero downtime
  • Backward compatibility
  • Easy adoption across services
  • Future-ready architecture

And we did just that.

This post describes how we achieved a frictionless, production-safe migration — from design decisions to rollout strategy.

The Existing Setup

In our current setup, every internal service communicates with external systems via Synapse, as shown in Figure 1. Synapse is our gateway that monitors and validates requests. To authenticate these requests, we relied on FusionAuth. Each service had its credentials, configured via environment variables and used to fetch tokens.

Tokens (JWT tokens) generated by FusionAuth were attached to requests (in headers) and validated by Synapse before being passed on. The setup worked fine.

We built a library named auth-lib. It is added as a dependency in the internal services. Auth-lib’s sole purpose is to generate a token via FusionAuth. The flow is shown in Figure 2.

Why Move Away from FusionAuth?

As CARS24 scaled, so did our microservices and auth demands. Here’s what we started noticing:

  • Different teams used different providers — FusionAuth, Okta, DeScope — creating inconsistencies and security blind spots.
  • Client Credentials Grant wasn’t supported in the open-source version.
  • FusionAuth, being a third-party service, meant limited control and opaque debugging.
  • We lacked fine-grained authorisation; any internal service could hit any external one.
  • Licensing and feature gating became blockers for horizontal scalability.

New Flow, Familiar Feel

Because of the above challenges, we needed to migrate from FusionAuth.

We needed a control centre — something that unified authentication, gave us visibility, and offered extensibility. So we leaned into something we already trusted internally: UMS, our Keycloak-based authentication platform.

With UMS, we moved toward a token-driven S2S auth flow we owned, extended, and customised — without hitting a paywall.

While the infrastructure changed under the hood, the surface-level service experience stayed the same, intentionally.

Let’s break it down:

  1. An internal service receives a request.
  2. It uses auth-lib to fetch a token using its credentials.
  3. Previously, auth-lib would call FusionAuth to generate a JWT.
  4. That token was added as an Authentication header and sent to Synapse.
  5. Synapse validated the token and forwarded the request to the appropriate external service.

We preserved this structure, but swapped out FusionAuth with UMS behind the scenes.

Technical Strategy

To make the transition painmigrate a core infrastructure componentless for our internal services, we made surgical improvements:

  1. auth-lib was updated to support token generation using UMS.
  2. Synapse now uses both FusionAuth and UMS to validate the token.

We knew all services wouldn’t update overnight. So, we added a factory-based validator in Synapse.

IF token.issuer IS "fusionauth" THEN
 call validateWithFusionAuth(token)
ELSE IF token.issuer IS "ums" THEN
 call validateWithUMS(token)
END IF

How did this help the migration?

  1. The token generation logic is in the auth-lib library, meaning no dependency on internal services.
  2. The token validation part now allows both old and new tokens to coexist, with no downtime, no disruption.

Action Items for Internal Services

Internal services had to do just two things:

  1. Upgrade the auth-lib dependency to the latest version.
  2. Update environment credentials.

That’s it. No API rewrites. No cascading changes. Adoption was quick and clean.

Engineering Wins from the Migration

This wasn’t just a provider swap — it was an opportunity to tighten up our architecture:

  • Pluggable Token Generators in auth-lib. Easily switch auth sources with one SDK upgrade.
  • Dynamic Token Validators in Synapse. Support both old and new tokens, determined at runtime.
  • Separation of Responsibilities. Token creation and validation are in separate components.
  • Unified Credential Handling. Replaced hardcoded FusionAuth credentials with centralised UMS config.
  • Ready for Authorisation. We’ve laid the groundwork to restrict which services can talk to which others.

Lessons Learned

  • Don’t reinvent the flow — refactor under the hood. Developers love familiarity. Keeping the token flow identical made adoption easy.
  • Design with coexistence in mind. Supporting both FusionAuth and UMS during the transition helped avoid downtime and pressure.
  • Loose coupling = easier upgrades. By abstracting token generation and validation, we enabled future migrations with minimal change.

Final Thoughts

We didn’t migrate to a new identity platform — we transitioned from one to another. But we did it with:

  • Zero downtime
  • Full backward compatibility
  • Minimal lift for internal teams

And now, we’re ready for the future — with centralised authentication, precise control, and the foundation for fine-grained authorisation.

From FusionAuth to UMS — not a rebuild, just a better way forward. 

Loved this article?

Hit the like button

Share this article

Spread the knowledge