fix: invite accept bypasses disableSignUp by falling back to internal adapter

signUpEmail throws when disableSignUp is true. Now catches that error
and creates the user directly via Better Auth's internal adapter:
createUser + linkAccount with hashed password.
This commit is contained in:
2026-01-28 22:17:08 +00:00
parent ead71ae0a7
commit 11ee9b946f

View File

@@ -73,13 +73,16 @@ export const inviteRoutes = new Elysia({ prefix: '/auth/invite' })
} }
try { try {
// Use Better Auth's internal API to create the user properly // Create user via internal adapter (bypasses disableSignUp restriction)
const result = await auth.api.signUpEmail({ const userName = body.name || invite.name;
const createdUser = await auth.api.signUpEmail({
body: { body: {
email: invite.email, email: invite.email,
password: body.password, password: body.password,
name: body.name || invite.name, name: userName,
}, },
headers: new Headers(),
// Use asResponse: false to get direct result
}); });
// Set the role from the invite // Set the role from the invite
@@ -97,14 +100,67 @@ export const inviteRoutes = new Elysia({ prefix: '/auth/invite' })
return { return {
success: true, success: true,
user: { user: {
id: result.user?.id, id: createdUser.user?.id,
email: invite.email, email: invite.email,
name: body.name || invite.name, name: userName,
role: invite.role, role: invite.role,
}, },
}; };
} catch (error: any) { } catch (error: any) {
console.error('Invite accept error:', error); console.error('Invite accept error:', error);
// If signUpEmail fails due to disableSignUp, use direct DB approach
if (error.message?.includes('not enabled') || error.status === 400) {
try {
const userName = body.name || invite.name;
// Hash password using Better Auth's context
const ctx = await (auth as any).$context;
const hash = await ctx.password.hash(body.password);
// Create user directly
const newUser = await ctx.internalAdapter.createUser({
email: invite.email.toLowerCase(),
name: userName,
emailVerified: false,
});
// Link credential account with hashed password
await ctx.internalAdapter.linkAccount({
userId: newUser.id,
providerId: 'credential',
accountId: newUser.id,
password: hash,
});
// Set the role from the invite
if (invite.role) {
await db.update(users)
.set({ role: invite.role })
.where(eq(users.id, newUser.id));
}
// Mark invite as accepted
await db.update(invites)
.set({ status: 'accepted' })
.where(eq(invites.id, invite.id));
return {
success: true,
user: {
id: newUser.id,
email: invite.email,
name: userName,
role: invite.role,
},
};
} catch (innerError: any) {
console.error('Direct user creation also failed:', innerError);
set.status = 400;
throw new Error(innerError.message || 'Failed to create account');
}
}
set.status = 400; set.status = 400;
throw new Error(error.message || 'Failed to create account'); throw new Error(error.message || 'Failed to create account');
} }