Admin

Admin 플러그인은 애플리케이션에서 사용자 관리를 위한 관리 기능 세트를 제공합니다. 관리자는 사용자 생성, 사용자 역할 관리, 사용자 차단/차단 해제, 사용자 위장, 기타 다양한 작업을 수행할 수 있습니다.

설치

auth 설정에 플러그인 추가

Admin 플러그인을 사용하려면 auth 설정에 추가합니다.

auth.ts
import { betterAuth } from "better-auth"
import { admin } from "better-auth/plugins"

export const auth = betterAuth({
    // ... 기타 설정 옵션
    plugins: [
        admin() 
    ]
})

데이터베이스 마이그레이션

마이그레이션을 실행하거나 스키마를 생성하여 필요한 필드와 테이블을 데이터베이스에 추가합니다.

npx @better-auth/cli migrate
npx @better-auth/cli generate

필드를 수동으로 추가하려면 스키마 섹션을 참조하세요.

클라이언트 플러그인 추가

다음으로, 인증 클라이언트 인스턴스에 admin 클라이언트 플러그인을 포함합니다.

auth-client.ts
import { createAuthClient } from "better-auth/client"
import { adminClient } from "better-auth/client/plugins"

export const authClient = createAuthClient({
    plugins: [
        adminClient()
    ]
})

사용법

관리자 작업을 수행하기 전에 사용자는 관리자 계정으로 인증되어야 합니다. 관리자는 admin 역할이 할당된 모든 사용자 또는 adminUserIds 옵션에 포함된 ID를 가진 모든 사용자입니다.

사용자 생성

관리자가 새 사용자를 생성할 수 있습니다.

POST
/admin/create-user
const { data: newUser, error } = await authClient.admin.createUser({    email: "user@example.com", // required    password: "some-secure-password", // required    name: "James Smith", // required    role: "user",    data: { customField: "customValue" },});
PropDescriptionType
email
사용자의 이메일
string
password
사용자의 비밀번호
string
name
사용자의 이름
string
role?
새 사용자에게 적용할 역할을 나타내는 문자열 또는 문자열 배열
string | string[]
data?
사용자의 추가 필드. 사용자 정의 추가 필드 포함
Record<string, any>

사용자 목록

관리자가 데이터베이스의 모든 사용자를 나열할 수 있습니다.

GET
/admin/list-users
Notes

모든 속성은 구성할 수 있는 선택 사항입니다. 기본적으로 100개의 행이 반환되며, limit 속성으로 이를 구성할 수 있습니다.

const { data: users, error } = await authClient.admin.listUsers({    query: {        searchValue: "some name",        searchField: "name",        searchOperator: "contains",        limit: 100,        offset: 100,        sortBy: "name",        sortDirection: "desc",        filterField: "email",        filterValue: "hello@example.com",        filterOperator: "eq",    },});
PropDescriptionType
query?
필터링, 검색 및 페이지네이션을 위한 쿼리 매개변수
Object
query.searchValue?
검색할 값
string
query.searchField?
검색할 필드, 기본값은 email. email 또는 name 가능
"email" | "name"
query.searchOperator?
검색에 사용할 연산자. contains, starts_with 또는 ends_with 가능
"contains" | "starts_with" | "ends_with"
query.limit?
반환할 사용자 수. 기본값은 100
string | number
query.offset?
시작할 오프셋
string | number
query.sortBy?
정렬할 필드
string
query.sortDirection?
정렬 방향
"asc" | "desc"
query.filterField?
필터링할 필드
string
query.filterValue?
필터링할 값
string | number | boolean
query.filterOperator?
필터에 사용할 연산자
"eq" | "ne" | "lt" | "lte" | "gt" | "gte"

쿼리 필터링

listUsers 함수는 eq, contains, starts_with, ends_with를 포함한 다양한 필터 연산자를 지원합니다.

페이지네이션

listUsers 함수는 사용자 목록과 함께 메타데이터를 반환하여 페이지네이션을 지원합니다. 응답에는 다음 필드가 포함됩니다:

{
  users: User[],   // 반환된 사용자 배열
  total: number,   // 필터 및 검색 쿼리 후 총 사용자 수
  limit: number | undefined,   // 쿼리에 제공된 제한
  offset: number | undefined   // 쿼리에 제공된 오프셋
}
페이지네이션 구현 방법

결과를 페이지네이션하려면 total, limit, offset 값을 사용하여 다음을 계산합니다:

  • 총 페이지 수: Math.ceil(total / limit)
  • 현재 페이지: (offset / limit) + 1
  • 다음 페이지 오프셋: Math.min(offset + limit, (total - 1)) – 다음 페이지에 대한 offset으로 사용할 값, 총 페이지 수를 초과하지 않도록 보장합니다.
  • 이전 페이지 오프셋: Math.max(0, offset - limit) – 이전 페이지에 대한 offset으로 사용할 값 (0 미만으로 가지 않도록 보장합니다).
사용 예제

페이지당 10명의 사용자로 두 번째 페이지 가져오기:

admin.ts
const pageSize = 10;
const currentPage = 2;

const users = await authClient.admin.listUsers({
    query: {
        limit: pageSize,
        offset: (currentPage - 1) * pageSize
    }
});

const totalUsers = users.total;
const totalPages = Math.ceil(totalUsers / pageSize)

사용자 역할 설정

사용자의 역할을 변경합니다.

POST
/admin/set-role
const { data, error } = await authClient.admin.setRole({    userId: "user-id",    role: "admin", // required});
PropDescriptionType
userId?
역할을 설정할 사용자 ID
string
role
설정할 역할, 문자열 또는 문자열 배열 가능
string | string[]

사용자 비밀번호 설정

사용자의 비밀번호를 변경합니다.

POST
/admin/set-user-password
const { data, error } = await authClient.admin.setUserPassword({    newPassword: 'new-password', // required    userId: 'user-id', // required});
PropDescriptionType
newPassword
새 비밀번호
string
userId
비밀번호를 설정할 사용자 ID
string

사용자 업데이트

사용자의 세부 정보를 업데이트합니다.

POST
/admin/update-user
const { data, error } = await authClient.admin.updateUser({    userId: "user-id", // required    data: { name: "John Doe" }, // required});
PropDescriptionType
userId
업데이트할 사용자 ID
string
data
업데이트할 데이터
Record<string, any>

사용자 차단

사용자를 차단하여 로그인을 방지하고 기존의 모든 세션을 취소합니다.

POST
/admin/ban-user
await authClient.admin.banUser({    userId: "user-id", // required    banReason: "Spamming",    banExpiresIn: 60 * 60 * 24 * 7,});
PropDescriptionType
userId
차단할 사용자 ID
string
banReason?
차단 사유
string
banExpiresIn?
차단이 만료될 때까지의 초 수. 제공되지 않으면 차단은 절대 만료되지 않습니다.
number

사용자 차단 해제

사용자의 차단을 제거하여 다시 로그인할 수 있도록 합니다.

POST
/admin/unban-user
await authClient.admin.unbanUser({    userId: "user-id", // required});
PropDescriptionType
userId
차단을 해제할 사용자 ID
string

사용자 세션 목록

사용자의 모든 세션을 나열합니다.

POST
/admin/list-user-sessions
const { data, error } = await authClient.admin.listUserSessions({    userId: "user-id", // required});
PropDescriptionType
userId
사용자 ID
string

사용자 세션 취소

사용자의 특정 세션을 취소합니다.

POST
/admin/revoke-user-session
const { data, error } = await authClient.admin.revokeUserSession({    sessionToken: "session_token_here", // required});
PropDescriptionType
sessionToken
취소할 세션 토큰
string

사용자의 모든 세션 취소

사용자의 모든 세션을 취소합니다.

POST
/admin/revoke-user-sessions
const { data, error } = await authClient.admin.revokeUserSessions({    userId: "user-id", // required});
PropDescriptionType
userId
모든 세션을 취소할 사용자 ID
string

사용자 위장

이 기능을 사용하면 관리자가 지정된 사용자를 모방하는 세션을 만들 수 있습니다. 세션은 브라우저 세션이 종료되거나 1시간에 도달할 때까지 활성 상태로 유지됩니다. impersonationSessionDuration 옵션을 설정하여 이 기간을 변경할 수 있습니다.

POST
/admin/impersonate-user
const { data, error } = await authClient.admin.impersonateUser({    userId: "user-id", // required});
PropDescriptionType
userId
위장할 사용자 ID
string

사용자 위장 중지

사용자 위장을 중지하고 관리자 계정으로 계속하려면 stopImpersonating을 사용할 수 있습니다

POST
/admin/stop-impersonating
await authClient.admin.stopImpersonating();

사용자 제거

데이터베이스에서 사용자를 영구적으로 삭제합니다.

POST
/admin/remove-user
const { data: deletedUser, error } = await authClient.admin.removeUser({    userId: "user-id", // required});
PropDescriptionType
userId
제거할 사용자 ID
string

접근 제어

admin 플러그인은 사용자의 역할을 기반으로 사용자 권한을 관리할 수 있는 매우 유연한 접근 제어 시스템을 제공합니다. 필요에 맞는 사용자 정의 권한 세트를 정의할 수 있습니다.

역할

기본적으로 두 가지 역할이 있습니다:

admin: admin 역할을 가진 사용자는 다른 사용자에 대한 전체 제어 권한을 가집니다.

user: user 역할을 가진 사용자는 다른 사용자에 대한 제어 권한이 없습니다.

사용자는 여러 역할을 가질 수 있습니다. 여러 역할은 쉼표(",")로 구분된 문자열로 저장됩니다.

권한

기본적으로 최대 6개의 권한을 가진 두 개의 리소스가 있습니다.

user: create list set-role ban impersonate delete set-password

session: list revoke delete

admin 역할을 가진 사용자는 모든 리소스와 작업에 대한 전체 제어 권한을 가집니다. user 역할을 가진 사용자는 이러한 작업에 대한 제어 권한이 없습니다.

사용자 정의 권한

플러그인은 각 역할에 대한 자체 권한 세트를 정의하는 쉬운 방법을 제공합니다.

접근 제어 생성

먼저 createAccessControl 함수를 호출하고 statement 객체를 전달하여 접근 컨트롤러를 생성해야 합니다. statement 객체는 리소스 이름을 키로, 작업 배열을 값으로 가져야 합니다.

permissions.ts
import { createAccessControl } from "better-auth/plugins/access";

/**
 * TypeScript가 타입을 올바르게 추론할 수 있도록 `as const`를 사용해야 합니다
 */
const statement = { 
    project: ["create", "share", "update", "delete"], 
} as const; 

const ac = createAccessControl(statement); 

역할 생성

접근 컨트롤러를 생성한 후, 정의한 권한으로 역할을 생성할 수 있습니다.

permissions.ts
import { createAccessControl } from "better-auth/plugins/access";

export const statement = {
    project: ["create", "share", "update", "delete"], // <-- 생성된 역할에 사용 가능한 권한
} as const;

const ac = createAccessControl(statement);

export const user = ac.newRole({ 
    project: ["create"], 
}); 

export const admin = ac.newRole({ 
    project: ["create", "update"], 
}); 

export const myCustomRole = ac.newRole({ 
    project: ["create", "update", "delete"], 
    user: ["ban"], 
}); 

기존 역할에 대한 사용자 정의 역할을 생성하면 해당 역할의 미리 정의된 권한이 재정의됩니다. 기존 권한을 사용자 정의 역할에 추가하려면 defaultStatements를 가져와서 새 statement와 병합하고, 역할의 권한 세트를 기본 역할과 병합해야 합니다.

permissions.ts
import { createAccessControl } from "better-auth/plugins/access";
import { defaultStatements, adminAc } from "better-auth/plugins/admin/access";

const statement = {
    ...defaultStatements, 
    project: ["create", "share", "update", "delete"],
} as const;

const ac = createAccessControl(statement);

const admin = ac.newRole({
    project: ["create", "update"],
    ...adminAc.statements, 
});

플러그인에 역할 전달

역할을 생성한 후 클라이언트와 서버 모두에서 admin 플러그인에 전달할 수 있습니다.

auth.ts
import { betterAuth } from "better-auth"
import { admin as adminPlugin } from "better-auth/plugins"
import { ac, admin, user } from "@/auth/permissions"

export const auth = betterAuth({
    plugins: [
        adminPlugin({
            ac,
            roles: {
                admin,
                user,
                myCustomRole
            }
        }),
    ],
});

또한 접근 컨트롤러와 역할을 클라이언트 플러그인에 전달해야 합니다.

auth-client.ts
import { createAuthClient } from "better-auth/client"
import { adminClient } from "better-auth/client/plugins"
import { ac, admin, user, myCustomRole } from "@/auth/permissions"

export const client = createAuthClient({
    plugins: [
        adminClient({
            ac,
            roles: {
                admin,
                user,
                myCustomRole
            }
        })
    ]
})

접근 제어 사용

권한 확인:

사용자의 권한을 확인하려면 클라이언트에서 제공하는 hasPermission 함수를 사용할 수 있습니다.

POST
/admin/has-permission
const { data, error } = await authClient.admin.hasPermission({    userId: "user-id",    permission: { "project": ["create", "update"] } /* 이것 또는 permissions를 사용해야 합니다 */,    permissions,});
PropDescriptionType
userId?
권한을 확인할 사용자 ID
string
permission?
선택적으로 단일 권한이 부여되었는지 확인. 이것 또는 permissions를 사용해야 합니다.
Record<string, string[]>
permissions?
선택적으로 여러 권한이 부여되었는지 확인. 이것 또는 permission을 사용해야 합니다.
Record<string, string[]>

사용 예제:

auth-client.ts
const canCreateProject = await authClient.admin.hasPermission({
  permissions: {
    project: ["create"],
  },
});

// 동시에 여러 리소스 권한을 확인할 수도 있습니다
const canCreateProjectAndCreateSale = await authClient.admin.hasPermission({
  permissions: {
    project: ["create"],
    sale: ["create"]
  },
});

서버 측에서 사용자의 권한을 확인하려면 api에서 제공하는 userHasPermission 작업을 사용할 수 있습니다.

api.ts
import { auth } from "@/auth";

await auth.api.userHasPermission({
  body: {
    userId: 'id', // 사용자 ID
    permissions: {
      project: ["create"], // 접근 제어의 구조와 일치해야 합니다
    },
  },
});

// 역할을 직접 전달할 수도 있습니다
await auth.api.userHasPermission({
  body: {
   role: "admin",
    permissions: {
      project: ["create"], // 접근 제어의 구조와 일치해야 합니다
    },
  },
});

// 동시에 여러 리소스 권한을 확인할 수도 있습니다
await auth.api.userHasPermission({
  body: {
   role: "admin",
    permissions: {
      project: ["create"], // 접근 제어의 구조와 일치해야 합니다
      sale: ["create"]
    },
  },
});

역할 권한 확인:

클라이언트 측에서 checkRolePermission 함수를 사용하여 특정 역할이 특정 권한을 가지고 있는지 확인합니다. 이는 역할과 권한을 정의한 후 서버에 연락하지 않고도 권한 확인을 수행할 수 있어 유용합니다.

이 함수는 현재 로그인한 사용자의 권한을 직접 확인하지 않습니다. 대신 지정된 역할에 할당된 권한을 확인합니다. 함수는 동기식이므로 호출할 때 await를 사용할 필요가 없습니다.

auth-client.ts
const canCreateProject = authClient.admin.checkRolePermission({
  permissions: {
    user: ["delete"],
  },
  role: "admin",
});

// 동시에 여러 리소스 권한을 확인할 수도 있습니다
const canDeleteUserAndRevokeSession = authClient.admin.checkRolePermission({
  permissions: {
    user: ["delete"],
    session: ["revoke"]
  },
  role: "admin",
});

스키마

이 플러그인은 user 테이블에 다음 필드를 추가합니다:

Field NameTypeKeyDescription
rolestring사용자의 역할. 기본값은 `user`. 관리자는 `admin` 역할을 가집니다.
bannedboolean사용자가 차단되었는지 여부를 나타냅니다.
banReasonstring사용자의 차단 사유
banExpiresdate사용자의 차단이 만료될 날짜

그리고 session 테이블에 하나의 필드를 추가합니다:

Field NameTypeKeyDescription
impersonatedBystring이 세션을 위장하고 있는 관리자의 ID

옵션

기본 역할

사용자의 기본 역할. 기본값은 user.

auth.ts
admin({
  defaultRole: "regular",
});

관리자 역할

관리자 역할로 간주되는 역할. 기본값은 ["admin"].

auth.ts
admin({
  adminRoles: ["admin", "superadmin"],
});

adminRoles 목록에 없는 역할은 권한이 있더라도 관리자로 간주되지 않습니다.

관리자 userId

관리자로 간주해야 하는 userId의 배열을 전달할 수 있습니다. 기본값은 []

auth.ts
admin({
    adminUserIds: ["user_id_1", "user_id_2"]
})

사용자가 adminUserIds 목록에 있으면 모든 관리자 작업을 수행할 수 있습니다.

impersonationSessionDuration

위장 세션의 기간(초). 기본값은 1시간.

auth.ts
admin({
  impersonationSessionDuration: 60 * 60 * 24, // 1일
});

기본 차단 사유

관리자가 생성한 사용자의 기본 차단 사유. 기본값은 No reason.

auth.ts
admin({
  defaultBanReason: "Spamming",
});

기본 차단 만료 시간

관리자가 생성한 사용자의 기본 차단 만료 시간(초). 기본값은 undefined (차단이 절대 만료되지 않음을 의미).

auth.ts
admin({
  defaultBanExpiresIn: 60 * 60 * 24, // 1일
});

bannedUserMessage

차단된 사용자가 로그인을 시도할 때 표시할 메시지. 기본값은 "You have been banned from this application. Please contact support if you believe this is an error."

auth.ts
admin({
  bannedUserMessage: "사용자 정의 차단 메시지",
});