Learn how to migrate users and organizations from Better Auth.
You can migrate your existing user data from a variety of sources into WorkOS using the AuthKit API. This guide will walk you through the steps to export your data from Better Auth, and import into WorkOS.
Better Auth stores user data directly in your database, which means you have full control over exporting your data. Unlike hosted authentication services, Better Auth does not provide a built-in export tool, but you can access your data directly through your database.
Better Auth uses multiple database tables to store authentication data. The main tables you’ll need to export are:
id, name, email, emailVerified, image, timestamps)You can export this data using your database’s native export tools, your ORM (for example, Prisma), or direct SQL queries.
To export users, query the user table directly.
SELECT * FROM user;
You can export this data to JSON, CSV, or any format that works for your migration script.
Better Auth stores password hashes in the account table with providerId set to 'credential'. The passwords are hashed using scrypt by default, though Better Auth supports custom hashing functions.
To export password hashes, query the account table:
SELECT userId, password FROM account WHERE providerId = 'credential';
If you’re using a custom password hashing algorithm in Better Auth, make note of the algorithm for the import step. The default is scrypt, which is supported by WorkOS.
Once you’ve exported your user data from Better Auth, you can import it into WorkOS using the WorkOS APIs.
With the data from your Better Auth database, you can use the Create User API to import each user. The API is rate-limited, so for large migrations, you may want to implement batching with appropriate delays. You can view the rate limits documentation for more information.
Using the Better Auth user table schema, use the following mapping to Create User API parameters:
| Better Auth | WorkOS | |
|---|---|---|
email | → | email |
emailVerified | → | email_verified |
name | → | first_name |
name | → | last_name |
image | → | (not supported) |
Better Auth stores a single name field, while WorkOS has separate first_name and last_name fields. You’ll need to parse the name field or use it entirely for first_name.
Here’s an example migration script:
import { WorkOS } from '@workos-inc/node'; const workos = new WorkOS(process.env.WORKOS_API_KEY); async function migrateUsers(betterAuthUsers) { for (const user of betterAuthUsers) { // Parse name into first and last name if possible const [firstName, ...lastNameParts] = user.name.split(' '); const lastName = lastNameParts.join(' ') || undefined; try { // In the JavaScript SDK, the property names are camelCase const workosUser = await workos.userManagement.createUser({ email: user.email, emailVerified: user.emailVerified, firstName: firstName, lastName: lastName, }); console.log(`Migrated user: ${user.email} -> ${workosUser.id}`); } catch (error) { console.error(`Failed to migrate user ${user.email}:`, error); } } }
If you exported password hashes from Better Auth, you can import them during the user creation process, or later using the Update User API.
Better Auth uses scrypt as the default password hashing algorithm, which is supported by WorkOS. Make sure to pass the following parameters:
password_hash_type set to 'scrypt'password_hash set to the password hash value from your Better Auth account tableThe password hash should be in PHC string format. If Better Auth is storing raw scrypt hashes, you’ll need to convert them to PHC format. See the other services migration guide for detailed information about PHC format parameters for scrypt.
If you configured Better Auth to use a different password hashing algorithm, such as bcrypt, argon2, or pbkdf2, WorkOS supports these as well. Refer to the password hash types documentation for format requirements.
If you have users who previously signed in through Better Auth using social auth providers, such as Google or Microsoft, those users can continue to sign in with those providers after you’ve migrated.
Better Auth stores social auth accounts in the account table with different providerId values (e.g., 'google', 'github', 'microsoft'). These accounts are linked to users via the userId field.
Check out our integrations page for guidance on configuring the relevant provider’s client credentials. After your provider is configured, users can sign in with their provider credentials and will be automatically linked to a user. The email address from the social auth provider is used to determine this match.
Some users may need to verify their email address through WorkOS if email verification is enabled in your environment’s authentication settings.
Email verification behavior varies depending on whether the provider is known to verify email addresses. For example, users signing in using Google OAuth and a gmail.com email domain will not need to perform the extra verification step.
Better Auth has an organization plugin that allows you to manage organization members and teams. If you’re using this plugin, you can migrate your organizations to WorkOS, which has native support for Organizations as a core B2B feature.
Better Auth stores organizations in an organization table with fields like id, name, slug, logo, and metadata. To migrate these, you’ll need to:
SELECT * FROM organization;
import { WorkOS } from '@workos-inc/node'; const workos = new WorkOS(process.env.WORKOS_API_KEY); async function migrateOrganizations(betterAuthOrgs) { for (const org of betterAuthOrgs) { try { const workosOrg = await workos.organizations.createOrganization({ name: org.name, // You may want to store the Better Auth slug in organization metadata metadata: { betterAuthSlug: org.slug, }, }); console.log(`Migrated organization: ${org.name} -> ${workosOrg.id}`); // Store this mapping for migrating user memberships later orgIdMap.set(org.id, workosOrg.id); } catch (error) { console.error(`Failed to migrate organization ${org.name}:`, error); } } }
Better Auth stores organization memberships in a member table that links users (and their roles) to organizations.
SELECT * FROM member;
Then use the Organization Membership API to add each user to their respective organization.
WorkOS offers RBAC capabilities through roles and permissions. When migrating, identify your roles defined in Better Auth, then create equivalent roles in the WorkOS Dashboard, and assign roles during migration by specifying the roleSlug parameter when creating organization memberships. If your Better Auth implementation uses complex RBAC policies with custom resources and actions, you may need to simplify to standard roles or implement custom authorization in your application logic.
async function migrateMemberships(betterAuthMemberships, orgIdMap, userIdMap) { for (const membership of betterAuthMemberships) { const orgId = orgIdMap.get(membership.organizationId); const userId = userIdMap.get(membership.userId); if (!orgId || !userId) { console.error(`Missing mapping for membership: ${membership.id}`); continue; } try { await workos.userManagement.createOrganizationMembership({ userId: userId, organizationId: orgId, // Better Auth uses custom role strings; map these to WorkOS roles as needed roleSlug: getRole(membership.role), }); console.log(`Migrated membership: ${membership.userId} -> ${orgId}`); } catch (error) { console.error(`Failed to migrate membership:`, error); } } }
If you’re using the teams feature within Better Auth (an optional hierarchical level within organizations), note that there is not a directly corresponding “teams” concept. However, you have several options:
For most B2B applications, flattening teams into separate organizations provides the cleanest migration path and takes full advantage of enterprise features like SSO and Directory Sync, which operate at the organization level.
There are several differences between Better Auth and WorkOS that you should be aware of during migration.
Better Auth offers a Two-Factor Authentication plugin that supports TOTP-based authenticators. If your Better Auth users have enrolled in 2FA, they will need to re-enroll in MFA after migrating, as MFA secrets cannot be migrated for security reasons. See the MFA guide for information on enrolling users in MFA.
WorkOS does not support SMS-based factors due to known security vulnerabilities with SMS.
Better Auth has sophisticated account linking capabilities that automatically link social accounts with matching verified email addresses. WorkOS also supports automatic account linking based on email addresses. When migrating users who have multiple linked accounts in Better Auth (e.g., password + Google OAuth), you should:
If your application allows users to sign up at any time, you should consider the timing of your migration. Users who sign up after you’ve exported data from Better Auth but before you’ve switched to WorkOS for authentication will be omitted from the migration.
There are two main strategies to handle this:
Schedule an appropriate time for the migration and temporarily disable signup functionality. This can be controlled using a feature flag in your application. After the migration is complete and your application is using WorkOS for authentication, re-enable signups.
For applications that cannot disable signups, implement a “dual-write” strategy. When a new user signs up, create records in both your Better Auth database and WorkOS using the Create User API. This keeps WorkOS synchronized with new users, though you’ll still need to perform the historical user migration. Be aware that you’ll need to keep user updates (email changes, password changes) synchronized between both systems until the migration is complete.
Since Better Auth stores data in your own database, you have flexibility in how you structure the migration:
With your users and organizations now imported, you can start using WorkOS to manage authentication for your application. If you haven’t already, take a look at our Quick Start guide to learn how to integrate AuthKit into your application.
These enterprise-ready features go beyond basic authentication:
If you have questions about your migration or need assistance, reach out to support@workos.com.