Loper Authentication Migration

By Sam Eigen on Jan 24, 2024
Supabase

Migrating your Authentication provider for any company that ships to even a small amount of users is fundamentally a dangerous operation. The absolute bare minimum your users can expect is to be able to log on to your platform, right? In this blog, I discuss how Loper was able to execute a rolling migration off of Auth0 over to Supabase with zero downtime.

Context

Loper is a mobile product for high school students to find their best fit post-secondary education. Since I joined the team in June of last year, we have surpassed over 100,000 users. Before I joined the team, contract developers set up the infrastructure, authentication flow, and general pipelines to go from local development to an App Store (and Google Play Store) ready product. Largely, they did a great job. I won’t get too into the nitty gritty details of the rest of our stack, but the gist of our authentication mechanism is simple:

Upon opening the mobile app, you simply enter one of either your phone number or email address and we send you a verificiation code. Pretty simple right? Auth0 managed this for us under the hood.

Why?

We were on Auth0’s startup program which was a very generous plan — up to 100k monthly active users for an enterprise platform delivered by Okta (a tool I’ve used several times in the security space). The developer experience with their SDK was great - templating emails and integration with SMS providers were just a couple clicks. However, when it came time to renew, the price tag that they gave us post-startup program was simply not viable for an early stage startup. If you are a founder looking for an authentication provider, I would deeply recommend that you look into what a renewal will cost even if you start on a generous free plan.

The team was left with these facts:

  • Lopers entire product suite’s authentication mechanisms were entirely dependent on Auth0
  • We had less than 1 month to migrate (over the holidays, nonetheless!)
  • Any amount of significant downtime for our userbase would be unacceptable as it has downstream impact on our customers

Discovery

Finding our next authentication provider proved to be a more challenging task than expected, but we had some non-negotiables:

  • Generous pricing even with massive scale
  • Reputable
  • Offered passwordless solutions in a free plan
  • Native integration with React Native + Expo
  • Verifiably secure

“Just roll your own!”

Many might wonder why we chose not to roll our own authentication if we just needed basic passwordless features. An argument could certainly be made (and it was discussed at length internally) of whether we just hook into the Twilio and Mandrill APIs for SMS and email respectively, generate our own unique identifiers per user for their selected authentication mechanism, and move on with it. We chose not to for a couple reasons:

  1. We are a small dev team - monitoring two different services becomes difficult at scale
  2. Ability to add on to authentication suite is limited - in the future, if we want to add social providers, this task becomes less of a 1-click and more of a technical burden
  3. The aforementioned APIs do not have the best native React Native integrations
  4. The onus of a supply-chain attack is on us (very rare, albeit not impossible)

Options

There were a few different options outside of rolling our own authentication that we considered. I firmly believe that all of these providers can be great for your use case, so just because they didn’t work for us doesn’t mean they wouldn’t work for you! This list is not exhaustive throughout our discovery.

Clerk

Clerk was an interesting one to us - we knew that it was rising the ranks as one of the most popular user management platforms out there. That being said, their React Native / Expo integrations seemed rather infantile. For example, some of their starter code looks like this for basic page protection:

export default function App() {
  return (
    <ClerkProvider publishableKey={Constants.expoConfig.extra.clerkPublishableKey}>
      <SafeAreaView style={styles.container}>
        <SignedIn>
          <Text>You are Signed in</Text>
        </SignedIn>
        <SignedOut>
        <Text>You are Signed out</Text>
        </SignedOut>
      </SafeAreaView>
    </ClerkProvider>
  );
}

This rubbed us the wrong way - these boilerplate and components would be incredibly nice if I was building the product from the ground up, but integrating these into a production codebase seemed like a larger lift than necessary.

WorkOS

This came as a recommendation but it was pretty much a nonstarter for us without native passwordless with SMS integration.

Stytch

This looked like a lovely platform to develop on, but the pricing did not scale in an overly friendly way in the (ideal) case where we are consistenly gaining more and more users over the coming months/years.

The Decision

We ultimately chose Supabase for a couple reasons. We evaluated several different solutions, both open-source, self-hosted, cloud plan, and everything in between. Supabase made the most sense for our use case - they offered a very generous plan, gave us passwordless with both SMS and Email out of the box (for free), came with an in-depth monitoring suite, and had a very well documented React Native and Expo integration.

Implementation

The migration from a code standpoint was actually relatively trivial. We ripped out the Auth0 dependencies in our frontend suite and replaced them with their Supabase equivalents. I set up the Twilio and Mandrill connections in the Supabase UI, and we were on to testing. There were a couple problems we found after testing that I will discuss later on, but generally the technical lift was relatively easy due to our choice of doing a rolling migration.

Migration Types

There are different types of authentication migrations, each that come with their own pros and cons - you may have heard of a Big Bang Migration (where you migrate all users at once, instantiating all prior users in your new authentication suite) or a Phased Migration (where you migrate X% of users, then in a next phase migrate Y% of users).

We went with a rolling migration - users that log in to the product get instantiated in the new authentication suite and their record is updated accordingly in our database.

A rolling migration made a ton of sense for us as we exclusively use passwordless authentication. Due to the fact that Auth0 didn’t store any passwords and that we already have a users email address, this was as seamless as updating a new field in the user table the first time a user authenticated post-migration.

Here is a chart that does a really good job displaying how we did this - all credit to Kevin Grüneberg’s article about how his company managed an Auth0 -> Supabase migration.

auth chart

Hiccups

What’s a migration without hiccups? The transition to Supabase was largely very smooth. However, we started receiving reports that users were being automatically logged out when opening the product. This was bizarre for plenty of reasons.

Notably, Supabase offers an “auto refresh token” functionality. If you are unfamiliar with the innerworking of a JWT, I would recommend reading the documentation but essentially there is an access token (the token that is used to validate an API request) and a refresh token (the token that refreshes the access token).

For security reasons, typically your access token will expire in 60 minutes (this is the Supabase default as well). When your access token expires, Supabase uses your refresh token to generate a new access token for you, which can then validate further requests.

After some hours of debugging, the issue presented itself in this little piece of code in our React Native codebase:

export const supabase = createClient(
    config.supabase.url,
    config.supabase.anonymousKey,
    {
        auth: {
            storage: AsyncStorage,
            autoRefreshToken: true, // insidious killer..
            persistSession: true,
            detectSessionInUrl: false
        }
    }
);

This code instantiates the Supabase client on the frontend. Essentially, by setting the ‘autoRefreshToken: true’, we were inadvertenly refreshing the token both manually and through the Supabase servers, which was registered by Supabase as spoofing - so they revoked the token. We simply set the autoRefreshToken value to false and our auto-logout woes subsided.

Conclusion

We are now about a month out from completing this migration and all is going very well. Seeing that first Supabase bill be only $50 almost made me shed a tear.

Migrating an authentication provider for a product, especially one with a substantial user base, is a complex and critical operation - albeit not impossible! In this blog, we discussed Loper’s successful execution of a rolling migration from Auth0 to Supabase, highlighting key insights and decisions made during the process.

Loper, a mobile product for high school students, faced the challenge of a migration due to cost constraints and a need for a more sustainable authentication solution. We needed to ensure a seamless transition without significant downtime or disruption to their user base.

The discovery phase involved identifying non-negotiable criteria for the new authentication provider, including generous pricing, reputation, passwordless solutions, native integration with React Native + Expo, and security.

The decision not to roll their own authentication was based on considerations of the team’s size, the potential limitations in adding future features, and the need for strong React Native integration.

Ultimately, Supabase was chosen as the new authentication provider due to its generous plan, passwordless options, monitoring suite, and well-documented React Native and Expo integration.

The implementation of the migration itself was relatively straightforward, involving the replacement of Auth0 dependencies with Supabase equivalents. A rolling migration approach was chosen, particularly suited for passwordless authentication, allowing users to seamlessly transition to the new system.

While the migration process was mostly smooth, some hiccups occurred, such as unexpected automatic logouts attributed to token refresh settings. These issues were addressed, ensuring a successful migration.

If you have any questions about our experience or want to connect, feel free to reach out at eigen@getloper.com!

Best, Sam Eigen

© Copyright 2024 by Sams Blog. Built with ♥ by CreativeDesignsGuru.