Supabase Auth에서 Better Auth로 마이그레이션

이 가이드에서는 Supabase Auth에서 Better Auth로 프로젝트를 마이그레이션하는 단계를 안내합니다.

이 마이그레이션은 모든 활성 세션을 무효화합니다. 이 가이드는 현재 2단계 인증(2FA) 또는 행 수준 보안(RLS) 구성 마이그레이션을 다루지 않지만, 추가 단계로 둘 다 가능합니다.

시작하기 전에

마이그레이션 프로세스를 시작하기 전에 프로젝트에서 Better Auth를 설정하세요. 설치 가이드를 따라 시작하세요.

데이터베이스 연결

사용자와 계정을 마이그레이션하려면 데이터베이스에 연결해야 합니다. Supabase 프로젝트에서 DATABASE_URL을 복사하여 데이터베이스에 연결하는 데 사용하세요. 이 예제에서는 데이터베이스에 연결하기 위해 pg를 설치해야 합니다.

npm install pg

다음 코드를 사용하여 데이터베이스에 연결할 수 있습니다.

auth.ts
import { Pool } from "pg";

export const auth = betterAuth({
    database: new Pool({
        connectionString: process.env.DATABASE_URL
    }),
})

이메일 및 비밀번호 활성화 (선택 사항)

인증 설정에서 이메일 및 비밀번호를 활성화하세요.

auth.ts
import { admin, anonymous } from "better-auth/plugins";

export const auth = betterAuth({
    database: new Pool({
        connectionString: process.env.DATABASE_URL
    }),
	emailVerification: {
		sendEmailVerification: async(user)=>{
			// 이메일 인증 이메일 전송
			// 여기에 자체 로직을 구현하세요
		}
	},
    emailAndPassword: { 
        enabled: true, 
    } 
})

소셜 프로바이더 설정 (선택 사항)

Supabase 프로젝트에서 활성화한 소셜 프로바이더를 인증 설정에 추가하세요.

auth.ts
import { admin, anonymous } from "better-auth/plugins";

export const auth = betterAuth({
    database: new Pool({
        connectionString: process.env.DATABASE_URL
    }),
    emailAndPassword: {
        enabled: true,
    },
    socialProviders: { 
        github: { 
            clientId: process.env.GITHUB_CLIENT_ID, 
            clientSecret: process.env.GITHUB_CLIENT_SECRET, 
        } 
    } 
})

admin 및 anonymous 플러그인 추가 (선택 사항)

인증 설정에 adminanonymous 플러그인을 추가하세요.

auth.ts
import { admin, anonymous } from "better-auth/plugins";

export const auth = betterAuth({
    database: new Pool({
        connectionString: process.env.DATABASE_URL
    }),
    emailAndPassword: {
        enabled: true,
    },
    socialProviders: {
        github: {
            clientId: process.env.GITHUB_CLIENT_ID!,
            clientSecret: process.env.GITHUB_CLIENT_SECRET!,
        }
    },
    plugins: [admin(), anonymous()], 
})

마이그레이션 실행

마이그레이션을 실행하여 데이터베이스에 필요한 테이블을 만드세요.

Terminal
npx @better-auth/cli migrate

이렇게 하면 데이터베이스에 다음 테이블이 생성됩니다:

이 테이블들은 public 스키마에 생성됩니다.

마이그레이션 스크립트 복사

이제 데이터베이스에 필요한 테이블이 있으므로 마이그레이션 스크립트를 실행하여 Supabase에서 Better Auth로 사용자와 계정을 마이그레이션할 수 있습니다.

프로젝트에 .ts 파일을 만드는 것부터 시작하세요.

Terminal
touch migration.ts

그런 다음 다음 코드를 복사하여 파일에 붙여넣으세요.

migration.ts
import { Pool } from "pg";
import { auth } from "./auth";
import { User as SupabaseUser } from "@supabase/supabase-js";

type User = SupabaseUser & {
	is_super_admin: boolean;
	raw_user_meta_data: {
		avatar_url: string;
	};
	encrypted_password: string;
	email_confirmed_at: string;
	created_at: string;
	updated_at: string;
	is_anonymous: boolean;
	identities: {
		provider: string;
		identity_data: {
			sub: string;
			email: string;
		};
		created_at: string;
		updated_at: string;
	};
};

const migrateFromSupabase = async () => {
	const ctx = await auth.$context;
	const db = ctx.options.database as Pool;
	const users = await db
		.query(`
			SELECT
				u.*,
				COALESCE(
					json_agg(
						i.* ORDER BY i.id
					) FILTER (WHERE i.id IS NOT NULL),
					'[]'::json
				) as identities
			FROM auth.users u
			LEFT JOIN auth.identities i ON u.id = i.user_id
			GROUP BY u.id
		`)
		.then((res) => res.rows as User[]);
	for (const user of users) {
		if (!user.email) {
			continue;
		}
		await ctx.adapter
			.create({
				model: "user",
				data: {
					id: user.id,
					email: user.email,
					name: user.email,
					role: user.is_super_admin ? "admin" : user.role,
					emailVerified: !!user.email_confirmed_at,
					image: user.raw_user_meta_data.avatar_url,
					createdAt: new Date(user.created_at),
					updatedAt: new Date(user.updated_at),
					isAnonymous: user.is_anonymous,
				},
			})
			.catch(() => {});
		for (const identity of user.identities) {
			const existingAccounts = await ctx.internalAdapter.findAccounts(user.id);

			if (identity.provider === "email") {
				const hasCredential = existingAccounts.find(
					(account) => account.providerId === "credential",
				);
				if (!hasCredential) {
					await ctx.adapter
						.create({
							model: "account",
							data: {
								userId: user.id,
								providerId: "credential",
								accountId: user.id,
								password: user.encrypted_password,
								createdAt: new Date(user.created_at),
								updatedAt: new Date(user.updated_at),
							},
						})
						.catch(() => {});
				}
			}
			const supportedProviders = Object.keys(ctx.options.socialProviders || {})
			if (supportedProviders.includes(identity.provider)) {
				const hasAccount = existingAccounts.find(
					(account) => account.providerId === identity.provider,
				);
				if (!hasAccount) {
					await ctx.adapter.create({
						model: "account",
						data: {
							userId: user.id,
							providerId: identity.provider,
							accountId: identity.identity_data?.sub,
							createdAt: new Date(identity.created_at ?? user.created_at),
							updatedAt: new Date(identity.updated_at ?? user.updated_at),
						},
					});
				}
			}
		}
	}
};
migrateFromSupabase();

마이그레이션 스크립트 사용자 정의 (선택 사항)

  • name: 마이그레이션 스크립트는 사용자의 이메일을 이름으로 사용합니다. 데이터베이스에 사용자 표시 이름이 있는 경우 사용자 정의할 수 있습니다.
  • socialProviderList: 마이그레이션 스크립트는 인증 설정에서 활성화한 소셜 프로바이더를 사용합니다. 인증 설정에서 활성화하지 않은 추가 소셜 프로바이더가 있는 경우 사용자 정의할 수 있습니다.
  • role: admin 플러그인을 사용하지 않는 경우 role을 제거하세요
  • isAnonymous: anonymous 플러그인을 사용하지 않는 경우 isAnonymous를 제거하세요.
  • users 테이블을 참조하는 다른 테이블을 업데이트하여 id 필드를 사용하세요.

마이그레이션 스크립트 실행

마이그레이션 스크립트를 실행하여 Supabase에서 Better Auth로 사용자와 계정을 마이그레이션하세요.

Terminal
bun migration.ts # 또는 node, ts-node 등을 사용하세요.

비밀번호 해싱 알고리즘 변경

기본적으로 Better Auth는 scrypt 알고리즘을 사용하여 비밀번호를 해시합니다. Supabase는 bcrypt를 사용하므로 Better Auth가 비밀번호 검증을 위해 bcrypt를 사용하도록 구성해야 합니다.

먼저 bcrypt를 설치하세요:

npm install bcrypt
npm install -D @types/bcrypt

그런 다음 인증 구성을 업데이트하세요:

auth.ts
import { betterAuth } from "better-auth";
import bcrypt from "bcrypt";

export const auth = betterAuth({
   emailAndPassword: {
       password: {
           hash: async (password) => {
               return await bcrypt.hash(password, 10);
           },
           verify: async ({ hash, password }) => {
               return await bcrypt.compare(password, hash);
           }
       }
   }
})

코드 업데이트

Supabase auth 호출에서 Better Auth API로 코드베이스를 업데이트하세요.

다음은 Supabase auth API 호출과 Better Auth의 대응 항목 목록입니다.

  • supabase.auth.signUp -> authClient.signUp.email
  • supabase.auth.signInWithPassword -> authClient.signIn.email
  • supabase.auth.signInWithOAuth -> authClient.signIn.social
  • supabase.auth.signInAnonymously -> authClient.signIn.anonymous
  • supabase.auth.signOut -> authClient.signOut
  • supabase.auth.getSession -> authClient.getSession - 반응형 상태를 위해 authClient.useSession을 사용할 수도 있습니다

더 알아보기:

  • 기본 사용법: auth 클라이언트를 사용하여 가입, 로그인, 로그아웃하는 방법을 배웁니다.
  • 이메일 및 비밀번호: 프로젝트에 이메일 및 비밀번호 인증을 추가하는 방법을 배웁니다.
  • 익명: 프로젝트에 익명 인증을 추가하는 방법을 배웁니다.
  • 관리자: 프로젝트에 관리자 인증을 추가하는 방법을 배웁니다.
  • 이메일 OTP: 프로젝트에 이메일 OTP 인증을 추가하는 방법을 배웁니다.
  • : 이벤트를 수신하는 데 훅을 사용하는 방법을 배웁니다.
  • Next.js: Next.js 프로젝트에서 auth 클라이언트를 사용하는 방법을 배웁니다.

미들웨어

미들웨어로 라우트를 보호하려면 Next.js 미들웨어 가이드 또는 프레임워크의 문서를 참조하세요.

마무리

축하합니다! Supabase Auth에서 Better Auth로 성공적으로 마이그레이션했습니다.

Better Auth는 더 큰 유연성과 더 많은 기능을 제공합니다. 문서를 탐색하여 전체 잠재력을 활용하세요.