Migrating ~8,000 Users from Custom Authentication to Supabase Auth
Stack
- Supabase Auth
- Node.js
- Express
- JWT
On this page
Key results
- Users migrated
- ~8,000
- Migration time
- ~1 month
- Custom authentication
- Retired
- Passwords, sessions, token refresh
- Managed
Problem
Better Coaching is a multi-tenant SaaS platform that football clubs and academies use to manage coaching, players and training content.
The platform relied on a custom authentication system built around usernames and passwords. That made the team responsible for some of the most security-sensitive code in the product: credential storage, the token lifecycle, password resets and session behavior. Users were also logged out roughly every 24 hours, which added friction for people using the platform every day.
Better Coaching wanted to move authentication to a managed provider that would handle:
- secure password storage;
- login and session management;
- token refresh;
- password resets and account recovery;
- integration with the existing Node.js backend.
The existing authorization model, including tenant relationships, roles and permissions, had to stay intact. And unlike adding authentication to a new product, about 8,000 existing users needed to keep getting into the system.
My role
I was the team lead on this migration. I set the direction for separating authentication from authorization, and led the team through integrating Supabase, designing the migration path for existing users and retiring the old system.
Most of the planning was about the users rather than the provider: how thousands of people would move to new credentials without the switch turning into a support problem.
Technical approach
Moving authentication to Supabase
Password storage, sessions, token refresh and recovery moved to Supabase Auth. The React application uses the Supabase JS client for login, password reset, session management and token refresh.
After sign-in, Supabase issues an access token and a refresh token. The frontend sends the access token with each API request, and Express middleware validates the Supabase JWT and extracts the user’s identity. Better Coaching-specific authorization stayed in our backend.
From username to email login
The old system identified users by username and password. Supabase is built around email and password:
- Username + password
- Email + password
For 8,000 existing accounts, those models weren’t automatically compatible. Email had never been required to log in, so some accounts had missing, dummy or invalid addresses. The user base couldn’t simply be copied into Supabase. We had to solve an identity-data problem before the authentication one.
Passwords that couldn’t move
Existing passwords couldn’t be transferred in a way that let users keep them, so every user had to set a new password under Supabase. For thousands of active accounts, an unclear process would quickly become a support problem, so the migration had to explain what was happening and give users a simple way in.
The migration flow
Users were imported into Supabase by email and then had to set a new password. The main path was an email explaining the change, with a password-reset link.
Not everyone would read it, and some users wouldn’t return for weeks. So when a user who hadn’t migrated yet tried the old login, the platform recognized the account and sent them into the reset flow instead of showing a generic “wrong password” error. That gave two routes in:
- Migration email
- Set new password
- Supabase authentication
- Old login attempt
- Migration detected
- Password reset
- Supabase authentication
Users didn’t have to complete the migration at one specific moment.
Missing and invalid emails
Under the new model, email was essential for both login and password recovery, but not every account had a usable one. Accounts with valid emails followed the normal path. Accounts without one needed extra handling before they could take part in an email-based system.
Preparing the user base for a different identity model mattered as much as the Supabase integration itself.
Decisions & trade-offs
Authentication vs. authorization
Moving authentication out didn’t mean moving business rules out. The split is explicit:
Supabase answers “Who is this user?” The application decides “Which tenant do they belong to, and what are they allowed to do?”
That removed security-sensitive code from our side without giving up control over roles, permissions and tenant access.
No parallel auth systems
We deliberately didn’t run the custom system and Supabase side by side indefinitely. Two systems would have meant two sources of truth for credentials and sessions, and more complexity everywhere. The migration was completed in about a month, after which Supabase was the only authentication system.
Data quality as the real risk
Integrating Supabase was the straightforward part. The hard questions were about the existing users: how they set new credentials, what happens if they ignore the email, how usernames map to emails, and what to do with accounts whose email data is invalid.
Changing authentication providers can expose data-quality assumptions that never mattered under the old system.
Results
Users moved to managed auth
About 8,000 existing users moved from the legacy username/password system to Supabase Auth, and the platform moved from a username-based to an email-based identity model.
Custom auth retired
The application no longer maintains its own password and session infrastructure. Supabase handles password storage, access tokens, the refresh-token lifecycle, sessions and password recovery, which took a significant amount of security-sensitive code off the team’s hands.
Better session handling
Instead of logging users out about every 24 hours, sessions now follow a standard access- and refresh-token lifecycle. That improves everyday use and gives a stronger base for future security work.
Authorization stayed in-house
Roles, tenant access, permissions and product-specific authorization are still controlled by Better Coaching’s backend. Supabase handles identity and sessions, and the application handles what authenticated users are allowed to do.