feat: Migrate from NextAuth to Better Auth
Backend changes: - Add better-auth and pg packages - Create Better Auth instance with PostgreSQL adapter - Add Better Auth route handler at /api/auth/* - Create migration script for Better Auth database schema - Update main index to use Better Auth routes instead of custom auth - Configure email/password and OAuth (GitHub/Google) providers Frontend changes: - Add better-auth client - Create Better Auth client instance configuration - Update lib/auth.ts to use Better Auth session - Rewrite sign-in page with Better Auth methods - Rewrite sign-up page with Better Auth methods - Remove NextAuth route handler Documentation: - Add comprehensive migration guide with setup instructions - Document environment variables and API endpoints - Include testing checklist and rollback plan Benefits: - Unified authentication for both Elysia backend and Next.js frontend - Database-backed sessions (more secure than JWT) - Better TypeScript support - Extensible plugin system for future features - Active development and frequent updates
This commit is contained in:
35
backend/src/auth.ts
Normal file
35
backend/src/auth.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { betterAuth } from "better-auth";
|
||||
import { Pool } from "pg";
|
||||
|
||||
export const auth = betterAuth({
|
||||
database: new Pool({
|
||||
connectionString: process.env.DATABASE_URL || 'postgres://postgres:postgres@localhost:5432/fiscal',
|
||||
}),
|
||||
emailAndPassword: {
|
||||
enabled: true,
|
||||
},
|
||||
socialProviders: {
|
||||
github: {
|
||||
clientId: process.env.GITHUB_ID as string,
|
||||
clientSecret: process.env.GITHUB_SECRET as string,
|
||||
},
|
||||
google: {
|
||||
clientId: process.env.GOOGLE_ID as string,
|
||||
clientSecret: process.env.GOOGLE_SECRET as string,
|
||||
},
|
||||
},
|
||||
user: {
|
||||
modelName: "users",
|
||||
additionalFields: {
|
||||
name: {
|
||||
type: "string",
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
advanced: {
|
||||
database: {
|
||||
generateId: false, // Use PostgreSQL serial for users table
|
||||
},
|
||||
},
|
||||
});
|
||||
109
backend/src/better-auth-migrate.ts
Normal file
109
backend/src/better-auth-migrate.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
import { db } from './db';
|
||||
|
||||
async function migrateToBetterAuth() {
|
||||
console.log('Migrating to Better Auth schema...');
|
||||
|
||||
try {
|
||||
// Add Better Auth columns to users table
|
||||
await db`
|
||||
ALTER TABLE users
|
||||
ADD COLUMN IF NOT EXISTS email_verified BOOLEAN DEFAULT FALSE
|
||||
`;
|
||||
|
||||
await db`
|
||||
ALTER TABLE users
|
||||
ADD COLUMN IF NOT EXISTS image TEXT
|
||||
`;
|
||||
|
||||
console.log('✅ Added Better Auth columns to users table');
|
||||
|
||||
// Create session table
|
||||
await db`
|
||||
CREATE TABLE IF NOT EXISTS session (
|
||||
id TEXT PRIMARY KEY,
|
||||
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
|
||||
token TEXT NOT NULL UNIQUE,
|
||||
expires_at TIMESTAMP NOT NULL,
|
||||
ip_address TEXT,
|
||||
user_agent TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
`;
|
||||
|
||||
console.log('✅ Created session table');
|
||||
|
||||
// Create account table
|
||||
await db`
|
||||
CREATE TABLE IF NOT EXISTS account (
|
||||
id TEXT PRIMARY KEY,
|
||||
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
|
||||
account_id TEXT NOT NULL,
|
||||
provider_id TEXT NOT NULL,
|
||||
access_token TEXT,
|
||||
refresh_token TEXT,
|
||||
access_token_expires_at TIMESTAMP,
|
||||
refresh_token_expires_at TIMESTAMP,
|
||||
scope TEXT,
|
||||
id_token TEXT,
|
||||
password TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE(user_id, provider_id, account_id)
|
||||
)
|
||||
`;
|
||||
|
||||
console.log('✅ Created account table');
|
||||
|
||||
// Create verification table
|
||||
await db`
|
||||
CREATE TABLE IF NOT EXISTS verification (
|
||||
id TEXT PRIMARY KEY,
|
||||
identifier TEXT NOT NULL,
|
||||
value TEXT NOT NULL,
|
||||
expires_at TIMESTAMP NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
`;
|
||||
|
||||
console.log('✅ Created verification table');
|
||||
|
||||
// Create indexes
|
||||
await db`CREATE INDEX IF NOT EXISTS idx_session_user_id ON session(user_id)`;
|
||||
await db`CREATE INDEX IF NOT EXISTS idx_session_token ON session(token)`;
|
||||
await db`CREATE INDEX IF NOT EXISTS idx_session_expires_at ON session(expires_at)`;
|
||||
await db`CREATE INDEX IF NOT EXISTS idx_account_user_id ON account(user_id)`;
|
||||
await db`CREATE INDEX IF NOT EXISTS idx_account_provider_id ON account(provider_id)`;
|
||||
await db`CREATE INDEX IF NOT EXISTS idx_verification_identifier ON verification(identifier)`;
|
||||
await db`CREATE INDEX IF NOT EXISTS idx_verification_expires_at ON verification(expires_at)`;
|
||||
|
||||
console.log('✅ Created indexes');
|
||||
|
||||
// Migrate existing users to account table for credential auth
|
||||
await db`
|
||||
INSERT INTO account (id, user_id, account_id, provider_id, password, created_at, updated_at)
|
||||
SELECT
|
||||
gen_random_uuid(),
|
||||
id,
|
||||
id::text,
|
||||
'credential',
|
||||
password,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM users
|
||||
WHERE password IS NOT NULL
|
||||
ON CONFLICT DO NOTHING
|
||||
`;
|
||||
|
||||
console.log('✅ Migrated existing users to account table');
|
||||
|
||||
console.log('✅ Better Auth migration completed!');
|
||||
process.exit(0);
|
||||
} catch (error) {
|
||||
console.error('❌ Migration failed:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
migrateToBetterAuth();
|
||||
@@ -9,8 +9,8 @@ import { db } from './db';
|
||||
import { filingsRoutes } from './routes/filings';
|
||||
import { portfolioRoutes } from './routes/portfolio';
|
||||
import { openclawRoutes } from './routes/openclaw';
|
||||
import { authRoutes } from './routes/auth';
|
||||
import { watchlistRoutes } from './routes/watchlist';
|
||||
import { betterAuthRoutes } from './routes/better-auth';
|
||||
|
||||
const app = new Elysia({
|
||||
prefix: '/api'
|
||||
@@ -29,7 +29,7 @@ const app = new Elysia({
|
||||
}
|
||||
}
|
||||
}))
|
||||
.use(authRoutes)
|
||||
.use(betterAuthRoutes)
|
||||
.use(filingsRoutes)
|
||||
.use(portfolioRoutes)
|
||||
.use(watchlistRoutes)
|
||||
|
||||
7
backend/src/routes/better-auth.ts
Normal file
7
backend/src/routes/better-auth.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { Elysia } from 'elysia';
|
||||
import { auth } from '../auth';
|
||||
|
||||
export const betterAuthRoutes = new Elysia()
|
||||
.all('/api/auth/*', async ({ request }) => {
|
||||
return auth.handler(request);
|
||||
});
|
||||
Reference in New Issue
Block a user