Start now →

Bridging Auth0 with Legacy IdPs

By bitsofinfo · Published April 10, 2026 · 8 min read · Source: Level Up Coding
Market Analysis
Bridging Auth0 with Legacy IdPs

“We know we need to move to Auth0 and OAuth standards eventually. But we can’t just flip a switch. Can we figure out a path where both worlds can coexist?”

That was the challenge from a client with a long running custom platform that had its own bespoke authentication and authorization system. Their existing custom login mechanism issued its own proprietary access tokens; not JWTs, not anything standards based, just a custom opaque token format that had been woven into the fabric of their application ecosystem over many years. The desire to move to Auth0 was there; the problem was how to get there without forcing every application to migrate at the same time.

What was needed was a bridge: a way to let users log in via Auth0, while still being able to obtain a legacy token for applications that were not yet ready to consume a JWT. New applications could adopt the Auth0 JWT directly. Older ones could continue getting the legacy token they already understood; or some mix of the two during migration. Both from the same login event.

This post documents a proof of concept that explored that approach. This was a prototype running in an isolated test environment and was not intended to be production ready. Several shortcuts were taken that would need to be addressed before anything like this was deployed for real. That said, it was a useful exercise in validating the pattern and identifying which Auth0 primitives could anchor the solution.

The legacy platform

The legacy platform had its own user credential store, its own login endpoint, and its own token issuance logic. When a user authenticated, the platform issued a proprietary opaque token that downstream services validated by calling back to the platform. No JWT, no standard claims, no JWKS endpoint.

The platform also supported service to service authentication; a service could log in with a service account and obtain a token, then use that token to make privileged API calls on behalf of users. This capability turned out to be a key ingredient in the bridging design.

The bridging concept

The idea was fairly straightforward once the pieces were in view. Auth0 would become the front door for authentication. After a user logged in through Auth0, there needed to be a mechanism to go fetch an equivalent legacy token for that user, so that applications still dependent on the legacy auth system could continue to function.

The question was: how do you prove to the legacy platform that a user legitimately authenticated through Auth0, in a way the legacy platform could verify, without fundamentally changing its core auth model?

The answer was a nonce. A nonce (number used once) is a one time use random value; the idea being that it can be generated at a known point in a flow, stored, and then consumed exactly once to verify that a specific event occurred. When a user authenticated through Auth0, the login flow would generate a nonce, store it against the user’s account in the legacy platform, and surface it as a claim in the Auth0 access token. An application receiving that token could then present the nonce to the legacy platform to say “this user just authenticated through Auth0; here is the proof, now issue their legacy token.” The legacy platform would validate the nonce, mark it consumed so it could not be replayed, and return the legacy token.

Neither system had to fundamentally change how it worked. Auth0 handled the login. The legacy platform handled legacy token issuance. The nonce was the handshake between them.

Auth0 database action scripts

Auth0 provides Database Action Scripts; hooks written in JavaScript that plug into Auth0’s authentication pipeline. When using Auth0’s Universal Login with a Custom Database connection, you can provide a login(email, password, callback) function that Auth0 invokes when a user attempts to authenticate. This is the hook used in the prototype.

The login script would take the user’s email and password, hash the password using the same algorithm the legacy platform used, and query the legacy credential store to validate the match. On success, it would generate a nonce, write it to the legacy platform’s database associated with the user’s account, and invoke the Auth0 callback with the user’s profile information including the nonce as a custom attribute. Auth0 would then include that nonce as a claim in the issued access token.

One important caveat: in a real production scenario you would never have an Auth0 action script talking directly to a database. You would go through an API or a secure intermediary. For a POC running in an isolated test environment, the direct database call was acceptable to prove the concept, but this would be one of the first things to rework on any path to production.

The legacy platform additions

A test branch of the legacy platform was created that exposed a new endpoint: login_auth0. This endpoint was protected by the service account authentication mechanism; it would only accept calls from a caller presenting a valid service token. The endpoint accepted a user email and a nonce value. It would look up the nonce for the given user, validate that it was active and matched what had been stored during the Auth0 login flow, mark it inactive to prevent replay, and return a legacy token issued on behalf of that user.

This was a contained addition to the legacy platform. The core authentication model was not changed and no user facing behavior was altered.

The test application

To wire everything together and validate the flow end to end, a small Node.js/Express application was built using the express-openid-connect library. This test app stood in for any application in the ecosystem that needed to be Auth0 enabled while still depending on a legacy token during the transition period.

The test app required a handful of configuration parameters: the Auth0 application CLIENT_ID, the ISSUER_BASE_URL for the Auth0 tenant, a session secret, and a service account identity and credential for the legacy platform. Once running, navigating to the login route would redirect the user to Auth0's Universal Login page. After authentication, the user was redirected back with an authorization code, which the app exchanged with Auth0 for an access token containing the nonce claim.

From there the app would authenticate against the legacy platform using its service account credentials to get a service token, then call the login_auth0 endpoint with the user's email and nonce. On success it received back the legacy token for that user. A /debug debug endpoint would display both tokens side by side; the Auth0 JWT and the legacy token, demonstrating that a single login event had produced both. From that point the user could present either token to downstream services as appropriate.

In a production version of this architecture, the token exchange logic embedded in the test app would be extracted into a dedicated intermediary authorization service. Rather than every application implementing this exchange themselves, they would delegate to that service. The test app was sufficient to validate the concept.

The end to end flow

Migration flexibility

One of the more useful properties of this design is that it decouples the authentication migration from the application migration. Once Auth0 is the front door, individual applications can choose when they cut over to consuming the JWT directly. An application that is ready can use the Auth0 access token. One that is not can still call the legacy token exchange to get what it needs. Both tokens are available from the same login event, so there is no hard cutover date that forces every application to move at once. Teams can migrate on their own schedule as they are ready.

What would still be needed for production

As noted up front, this was a POC and a fair amount of work would remain before any version of this could be considered production ready. At a minimum:

None of these are blockers to the pattern; they are the expected hardening work that comes with taking any proof of concept toward production.

Summary

Overall this was a useful exercise in validating how Auth0’s extensibility primitives, specifically its db action acripts and custom database connection, can be used to bridge a modern oauth based identity provider with a legacy proprietary auth system. The nonce based handshake kept the surface area of changes small on both sides, and the dual token result from a single login event gives a reasonable incremental migration path.

Auth0’s documentation on database action scripts is a good starting point if you want to dig further into this space: https://auth0.com/docs/authenticate/database-connections/custom-db

Originally published at http://bitsofinfo.wordpress.com on September 9, 2022.


Bridging Auth0 with Legacy IdPs was originally published in Level Up Coding on Medium, where people are continuing the conversation by highlighting and responding to this story.

This article was originally published on Level Up Coding and is republished here under RSS syndication for informational purposes. All rights and intellectual property remain with the original author. If you are the author and wish to have this article removed, please contact us at [email protected].

NexaPay — Accept Card Payments, Receive Crypto

No KYC · Instant Settlement · Visa, Mastercard, Apple Pay, Google Pay

Get Started →