diff --git a/docs/content/docs/plugins/admin.mdx b/docs/content/docs/plugins/admin.mdx index 23ab5084..e9a9e339 100644 --- a/docs/content/docs/plugins/admin.mdx +++ b/docs/content/docs/plugins/admin.mdx @@ -514,6 +514,20 @@ admin({ defaultRole: "regular", }); ``` +### Admin Roles + +The roles that are considered admin roles. Defaults to `["admin"]`. + +```ts title="auth.ts" +admin({ + adminRoles: ["admin", "superadmin"], +}); +``` + + + Any role that isn't in the `adminRoles` list, even if they have the permission, + will not be considered an admin. + ### Admin userIds @@ -525,6 +539,8 @@ admin({ }) ``` +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. diff --git a/packages/better-auth/src/plugins/admin/admin.test.ts b/packages/better-auth/src/plugins/admin/admin.test.ts index 9ad91222..5ef0b60a 100644 --- a/packages/better-auth/src/plugins/admin/admin.test.ts +++ b/packages/better-auth/src/plugins/admin/admin.test.ts @@ -290,7 +290,7 @@ describe("Admin plugin", async () => { }, }); //should reject cause the user is not admin - expect(impersonatedUserRes.error?.status).toBe(403); + expect(impersonatedUserRes.error?.status).toBe(401); const res = await client.admin.stopImpersonating( {}, { diff --git a/packages/better-auth/src/plugins/admin/admin.ts b/packages/better-auth/src/plugins/admin/admin.ts index b798243e..dec3b2f5 100644 --- a/packages/better-auth/src/plugins/admin/admin.ts +++ b/packages/better-auth/src/plugins/admin/admin.ts @@ -18,7 +18,6 @@ import { getDate } from "../../utils/date"; import { getEndpointResponse } from "../../utils/plugin-helper"; import { mergeSchema } from "../../db/schema"; import { type AccessControl, type Role } from "../access"; -import { adminMiddleware } from "./call"; import { ADMIN_ERROR_CODES } from "./error-codes"; import { defaultStatements } from "./access"; import { hasPermission } from "./has-permission"; @@ -41,6 +40,15 @@ export interface AdminOptions { * @default "user" */ defaultRole?: string; + /** + * Roles that are considered admin roles. + * + * Any user role that isn't in this list, even if they have the permission, + * will not be considered an admin. + * + * @default ["admin"] + */ + adminRoles?: string | string[]; /** * A default ban reason * @@ -85,12 +93,32 @@ export interface AdminOptions { export const admin = (options?: O) => { const opts = { defaultRole: "user", + adminRoles: ["admin"], ...options, }; type DefaultStatements = typeof defaultStatements; type Statements = O["ac"] extends AccessControl ? S : DefaultStatements; + + const adminMiddleware = createAuthMiddleware(async (ctx) => { + const session = await getSessionFromCtx(ctx); + if ( + (!session?.session || !opts.adminRoles.includes(session.user.role)) && + !opts.adminUserIds?.includes(session?.session?.user.id) + ) { + throw new APIError("UNAUTHORIZED"); + } + return { + session, + } as { + session: { + user: UserWithRole; + session: Session; + }; + }; + }); + return { id: "admin", init(ctx) {