Plugins

Admin

Manage users, roles, bans, sessions, and impersonation.

The Admin plugin adds a protected user-management surface to your auth API: listing and creating users, changing roles, banning, revoking sessions, impersonating, setting passwords, and permission checks. Access is gated by role (admin_roles) or by an explicit allowlist of user ids (admin_user_ids).

Installation

Add the plugin to your server

Pass admin() to your Kernia config. admin_user_ids is the bootstrap escape hatch for your first admin account.

auth.py
from kernia import KerniaOptions
from kernia.auth import init
from kernia.plugins.admin import AdminOptions, admin

auth = init(KerniaOptions(
    database=adapter,
    secret=os.environ["KERNIA_SECRET"],
    base_url=os.environ["KERNIA_BASE_URL"],
    plugins=[
        admin(AdminOptions(
            default_role="user",
            admin_roles=("admin",),
            admin_user_ids=(os.environ["BOOTSTRAP_ADMIN_USER_ID"],),
        )),
    ],
))

Add the client plugin

Add the admin client plugin:

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

export const authClient = createAuthClient({
  baseURL: "/api/auth",
  plugins: [adminClient()],
});

Usage

All calls below require an authenticated admin (a user whose role is in admin_roles, or whose id is in admin_user_ids).

List users

const { data } = await authClient.admin.listUsers({
  query: { limit: 50, offset: 0, searchField: "email", searchValue: "@example.com" },
});

Create a user

await authClient.admin.createUser({
  email: "user@example.com",
  password: "secure-password",
  name: "Jane",
  role: "user",
});

Set a user's role

await authClient.admin.setRole({ userId: "user_123", role: "admin" });

Ban and unban

await authClient.admin.banUser({ userId: "user_123", banReason: "abuse", banExpiresIn: 604800 });
await authClient.admin.unbanUser({ userId: "user_123" });

Banning revokes the user's sessions and blocks sign-in until the ban lapses or is lifted.

Impersonate a user

await authClient.admin.impersonateUser({ userId: "user_123" });
await authClient.admin.stopImpersonating();

impersonateUser issues a session for the target and stamps the session's impersonatedBy field with your admin id.

Manage sessions and passwords

await authClient.admin.listUserSessions({ userId: "user_123" });
await authClient.admin.revokeUserSession({ sessionToken: "..." });
await authClient.admin.setUserPassword({ userId: "user_123", newPassword: "new-secure-password" });
await authClient.admin.removeUser({ userId: "user_123" });

Options

OptionTypeDefaultDescription
default_rolestr"user"Role assigned to new users.
admin_rolestuple[str, ...]("admin",)Roles allowed to call admin endpoints. Each must exist in roles.
admin_user_idstuple[str, ...]()User ids treated as admins regardless of role.
rolesMapping[str, Role] | Nonedefault rolesCustom role/permission map from the access-control primitive.
banned_user_messagestrsupport messageMessage returned when a banned user is rejected.
default_ban_reasonstr | NoneNoneReason applied when banReason is omitted.
default_ban_expires_inint | NoneNoneDefault ban duration in seconds (None = permanent).
impersonation_session_durationint | NoneNoneLifetime of an impersonation session in seconds.
allow_impersonating_adminsboolFalseAllow impersonating other admins.

Schema

The plugin extends the user and session tables:

user extensions
role
stringoptional
Role name used by the admin gate.
banned
booleanoptional
Whether sign-in and protected operations are blocked.
banReason
stringoptional
Admin-supplied ban reason.
banExpires
numberoptional
Unix timestamp when a temporary ban expires.
session extensions
impersonatedBy
stringoptional
Admin user id that started the impersonation session.

The ban check runs as a before-hook on every gated request, so a mid-session ban takes effect immediately — the banned user is rejected with USER_BANNED on their next call, not just at sign-in.