Files
better-auth/docs/content/docs/plugins/admin.mdx
Bereket Engida 2ca71ee3cb feat(admin): custom banned user error message (#1692)
* 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
2025-03-14 00:10:31 +03:00

584 lines
15 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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 doesnt 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",
});
```