mirror of
https://github.com/LukeHagar/better-auth.git
synced 2025-12-10 12:27:44 +00:00
Add API option to add member without invite (#378)
Co-authored-by: Bereket Engida <86073083+Bekacru@users.noreply.github.com> Co-authored-by: Bereket Engida <bekacru@gmail.com>
This commit is contained in:
@@ -393,6 +393,21 @@ To get the current member of the organization you can use the `organization.getA
|
|||||||
const member = await client.organization.getActiveMember()
|
const member = await client.organization.getActiveMember()
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Add Member
|
||||||
|
|
||||||
|
If you want to add a member directly to an organization without sending an invitation, you can use the `addMember` function which can only be invoked on the server.
|
||||||
|
|
||||||
|
```ts title="api.ts"
|
||||||
|
import { auth } from "@/auth";
|
||||||
|
|
||||||
|
auth.api.addMember({
|
||||||
|
body: {
|
||||||
|
userId: "user-id",
|
||||||
|
organizationId: "organization-id",
|
||||||
|
role: "admin"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
## Access Control
|
## Access Control
|
||||||
|
|
||||||
|
|||||||
@@ -214,7 +214,7 @@ export const getSessionFromCtx = async <
|
|||||||
return session as {
|
return session as {
|
||||||
session: S & Session;
|
session: S & Session;
|
||||||
user: U & User;
|
user: U & User;
|
||||||
};
|
} | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const sessionMiddleware = createAuthMiddleware(async (ctx) => {
|
export const sessionMiddleware = createAuthMiddleware(async (ctx) => {
|
||||||
|
|||||||
@@ -302,6 +302,31 @@ describe("organization", async (it) => {
|
|||||||
expectTypeOf(auth.api.createOrganization).toBeFunction();
|
expectTypeOf(auth.api.createOrganization).toBeFunction();
|
||||||
expectTypeOf(auth.api.getInvitation).toBeFunction();
|
expectTypeOf(auth.api.getInvitation).toBeFunction();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should add member on the server directly", async () => {
|
||||||
|
const newUser = await auth.api.signUpEmail({
|
||||||
|
body: {
|
||||||
|
email: "new-member@email.com",
|
||||||
|
password: "password",
|
||||||
|
name: "new member",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const org = await auth.api.createOrganization({
|
||||||
|
body: {
|
||||||
|
name: "test",
|
||||||
|
slug: "test",
|
||||||
|
},
|
||||||
|
headers,
|
||||||
|
});
|
||||||
|
const member = await auth.api.addMember({
|
||||||
|
body: {
|
||||||
|
organizationId: org?.id,
|
||||||
|
userId: newUser.user.email,
|
||||||
|
role: "admin",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(member?.role).toBe("admin");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("access control", async (it) => {
|
describe("access control", async (it) => {
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import {
|
|||||||
rejectInvitation,
|
rejectInvitation,
|
||||||
} from "./routes/crud-invites";
|
} from "./routes/crud-invites";
|
||||||
import {
|
import {
|
||||||
|
addMember,
|
||||||
getActiveMember,
|
getActiveMember,
|
||||||
removeMember,
|
removeMember,
|
||||||
updateMemberRole,
|
updateMemberRole,
|
||||||
@@ -215,6 +216,7 @@ export const organization = <O extends OrganizationOptions>(options?: O) => {
|
|||||||
acceptInvitation,
|
acceptInvitation,
|
||||||
getInvitation,
|
getInvitation,
|
||||||
rejectInvitation,
|
rejectInvitation,
|
||||||
|
addMember: addMember<O>(),
|
||||||
removeMember,
|
removeMember,
|
||||||
updateMemberRole: updateMemberRole(options as O),
|
updateMemberRole: updateMemberRole(options as O),
|
||||||
getActiveMember,
|
getActiveMember,
|
||||||
|
|||||||
@@ -4,7 +4,78 @@ import { getOrgAdapter } from "../adapter";
|
|||||||
import { orgMiddleware, orgSessionMiddleware } from "../call";
|
import { orgMiddleware, orgSessionMiddleware } from "../call";
|
||||||
import type { InferRolesFromOption, Member } from "../schema";
|
import type { InferRolesFromOption, Member } from "../schema";
|
||||||
import { APIError } from "better-call";
|
import { APIError } from "better-call";
|
||||||
|
import type { User } from "../../../db/schema";
|
||||||
|
import { generateId } from "../../../utils";
|
||||||
import type { OrganizationOptions } from "../organization";
|
import type { OrganizationOptions } from "../organization";
|
||||||
|
import { getSessionFromCtx } from "../../../api";
|
||||||
|
|
||||||
|
export const addMember = <O extends OrganizationOptions>() =>
|
||||||
|
createAuthEndpoint(
|
||||||
|
"/organization/add-member",
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
body: z.object({
|
||||||
|
userId: z.string(),
|
||||||
|
role: z.string() as unknown as InferRolesFromOption<O>,
|
||||||
|
organizationId: z.string().optional(),
|
||||||
|
}),
|
||||||
|
use: [orgMiddleware],
|
||||||
|
metadata: {
|
||||||
|
SERVER_ONLY: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async (ctx) => {
|
||||||
|
const session = ctx.body.userId
|
||||||
|
? await getSessionFromCtx<{
|
||||||
|
session: {
|
||||||
|
activeOrganizationId?: string;
|
||||||
|
};
|
||||||
|
}>(ctx).catch((e) => null)
|
||||||
|
: null;
|
||||||
|
const orgId =
|
||||||
|
ctx.body.organizationId || session?.session.activeOrganizationId;
|
||||||
|
if (!orgId) {
|
||||||
|
return ctx.json(null, {
|
||||||
|
status: 400,
|
||||||
|
body: {
|
||||||
|
message: "No active organization found!",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const adapter = getOrgAdapter(ctx.context, ctx.context.orgOptions);
|
||||||
|
|
||||||
|
const user = await ctx.context.internalAdapter.findUserById(
|
||||||
|
ctx.body.userId,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw new APIError("BAD_REQUEST", {
|
||||||
|
message: "User not found!",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const alreadyMember = await adapter.findMemberByEmail({
|
||||||
|
email: user.email,
|
||||||
|
organizationId: orgId,
|
||||||
|
});
|
||||||
|
if (alreadyMember) {
|
||||||
|
throw new APIError("BAD_REQUEST", {
|
||||||
|
message: "User is already a member of this organization",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const createdMember = await adapter.createMember({
|
||||||
|
id: generateId(),
|
||||||
|
organizationId: orgId,
|
||||||
|
userId: user.id,
|
||||||
|
role: ctx.body.role as string,
|
||||||
|
createdAt: new Date(),
|
||||||
|
});
|
||||||
|
|
||||||
|
return ctx.json(createdMember);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
export const removeMember = createAuthEndpoint(
|
export const removeMember = createAuthEndpoint(
|
||||||
"/organization/remove-member",
|
"/organization/remove-member",
|
||||||
|
|||||||
Reference in New Issue
Block a user