diff --git a/packages/better-auth/src/plugins/admin/admin.test.ts b/packages/better-auth/src/plugins/admin/admin.test.ts index 24ea0076f7..dd2b58b72a 100644 --- a/packages/better-auth/src/plugins/admin/admin.test.ts +++ b/packages/better-auth/src/plugins/admin/admin.test.ts @@ -6,37 +6,42 @@ import { createAccessControl } from "../access"; import { createAuthClient } from "../../client"; describe("Admin plugin", async () => { - const { signInWithTestUser, signInWithUser, cookieSetter, customFetchImpl } = - await getTestInstance( - { - plugins: [ - admin({ - bannedUserMessage: "Custom banned user message", - }), - ], - databaseHooks: { - user: { - create: { - before: async (user) => { - if (user.name === "Admin") { - return { - data: { - ...user, - role: "admin", - }, - }; - } - }, + const { + auth, + signInWithTestUser, + signInWithUser, + cookieSetter, + customFetchImpl, + } = await getTestInstance( + { + plugins: [ + admin({ + bannedUserMessage: "Custom banned user message", + }), + ], + databaseHooks: { + user: { + create: { + before: async (user) => { + if (user.name === "Admin") { + return { + data: { + ...user, + role: "admin", + }, + }; + } }, }, }, }, - { - testUser: { - name: "Admin", - }, + }, + { + testUser: { + name: "Admin", }, - ); + }, + ); const client = createAuthClient({ fetchOptions: { customFetchImpl, @@ -530,6 +535,21 @@ describe("Admin plugin", async () => { ); expect(res.error?.status).toBe(403); }); + + it("should allow creating users from server", async () => { + const res = await auth.api.createUser({ + body: { + email: "test2@test.com", + password: "password", + name: "Test User", + }, + }); + expect(res.user).toMatchObject({ + email: "test2@test.com", + name: "Test User", + role: "user", + }); + }); }); describe("access control", async (it) => { diff --git a/packages/better-auth/src/plugins/admin/admin.ts b/packages/better-auth/src/plugins/admin/admin.ts index c95777499b..7d82c545d0 100644 --- a/packages/better-auth/src/plugins/admin/admin.ts +++ b/packages/better-auth/src/plugins/admin/admin.ts @@ -277,9 +277,11 @@ export const admin = (options?: O) => { name: z.string({ description: "The name of the user", }), - role: z.string({ - description: "The role of the user", - }), + role: z + .string({ + description: "The role of the user", + }) + .optional(), /** * extra fields for user */ @@ -290,7 +292,6 @@ export const admin = (options?: O) => { }), ), }), - use: [adminMiddleware], metadata: { openapi: { operationId: "createUser", @@ -317,19 +318,24 @@ export const admin = (options?: O) => { }, }, async (ctx) => { - const session = ctx.context.session; - const canCreateUser = hasPermission({ - userId: ctx.context.session.user.id, - role: session.user.role, - options: opts, - permission: { - user: ["create"], - }, - }); - if (!canCreateUser) { - throw new APIError("FORBIDDEN", { - message: ADMIN_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_CREATE_USERS, + const session = await getSessionFromCtx<{ role: string }>(ctx); + if (!session && (ctx.request || ctx.headers)) { + throw ctx.error("UNAUTHORIZED"); + } + if (session) { + const canCreateUser = hasPermission({ + userId: session.user.id, + role: session.user.role, + options: opts, + permission: { + user: ["create"], + }, }); + if (!canCreateUser) { + throw new APIError("FORBIDDEN", { + message: ADMIN_ERROR_CODES.YOU_ARE_NOT_ALLOWED_TO_CREATE_USERS, + }); + } } const existUser = await ctx.context.internalAdapter.findUserByEmail( ctx.body.email, @@ -343,7 +349,7 @@ export const admin = (options?: O) => { await ctx.context.internalAdapter.createUser({ email: ctx.body.email, name: ctx.body.name, - role: ctx.body.role, + role: ctx.body.role ?? options?.defaultRole ?? "user", ...ctx.body.data, });