Cannot send organization invite without an owner role #513

Closed
opened 2026-03-13 07:50:30 -05:00 by GiteaMirror · 2 comments
Owner

Originally created by @BenFawley on GitHub (Jan 2, 2025).

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

  1. Follow the installation steps in the docs with the drizzle adapter and postgres database: https://www.better-auth.com/docs/installation
  2. Add the organization and admin plugins

// auth client:

import { createAuthClient } from "better-auth/vue";
import {
  adminClient,
  organizationClient,
  inferAdditionalFields,
} from "better-auth/client/plugins";

import { permissions } from "~/lib/auth/permissions";

export const authClient = createAuthClient({
  plugins: [
    organizationClient({
      ac: permissions.ac,
      roles: permissions.roles,
    }),
    adminClient(),
    inferAdditionalFields<typeof auth>(),
  ],
});

// auth config:

import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { admin, organization } from "better-auth/plugins";

import { permissions } from "~/lib/auth/permissions";
import { sendOrganizationEmailInvite } from "~/lib/email/resend";

import useDrizzle from "../db/drizzle";
import * as schema from "../db/schema";

const db = useDrizzle();

export const auth = betterAuth({
  database: drizzleAdapter(db, {
    provider: "pg",
  }),
  emailAndPassword: {
    enabled: true,
  },
  schema: {
    ...schema,
  },
  plugins: [
    organization({
      ac: permissions.ac,
      roles: permissions.roles,
      allowUserToCreateOrganization: async () => {
        return false;
      },
      async sendInvitationEmail(data) {
        const props = {
          username: data.email,
          invitedByUsername: data.inviter.user.name,
          invitedByEmail: data.inviter.user.email,
          teamName: data.organization.name,
          inviteLink:
            process.env.NODE_ENV === "development"
              ? `http://localhost:3000/accept-invitation/${data.id}`
              : `${
                  process.env.BETTER_AUTH_URL
                }/accept-invitation/${data.id}`,
        };
        const email = await sendOrganizationEmailInvite(props);
      },
    }),
    admin(),
  ],
});
  1. Sign in as a user with an admin role who is a member of the organization you are trying to invite somebody to
  2. Try to call either the client.organization.inviteMember() or auth.api.createInvitation() functions and a 403 is returned.
  const invite = await authClient.organization.inviteMember({
    email: "test@test.com",
    role: "member",
    organizationId: "test-org",
  });

Current vs. Expected behavior

I expected an organization invite to be sent but instead it throws a 403 forbidden error:

image

Session data:

image

I have tried creating a new custom role with custom permissions but it still throws the same error. It only allows sends the invite if the user has the 'owner' role.

import {
  createAccessControl,
  defaultStatements,
  adminAc,
  memberAc,
  ownerAc,
} from "better-auth/plugins/access";

const statement = {
  ...defaultStatements,
} as const;

const ac = createAccessControl(statement);

const admin = ac.newRole({
  ...adminAc.statements,
  invitation: ["create", "cancel"],
});

const member = ac.newRole({
  ...memberAc.statements,
});

const owner = ac.newRole({
  ...ownerAc.statements,
});

const roles = { admin, member, owner };

export const permissions = { roles, ac };

What version of Better Auth are you using?

1.1.6

Provide environment information

Browser: Chrome
Operating System: Linux Mint 21.2
"drizzle-orm": "^0.36.1",
"nuxt": "3.12.4",

Which area(s) are affected? (Select all that apply)

Backend, Client

Auth config (if applicable)

import { betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { admin, organization } from "better-auth/plugins";

import { permissions } from "~/lib/auth/permissions";
import { sendOrganizationEmailInvite } from "~/lib/email/resend";

import useDrizzle from "../db/drizzle";
import * as schema from "../db/schema";

const db = useDrizzle();

export const auth = betterAuth({
  database: drizzleAdapter(db, {
    provider: "pg",
  }),
  emailAndPassword: {
    enabled: true,
  },
  schema: {
    ...schema,
  },
  plugins: [
    organization({
      ac: permissions.ac,
      roles: permissions.roles,
      allowUserToCreateOrganization: async () => {
        return false;
      },
      async sendInvitationEmail(data) {
        const props = {
          username: data.email,
          invitedByUsername: data.inviter.user.name,
          invitedByEmail: data.inviter.user.email,
          teamName: data.organization.name,
          inviteLink:
            process.env.NODE_ENV === "development"
              ? `http://localhost:3000/accept-invitation/${data.id}`
              : `${
                  process.env.BETTER_AUTH_URL
                }/accept-invitation/${data.id}`,
        };
        const email = await sendOrganizationEmailInvite(props);
      },
    }),
    admin(),
  ],
});

Additional context

No response

Originally created by @BenFawley on GitHub (Jan 2, 2025). ### Is this suited for github? - [X] Yes, this is suited for github ### To Reproduce 1. Follow the installation steps in the docs with the drizzle adapter and postgres database: https://www.better-auth.com/docs/installation 2. Add the organization and admin plugins // auth client: ``` import { createAuthClient } from "better-auth/vue"; import { adminClient, organizationClient, inferAdditionalFields, } from "better-auth/client/plugins"; import { permissions } from "~/lib/auth/permissions"; export const authClient = createAuthClient({ plugins: [ organizationClient({ ac: permissions.ac, roles: permissions.roles, }), adminClient(), inferAdditionalFields<typeof auth>(), ], }); ``` // auth config: ``` import { betterAuth } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; import { admin, organization } from "better-auth/plugins"; import { permissions } from "~/lib/auth/permissions"; import { sendOrganizationEmailInvite } from "~/lib/email/resend"; import useDrizzle from "../db/drizzle"; import * as schema from "../db/schema"; const db = useDrizzle(); export const auth = betterAuth({ database: drizzleAdapter(db, { provider: "pg", }), emailAndPassword: { enabled: true, }, schema: { ...schema, }, plugins: [ organization({ ac: permissions.ac, roles: permissions.roles, allowUserToCreateOrganization: async () => { return false; }, async sendInvitationEmail(data) { const props = { username: data.email, invitedByUsername: data.inviter.user.name, invitedByEmail: data.inviter.user.email, teamName: data.organization.name, inviteLink: process.env.NODE_ENV === "development" ? `http://localhost:3000/accept-invitation/${data.id}` : `${ process.env.BETTER_AUTH_URL }/accept-invitation/${data.id}`, }; const email = await sendOrganizationEmailInvite(props); }, }), admin(), ], }); ``` 3. Sign in as a user with an admin role who is a member of the organization you are trying to invite somebody to 4. Try to call either the `client.organization.inviteMember()` or `auth.api.createInvitation() `functions and a 403 is returned. ``` const invite = await authClient.organization.inviteMember({ email: "test@test.com", role: "member", organizationId: "test-org", }); ``` ### Current vs. Expected behavior I expected an organization invite to be sent but instead it throws a 403 forbidden error: ![image](https://github.com/user-attachments/assets/46ea707a-da83-4e70-ae07-ec5e047b6d60) Session data: ![image](https://github.com/user-attachments/assets/e3015d66-0dea-446c-b7a9-1da449288f9e) I have tried creating a new custom role with custom permissions but it still throws the same error. It only allows sends the invite if the user has the 'owner' role. ``` import { createAccessControl, defaultStatements, adminAc, memberAc, ownerAc, } from "better-auth/plugins/access"; const statement = { ...defaultStatements, } as const; const ac = createAccessControl(statement); const admin = ac.newRole({ ...adminAc.statements, invitation: ["create", "cancel"], }); const member = ac.newRole({ ...memberAc.statements, }); const owner = ac.newRole({ ...ownerAc.statements, }); const roles = { admin, member, owner }; export const permissions = { roles, ac }; ``` ### What version of Better Auth are you using? 1.1.6 ### Provide environment information ```bash Browser: Chrome Operating System: Linux Mint 21.2 "drizzle-orm": "^0.36.1", "nuxt": "3.12.4", ``` ### Which area(s) are affected? (Select all that apply) Backend, Client ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; import { admin, organization } from "better-auth/plugins"; import { permissions } from "~/lib/auth/permissions"; import { sendOrganizationEmailInvite } from "~/lib/email/resend"; import useDrizzle from "../db/drizzle"; import * as schema from "../db/schema"; const db = useDrizzle(); export const auth = betterAuth({ database: drizzleAdapter(db, { provider: "pg", }), emailAndPassword: { enabled: true, }, schema: { ...schema, }, plugins: [ organization({ ac: permissions.ac, roles: permissions.roles, allowUserToCreateOrganization: async () => { return false; }, async sendInvitationEmail(data) { const props = { username: data.email, invitedByUsername: data.inviter.user.name, invitedByEmail: data.inviter.user.email, teamName: data.organization.name, inviteLink: process.env.NODE_ENV === "development" ? `http://localhost:3000/accept-invitation/${data.id}` : `${ process.env.BETTER_AUTH_URL }/accept-invitation/${data.id}`, }; const email = await sendOrganizationEmailInvite(props); }, }), admin(), ], }); ``` ### Additional context _No response_
GiteaMirror added the bug label 2026-03-13 07:50:30 -05:00
Author
Owner

@Bekacru commented on GitHub (Jan 3, 2025):

this has been shortly fixed on the next release. update to latest

@Bekacru commented on GitHub (Jan 3, 2025): this has been shortly fixed on the next release. update to latest
Author
Owner

@BenFawley commented on GitHub (Jan 3, 2025):

thank you

@BenFawley commented on GitHub (Jan 3, 2025): thank you
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#513