mirror of
https://github.com/better-auth/better-auth.git
synced 2026-05-27 09:32:20 -05:00
* feat(admin): custom banned user error message * docs(admin): add documentation for bannedUserMessage configuration * chore: export types from @better-fetch/fetch in clients * chore: remove update tet helper
584 lines
15 KiB
Plaintext
584 lines
15 KiB
Plaintext
---
|
||
title: Admin
|
||
description: Admin plugin for Better Auth
|
||
---
|
||
|
||
The Admin plugin provides a set of administrative functions for user management in your application. It allows administrators to perform various operations such as creating users, managing user roles, banning/unbanning users, impersonating users, and more.
|
||
|
||
## Installation
|
||
|
||
<Steps>
|
||
<Step>
|
||
### Add the plugin to your auth config
|
||
|
||
To use the Admin plugin, add it to your auth config.
|
||
|
||
```ts title="auth.ts"
|
||
import { betterAuth } from "better-auth"
|
||
import { admin } from "better-auth/plugins" // [!code highlight]
|
||
|
||
export const auth = betterAuth({
|
||
// ... other config options
|
||
plugins: [
|
||
admin() // [!code highlight]
|
||
]
|
||
})
|
||
```
|
||
|
||
</Step>
|
||
|
||
<Step>
|
||
### Migrate the database
|
||
|
||
Run the migration or generate the schema to add the necessary fields and tables to the database.
|
||
|
||
<Tabs items={["migrate", "generate"]}>
|
||
<Tab value="migrate">
|
||
```bash
|
||
npx @better-auth/cli migrate
|
||
```
|
||
</Tab>
|
||
<Tab value="generate">
|
||
```bash
|
||
npx @better-auth/cli generate
|
||
```
|
||
</Tab>
|
||
</Tabs>
|
||
See the [Schema](#schema) section to add the fields manually.
|
||
</Step>
|
||
|
||
<Step>
|
||
### Add the client plugin
|
||
|
||
Next, include the admin client plugin in your authentication client instance.
|
||
|
||
```ts title="auth-client.ts"
|
||
import { createAuthClient } from "better-auth/client"
|
||
import { adminClient } from "better-auth/client/plugins"
|
||
|
||
const authClient = createAuthClient({
|
||
plugins: [
|
||
adminClient()
|
||
]
|
||
})
|
||
```
|
||
|
||
</Step>
|
||
</Steps>
|
||
|
||
## Usage
|
||
|
||
Before performing any admin operations, the user must be authenticated with an admin account. An admin is any user assigned the `admin` role or any user whose ID is included in the `adminUserIds` option.
|
||
|
||
### Create User
|
||
|
||
Allows an admin to create a new user.
|
||
|
||
```ts title="admin.ts"
|
||
const newUser = await authClient.admin.createUser({
|
||
name: "Test User",
|
||
email: "test@example.com",
|
||
password: "password123",
|
||
role: "user",
|
||
data: {
|
||
// any additional on the user table including plugin fields and custom fields
|
||
customField: "customValue",
|
||
},
|
||
});
|
||
```
|
||
|
||
### List Users
|
||
|
||
Allows an admin to list all users in the database.
|
||
|
||
```ts title="admin.ts"
|
||
const users = await authClient.admin.listUsers({
|
||
query: {
|
||
limit: 10,
|
||
},
|
||
});
|
||
```
|
||
|
||
By default, 100 users are returned. You can adjust the limit and offset using the following query parameters:
|
||
|
||
- `search`: The search query to apply to the users. It can be an object with the following properties:
|
||
- `field`: The field to search on, which can be `email` or `name`.
|
||
- `operator`: The operator to use for the search. It can be `contains`, `starts_with`, or `ends_with`.
|
||
- `value`: The value to search for.
|
||
- `limit`: The number of users to return.
|
||
- `offset`: The number of users to skip.
|
||
- `sortBy`: The field to sort the users by.
|
||
- `sortDirection`: The direction to sort the users by. Defaults to `asc`.
|
||
- `filter`: The filter to apply to the users. It can be an array of objects.
|
||
|
||
```ts title="admin.ts"
|
||
const users = await authClient.admin.listUsers({
|
||
query: {
|
||
searchField: "email",
|
||
searchOperator: "contains",
|
||
searchValue: "@example.com",
|
||
limit: 10,
|
||
offset: 0,
|
||
sortBy: "createdAt",
|
||
sortDirection: "desc"
|
||
filterField: "role",
|
||
filterOperator: "eq",
|
||
filterValue: "admin"
|
||
}
|
||
});
|
||
```
|
||
|
||
#### Pagination
|
||
|
||
The `listUsers` function supports pagination by returning metadata alongside the user list. The response includes the following fields:
|
||
|
||
```ts
|
||
{
|
||
users: User[], // Array of returned users
|
||
total: number, // Total number of users after filters and search queries
|
||
limit: number | undefined, // The limit provided in the query
|
||
offset: number | undefined // The offset provided in the query
|
||
}
|
||
```
|
||
|
||
##### How to Implement Pagination
|
||
|
||
To paginate results, use the `total`, `limit`, and `offset` values to calculate:
|
||
|
||
- **Total pages:** `Math.ceil(total / limit)`
|
||
- **Current page:** `(offset / limit) + 1`
|
||
- **Next page offset:** `Math.min(offset + limit, (total - 1))` – The value to use as `offset` for the next page, ensuring it does not exceed the total number of pages.
|
||
- **Previous page offset:** `Math.max(0, offset - limit)` – The value to use as `offset` for the previous page (ensuring it doesn’t go below zero).
|
||
|
||
##### Example Usage
|
||
|
||
Fetching the second page with 10 users per page:
|
||
|
||
```ts title="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 / limit)
|
||
```
|
||
|
||
### Set User Role
|
||
|
||
Changes the role of a user.
|
||
|
||
```ts title="admin.ts"
|
||
const updatedUser = await authClient.admin.setRole({
|
||
userId: "user_id_here",
|
||
role: "admin",
|
||
});
|
||
```
|
||
|
||
### Ban User
|
||
|
||
Bans a user, preventing them from signing in and revokes all of their existing sessions.
|
||
|
||
```ts title="admin.ts"
|
||
const bannedUser = await authClient.admin.banUser({
|
||
userId: "user_id_here",
|
||
banReason: "Spamming", // Optional (if not provided, the default ban reason will be used - No reason)
|
||
banExpiresIn: 60 * 60 * 24 * 7, // Optional (if not provided, the ban will never expire)
|
||
});
|
||
```
|
||
|
||
### Unban User
|
||
|
||
Removes the ban from a user, allowing them to sign in again.
|
||
|
||
```ts title="admin.ts"
|
||
const unbannedUser = await authClient.admin.unbanUser({
|
||
userId: "user_id_here",
|
||
});
|
||
```
|
||
|
||
### List User Sessions
|
||
|
||
Lists all sessions for a user.
|
||
|
||
```ts title="admin.ts"
|
||
const sessions = await authClient.admin.listUserSessions({
|
||
userId: "user_id_here",
|
||
});
|
||
```
|
||
|
||
### Revoke User Session
|
||
|
||
Revokes a specific session for a user.
|
||
|
||
```ts title="admin.ts"
|
||
const revokedSession = await authClient.admin.revokeUserSession({
|
||
sessionToken: "session_token_here",
|
||
});
|
||
```
|
||
|
||
### Revoke All Sessions for a User
|
||
|
||
Revokes all sessions for a user.
|
||
|
||
```ts title="admin.ts"
|
||
const revokedSessions = await authClient.admin.revokeUserSessions({
|
||
userId: "user_id_here",
|
||
});
|
||
```
|
||
|
||
### Impersonate User
|
||
|
||
This feature allows an admin to create a session that mimics the specified user. The session will remain active until either the browser session ends or it reaches 1 hour. You can change this duration by setting the `impersonationSessionDuration` option.
|
||
|
||
```ts title="admin.ts"
|
||
const impersonatedSession = await authClient.admin.impersonateUser({
|
||
userId: "user_id_here",
|
||
});
|
||
```
|
||
|
||
### Stop Impersonating User
|
||
|
||
To stop impersonating a user and continue with the admin account, you can use `stopImpersonating`
|
||
|
||
```ts title="admin.ts"
|
||
await authClient.admin.stopImpersonating();
|
||
```
|
||
|
||
### Remove User
|
||
|
||
Hard deletes a user from the database.
|
||
|
||
```ts title="admin.ts"
|
||
const deletedUser = await authClient.admin.removeUser({
|
||
userId: "user_id_here",
|
||
});
|
||
```
|
||
|
||
## Access Control
|
||
|
||
The admin plugin offers a highly flexible access control system, allowing you to manage user permissions based on their role. You can define custom permission sets to fit your needs.
|
||
|
||
### Roles
|
||
|
||
By default, there are two roles:
|
||
|
||
`admin`: Users with the admin role have full control over other users.
|
||
|
||
`user`: Users with the user role have no control over other users.
|
||
|
||
### Permissions
|
||
|
||
By default, there are two resources with up to six permissions.
|
||
|
||
**user**:
|
||
`create` `list` `set-role` `ban` `impersonate` `delete` `set-password`
|
||
|
||
**session**:
|
||
`list` `revoke` `delete`
|
||
|
||
The admin have full control over all the resources and actions. The user have no control over any of those actions.
|
||
|
||
### Custom Permissions
|
||
|
||
the plugin providers easy way to define your own set of permission for each role.
|
||
|
||
<Steps>
|
||
<Step>
|
||
#### Create Access Control
|
||
|
||
You first need to create access controller by calling `createAccessControl` function and passing the statement object. The statement object should have the resource name as the key and the array of actions as the value.
|
||
```ts title="permissions.ts"
|
||
import { createAccessControl } from "better-auth/plugins/access";
|
||
|
||
/**
|
||
* make sure to use `as const` so typescript can infer the type correctly
|
||
*/
|
||
const statement = { // [!code highlight]
|
||
project: ["create", "share", "update", "delete"], // [!code highlight]
|
||
} as const; // [!code highlight]
|
||
|
||
const ac = createAccessControl(statement); // [!code highlight]
|
||
```
|
||
</Step>
|
||
|
||
<Step>
|
||
#### Create Roles
|
||
|
||
Once you have created the access controller you can create roles with the permissions you have defined.
|
||
|
||
```ts title="permissions.ts"
|
||
import { createAccessControl } from "better-auth/plugins/access";
|
||
|
||
const statement = {
|
||
project: ["create", "share", "update", "delete"],
|
||
} as const;
|
||
|
||
const ac = createAccessControl(statement);
|
||
|
||
const user = ac.newRole({ // [!code highlight]
|
||
project: ["create"], // [!code highlight]
|
||
}); // [!code highlight]
|
||
|
||
const admin = ac.newRole({ // [!code highlight]
|
||
project: ["create", "update"], // [!code highlight]
|
||
}); // [!code highlight]
|
||
|
||
const myCustomRole = ac.newRole({ // [!code highlight]
|
||
project: ["create", "update", "delete"], // [!code highlight]
|
||
user: ["ban"], // [!code highlight]
|
||
}); // [!code highlight]
|
||
```
|
||
|
||
When you create custom roles for existing roles, the predefined permissions for those roles will be overridden. To add the existing permissions to the custom role, you need to import `defaultStatements` and merge it with your new statement, plus merge the roles' permissions set with the default roles.
|
||
|
||
```ts title="permissions.ts"
|
||
import { createAccessControl } from "better-auth/plugins/access";
|
||
import { defaultStatements, adminAc } from "better-auth/plugins/admin/access";
|
||
|
||
const statement = {
|
||
...defaultStatements, // [!code highlight]
|
||
project: ["create", "share", "update", "delete"],
|
||
} as const;
|
||
|
||
const ac = createAccessControl(statement);
|
||
|
||
const admin = ac.newRole({
|
||
project: ["create", "update"],
|
||
...adminAc.statements, // [!code highlight]
|
||
});
|
||
```
|
||
|
||
</Step>
|
||
|
||
<Step>
|
||
#### Pass Roles to the Plugin
|
||
|
||
Once you have created the roles you can pass them to the organization plugin both on the client and the server.
|
||
|
||
```ts title="auth.ts"
|
||
import { ac, admin, user } from "@/auth/permissions"
|
||
import { betterAuth } from "better-auth"
|
||
import { admin } from "better-auth/plugins"
|
||
|
||
export const auth = betterAuth({
|
||
plugins: [
|
||
admin({
|
||
ac: ac,
|
||
roles: {
|
||
admin,
|
||
user,
|
||
myCustomRole
|
||
}
|
||
}),
|
||
],
|
||
});
|
||
```
|
||
|
||
You also need to pass the access controller and the roles to the client plugin.
|
||
|
||
```ts title="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: ac,
|
||
roles: {
|
||
admin,
|
||
user,
|
||
myCustomRole
|
||
}
|
||
})
|
||
]
|
||
})
|
||
```
|
||
</Step>
|
||
|
||
</Steps>
|
||
|
||
### Access Control Usage
|
||
|
||
**Has Permission**:
|
||
|
||
To check the permission of the user, you can use the `hasPermission` function provided by the client.
|
||
|
||
```ts title="auth-client.ts"
|
||
const canCreateProject = await authClient.admin.hasPermission({
|
||
permission: {
|
||
project: ["create"],
|
||
},
|
||
});
|
||
```
|
||
|
||
If you want to use the server instead to check if a user has a permission, you can use `adminHasPermission` action provided by the `api` to check the permission of the user.
|
||
|
||
```ts title="api.ts"
|
||
import { auth } from "@/auth";
|
||
auth.api.adminHasPermission({
|
||
body: {
|
||
userId: 'id', //the user id
|
||
permission: {
|
||
project: ["create"], // This must match the structure in your access control
|
||
},
|
||
},
|
||
});
|
||
|
||
//you can also just pass the role directly
|
||
auth.api.adminHasPermission({
|
||
body: {
|
||
role: "admin",
|
||
permission: {
|
||
project: ["create"], // This must match the structure in your access control
|
||
},
|
||
},
|
||
});
|
||
```
|
||
|
||
|
||
**Check Role Permission**:
|
||
|
||
Once you have defined the roles and permissions to avoid checking the permission from the server you can use the `checkRolePermission` function provided by the client.
|
||
|
||
```ts title="auth-client.ts"
|
||
const canCreateProject = client.admin.checkRolePermission({
|
||
permission: {
|
||
user: ["delete"],
|
||
},
|
||
role: "admin",
|
||
});
|
||
```
|
||
|
||
## Schema
|
||
|
||
This plugin adds the following fields to the `user` table:
|
||
|
||
<DatabaseTable
|
||
fields={[
|
||
{
|
||
name: "role",
|
||
type: "string",
|
||
description:
|
||
"The user's role. Defaults to `user`. Admins will have the `admin` role.",
|
||
isOptional: true,
|
||
},
|
||
{
|
||
name: "banned",
|
||
type: "boolean",
|
||
description: "Indicates whether the user is banned.",
|
||
isOptional: true,
|
||
},
|
||
{
|
||
name: "banReason",
|
||
type: "string",
|
||
description: "The reason for the user's ban.",
|
||
isOptional: true,
|
||
},
|
||
{
|
||
name: "banExpires",
|
||
type: "number",
|
||
description: "The Unix timestamp when the user's ban will expire.",
|
||
isOptional: true,
|
||
},
|
||
]}
|
||
/>
|
||
|
||
And adds one field in the `session` table:
|
||
|
||
<DatabaseTable
|
||
fields={[
|
||
{
|
||
name: "impersonatedBy",
|
||
type: "string",
|
||
description: "The ID of the admin that is impersonating this session.",
|
||
isOptional: true,
|
||
},
|
||
]}
|
||
/>
|
||
|
||
## Options
|
||
|
||
### Default Role
|
||
|
||
The default role for a user. Defaults to `user`.
|
||
|
||
```ts title="auth.ts"
|
||
admin({
|
||
defaultRole: "regular",
|
||
});
|
||
```
|
||
### Admin Roles
|
||
|
||
The roles that are considered admin roles. Defaults to `["admin"]`.
|
||
|
||
```ts title="auth.ts"
|
||
admin({
|
||
adminRoles: ["admin", "superadmin"],
|
||
});
|
||
```
|
||
|
||
<Callout type="warning">
|
||
Any role that isn't in the `adminRoles` list, even if they have the permission,
|
||
will not be considered an admin.
|
||
</Callout>
|
||
|
||
### Admin userIds
|
||
|
||
You can pass an array of userIds that should be considered as admin. Default to `[]`
|
||
|
||
```ts title="auth.ts"
|
||
admin({
|
||
adminUserIds: ["user_id_1", "user_id_2"]
|
||
})
|
||
```
|
||
|
||
If a user is in the `adminUserIds` list, they will be able to perform any admin operation.
|
||
|
||
### impersonationSessionDuration
|
||
|
||
The duration of the impersonation session in seconds. Defaults to 1 hour.
|
||
|
||
```ts title="auth.ts"
|
||
admin({
|
||
impersonationSessionDuration: 60 * 60 * 24, // 1 day
|
||
});
|
||
```
|
||
|
||
### Default Ban Reason
|
||
|
||
The default ban reason for a user created by the admin. Defaults to `No reason`.
|
||
|
||
```ts title="auth.ts"
|
||
admin({
|
||
defaultBanReason: "Spamming",
|
||
});
|
||
```
|
||
|
||
### Default Ban Expires In
|
||
|
||
The default ban expires in for a user created by the admin in seconds. Defaults to `undefined` (meaning the ban never expires).
|
||
|
||
```ts title="auth.ts"
|
||
admin({
|
||
defaultBanExpiresIn: 60 * 60 * 24, // 1 day
|
||
});
|
||
```
|
||
|
||
### bannedUserMessage
|
||
|
||
The message to show when a banned user tries to sign in. Defaults to "You have been banned from this application. Please contact support if you believe this is an error."
|
||
|
||
```ts title="auth.ts"
|
||
admin({
|
||
bannedUserMessage: "Custom banned user message",
|
||
});
|
||
```
|
||
|