Unofficial, development-only tooling for Better Auth. The server endpoints are always disabled when
NODE_ENV=production.
Create managed test users, switch sessions, inspect live auth state, and optionally edit approved user fields from a floating React panel.
pnpm add better-auth-devtoolsPeer requirements:
pnpm add better-auth react react-domAdd the plugin to Better Auth:
import { betterAuth } from "better-auth";
import { devtools } from "better-auth-devtools";
export const auth = betterAuth({
database,
plugins: [devtools({ enabled: true })],
});Mount the panel anywhere in your React application:
"use client";
import { BetterAuthDevtools } from "better-auth-devtools/react";
export function Devtools() {
return <BetterAuthDevtools />;
}That is the complete runtime setup. enabled: true is an explicit development opt-in and can never override the production guard. You can use DEV_AUTH_ENABLED=true instead. The panel discovers its templates and capabilities from the server; it does not require a client plugin, shared config, or server-generated props.
The plugin stores the IDs of users it creates so arbitrary application users cannot be impersonated. Apply its Better Auth schema after installation.
Built-in Kysely adapter:
npx auth@latest migratePrisma, Drizzle, and other ORM adapters:
npx auth@latest generateThen apply the generated change with your normal ORM migration workflow. Re-run the command after plugin schema changes.
devtools():
- Requires
enabled: trueorDEV_AUTH_ENABLED=trueoutside production. - Provides a default Test User template.
- Creates a verified Better Auth user with a generated
@test.localaddress. - Only permits switching to users recorded as DevTools-managed.
- Shows the current Better Auth user and session.
- Limits managed-user listing to the newest 100 records.
- Protects write endpoints with Better Auth origin and CSRF middleware.
- Enforces an in-memory rate limit across all DevTools endpoints, independently of Better Auth's global development setting.
- Redacts the raw session token and secret-like user fields from the default session view.
- Supports deleting managed users and their sessions/accounts through Better Auth.
Set DEV_AUTH_ENABLED=false for an environment-level kill switch. Setting it to true never enables the plugin in production. Browser writes must include a trusted origin; origin-less and cross-origin writes are rejected.
If role belongs to Better Auth's user model, declare it with
user.additionalFields and map each DevTools persona to a role. A column in
your ORM schema alone is not enough; Better Auth must know about the field.
import { betterAuth } from "better-auth";
import { devtools } from "better-auth-devtools";
export const auth = betterAuth({
database,
user: {
additionalFields: {
role: {
type: ["admin", "editor", "viewer"],
defaultValue: "viewer",
input: false,
},
},
},
plugins: [
devtools({
enabled: true,
templates: {
admin: { label: "Admin", user: { role: "admin" } },
editor: { label: "Editor", user: { role: "editor" } },
viewer: { label: "Viewer", user: { role: "viewer" } },
},
// Optional: omit this block if roles should not be edited in the panel.
editableFields: [
{
key: "role",
label: "Role",
type: "select",
options: ["admin", "editor", "viewer"],
},
],
}),
],
});Mount <BetterAuthDevtools /> as shown in the quick start, then run the schema
command above. The panel discovers the templates and editable fields from the
server automatically.
If Better Auth's Admin plugin already provides role, keep admin() in the
Better Auth plugin list and omit the user.additionalFields.role declaration;
the same DevTools templates work without another mapping layer.
Template user values are passed to Better Auth's internal user adapter. This
works with Better Auth additional fields, plugin-owned fields, and adapter
mappings. Required custom fields without database defaults must be included in
the template.
Supported editable field types are string, number, boolean, and select. Without a custom callback, approved edits update the Better Auth user model.
If role lives in a separate application profile or membership table, use the
advanced hooks below instead of user.additionalFields.
Use callbacks only when your application stores persona or session data outside the Better Auth user model:
const authDevtools = devtools({
templates: {
teamAdmin: { label: "Team Admin", user: { role: "admin" } },
},
async createManagedUser({ template, email }) {
const user = await createApplicationUser({
name: template.label,
email,
role: String(template.user?.role ?? "member"),
});
return { userId: user.id, email: user.email, label: user.name };
},
async beforeDeleteManagedUser({ userId }) {
await deleteApplicationProfile(userId);
},
async getSessionView({ userId, sessionId }) {
const profile = await loadProfile(userId);
return {
userId,
label: profile.name,
email: profile.email,
fields: { role: profile.role, sessionId },
editableFields: ["role"],
};
},
async patchSession({ userId, sessionId, patch }) {
const profile = await updateProfile(userId, patch);
return {
userId,
label: profile.name,
email: profile.email,
fields: { role: profile.role, sessionId },
editableFields: ["role"],
};
},
});The React panel calls the DevTools endpoints directly. If application code also needs typed actions, add the optional Better Auth client plugin:
import { createAuthClient } from "better-auth/react";
import { devtoolsClientPlugin } from "better-auth-devtools/plugin";
export const authClient = createAuthClient({
plugins: [devtoolsClientPlugin()],
});<BetterAuthDevtools
basePath="/api/auth"
defaultOpen={false}
position="bottom-right"
triggerLabel="Auth DevTools"
reloadOnSessionChange
/>The panel hides itself if the server reports that DevTools is disabled or unavailable.
This package can create users, issue sessions, update approved fields, and delete managed users. Treat it like a privileged local development tool.
- Production is always disabled.
- Development requires an explicit code or environment opt-in.
- Session switching and deletion require a DevTools-managed user record.
- Request bodies are runtime validated.
- Writes require a trusted browser origin and use Better Auth CSRF protection.
- An internal limiter protects DevTools endpoints even when Better Auth's development limiter is disabled.
- The default session view never returns the Better Auth session token.
- Raw server errors are logged server-side and are not exposed to the browser.
- Do not expose a development server containing sensitive data to an untrusted network.
- Better Auth
>=1.6.11 <2 - React
>=18 - React DOM
>=18 - Node.js
>=20
The package is ESM-only.
pnpm install
pnpm lint
pnpm typecheck
pnpm test
pnpm build
pnpm package:check
pnpm package:auditThe demo app imports the public better-auth-devtools and better-auth-devtools/react exports, so it exercises the same code shipped to npm.
- Panel not visible: confirm
enabled: trueorDEV_AUTH_ENABLED=true, then verify the plugin table migration was applied. 403 UNTRUSTED_ORIGIN: call the browser panel from an origin included in Better Auth'strustedOrigins.429 RATE_LIMITED: wait for the 60-second window or configurerateLimit: { max, window }for your development workflow.- ORM schema missing: re-run
npx auth@latest generateafter adding or updating the plugin, then apply the generated migration.
MIT