Supabase Auth에서 Better Auth로 마이그레이션
이 가이드에서는 Supabase Auth에서 Better Auth로 프로젝트를 마이그레이션하는 단계를 안내합니다.
이 마이그레이션은 모든 활성 세션을 무효화합니다. 이 가이드는 현재 2단계 인증(2FA) 또는 행 수준 보안(RLS) 구성 마이그레이션을 다루지 않지만, 추가 단계로 둘 다 가능합니다.
시작하기 전에
마이그레이션 프로세스를 시작하기 전에 프로젝트에서 Better Auth를 설정하세요. 설치 가이드를 따라 시작하세요.
데이터베이스 연결
사용자와 계정을 마이그레이션하려면 데이터베이스에 연결해야 합니다. Supabase 프로젝트에서 DATABASE_URL을 복사하여 데이터베이스에 연결하는 데 사용하세요. 이 예제에서는 데이터베이스에 연결하기 위해 pg를 설치해야 합니다.
npm install pg다음 코드를 사용하여 데이터베이스에 연결할 수 있습니다.
import { Pool } from "pg";
export const auth = betterAuth({
database: new Pool({
connectionString: process.env.DATABASE_URL
}),
})이메일 및 비밀번호 활성화 (선택 사항)
인증 설정에서 이메일 및 비밀번호를 활성화하세요.
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 프로젝트에서 활성화한 소셜 프로바이더를 인증 설정에 추가하세요.
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 플러그인 추가 (선택 사항)
인증 설정에 admin 및 anonymous 플러그인을 추가하세요.
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()],
})마이그레이션 실행
마이그레이션을 실행하여 데이터베이스에 필요한 테이블을 만드세요.
npx @better-auth/cli migrate이렇게 하면 데이터베이스에 다음 테이블이 생성됩니다:
이 테이블들은 public 스키마에 생성됩니다.
마이그레이션 스크립트 복사
이제 데이터베이스에 필요한 테이블이 있으므로 마이그레이션 스크립트를 실행하여 Supabase에서 Better Auth로 사용자와 계정을 마이그레이션할 수 있습니다.
프로젝트에 .ts 파일을 만드는 것부터 시작하세요.
touch 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로 사용자와 계정을 마이그레이션하세요.
bun migration.ts # 또는 node, ts-node 등을 사용하세요.비밀번호 해싱 알고리즘 변경
기본적으로 Better Auth는 scrypt 알고리즘을 사용하여 비밀번호를 해시합니다. Supabase는 bcrypt를 사용하므로 Better Auth가 비밀번호 검증을 위해 bcrypt를 사용하도록 구성해야 합니다.
먼저 bcrypt를 설치하세요:
npm install bcrypt
npm install -D @types/bcrypt그런 다음 인증 구성을 업데이트하세요:
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.emailsupabase.auth.signInWithPassword->authClient.signIn.emailsupabase.auth.signInWithOAuth->authClient.signIn.socialsupabase.auth.signInAnonymously->authClient.signIn.anonymoussupabase.auth.signOut->authClient.signOutsupabase.auth.getSession->authClient.getSession- 반응형 상태를 위해authClient.useSession을 사용할 수도 있습니다
더 알아보기:
미들웨어
미들웨어로 라우트를 보호하려면 Next.js 미들웨어 가이드 또는 프레임워크의 문서를 참조하세요.
마무리
축하합니다! Supabase Auth에서 Better Auth로 성공적으로 마이그레이션했습니다.
Better Auth는 더 큰 유연성과 더 많은 기능을 제공합니다. 문서를 탐색하여 전체 잠재력을 활용하세요.