사용자 및 계정

사용자 인증 외에도 Better Auth는 사용자를 관리하는 메서드 세트를 제공합니다. 여기에는 사용자 정보 업데이트, 비밀번호 변경 등이 포함됩니다.

user 테이블은 사용자의 인증 데이터를 저장합니다 스키마를 보려면 여기를 클릭하세요.

user 테이블은 추가 필드를 사용하거나 플러그인을 통해 추가 데이터를 저장하도록 확장할 수 있습니다.

사용자 업데이트

사용자 정보 업데이트

사용자 정보를 업데이트하려면 클라이언트에서 제공하는 updateUser 함수를 사용할 수 있습니다. updateUser 함수는 다음 속성을 가진 객체를 받습니다:

await authClient.updateUser({
    image: "https://example.com/image.jpg",
    name: "John Doe",
})

이메일 변경

사용자가 이메일을 변경할 수 있도록 하려면 먼저 기본적으로 비활성화되어 있는 changeEmail 기능을 활성화하세요. changeEmail.enabledtrue로 설정하세요:

export const auth = betterAuth({
    user: {
        changeEmail: {
            enabled: true,
        }
    }
})

검증된 이메일을 가진 사용자의 경우 sendChangeEmailVerification 함수를 제공하세요. 이 함수는 사용자가 이메일을 변경할 때 트리거되며, URL과 토큰이 포함된 검증 이메일을 보냅니다. 현재 이메일이 검증되지 않은 경우 검증 없이 즉시 변경됩니다.

export const auth = betterAuth({
    user: {
        changeEmail: {
            enabled: true,
            sendChangeEmailVerification: async ({ user, newEmail, url, token }, request) => {
                await sendEmail({
                    to: user.email, // 검증 이메일은 변경을 승인하기 위해 현재 사용자 이메일로 전송되어야 합니다
                    subject: '이메일 변경 승인',
                    text: `변경을 승인하려면 링크를 클릭하세요: ${url}`
                })
            }
        }
    }
})

활성화되면 클라이언트에서 changeEmail 함수를 사용하여 사용자의 이메일을 업데이트할 수 있습니다. 사용자는 이메일을 변경하기 전에 현재 이메일을 검증해야 합니다.

await authClient.changeEmail({
    newEmail: "new-email@email.com",
    callbackURL: "/dashboard", //검증 후 리디렉션할 URL
});

검증 후 새 이메일이 user 테이블에 업데이트되고 새 주소로 확인이 전송됩니다.

현재 이메일이 검증되지 않은 경우 검증 단계 없이 새 이메일이 업데이트됩니다.

비밀번호 변경

사용자의 비밀번호는 user 테이블에 저장되지 않습니다. 대신 account 테이블에 저장됩니다. 사용자의 비밀번호를 변경하려면 다음 접근 방식 중 하나를 사용할 수 있습니다:

POST
/change-password
const { data, error } = await authClient.changePassword({    newPassword: "newpassword1234", // required    currentPassword: "oldpassword1234", // required    revokeOtherSessions: true,});
PropDescriptionType
newPassword
설정할 새 비밀번호
string
currentPassword
현재 사용자 비밀번호
string
revokeOtherSessions?
true로 설정하면 이 사용자의 다른 모든 활성 세션이 무효화됩니다
boolean

비밀번호 설정

사용자가 OAuth 또는 다른 제공자를 사용하여 등록한 경우 비밀번호나 credential 계정이 없습니다. 이 경우 setPassword 액션을 사용하여 사용자의 비밀번호를 설정할 수 있습니다. 보안상의 이유로 이 함수는 서버에서만 호출할 수 있습니다. 사용자가 '비밀번호 찾기' 플로우를 거쳐 계정의 비밀번호를 설정하도록 하는 것을 권장합니다.

await auth.api.setPassword({
    body: { newPassword: "password" },
    headers: // 사용자의 세션 토큰을 포함하는 헤더
});

사용자 삭제

Better Auth는 데이터베이스에서 사용자를 완전히 삭제하는 유틸리티를 제공합니다. 기본적으로 비활성화되어 있지만 enabled:true를 전달하여 쉽게 활성화할 수 있습니다

export const auth = betterAuth({
    //...기타 설정
    user: {
        deleteUser: { 
            enabled: true
        } 
    }
})

활성화되면 authClient.deleteUser를 호출하여 데이터베이스에서 사용자 데이터를 영구적으로 삭제할 수 있습니다.

삭제 전 검증 추가하기

보안을 강화하기 위해 계정을 삭제하기 전에 사용자의 의도를 확인하는 것이 좋습니다. 일반적인 접근 방식은 검증 이메일을 보내는 것입니다. Better Auth는 이를 위한 sendDeleteAccountVerification 유틸리티를 제공합니다. 이는 특히 OAuth 설정이 있고 사용자가 새 세션을 위해 다시 로그인하지 않고도 계정을 삭제할 수 있도록 하려는 경우에 필요합니다.

설정 방법은 다음과 같습니다:

export const auth = betterAuth({
    user: {
        deleteUser: {
            enabled: true,
            sendDeleteAccountVerification: async (
                {
                    user,   // user 객체
                    url, // 삭제를 위한 자동 생성된 URL
                    token  // 검증 토큰 (커스텀 URL 생성에 사용 가능)
                },
                request  // 원래 요청 객체 (선택 사항)
            ) => {
                // 여기에 이메일 전송 로직
                // 예: sendEmail(data.user.email, "삭제 검증", data.url);
            },
        },
    },
});

콜백 검증 작동 방식:

  • 콜백 URL: sendDeleteAccountVerification에 제공된 URL은 접근 시 사용자 데이터를 삭제하는 미리 생성된 링크입니다.
delete-user.ts
await authClient.deleteUser({
    callbackURL: "/goodbye" // 삭제 후 리디렉션할 콜백 URL을 제공할 수 있습니다
});
  • 인증 확인: 사용자는 삭제하려는 계정에 로그인되어 있어야 합니다. 로그인되어 있지 않으면 삭제 프로세스가 실패합니다.

커스텀 URL을 보낸 경우 토큰과 함께 deleteUser 메서드를 사용하여 사용자를 삭제할 수 있습니다.

delete-user.ts
await authClient.deleteUser({
    token
});

인증 요구 사항

사용자를 삭제하려면 사용자가 다음 요구 사항 중 하나를 충족해야 합니다:

  1. 유효한 비밀번호

사용자가 비밀번호를 가지고 있는 경우 비밀번호를 제공하여 계정을 삭제할 수 있습니다.

delete-user.ts
await authClient.deleteUser({
    password: "password"
});
  1. 새로운 세션

사용자는 fresh 세션 토큰을 가지고 있어야 하며, 이는 사용자가 최근에 로그인했음을 의미합니다. 이는 비밀번호가 제공되지 않은 경우 확인됩니다.

기본적으로 session.freshAge60 * 60 * 24 (1일)로 설정됩니다. auth 설정에 session 객체를 전달하여 이 값을 변경할 수 있습니다. 0으로 설정하면 신선도 확인이 비활성화됩니다. 계정 삭제를 위한 이메일 검증을 사용하지 않는 경우 이 확인을 비활성화하지 않는 것이 좋습니다.

delete-user.ts
await authClient.deleteUser();
  1. 이메일 검증 활성화 (OAuth 사용자에게 필요)

OAuth 사용자는 비밀번호가 없으므로 사용자의 계정 삭제 의도를 확인하기 위해 검증 이메일을 보내야 합니다. sendDeleteAccountVerification 콜백을 이미 추가한 경우 다른 정보를 제공하지 않고 deleteUser 메서드를 호출하기만 하면 됩니다.

delete-user.ts
await authClient.deleteUser();
  1. 커스텀 계정 삭제 페이지가 있고 sendDeleteAccountVerification 콜백을 통해 해당 URL을 보낸 경우. 삭제를 완료하려면 토큰과 함께 deleteUser 메서드를 호출해야 합니다.
delete-user.ts
await authClient.deleteUser({
    token
});

콜백

beforeDelete: 이 콜백은 사용자가 삭제되기 전에 호출됩니다. 이 콜백을 사용하여 사용자를 삭제하기 전에 정리 작업이나 추가 확인을 수행할 수 있습니다.

auth.ts
export const auth = betterAuth({
    user: {
        deleteUser: {
            enabled: true,
            beforeDelete: async (user) => {
                // 여기에서 정리 작업이나 추가 확인을 수행
            },
        },
    },
});

APIError를 throw하여 삭제 프로세스를 중단할 수도 있습니다.

auth.ts
import { betterAuth } from "better-auth";
import { APIError } from "better-auth/api";

export const auth = betterAuth({
    user: {
        deleteUser: {
            enabled: true,
            beforeDelete: async (user, request) => {
                if (user.email.includes("admin")) {
                    throw new APIError("BAD_REQUEST", {
                        message: "관리자 계정은 삭제할 수 없습니다",
                    });
                }
            },
        },
    },
});

afterDelete: 이 콜백은 사용자가 삭제된 후에 호출됩니다. 이 콜백을 사용하여 사용자가 삭제된 후 정리 작업이나 추가 액션을 수행할 수 있습니다.

auth.ts
export const auth = betterAuth({
    user: {
        deleteUser: {
            enabled: true,
            afterDelete: async (user, request) => {
                // 여기에서 정리 작업이나 추가 액션을 수행
            },
        },
    },
});

계정

Better Auth는 여러 인증 방법을 지원합니다. 각 인증 방법을 제공자라고 합니다. 예를 들어 이메일 및 비밀번호 인증은 제공자이고, Google 인증은 제공자입니다.

사용자가 제공자를 사용하여 로그인하면 사용자에 대한 계정이 생성됩니다. 계정은 제공자가 반환한 인증 데이터를 저장합니다. 이 데이터에는 access token, refresh token 및 제공자가 반환한 기타 정보가 포함됩니다.

account 테이블은 사용자의 인증 데이터를 저장합니다 스키마를 보려면 여기를 클릭하세요

사용자 계정 나열

사용자 계정을 나열하려면 client.user.listAccounts 메서드를 사용할 수 있습니다. 이는 사용자와 연결된 모든 계정을 반환합니다.

const accounts = await authClient.listAccounts();

토큰 암호화

Better Auth는 기본적으로 토큰을 암호화하지 않으며 이는 의도적입니다. 혼란스럽거나 제한적일 수 있는 동작을 구워 넣기보다는 암호화 및 복호화 처리 방법을 완전히 제어할 수 있기를 원합니다. 암호화된 토큰(accessToken 또는 refreshToken 같은)을 저장해야 하는 경우 databaseHooks를 사용하여 데이터베이스에 저장되기 전에 암호화할 수 있습니다.

export const auth = betterAuth({
    databaseHooks: {
        account: {
            create: {
                before(account, context) {
                    const withEncryptedTokens = { ...account };
                    if (account.accessToken) {
                        const encryptedAccessToken = encrypt(account.accessToken)  
                        withEncryptedTokens.accessToken = encryptedAccessToken;
                    }
                    if (account.refreshToken) {
                        const encryptedRefreshToken = encrypt(account.refreshToken); 
                        withEncryptedTokens.refreshToken = encryptedRefreshToken;
                    }
                    return {
                        data: withEncryptedTokens
                    }
                },
            }
        }
    }
})

그런 다음 계정을 다시 가져올 때마다 사용하기 전에 토큰을 복호화해야 합니다.

계정 연결

계정 연결을 통해 사용자는 여러 인증 방법을 단일 계정에 연결할 수 있습니다. Better Auth를 사용하면 제공자가 사용자의 이메일을 검증된 것으로 확인하는 경우 사용자가 추가 소셜 로그인 또는 OAuth 제공자를 기존 계정에 연결할 수 있습니다.

계정 연결이 비활성화된 경우 제공자나 이메일 검증 상태에 관계없이 계정을 연결할 수 없습니다.

auth.ts
export const auth = betterAuth({
    account: {
        accountLinking: {
            enabled: true,
        }
    },
});

강제 연결

"신뢰할 수 있는 제공자" 목록을 지정할 수 있습니다. 사용자가 신뢰할 수 있는 제공자를 사용하여 로그인하면 제공자가 이메일 검증 상태를 확인하지 않아도 계정이 자동으로 연결됩니다. 계정 탈취 위험이 증가할 수 있으므로 주의해서 사용하세요.

auth.ts
export const auth = betterAuth({
    account: {
        accountLinking: {
            enabled: true,
            trustedProviders: ["google", "github"]
        }
    },
});

수동으로 계정 연결하기

이미 로그인한 사용자는 수동으로 계정을 추가 소셜 제공자 또는 credential 기반 계정에 연결할 수 있습니다.

  • 소셜 계정 연결: 클라이언트에서 linkSocial 메서드를 사용하여 소셜 제공자를 사용자의 계정에 연결합니다.

    await authClient.linkSocial({
        provider: "google", // 연결할 제공자
        callbackURL: "/callback" // 연결 완료 후 콜백 URL
    });

    소셜 계정을 연결할 때 특정 scope를 요청할 수도 있으며, 이는 초기 인증 중에 사용된 scope와 다를 수 있습니다:

    await authClient.linkSocial({
        provider: "google",
        callbackURL: "/callback",
        scopes: ["https://www.googleapis.com/auth/drive.readonly"] // 추가 scope 요청
    });

    제공자의 OAuth 플로우로 리디렉션하지 않고 ID 토큰을 직접 사용하여 계정을 연결할 수도 있습니다:

    await authClient.linkSocial({
        provider: "google",
        idToken: {
            token: "id_token_from_provider",
            nonce: "nonce_used_for_token", // 선택 사항
            accessToken: "access_token", // 선택 사항, 일부 제공자에 필요할 수 있음
            refreshToken: "refresh_token" // 선택 사항
        }
    });

    이는 제공자로부터 이미 유효한 토큰을 가지고 있는 경우에 유용합니다. 예를 들어:

    • 네이티브 SDK로 로그인한 후
    • 인증을 처리하는 모바일 앱을 사용할 때
    • 커스텀 OAuth 플로우를 구현할 때

    ID 토큰은 유효해야 하며 제공자가 ID 토큰 검증을 지원해야 합니다.

    사용자의 이메일 주소와 다른 이메일 주소로 소셜 계정을 연결할 수 있도록 하거나, 이메일 주소를 반환하지 않는 제공자를 사용하려는 경우 계정 연결 설정에서 이를 활성화해야 합니다.

    auth.ts
    export const auth = betterAuth({
        account: {
            accountLinking: {
                allowDifferentEmails: true
            }
        },
    });

    새로 연결된 계정이 사용자 정보를 업데이트하도록 하려면 계정 연결 설정에서 이를 활성화해야 합니다.

    auth.ts
    export const auth = betterAuth({
        account: {
            accountLinking: {
                updateUserInfoOnLink: true
            }
        },
    });
  • Credential 기반 계정 연결: credential 기반 계정(예: 이메일 및 비밀번호)을 연결하려면 사용자가 "비밀번호 찾기" 플로우를 시작하거나 서버에서 setPassword 메서드를 호출할 수 있습니다.

    await auth.api.setPassword({
        headers: /* 사용자의 세션 토큰을 포함하는 헤더 */,
        password: /* 새 비밀번호 */
    });

setPassword는 보안상의 이유로 클라이언트에서 호출할 수 없습니다.

계정 연결 해제

providerId를 제공하여 사용자 계정을 연결 해제할 수 있습니다.

await authClient.unlinkAccount({
    providerId: "google"
});

// 특정 계정 연결 해제
await authClient.unlinkAccount({
    providerId: "google",
    accountId: "123"
});

계정이 존재하지 않으면 오류가 발생합니다. 또한 사용자가 하나의 계정만 가지고 있는 경우 계정 잠금을 방지하기 위해 연결 해제가 금지됩니다 (allowUnlinkingAlltrue로 설정되지 않은 한).

auth.ts
export const auth = betterAuth({
    account: {
        accountLinking: {
            allowUnlinkingAll: true
        }
    },
});