Organization
Organization은 사용자 접근 권한 및 권한 관리를 단순화합니다. 역할과 권한을 할당하여 프로젝트 관리, 팀 협업 및 파트너십을 간소화할 수 있습니다.
설치
auth 설정에 플러그인 추가하기
import { betterAuth } from "better-auth"
import { organization } from "better-auth/plugins"
export const auth = betterAuth({
plugins: [
organization()
]
})데이터베이스 마이그레이션
마이그레이션을 실행하거나 스키마를 생성하여 필요한 필드와 테이블을 데이터베이스에 추가합니다.
npx @better-auth/cli migratenpx @better-auth/cli generate필드를 수동으로 추가하려면 Schema 섹션을 참조하세요.
클라이언트 플러그인 추가하기
import { createAuthClient } from "better-auth/client"
import { organizationClient } from "better-auth/client/plugins"
export const authClient = createAuthClient({
plugins: [
organizationClient()
]
})사용법
플러그인을 설치하면 조직의 멤버와 팀을 관리하는 데 organization 플러그인을 사용할 수 있습니다. 클라이언트 플러그인은 organization 네임스페이스 아래에 메서드를 제공하며, 서버 api는 조직을 관리하는 데 필요한 엔드포인트를 제공하고 자체 백엔드에서 함수를 더 쉽게 호출할 수 있는 방법을 제공합니다.
Organization
조직 생성하기
const metadata = { someKey: "someValue" };const { data, error } = await authClient.organization.create({ name: "My Organization", // required slug: "my-org", // required logo: "https://example.com/logo.png", metadata, keepCurrentActiveOrganization: false,});| Prop | Description | Type |
|---|---|---|
name | 조직 이름입니다. | string |
slug | 조직 슬러그입니다. | string |
logo? | 조직 로고입니다. | string |
metadata? | 조직의 메타데이터입니다. | Record<string, any> |
keepCurrentActiveOrganization? | 새 조직을 생성한 후 현재 활성 조직을 활성 상태로 유지할지 여부입니다. | boolean |
조직 생성 권한 제한하기
기본적으로 모든 사용자가 조직을 생성할 수 있습니다. 이를 제한하려면 allowUserToCreateOrganization 옵션을 부울을 반환하는 함수로 설정하거나 직접 true 또는 false로 설정하세요.
import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins";
const auth = betterAuth({
//...
plugins: [
organization({
allowUserToCreateOrganization: async (user) => {
const subscription = await getSubscription(user.id);
return subscription.plan === "pro";
},
}),
],
});조직 슬러그 사용 여부 확인하기
조직 슬러그가 사용 중인지 확인하려면 클라이언트에서 제공하는 checkSlug 함수를 사용할 수 있습니다. 이 함수는 다음 속성을 가진 객체를 받습니다:
const { data, error } = await authClient.organization.checkSlug({ slug: "my-org", // required});| Prop | Description | Type |
|---|---|---|
slug | 확인할 조직 슬러그입니다. | string |
Organization Hooks
다양한 조직 관련 활동 전후에 실행되는 훅을 사용하여 조직 작업을 커스터마이즈할 수 있습니다. Better Auth는 두 가지 방법으로 훅을 설정할 수 있습니다:
- 레거시 organizationCreation hooks (더 이상 사용되지 않으며,
organizationHooks를 사용하세요) - 최신 organizationHooks (권장) - 모든 조직 관련 활동에 대한 포괄적인 제어를 제공합니다
조직 생성 및 관리 훅
조직 라이프사이클 작업을 제어합니다:
import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins";
export const auth = betterAuth({
plugins: [
organization({
organizationHooks: {
// 조직 생성 훅
beforeCreateOrganization: async ({ organization, user }) => {
// 조직이 생성되기 전에 사용자 정의 로직 실행
// 선택적으로 조직 데이터 수정
return {
data: {
...organization,
metadata: {
customField: "value",
},
},
};
},
afterCreateOrganization: async ({ organization, member, user }) => {
// 조직이 생성된 후 사용자 정의 로직 실행
// 예: 기본 리소스 생성, 알림 전송
await setupDefaultResources(organization.id);
},
// 조직 업데이트 훅
beforeUpdateOrganization: async ({ organization, user, member }) => {
// 업데이트 검증, 비즈니스 규칙 적용
return {
data: {
...organization,
name: organization.name?.toLowerCase(),
},
};
},
afterUpdateOrganization: async ({ organization, user, member }) => {
// 외부 시스템에 변경사항 동기화
await syncOrganizationToExternalSystems(organization);
},
},
}),
],
});레거시 organizationCreation 훅은 여전히 지원되지만 더 이상 사용되지 않습니다.
새 프로젝트에서는 organizationHooks.beforeCreateOrganization과
organizationHooks.afterCreateOrganization을 사용하세요.
멤버 훅
조직 내 멤버 작업을 제어합니다:
import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins";
export const auth = betterAuth({
plugins: [
organization({
organizationHooks: {
// 멤버가 조직에 추가되기 전
beforeAddMember: async ({ member, user, organization }) => {
// 사용자 정의 검증 또는 수정
console.log(`${user.email}을(를) ${organization.name}에 추가 중`);
// 선택적으로 멤버 데이터 수정
return {
data: {
...member,
role: "custom-role", // 역할 재정의
},
};
},
// 멤버가 추가된 후
afterAddMember: async ({ member, user, organization }) => {
// 환영 이메일 전송, 기본 리소스 생성 등
await sendWelcomeEmail(user.email, organization.name);
},
// 멤버가 제거되기 전
beforeRemoveMember: async ({ member, user, organization }) => {
// 사용자의 리소스 정리, 알림 전송 등
await cleanupUserResources(user.id, organization.id);
},
// 멤버가 제거된 후
afterRemoveMember: async ({ member, user, organization }) => {
await logMemberRemoval(user.id, organization.id);
},
// 멤버의 역할을 업데이트하기 전
beforeUpdateMemberRole: async ({
member,
newRole,
user,
organization,
}) => {
// 역할 변경 권한 검증
if (newRole === "owner" && !hasOwnerUpgradePermission(user)) {
throw new Error("owner 역할로 업그레이드할 수 없습니다");
}
// 선택적으로 역할 수정
return {
data: {
role: newRole,
},
};
},
// 멤버의 역할을 업데이트한 후
afterUpdateMemberRole: async ({
member,
previousRole,
user,
organization,
}) => {
await logRoleChange(user.id, previousRole, member.role);
},
},
}),
],
});초대 훅
초대 라이프사이클을 제어합니다:
export const auth = betterAuth({
plugins: [
organization({
organizationHooks: {
// 초대를 생성하기 전
beforeCreateInvitation: async ({
invitation,
inviter,
organization,
}) => {
// 사용자 정의 검증 또는 만료 로직
const customExpiration = new Date(
Date.now() + 1000 * 60 * 60 * 24 * 7
); // 7일
return {
data: {
...invitation,
expiresAt: customExpiration,
},
};
},
// 초대를 생성한 후
afterCreateInvitation: async ({
invitation,
inviter,
organization,
}) => {
// 사용자 정의 초대 이메일 전송, 메트릭 추적 등
await sendCustomInvitationEmail(invitation, organization);
},
// 초대를 수락하기 전
beforeAcceptInvitation: async ({ invitation, user, organization }) => {
// 수락 전 추가 검증
await validateUserEligibility(user, organization);
},
// 초대를 수락한 후
afterAcceptInvitation: async ({
invitation,
member,
user,
organization,
}) => {
// 사용자 계정 설정, 기본 리소스 할당
await setupNewMemberResources(user, organization);
},
// 초대를 거부하기 전/후
beforeRejectInvitation: async ({ invitation, user, organization }) => {
// 거부 이유 로깅, 초대자에게 알림 전송
},
afterRejectInvitation: async ({ invitation, user, organization }) => {
await notifyInviterOfRejection(invitation.inviterId, user.email);
},
// 초대를 취소하기 전/후
beforeCancelInvitation: async ({
invitation,
cancelledBy,
organization,
}) => {
// 취소 권한 확인
},
afterCancelInvitation: async ({
invitation,
cancelledBy,
organization,
}) => {
await logInvitationCancellation(invitation.id, cancelledBy.id);
},
},
}),
],
});팀 훅
팀 작업을 제어합니다 (팀이 활성화된 경우):
export const auth = betterAuth({
plugins: [
organization({
teams: { enabled: true },
organizationHooks: {
// 팀을 생성하기 전
beforeCreateTeam: async ({ team, user, organization }) => {
// 팀 이름 검증, 명명 규칙 적용
return {
data: {
...team,
name: team.name.toLowerCase().replace(/\s+/g, "-"),
},
};
},
// 팀을 생성한 후
afterCreateTeam: async ({ team, user, organization }) => {
// 기본 팀 리소스, 채널 등 생성
await createDefaultTeamResources(team.id);
},
// 팀을 업데이트하기 전
beforeUpdateTeam: async ({ team, updates, user, organization }) => {
// 업데이트 검증, 비즈니스 규칙 적용
return {
data: {
...updates,
name: updates.name?.toLowerCase(),
},
};
},
// 팀을 업데이트한 후
afterUpdateTeam: async ({ team, user, organization }) => {
await syncTeamChangesToExternalSystems(team);
},
// 팀을 삭제하기 전
beforeDeleteTeam: async ({ team, user, organization }) => {
// 팀 데이터 백업, 멤버에게 알림
await backupTeamData(team.id);
},
// 팀을 삭제한 후
afterDeleteTeam: async ({ team, user, organization }) => {
await cleanupTeamResources(team.id);
},
// 팀 멤버 작업
beforeAddTeamMember: async ({
teamMember,
team,
user,
organization,
}) => {
// 팀 멤버십 제한, 권한 검증
const memberCount = await getTeamMemberCount(team.id);
if (memberCount >= 10) {
throw new Error("팀이 가득 찼습니다");
}
},
afterAddTeamMember: async ({
teamMember,
team,
user,
organization,
}) => {
await grantTeamAccess(user.id, team.id);
},
beforeRemoveTeamMember: async ({
teamMember,
team,
user,
organization,
}) => {
// 사용자의 팀 관련 데이터 백업
await backupTeamMemberData(user.id, team.id);
},
afterRemoveTeamMember: async ({
teamMember,
team,
user,
organization,
}) => {
await revokeTeamAccess(user.id, team.id);
},
},
}),
],
});훅 오류 처리
모든 훅은 오류 처리를 지원합니다. before 훅에서 오류를 던지면 작업이 진행되지 않습니다:
import { APIError } from "better-auth/api";
export const auth = betterAuth({
plugins: [
organization({
organizationHooks: {
beforeAddMember: async ({ member, user, organization }) => {
// 사용자의 위반 사항 확인
const violations = await checkUserViolations(user.id);
if (violations.length > 0) {
throw new APIError("BAD_REQUEST", {
message:
"사용자에게 미해결 위반 사항이 있어 조직에 가입할 수 없습니다",
});
}
},
beforeCreateTeam: async ({ team, user, organization }) => {
// 팀 이름 고유성 검증
const existingTeam = await findTeamByName(team.name, organization.id);
if (existingTeam) {
throw new APIError("BAD_REQUEST", {
message: "이 조직에 이미 존재하는 팀 이름입니다",
});
}
},
},
}),
],
});사용자의 조직 목록 보기
사용자가 멤버인 조직을 나열하려면 useListOrganizations 훅을 사용할 수 있습니다. 사용자가 멤버인 조직을 반응적으로 가져올 수 있습니다.
import { authClient } from "@/lib/auth-client"
function App(){
const { data: organizations } = authClient.useListOrganizations()
return (
<div>
{organizations.map((org) => (
<p>{org.name}</p>
))}
</div>)
}<script lang="ts">
import { authClient } from "$lib/auth-client";
const organizations = authClient.useListOrganizations();
</script>
<h1>조직</h1>
{#if $organizations.isPending}
<p>로딩 중...</p>
{:else if !$organizations.data?.length}
<p>조직이 없습니다.</p>
{:else}
<ul>
{#each $organizations.data as organization}
<li>{organization.name}</li>
{/each}
</ul>
{/if}<script lang="ts">;
export default {
setup() {
const organizations = authClient.useListOrganizations()
return { organizations };
}
};
</script>
<template>
<div>
<h1>조직</h1>
<div v-if="organizations.isPending">로딩 중...</div>
<div v-else-if="organizations.data === null">조직이 없습니다.</div>
<ul v-else>
<li v-for="organization in organizations.data" :key="organization.id">
{{ organization.name }}
</li>
</ul>
</div>
</template>또는 훅을 사용하지 않으려면 organization.list를 호출할 수 있습니다.
const { data, error } = await authClient.organization.list();활성 조직
활성 조직은 사용자가 현재 작업 중인 작업 공간입니다. 기본적으로 사용자가 로그인하면 활성 조직은 null로 설정됩니다. 활성 조직을 사용자 세션에 설정할 수 있습니다.
항상 활성 조직을 세션에 유지하고 싶지 않을 수 있습니다. 클라이언트 측에서만 활성 조직을 관리할 수 있습니다. 예를 들어, 여러 탭에서 다른 활성 조직을 가질 수 있습니다.
활성 조직 설정하기
organization.setActive 함수를 호출하여 활성 조직을 설정할 수 있습니다. 이렇게 하면 사용자 세션의 활성 조직이 설정됩니다.
일부 애플리케이션에서는 활성 조직을 해제하는 기능이 필요할 수 있습니다.
이 경우 organizationId를 null로 설정하여 이 엔드포인트를 호출할 수 있습니다.
const { data, error } = await authClient.organization.setActive({ organizationId: "org-id", organizationSlug: "org-slug",});| Prop | Description | Type |
|---|---|---|
organizationId? | 활성으로 설정할 조직 ID입니다. 활성 조직을 해제하려면 null일 수 있습니다. | string | null |
organizationSlug? | 활성으로 설정할 조직 슬러그입니다. organizationId가 제공되지 않으면 활성 조직을 해제하려면 null일 수 있습니다. | string |
세션이 생성될 때 활성 조직을 설정하려면 데이터베이스 훅을 사용할 수 있습니다.
export const auth = betterAuth({
databaseHooks: {
session: {
create: {
before: async (session) => {
const organization = await getActiveOrganization(session.userId);
return {
data: {
...session,
activeOrganizationId: organization.id,
},
};
},
},
},
},
});활성 조직 사용하기
사용자의 활성 조직을 가져오려면 useActiveOrganization 훅을 호출할 수 있습니다. 사용자의 활성 조직을 반환합니다. 활성 조직이 변경되면 훅이 재평가되고 새 활성 조직을 반환합니다.
import { authClient } from "@/lib/auth-client"
function App(){
const { data: activeOrganization } = authClient.useActiveOrganization()
return (
<div>
{activeOrganization ? <p>{activeOrganization.name}</p> : null}
</div>
)
}<script lang="ts">
import { authClient } from "$lib/auth-client";
const activeOrganization = authClient.useActiveOrganization();
</script>
<h2>활성 조직</h2>
{#if $activeOrganization.isPending}
<p>로딩 중...</p>
{:else if $activeOrganization.data === null}
<p>활성 조직이 없습니다.</p>
{:else}
<p>{$activeOrganization.data.name}</p>
{/if}<script lang="ts">;
export default {
setup() {
const activeOrganization = authClient.useActiveOrganization();
return { activeOrganization };
}
};
</script>
<template>
<div>
<h2>활성 조직</h2>
<div v-if="activeOrganization.isPending">로딩 중...</div>
<div v-else-if="activeOrganization.data === null">활성 조직이 없습니다.</div>
<div v-else>
{{ activeOrganization.data.name }}
</div>
</div>
</template>전체 조직 정보 가져오기
조직의 전체 세부 정보를 가져오려면 getFullOrganization 함수를 사용할 수 있습니다.
기본적으로 속성을 전달하지 않으면 활성 조직을 사용합니다.
const { data, error } = await authClient.organization.getFullOrganization({ organizationId: "org-id", organizationSlug: "org-slug", membersLimit: 100,});| Prop | Description | Type |
|---|---|---|
organizationId? | 가져올 조직 ID입니다. 기본적으로 활성 조직을 사용합니다. | string |
organizationSlug? | 가져올 조직 슬러그입니다. | string |
membersLimit? | 가져올 멤버의 제한입니다. 기본적으로 100으로 기본 설정되는 membershipLimit 옵션을 사용합니다. | number |
조직 업데이트하기
조직 정보를 업데이트하려면 organization.update를 사용할 수 있습니다.
const { data, error } = await authClient.organization.update({ data: { // required name: "updated-name", slug: "updated-slug", logo: "new-logo.url", metadata: { customerId: "test" }, }, organizationId: "org-id",});| Prop | Description | Type |
|---|---|---|
data | 조직을 업데이트할 데이터의 일부 목록입니다. | Object |
data.name? | 조직의 이름입니다. | string |
data.slug? | 조직의 슬러그입니다. | string |
data.logo? | 조직의 로고입니다. | string |
data.metadata? | 조직의 메타데이터입니다. | Record<string, any> | null |
organizationId? | 업데이트할 조직 ID입니다. | string |
조직 삭제하기
사용자 소유 조직을 제거하려면 organization.delete를 사용할 수 있습니다.
const { data, error } = await authClient.organization.delete({ organizationId: "org-id", // required});| Prop | Description | Type |
|---|---|---|
organizationId | 삭제할 조직 ID입니다. | string |
사용자가 지정된 조직에서 필요한 권한(기본값: 역할이 owner)을 가지고 있으면 모든 멤버, 초대 및 조직 정보가 제거됩니다.
organizationDeletion 옵션을 통해 조직 삭제 처리 방법을 구성할 수 있습니다:
const auth = betterAuth({
plugins: [
organization({
disableOrganizationDeletion: true, //완전히 비활성화하려면
organizationHooks: {
beforeDeleteOrganization: async (data, request) => {
// 조직을 삭제하기 전에 실행할 콜백
},
afterDeleteOrganization: async (data, request) => {
// 조직을 삭제한 후 실행할 콜백
},
},
}),
],
});초대
조직에 멤버를 추가하려면 먼저 사용자에게 초대를 보내야 합니다. 사용자는 초대 링크가 포함된 이메일/SMS를 받게 됩니다. 사용자가 초대를 수락하면 조직에 추가됩니다.
초대 이메일 설정하기
멤버 초대가 작동하려면 먼저 better-auth 인스턴스에 sendInvitationEmail을 제공해야 합니다. 이 함수는 사용자에게 초대 이메일을 보내는 역할을 합니다.
초대 링크를 구성하여 사용자에게 보내야 합니다. 링크에는 초대 ID가 포함되어야 하며, 사용자가 클릭하면 acceptInvitation 함수와 함께 사용됩니다.
import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins";
import { sendOrganizationInvitation } from "./email";
export const auth = betterAuth({
plugins: [
organization({
async sendInvitationEmail(data) {
const inviteLink = `https://example.com/accept-invitation/${data.id}`;
sendOrganizationInvitation({
email: data.email,
invitedByUsername: data.inviter.user.name,
invitedByEmail: data.inviter.user.email,
teamName: data.organization.name,
inviteLink,
});
},
}),
],
});초대 보내기
조직에 사용자를 초대하려면 클라이언트에서 제공하는 invite 함수를 사용할 수 있습니다. invite 함수는 다음 속성을 가진 객체를 받습니다:
const { data, error } = await authClient.organization.inviteMember({ email: "example@gmail.com", // required role: "member", // required organizationId: "org-id", resend: true, teamId: "team-id",});| Prop | Description | Type |
|---|---|---|
email | 초대할 사용자의 이메일 주소입니다. | string |
role | 사용자에게 할당할 역할입니다. admin, member, 또는 guest일 수 있습니다. | string | string[] |
organizationId? | 사용자를 초대할 조직 ID입니다. 기본값은 활성 조직입니다. | string |
resend? | 사용자가 이미 초대된 경우 초대 이메일을 다시 보냅니다. | boolean |
teamId? | 사용자를 초대할 팀 ID입니다. | string |
- 사용자가 이미 조직의 멤버인 경우 초대가 취소됩니다. - 사용자가 이미 조직에
초대된 경우
resend가true로 설정되지 않으면 초대가 다시 전송되지 않습니다. -cancelPendingInvitationsOnReInvite가true로 설정된 경우 사용자가 이미 조직에 초대되어 있고 새 초대가 전송되면 초대가 취소됩니다.
초대 수락하기
사용자가 초대 이메일을 받으면 초대 링크를 클릭하여 초대를 수락할 수 있습니다. 초대 링크에는 초대를 수락하는 데 사용될 초대 ID가 포함되어야 합니다.
사용자가 로그인한 후 acceptInvitation 함수를 호출해야 합니다.
const { data, error } = await authClient.organization.acceptInvitation({ invitationId: "invitation-id", // required});| Prop | Description | Type |
|---|---|---|
invitationId | 수락할 초대 ID입니다. | string |
이메일 확인 요구 사항
조직 구성에서 requireEmailVerificationOnInvitation 옵션이 활성화된 경우 사용자는 초대를 수락하기 전에 이메일 주소를 확인해야 합니다. 이는 확인된 사용자만 조직에 가입할 수 있도록 추가 보안 계층을 추가합니다.
import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins";
export const auth = betterAuth({
plugins: [
organization({
requireEmailVerificationOnInvitation: true,
async sendInvitationEmail(data) {
// ... 이메일 전송 로직
},
}),
],
});초대 수락 콜백
초대가 수락될 때 콜백 함수를 실행하도록 Better Auth를 구성할 수 있습니다. 이는 이벤트 로깅, 분석 업데이트, 알림 전송 또는 누군가가 조직에 가입할 때 실행해야 하는 기타 사용자 정의 로직에 유용합니다.
import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins";
export const auth = betterAuth({
plugins: [
organization({
async sendInvitationEmail(data) {
// ... 초대 이메일 로직
},
async onInvitationAccepted(data) {
// 이 콜백은 초대가 수락될 때 트리거됩니다
},
}),
],
});콜백은 다음 데이터를 받습니다:
id: 초대 IDrole: 사용자에게 할당된 역할organization: 사용자가 가입한 조직invitation: 초대 객체inviter: 초대를 보낸 멤버 (사용자 세부 정보 포함)acceptedUser: 초대를 수락한 사용자
초대 취소하기
사용자가 초대를 보낸 경우 이 메서드를 사용하여 취소할 수 있습니다.
사용자가 초대를 거부하는 방법을 찾고 있다면 여기에서 찾을 수 있습니다.
await authClient.organization.cancelInvitation({ invitationId: "invitation-id", // required});| Prop | Description | Type |
|---|---|---|
invitationId | 취소할 초대 ID입니다. | string |
초대 거부하기
이 사용자가 초대를 받았지만 거절하려는 경우 이 메서드를 사용하여 거부할 수 있습니다.
await authClient.organization.rejectInvitation({ invitationId: "invitation-id", // required});| Prop | Description | Type |
|---|---|---|
invitationId | 거부할 초대 ID입니다. | string |
초대 수락과 마찬가지로 초대 거부도 requireEmailVerificationOnInvitation 옵션이
활성화된 경우 이메일 확인이 필요합니다. 확인되지 않은 이메일을 가진 사용자는 초대를
거부하려고 할 때 오류를 받게 됩니다.
초대 가져오기
초대를 가져오려면 클라이언트에서 제공하는 organization.getInvitation 함수를 사용할 수 있습니다. 초대 ID를 쿼리 매개변수로 제공해야 합니다.
const { data, error } = await authClient.organization.getInvitation({ id: "invitation-id", // required});| Prop | Description | Type |
|---|---|---|
id | 가져올 초대 ID입니다. | string |
초대 목록 보기
지정된 조직의 모든 초대를 나열하려면 클라이언트에서 제공하는 listInvitations 함수를 사용할 수 있습니다.
const { data, error } = await authClient.organization.listInvitations({ organizationId: "organization-id",});| Prop | Description | Type |
|---|---|---|
organizationId? | 초대를 나열할 조직의 선택적 ID입니다. 제공되지 않으면 사용자의 활성 조직으로 기본 설정됩니다. | string |
사용자 초대 목록 보기
지정된 사용자의 모든 초대를 나열하려면 클라이언트에서 제공하는 listUserInvitations 함수를 사용할 수 있습니다.
const invitations = await authClient.organization.listUserInvitations();서버에서는 사용자 ID를 쿼리 매개변수로 전달할 수 있습니다.
const invitations = await auth.api.listUserInvitations({
query: {
email: "user@example.com",
},
});email 쿼리 매개변수는 특정 사용자에 대한 초대를 쿼리하기 위해 서버에서만
사용할 수 있습니다.
멤버
멤버 목록 보기
조직의 모든 멤버를 나열하려면 listMembers 함수를 사용할 수 있습니다.
const { data, error } = await authClient.organization.listMembers({ organizationId: "organization-id", limit: 100, offset: 0, sortBy: "createdAt", sortDirection: "desc", filterField: "createdAt", filterOperator: "eq", filterValue: "value",});| Prop | Description | Type |
|---|---|---|
organizationId? | 멤버를 나열할 선택적 조직 ID입니다. 제공되지 않으면 사용자의 활성 조직으로 기본 설정됩니다. | string |
limit? | 반환할 멤버의 제한입니다. | number |
offset? | 시작할 오프셋입니다. | number |
sortBy? | 정렬할 필드입니다. | string |
sortDirection? | 정렬 방향입니다. | "asc" | "desc" |
filterField? | 필터링할 필드입니다. | string |
filterOperator? | 필터링할 연산자입니다. | "eq" | "ne" | "gt" | "gte" | "lt" | "lte" | "in" | "nin" | "contains" |
filterValue? | 필터링할 값입니다. | string |
멤버 제거하기
멤버를 제거하려면 organization.removeMember를 사용할 수 있습니다.
const { data, error } = await authClient.organization.removeMember({ memberIdOrEmail: "user@example.com", // required organizationId: "org-id",});| Prop | Description | Type |
|---|---|---|
memberIdOrEmail | 제거할 멤버의 ID 또는 이메일입니다. | string |
organizationId? | 멤버를 제거할 조직의 ID입니다. 제공되지 않으면 활성 조직이 사용됩니다. | string |
멤버 역할 업데이트하기
조직에서 멤버의 역할을 업데이트하려면 organization.updateMemberRole을 사용할 수 있습니다. 사용자가 멤버의 역할을 업데이트할 권한이 있으면 역할이 업데이트됩니다.
await authClient.organization.updateMemberRole({ role: ["admin", "sale"], // required memberId: "member-id", // required organizationId: "organization-id",});| Prop | Description | Type |
|---|---|---|
role | 적용할 새 역할입니다. 역할을 나타내는 문자열 또는 문자열 배열일 수 있습니다. | string | string[] |
memberId | 역할 업데이트를 적용할 멤버 ID입니다. | string |
organizationId? | 멤버가 속한 선택적 조직 ID입니다. 제공되지 않으면 활성 조직을 가져오기 위해 세션 헤더를 제공해야 합니다. | string |
활성 멤버 가져오기
활성 조직의 현재 멤버를 가져오려면 organization.getActiveMember 함수를 사용할 수 있습니다. 이 함수는 활성 조직에서 사용자의 멤버 세부 정보를 반환합니다.
const { data: member, error } = await authClient.organization.getActiveMember();활성 멤버 역할 가져오기
활성 조직의 현재 역할 멤버를 가져오려면 organization.getActiveMemberRole 함수를 사용할 수 있습니다. 이 함수는 활성 조직에서 사용자의 멤버 역할을 반환합니다.
const { data: { role }, error } = await authClient.organization.getActiveMemberRole();멤버 추가하기
초대를 보내지 않고 조직에 멤버를 직접 추가하려면 서버에서만 호출할 수 있는 addMember 함수를 사용할 수 있습니다.
const data = await auth.api.addMember({ body: { userId: "user-id", role: ["admin", "sale"], // required organizationId: "org-id", teamId: "team-id", },});| Prop | Description | Type |
|---|---|---|
userId? | 멤버로 추가될 사용자를 나타내는 사용자 ID입니다. null이 제공되면 세션 헤더를 제공해야 합니다. | string | null |
role | 새 멤버에게 할당할 역할입니다. | string | string[] |
organizationId? | 전달할 선택적 조직 ID입니다. 제공되지 않으면 사용자의 활성 조직으로 기본 설정됩니다. | string |
teamId? | 멤버를 추가할 선택적 팀 ID입니다. | string |
조직 탈퇴하기
조직을 탈퇴하려면 organization.leave 함수를 사용할 수 있습니다. 이 함수는 조직에서 현재 사용자를 제거합니다.
await authClient.organization.leave({ organizationId: "organization-id", // required});| Prop | Description | Type |
|---|---|---|
organizationId | 멤버가 탈퇴할 조직 ID입니다. | string |
접근 제어
organization 플러그인은 매우 유연한 접근 제어 시스템을 제공합니다. 조직에서 사용자가 가진 역할에 따라 사용자의 접근 권한을 제어할 수 있습니다. 사용자의 역할에 따라 자체 권한 세트를 정의할 수 있습니다.
역할
기본적으로 조직에는 세 가지 역할이 있습니다:
owner: 기본적으로 조직을 만든 사용자입니다. 소유자는 조직에 대한 전체 제어 권한을 가지며 모든 작업을 수행할 수 있습니다.
admin: 관리자 역할을 가진 사용자는 조직 삭제 또는 소유자 변경을 제외한 조직에 대한 전체 제어 권한을 갖습니다.
member: 멤버 역할을 가진 사용자는 조직에 대한 제한된 제어 권한을 갖습니다. 프로젝트를 만들고, 사용자를 초대하고, 자신이 만든 프로젝트를 관리할 수 있습니다.
사용자는 여러 역할을 가질 수 있습니다. 여러 역할은 쉼표(",")로 구분된 문자열로 저장됩니다.
권한
기본적으로 세 가지 리소스가 있으며, 각각 2~3개의 작업이 있습니다.
organization:
update delete
member:
create update delete
invitation:
create cancel
소유자는 모든 리소스와 작업에 대한 전체 제어 권한을 갖습니다. 관리자는 조직 삭제 또는 소유자 변경을 제외한 모든 리소스에 대한 전체 제어 권한을 갖습니다. 멤버는 데이터 읽기를 제외하고는 이러한 작업에 대한 제어 권한이 없습니다.
사용자 정의 권한
플러그인은 각 역할에 대한 자체 권한 세트를 정의하는 쉬운 방법을 제공합니다.
접근 제어 생성하기
먼저 createAccessControl 함수를 호출하고 statement 객체를 전달하여 접근 제어기를 만들어야 합니다. statement 객체는 리소스 이름을 키로, 작업 배열을 값으로 가져야 합니다.
import { createAccessControl } from "better-auth/plugins/access";
/**
* typescript가 타입을 올바르게 추론할 수 있도록 `as const`를 사용하세요
*/
const statement = {
project: ["create", "share", "update", "delete"],
} as const;
const ac = createAccessControl(statement); 역할 생성하기
접근 제어기를 만든 후 정의한 권한을 가진 역할을 만들 수 있습니다.
import { createAccessControl } from "better-auth/plugins/access";
const statement = {
project: ["create", "share", "update", "delete"],
} as const;
const ac = createAccessControl(statement);
const member = ac.newRole({
project: ["create"],
});
const admin = ac.newRole({
project: ["create", "update"],
});
const owner = ac.newRole({
project: ["create", "update", "delete"],
});
const myCustomRole = ac.newRole({
project: ["create", "update", "delete"],
organization: ["update"],
}); 기존 역할에 대한 사용자 정의 역할을 만들면 해당 역할에 대해 미리 정의된 권한이 재정의됩니다. 사용자 정의 역할에 기존 권한을 추가하려면 defaultStatements를 가져와 새 statement와 병합하고 역할의 권한 세트를 기본 역할과 병합해야 합니다.
import { createAccessControl } from "better-auth/plugins/access";
import { defaultStatements, adminAc } from 'better-auth/plugins/organization/access'
const statement = {
...defaultStatements,
project: ["create", "share", "update", "delete"],
} as const;
const ac = createAccessControl(statement);
const admin = ac.newRole({
project: ["create", "update"],
...adminAc.statements,
});플러그인에 역할 전달하기
역할을 만든 후 클라이언트와 서버 모두에서 organization 플러그인에 전달할 수 있습니다.
import { betterAuth } from "better-auth"
import { organization } from "better-auth/plugins"
import { ac, owner, admin, member } from "@/auth/permissions"
export const auth = betterAuth({
plugins: [
organization({
ac,
roles: {
owner,
admin,
member,
myCustomRole
}
}),
],
});클라이언트 플러그인에도 접근 제어기와 역할을 전달해야 합니다.
import { createAuthClient } from "better-auth/client"
import { organizationClient } from "better-auth/client/plugins"
import { ac, owner, admin, member, myCustomRole } from "@/auth/permissions"
export const authClient = createAuthClient({
plugins: [
organizationClient({
ac,
roles: {
owner,
admin,
member,
myCustomRole
}
})
]
})접근 제어 사용법
권한 확인하기:
api에서 제공하는 hasPermission 작업을 사용하여 사용자의 권한을 확인할 수 있습니다.
import { auth } from "@/auth";
await auth.api.hasPermission({
headers: await headers(),
body: {
permissions: {
project: ["create"], // 접근 제어의 구조와 일치해야 합니다
},
},
});
// 여러 리소스 권한을 동시에 확인할 수도 있습니다
await auth.api.hasPermission({
headers: await headers(),
body: {
permissions: {
project: ["create"], // 접근 제어의 구조와 일치해야 합니다
sale: ["create"],
},
},
});서버에서 클라이언트의 사용자 권한을 확인하려면 클라이언트에서 제공하는 hasPermission 함수를 사용할 수 있습니다.
const canCreateProject = await authClient.organization.hasPermission({
permissions: {
project: ["create"],
},
});
// 여러 리소스 권한을 동시에 확인할 수도 있습니다
const canCreateProjectAndCreateSale =
await authClient.organization.hasPermission({
permissions: {
project: ["create"],
sale: ["create"],
},
});역할 권한 확인하기:
역할과 권한을 정의한 후 서버에서 권한을 확인하지 않고도 클라이언트에서 제공하는 checkRolePermission 함수를 사용할 수 있습니다.
const canCreateProject = authClient.organization.checkRolePermission({
permissions: {
organization: ["delete"],
},
role: "admin",
});
// 여러 리소스 권한을 동시에 확인할 수도 있습니다
const canCreateProjectAndCreateSale =
authClient.organization.checkRolePermission({
permissions: {
organization: ["delete"],
member: ["delete"],
},
role: "admin",
});모든 것이 클라이언트 측에서 동기적으로 실행되므로 동적 역할은 포함되지 않습니다. 동적 역할 및 권한 확인을 포함하려면 hasPermission API를 사용하세요.
동적 접근 제어
동적 접근 제어를 사용하면 조직에 대한 역할을 런타임에 생성할 수 있습니다. 이는 조직과 관련된 생성된 역할과 권한을 데이터베이스 테이블에 저장하여 달성됩니다.
동적 접근 제어 활성화하기
동적 접근 제어를 활성화하려면 서버 및 클라이언트 플러그인 모두에 enabled가 true로 설정된 dynamicAccessControl 구성 옵션을 전달하세요.
서버 auth 플러그인에 ac 인스턴스를 미리 정의해야 합니다.
이는 사용 가능한 권한을 추론할 수 있는 방법이기 때문에 중요합니다.
import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins";
import { ac } from "@/auth/permissions";
export const auth = betterAuth({
plugins: [
organization({
ac, // 동적 접근 제어가 작동하려면 정의해야 합니다
dynamicAccessControl: {
enabled: true,
},
})
]
})import { createAuthClient } from "better-auth/client";
import { organizationClient } from "better-auth/client/plugins";
export const authClient = createAuthClient({
plugins: [
organizationClient({
dynamicAccessControl: {
enabled: true,
},
})
]
})데이터베이스에 새 organizationRole 테이블을 추가하려면 마이그레이션을 실행해야 합니다.
authClient.organization.checkRolePermission 함수는 모든 것이 클라이언트 측에서 동기적으로 실행되므로 동적 역할을 포함하지 않습니다.
동적 역할 확인을 포함하려면 hasPermission API를 사용하세요.
역할 생성하기
런타임에 조직에 대한 새 역할을 만들려면 createRole 함수를 사용할 수 있습니다.
ac 리소스에 create 권한이 포함된 역할을 가진 사용자만 새 역할을 만들 수 있습니다.
기본적으로 admin 및 owner 역할만 이 권한을 갖습니다. 또한 해당 조직에서 현재 역할이 이미 액세스할 수 없는 권한은 추가할 수 없습니다.
팁: organization 플러그인 구성에서 dynamicAccessControl.validateRoleName 옵션을 사용하여 역할 이름을 검증할 수 있습니다.
여기에서 자세히 알아보세요.
// 사용자 정의 리소스 또는 권한을 사용하려면,// organization 구성의 `ac` 인스턴스에 정의되어 있는지 확인하세요.const permission = { project: ["create", "update", "delete"]}await authClient.organization.createRole({ role: "my-unique-role", // required permission: permission, organizationId: "organization-id",});| Prop | Description | Type |
|---|---|---|
role | 생성할 역할의 고유한 이름입니다. | string |
permission? | 역할에 할당할 권한입니다. | Record<string, string[]> |
organizationId? | 역할이 생성될 조직 ID입니다. 기본값은 활성 조직입니다. | string |
이제 자유롭게 updateMemberRole을 호출하여 새로 생성한 역할로 멤버의 역할을 업데이트할 수 있습니다!
역할 삭제하기
역할을 삭제하려면 deleteRole 함수를 사용하고 roleName 또는 roleId 매개변수와 함께 organizationId 매개변수를 제공할 수 있습니다.
await authClient.organization.deleteRole({ roleName: "my-role", roleId: "role-id", organizationId: "organization-id",});| Prop | Description | Type |
|---|---|---|
roleName? | 삭제할 역할의 이름입니다. 또는 대신 roleId 매개변수를 전달할 수 있습니다. | string |
roleId? | 삭제할 역할의 ID입니다. 또는 대신 roleName 매개변수를 전달할 수 있습니다. | string |
organizationId? | 역할이 삭제될 조직 ID입니다. 기본값은 활성 조직입니다. | string |
역할 목록 보기
역할을 나열하려면 listOrgRoles 함수를 사용할 수 있습니다.
멤버가 역할을 나열하려면 ac 리소스에 read 권한이 필요합니다.
const { data: roles, error } = await authClient.organization.listRoles({ organizationId: "organization-id",});| Prop | Description | Type |
|---|---|---|
organizationId? | 나열할 역할이 속한 조직 ID입니다. 기본값은 사용자의 활성 조직입니다. | string |
특정 역할 가져오기
특정 역할을 가져오려면 getOrgRole 함수를 사용하고 roleName 또는 roleId 매개변수를 전달할 수 있습니다.
멤버가 역할을 가져오려면 ac 리소스에 read 권한이 필요합니다.
const { data: role, error } = await authClient.organization.getRole({ roleName: "my-role", roleId: "role-id", organizationId: "organization-id",});| Prop | Description | Type |
|---|---|---|
roleName? | 가져올 역할의 이름입니다. 또는 대신 roleId 매개변수를 전달할 수 있습니다. | string |
roleId? | 가져올 역할의 ID입니다. 또는 대신 roleName 매개변수를 전달할 수 있습니다. | string |
organizationId? | 역할이 삭제될 조직 ID입니다. 기본값은 활성 조직입니다. | string |
역할 업데이트하기
역할을 업데이트하려면 updateOrgRole 함수를 사용하고 roleName 또는 roleId 매개변수를 전달할 수 있습니다.
const { data: updatedRole, error } = await authClient.organization.updateRole({ roleName: "my-role", roleId: "role-id", organizationId: "organization-id", data: { // required permission: { project: ["create", "update", "delete"] }, roleName: "my-new-role", },});| Prop | Description | Type |
|---|---|---|
roleName? | 업데이트할 역할의 이름입니다. 또는 대신 roleId 매개변수를 전달할 수 있습니다. | string |
roleId? | 업데이트할 역할의 ID입니다. 또는 대신 roleName 매개변수를 전달할 수 있습니다. | string |
organizationId? | 역할이 업데이트될 조직 ID입니다. 기본값은 활성 조직입니다. | string |
data | 업데이트될 데이터입니다 | Object |
data.permission? | 선택적으로 역할의 권한을 업데이트합니다. | Record<string, string[]> |
data.roleName? | 선택적으로 역할의 이름을 업데이트합니다. | string |
구성 옵션
다음은 dynamicAccessControl 객체에 전달할 수 있는 옵션 목록입니다.
enabled
이 옵션은 동적 접근 제어를 활성화하거나 비활성화하는 데 사용됩니다. 기본적으로 비활성화되어 있습니다.
organization({
dynamicAccessControl: {
enabled: true
}
})maximumRolesPerOrganization
이 옵션은 조직에 대해 생성할 수 있는 역할 수를 제한하는 데 사용됩니다.
기본적으로 조직에 대해 생성할 수 있는 최대 역할 수는 무제한입니다.
organization({
dynamicAccessControl: {
maximumRolesPerOrganization: 10
}
})숫자를 반환하는 함수를 전달할 수도 있습니다.
organization({
dynamicAccessControl: {
maximumRolesPerOrganization: async (organizationId) => {
const organization = await getOrganization(organizationId);
return organization.plan === "pro" ? 100 : 10;
}
}
})추가 필드
organizationRole 테이블에 추가 필드를 추가하려면 organization 플러그인에 additionalFields 구성 옵션을 전달할 수 있습니다.
organization({
schema: {
organizationRole: {
additionalFields: {
// 역할 색상!
color: {
type: "string",
defaultValue: "#ffffff",
},
//... 기타 필드
},
},
},
})그런 다음 추가 필드를 추론하기 위해 inferOrgAdditionalFields를 아직 사용하지 않는 경우 이를 사용하여 추가 필드를 추론할 수 있습니다.
import { createAuthClient } from "better-auth/client"
import { organizationClient, inferOrgAdditionalFields } from "better-auth/client/plugins"
import type { auth } from "./auth"
export const authClient = createAuthClient({
plugins: [
organizationClient({
schema: inferOrgAdditionalFields<typeof auth>()
})
]
})그렇지 않으면 서버의 org 플러그인에서와 동일한 방식으로 스키마 값을 직접 전달할 수 있습니다.
import { createAuthClient } from "better-auth/client"
import { organizationClient } from "better-auth/client/plugins"
export const authClient = createAuthClient({
plugins: [
organizationClient({
schema: {
organizationRole: {
additionalFields: {
color: {
type: "string",
defaultValue: "#ffffff",
}
}
}
}
})
]
})팀
팀을 사용하면 조직 내에서 멤버를 그룹화할 수 있습니다. 팀 기능은 추가적인 조직 구조를 제공하며 보다 세밀한 수준에서 권한을 관리하는 데 사용할 수 있습니다.
팀 활성화하기
팀을 활성화하려면 서버 및 클라이언트 플러그인 모두에 teams 구성 옵션을 전달하세요:
import { betterAuth } from "better-auth";
import { organization } from "better-auth/plugins";
export const auth = betterAuth({
plugins: [
organization({
teams: {
enabled: true,
maximumTeams: 10, // 선택 사항: 조직당 팀 제한
allowRemovingAllTeams: false, // 선택 사항: 마지막 팀 제거 방지
},
}),
],
});import { createAuthClient } from "better-auth/client";
import { organizationClient } from "better-auth/client/plugins";
export const authClient = createAuthClient({
plugins: [
organizationClient({
teams: {
enabled: true,
},
}),
],
});팀 관리하기
팀 생성하기
조직 내에서 새 팀을 만듭니다:
const { data, error } = await authClient.organization.createTeam({ name: "my-team", // required organizationId: "organization-id",});| Prop | Description | Type |
|---|---|---|
name | 팀의 이름입니다. | string |
organizationId? | 팀이 생성될 조직 ID입니다. 기본값은 활성 조직입니다. | string |
팀 목록 보기
조직의 모든 팀을 가져옵니다:
const { data, error } = await authClient.organization.listTeams({ query: { organizationId: "organization-id", },});| Prop | Description | Type |
|---|---|---|
query? | 팀 목록을 필터링하거나 범위를 지정하기 위한 쿼리 매개변수입니다. | Object |
query.organizationId? | 나열할 팀이 속한 조직 ID입니다. 기본값은 사용자의 활성 조직입니다. | string |
팀 업데이트하기
팀의 세부 정보를 업데이트합니다:
const { data, error } = await authClient.organization.updateTeam({ teamId: "team-id", // required data: { // required name: "My new team name", organizationId: "My new organization ID for this team", createdAt: new Date(), updatedAt: new Date(), },});| Prop | Description | Type |
|---|---|---|
teamId | 업데이트할 팀의 ID입니다. | string |
data | 업데이트할 옵션이 포함된 부분 객체입니다. | Object |
data.name? | 업데이트할 팀의 이름입니다. | string |
data.organizationId? | 팀이 속한 조직 ID입니다. | string |
data.createdAt? | 팀이 생성된 타임스탬프입니다. | Date |
data.updatedAt? | 팀이 마지막으로 업데이트된 타임스탬프입니다. | Date |
팀 제거하기
조직에서 팀을 삭제합니다:
const { data, error } = await authClient.organization.removeTeam({ teamId: "team-id", // required organizationId: "organization-id",});| Prop | Description | Type |
|---|---|---|
teamId | 제거할 팀의 팀 ID입니다. | string |
organizationId? | 팀이 속한 조직 ID입니다. 제공되지 않으면 사용자의 활성 조직으로 기본 설정됩니다. | string |
활성 팀 설정하기
주어진 팀을 현재 활성 팀으로 설정합니다. teamId가 null이면 현재 활성 팀이 해제됩니다.
const { data, error } = await authClient.organization.setActiveTeam({ teamId: "team-id",});| Prop | Description | Type |
|---|---|---|
teamId? | 현재 활성 팀으로 설정할 팀 ID입니다. | string |
사용자 팀 목록 보기
현재 사용자가 속한 모든 팀을 나열합니다.
const { data, error } = await authClient.organization.listUserTeams();팀 멤버 목록 보기
주어진 팀의 멤버를 나열합니다.
const { data, error } = await authClient.organization.listTeamMembers({ teamId: "team-id",});| Prop | Description | Type |
|---|---|---|
teamId? | 멤버를 반환할 팀입니다. 제공되지 않으면 현재 활성 팀의 멤버가 반환됩니다. | string |
팀 멤버 추가하기
팀에 멤버를 추가합니다.
const { data, error } = await authClient.organization.addTeamMember({ teamId: "team-id", // required userId: "user-id", // required});| Prop | Description | Type |
|---|---|---|
teamId | 사용자가 멤버가 되어야 하는 팀입니다. | string |
userId | 멤버로 추가될 사용자를 나타내는 사용자 ID입니다. | string |
팀 멤버 제거하기
팀에서 멤버를 제거합니다.
const { data, error } = await authClient.organization.removeTeamMember({ teamId: "team-id", // required userId: "user-id", // required});| Prop | Description | Type |
|---|---|---|
teamId | 사용자가 제거되어야 하는 팀입니다. | string |
userId | 팀에서 제거되어야 하는 사용자입니다. | string |
팀 권한
팀은 조직의 권한 시스템을 따릅니다. 팀을 관리하려면 사용자에게 다음 권한이 필요합니다:
team:create- 새 팀 생성team:update- 팀 세부 정보 업데이트team:delete- 팀 제거
기본적으로:
- 조직 소유자와 관리자는 팀을 관리할 수 있습니다
- 일반 멤버는 팀을 생성, 업데이트 또는 삭제할 수 없습니다
팀 구성 옵션
팀 기능은 여러 구성 옵션을 지원합니다:
-
maximumTeams: 조직당 팀 수 제한teams: { enabled: true, maximumTeams: 10 // 고정 숫자 // 또는 maximumTeams: async ({ organizationId, session }, request) => { // 조직 플랜에 따른 동적 제한 const plan = await getPlan(organizationId) return plan === 'pro' ? 20 : 5 }, maximumMembersPerTeam: 10 // 고정 숫자 // 또는 maximumMembersPerTeam: async ({ teamId, session, organizationId }, request) => { // 팀 플랜에 따른 동적 제한 const plan = await getPlan(organizationId, teamId) return plan === 'pro' ? 50 : 10 }, } -
allowRemovingAllTeams: 마지막 팀을 제거할 수 있는지 제어teams: { enabled: true, allowRemovingAllTeams: false // 마지막 팀 제거 방지 }
팀 멤버
조직에 멤버를 초대할 때 팀을 지정할 수 있습니다:
await authClient.organization.inviteMember({
email: "user@example.com",
role: "member",
teamId: "team-id",
});초대된 멤버는 초대를 수락하면 지정된 팀에 추가됩니다.
데이터베이스 스키마
팀이 활성화되면 새로운 team 및 teamMember 테이블이 데이터베이스에 추가됩니다.
테이블 이름: team
| Field Name | Type | Key | Description |
|---|---|---|---|
| id | string | 각 팀의 고유 식별자 | |
| name | string | - | 팀의 이름 |
| organizationId | string | 조직의 ID | |
| createdAt | Date | - | 팀이 생성된 타임스탬프 |
| updatedAt | Date | 팀이 생성된 타임스탬프 |
테이블 이름: teamMember
| Field Name | Type | Key | Description |
|---|---|---|---|
| id | string | 각 팀 멤버의 고유 식별자 | |
| teamId | string | 각 팀의 고유 식별자 | |
| userId | string | 사용자의 ID | |
| createdAt | Date | - | 팀 멤버가 생성된 타임스탬프 |
스키마
organization 플러그인은 데이터베이스에 다음 테이블을 추가합니다:
Organization
테이블 이름: organization
| Field Name | Type | Key | Description |
|---|---|---|---|
| id | string | 각 조직의 고유 식별자 | |
| name | string | - | 조직의 이름 |
| slug | string | - | 조직의 슬러그 |
| logo | string | 조직의 로고 | |
| metadata | string | 조직의 추가 메타데이터 | |
| createdAt | Date | - | 조직이 생성된 타임스탬프 |
Member
테이블 이름: member
| Field Name | Type | Key | Description |
|---|---|---|---|
| id | string | 각 멤버의 고유 식별자 | |
| userId | string | 사용자의 ID | |
| organizationId | string | 조직의 ID | |
| role | string | - | 조직에서 사용자의 역할 |
| createdAt | Date | - | 멤버가 조직에 추가된 타임스탬프 |
Invitation
테이블 이름: invitation
| Field Name | Type | Key | Description |
|---|---|---|---|
| id | string | 각 초대의 고유 식별자 | |
| string | - | 사용자의 이메일 주소 | |
| inviterId | string | 초대자의 ID | |
| organizationId | string | 조직의 ID | |
| role | string | - | 조직에서 사용자의 역할 |
| status | string | - | 초대의 상태 |
| createdAt | Date | - | 초대가 생성된 타임스탬프 |
| expiresAt | Date | - | 초대가 만료되는 타임스탬프 |
팀이 활성화된 경우 invitation 테이블에 다음 필드를 추가해야 합니다:
| Field Name | Type | Key | Description |
|---|---|---|---|
| teamId | string | 팀의 ID |
Session
테이블 이름: session
활성 조직 ID와 활성 팀 ID를 저장하기 위해 세션 테이블에 두 개의 필드를 더 추가해야 합니다.
| Field Name | Type | Key | Description |
|---|---|---|---|
| activeOrganizationId | string | 활성 조직의 ID | |
| activeTeamId | string | 활성 팀의 ID |
Teams (선택 사항)
테이블 이름: team
| Field Name | Type | Key | Description |
|---|---|---|---|
| id | string | 각 팀의 고유 식별자 | |
| name | string | - | 팀의 이름 |
| organizationId | string | 조직의 ID | |
| createdAt | Date | - | 팀이 생성된 타임스탬프 |
| updatedAt | Date | 팀이 생성된 타임스탬프 |
테이블 이름: teamMember
| Field Name | Type | Key | Description |
|---|---|---|---|
| id | string | 각 팀 멤버의 고유 식별자 | |
| teamId | string | 각 팀의 고유 식별자 | |
| userId | string | 사용자의 ID | |
| createdAt | Date | - | 팀 멤버가 생성된 타임스탬프 |
테이블 이름: invitation
| Field Name | Type | Key | Description |
|---|---|---|---|
| teamId | string | 팀의 ID |
스키마 커스터마이징하기
스키마 테이블 이름이나 필드를 변경하려면 organization 플러그인에 schema 옵션을 전달할 수 있습니다.
const auth = betterAuth({
plugins: [
organization({
schema: {
organization: {
modelName: "organizations", //organization 테이블을 organizations로 매핑
fields: {
name: "title", //name 필드를 title로 매핑
},
additionalFields: {
// organization 테이블에 새 필드 추가
myCustomField: {
type: "string",
input: true,
required: false,
},
},
},
},
}),
],
});추가 필드
Better Auth v1.3부터 organization, invitation, member, team 테이블에 사용자 정의 필드를 쉽게 추가할 수 있습니다.
모델에 추가 필드를 추가하면 관련 API 엔드포인트가 자동으로 이러한 새 속성을 수락하고 반환합니다. 예를 들어 organization 테이블에 사용자 정의 필드를 추가하면 createOrganization 엔드포인트는 필요에 따라 요청 및 응답 페이로드에 이 필드를 포함합니다.
const auth = betterAuth({
plugins: [
organization({
schema: {
organization: {
additionalFields: {
myCustomField: {
type: "string",
input: true,
required: false,
},
},
},
},
}),
],
});추가 필드를 추론하려면 inferOrgAdditionalFields 함수를 사용할 수 있습니다. 이 함수는 auth 객체 타입에서 추가 필드를 추론합니다.
import { createAuthClient } from "better-auth/client";
import {
inferOrgAdditionalFields,
organizationClient,
} from "better-auth/client/plugins";
import type { auth } from "@/auth"; // auth 객체 타입만 가져오기
const client = createAuthClient({
plugins: [
organizationClient({
schema: inferOrgAdditionalFields<typeof auth>(),
}),
],
});auth 객체 타입을 가져올 수 없는 경우 제네릭 없이 inferOrgAdditionalFields 함수를 사용할 수 있습니다. 이 함수는 스키마 객체에서 추가 필드를 추론합니다.
const client = createAuthClient({
plugins: [
organizationClient({
schema: inferOrgAdditionalFields({
organization: {
additionalFields: {
newField: {
type: "string",
},
},
},
}),
}),
],
});
//사용 예제
await client.organization.create({
name: "Test",
slug: "test",
newField: "123", //허용되어야 합니다
//@ts-expect-error - 이 필드는 사용할 수 없습니다
unavalibleField: "123", //허용되지 않아야 합니다
});옵션
allowUserToCreateOrganization: boolean | ((user: User) => Promise<boolean> | boolean) - 사용자가 조직을 생성할 수 있는지 여부를 결정하는 함수입니다. 기본값은 true입니다. 사용자가 조직을 생성하지 못하도록 제한하려면 false로 설정할 수 있습니다.
organizationLimit: number | ((user: User) => Promise<boolean> | boolean) - 사용자에게 허용되는 최대 조직 수입니다. 기본값은 5입니다. 원하는 숫자나 부울을 반환하는 함수로 설정할 수 있습니다.
creatorRole: admin | owner - 조직을 만든 사용자의 역할입니다. 기본값은 owner입니다. admin으로 설정할 수 있습니다.
membershipLimit: number - 조직에 허용되는 최대 멤버 수입니다. 기본값은 100입니다. 원하는 숫자로 설정할 수 있습니다.
sendInvitationEmail: async (data) => Promise<void> - 사용자에게 초대 이메일을 보내는 함수입니다.
invitationExpiresIn : number - 초대 링크가 유효한 시간(초)입니다. 기본값은 48시간(2일)입니다.
cancelPendingInvitationsOnReInvite: boolean - 사용자가 이미 조직에 초대된 경우 대기 중인 초대를 취소할지 여부입니다. 기본값은 false입니다.
invitationLimit: number | ((user: User) => Promise<boolean> | boolean) - 사용자에게 허용되는 최대 초대 수입니다. 기본값은 100입니다. 원하는 숫자나 부울을 반환하는 함수로 설정할 수 있습니다.
requireEmailVerificationOnInvitation: boolean - 초대를 수락하거나 거부하기 전에 이메일 확인을 요구할지 여부입니다. 기본값은 false입니다. 활성화하면 사용자가 조직 초대를 수락하거나 거부하기 전에 이메일 주소를 확인해야 합니다.