feat: add getActiveRoleMember (#4484)

Co-authored-by: KinfeMichael Tariku <65047246+Kinfe123@users.noreply.github.com>
Co-authored-by: Alex Yang <himself65@outlook.com>
This commit is contained in:
Fathi
2025-09-09 14:48:55 -07:00
committed by GitHub
co-authored by KinfeMichael Tariku Alex Yang
parent 7faaeac9fc
commit f6f1902b59
6 changed files with 206 additions and 0 deletions
+2
View File
@@ -56,4 +56,6 @@ export const {
organization,
useListOrganizations,
useActiveOrganization,
useActiveMember,
useActiveMemberRole,
} = client;
@@ -1162,6 +1162,21 @@ To get the current member of the active organization you can use the `organizati
```
</APIMethod>
### Get Active Member Role
To get the current role member of the active organization you can use the `organization.getActiveMemberRole` function. This function will return the user's member role in their active organization.
<APIMethod
path="/organization/get-active-member-role"
method="GET"
requireSession
resultVariable="{ role }"
>
```ts
type getActiveMemberRole = {
}
```
</APIMethod>
### 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.
@@ -64,6 +64,7 @@ export const organizationClient = <CO extends OrganizationClientOptions>(
const $listOrg = atom<boolean>(false);
const $activeOrgSignal = atom<boolean>(false);
const $activeMemberSignal = atom<boolean>(false);
const $activeMemberRoleSignal = atom<boolean>(false);
type DefaultStatements = typeof defaultStatements;
type Statements = CO["ac"] extends AccessControl<infer S>
@@ -204,13 +205,24 @@ export const organizationClient = <CO extends OrganizationClientOptions>(
},
);
const activeMemberRole = useAuthQuery<{ role: string }>(
[$activeMemberRoleSignal],
"/organization/get-active-member-role",
$fetch,
{
method: "GET",
},
);
return {
$listOrg,
$activeOrgSignal,
$activeMemberSignal,
$activeMemberRoleSignal,
activeOrganization,
listOrganizations,
activeMember,
activeMemberRole,
};
},
pathMethods: {
@@ -246,6 +258,12 @@ export const organizationClient = <CO extends OrganizationClientOptions>(
},
signal: "$activeMemberSignal",
},
{
matcher(path) {
return path.includes("/organization/update-member-role");
},
signal: "$activeMemberRoleSignal",
},
],
} satisfies BetterAuthClientPlugin;
};
@@ -25,6 +25,7 @@ import {
listMembers,
removeMember,
updateMemberRole,
getActiveMemberRole,
} from "./routes/crud-members";
import {
checkOrganizationSlug,
@@ -405,6 +406,22 @@ export const organization = <O extends OrganizationOptions>(options?: O) => {
* `authClient.organization.listMembers`
*/
listMembers: listMembers(options as O),
/**
* ### Endpoint
*
* GET `/organization/get-active-member-role`
*
* ### API Methods
*
* **server:**
* `auth.api.getActiveMemberRole`
*
* **client:**
* `authClient.organization.getActiveMemberRole`
*
* @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/organization#api-method-organization-get-active-member-role)
*/
getActiveMemberRole: getActiveMemberRole(options as O),
};
const teamSupport = options?.teams?.enabled;
const teamEndpoints = {
@@ -333,3 +333,100 @@ describe("updateMemberRole", async () => {
);
});
});
describe("activeMemberRole", async () => {
const { auth, signInWithTestUser, cookieSetter } = await getTestInstance({
plugins: [organization()],
});
const ctx = await auth.$context;
const { headers } = await signInWithTestUser();
const client = createAuthClient({
plugins: [organizationClient()],
baseURL: "http://localhost:3000/api/auth",
fetchOptions: {
customFetchImpl: async (url, init) => {
return auth.handler(new Request(url, init));
},
},
});
const org = await client.organization.create({
name: "test",
slug: "test",
metadata: {
test: "test",
},
fetchOptions: {
headers,
},
});
const secondOrg = await client.organization.create({
name: "test-second",
slug: "test-second",
metadata: {
test: "second-org",
},
fetchOptions: {
headers,
},
});
let selectedUserId = "";
for (let i = 0; i < 10; i++) {
const user = await ctx.adapter.create({
model: "user",
data: {
email: `test${i}@test.com`,
name: `test${i}`,
},
});
if (i == 0) {
selectedUserId = user.id;
}
await auth.api.addMember({
body: {
organizationId: org.data?.id as string,
userId: user.id,
role: "member",
},
});
}
it("should return the active member role on active organization", async () => {
await client.organization.setActive({
organizationId: org.data?.id as string,
fetchOptions: {
headers,
},
});
const activeMember = await client.organization.getActiveMemberRole({
fetchOptions: {
headers,
},
});
expect(activeMember.data?.role).toBe("owner");
});
it("should return active member role on organization", async () => {
await client.organization.setActive({
organizationId: org.data?.id as string,
fetchOptions: {
headers,
},
});
const activeMember = await client.organization.getActiveMemberRole({
query: {
userId: selectedUserId,
},
fetchOptions: {
headers,
},
});
expect(activeMember.data?.role).toBe("member");
});
});
@@ -904,3 +904,60 @@ export const listMembers = <O extends OrganizationOptions>(options: O) =>
});
},
);
export const getActiveMemberRole = <O extends OrganizationOptions>(
options: O,
) =>
createAuthEndpoint(
"/organization/get-active-member-role",
{
method: "GET",
query: z
.object({
userId: z
.string()
.meta({
description:
"The user ID to get the role for. If not provided, will default to the current user's",
})
.optional(),
organizationId: z
.string()
.meta({
description:
'The organization ID to list members for. If not provided, will default to the user\'s active organization. Eg: "organization-id"',
})
.optional(),
})
.optional(),
use: [orgMiddleware, orgSessionMiddleware],
},
async (ctx) => {
const session = ctx.context.session;
const organizationId =
ctx.query?.organizationId || session.session.activeOrganizationId;
if (!organizationId) {
throw new APIError("BAD_REQUEST", {
message: ORGANIZATION_ERROR_CODES.NO_ACTIVE_ORGANIZATION,
});
}
const userId = ctx.query?.userId || session.user.id;
const adapter = getOrgAdapter<O>(ctx.context, options);
const member = await adapter.findMemberByOrgId({
userId,
organizationId,
});
if (!member) {
throw new APIError("FORBIDDEN", {
message:
ORGANIZATION_ERROR_CODES.YOU_ARE_NOT_A_MEMBER_OF_THIS_ORGANIZATION,
});
}
return ctx.json({
role: member?.role,
});
},
);