feat(admin): make password field optional on create user (#7441)

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
This commit is contained in:
Bereket Engida
2026-01-17 20:11:47 -08:00
committed by Alex Yang
parent 308f8ae14e
commit bc9fe98bd0
2 changed files with 49 additions and 10 deletions

View File

@@ -173,6 +173,39 @@ describe("Admin plugin", async () => {
expect(newUser?.role).toBe("user");
});
it("should allow admin to create users without password", async () => {
const res = await client.admin.createUser(
{
name: "Passwordless User",
email: "passwordless@email.com",
role: "user",
},
{
headers: adminHeaders,
},
);
expect(res.data?.user?.email).toBe("passwordless@email.com");
expect(res.data?.user?.name).toBe("Passwordless User");
expect(res.data?.user?.role).toBe("user");
// User should not be able to sign in with password since no credential account exists
const signInRes = await client.signIn.email({
email: "passwordless@email.com",
password: "anypassword",
});
expect(signInRes.error).toBeDefined();
// Clean up
await client.admin.removeUser(
{
userId: res.data?.user?.id || "",
},
{
headers: adminHeaders,
},
);
});
it("should allow admin to create user with multiple roles", async () => {
const res = await client.admin.createUser(
{

View File

@@ -236,8 +236,9 @@ const createUserBodySchema = z.object({
email: z.string().meta({
description: "The email of the user",
}),
password: z.string().meta({
description: "The password of the user",
password: z.string().optional().meta({
description:
"The password of the user. If not provided, the user will be created without a credential account (useful for magic link or social login only users).",
}),
name: z.string().meta({
description: "The name of the user",
@@ -313,7 +314,7 @@ export const createUser = <O extends AdminOptions>(opts: O) =>
$Infer: {
body: {} as {
email: string;
password: string;
password?: string | undefined;
name: string;
role?:
| (InferAdminRolesFromOption<O> | InferAdminRolesFromOption<O>[])
@@ -374,13 +375,18 @@ export const createUser = <O extends AdminOptions>(opts: O) =>
message: ADMIN_ERROR_CODES.FAILED_TO_CREATE_USER,
});
}
const hashedPassword = await ctx.context.password.hash(ctx.body.password);
await ctx.context.internalAdapter.linkAccount({
accountId: user.id,
providerId: "credential",
password: hashedPassword,
userId: user.id,
});
// Only create credential account if password is provided
if (ctx.body.password) {
const hashedPassword = await ctx.context.password.hash(
ctx.body.password,
);
await ctx.context.internalAdapter.linkAccount({
accountId: user.id,
providerId: "credential",
password: hashedPassword,
userId: user.id,
});
}
return ctx.json({
user: parseUserOutput(ctx.context.options, user) as UserWithRole,
});