docs/self-hosting/migration/v2/auth/migration-internals.mdx
This document explains the technical principles behind authentication migration in LobeHub. It's intended for users with database and development experience who want to understand how migration works under the hood.
<Callout type={'info'}> For step-by-step migration instructions, see NextAuth Migration or Clerk Migration. </Callout>
Understanding the database schema is essential for troubleshooting migration issues.
The users table is auth-framework agnostic - it's a generic user table that works with any authentication system. Each record represents a unique user identity with associated profile information.
Key fields:
| Field | Description |
|---|---|
id | Unique user identifier (primary key) |
email | User's email address (unique) |
avatar, firstName, lastName | Profile information |
The accounts table stores authentication provider connections. Different SSO providers and email/password authentication are all treated as separate "accounts" linked to a user.
Examples of accounts:
Relationship: One user can have multiple accounts (1:N relationship).
┌─────────────────┐ ┌─────────────────┐
│ users │ │ accounts │
├─────────────────┤ ├─────────────────┤
│ id (PK) │◄───────┤│ user_id (FK) │
│ email (unique) │ │ provider │
│ avatar │ │ provider_id │
│ ... │ │ ... │
└─────────────────┘ └─────────────────┘
│
┌───────┴───────┐
│ │
┌───────▼──┐ ┌──────▼───┐
│ Google │ │ GitHub │
│ Account │ │ Account │
└──────────┘ └──────────┘
<Callout type={'warning'}> Migration does not preserve login sessions. Users must log in again after migration. </Callout>
Simple migration only migrates the users table and ignores the accounts table.
How it works:
users tableaccounts tableWhy it works:
When you reset password or login with SSO, if the email returned by the provider matches an existing email in the users table, the system links you to that existing user.
Limitation:
Without migrating the accounts table, the system doesn't know about previously linked accounts.
Example scenario:
[email protected]) and Microsoft ([email protected]) linked to the same userusers table stores primary email: [email protected][email protected] → ✅ Links to existing user[email protected] → ❌ Creates a new user (no account record linking [email protected] to the original user)Full migration transfers account data from the previous auth system to Better Auth's accounts table.
Data migrated:
nextauth_accounts table → Better Auth accounts tableaccounts tableResult:
All previously linked accounts continue to work. Login with [email protected] will correctly link to the existing user.
This typically happens with simple migration when logging in with a secondary email.
Diagnosis steps:
Find your original user record
Query the users table to find your original user by primary email:
SELECT id, email FROM users WHERE email = '[email protected]';
Note the id value.
Check accounts linked to this user
Query the accounts table using the user ID:
SELECT * FROM accounts WHERE user_id = 'your-user-id';
Find the incorrectly created account
If you logged in with a secondary email and it created a new user, find that account:
SELECT * FROM accounts WHERE provider_account_id = '[email protected]';
-- or for SSO providers, search by the provider's user ID
Fix:
Delete the incorrectly created account record:
DELETE FROM accounts WHERE id = 'incorrect-account-id';
If a new user was created, you may also need to delete it:
DELETE FROM users WHERE id = 'new-user-id';
Log in using your primary email (the one stored in the users table)
After logging in, go to Settings → Profile → Linked Accounts to re-link your secondary accounts
Check:
Ensure the SSO provider is configured in AUTH_SSO_PROVIDERS
Verify the provider credentials (AUTH_<PROVIDER>_ID, AUTH_<PROVIDER>_SECRET)
For full migration, verify account records were created correctly:
SELECT * FROM accounts WHERE provider = 'google'; -- or your provider
Check:
Verify the email you're using matches the one in the users table
Check if you might have used a different email or SSO provider originally
Query the database directly to find users matching your criteria:
SELECT * FROM users WHERE email LIKE '%your-domain.com';
<Card href={'/docs/self-hosting/migration/v2/auth/clerk-to-betterauth'} title={'Clerk Migration Guide'} />
<Card href={'/docs/self-hosting/auth'} title={'Authentication Configuration'} /> </Cards>