[GH-ISSUE #6598] beforeCreateInvitation hook receives session.user as inviter instead of member data #10565

Closed
opened 2026-04-13 06:47:32 -05:00 by GiteaMirror · 5 comments
Owner

Originally created by @SawkaDev on GitHub (Dec 8, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/6598

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

The beforeCreateInvitation hook receives session.user as the inviter parameter, which contains the User table's role field (platform-level admin role from the admin plugin). However, sendInvitationEmail correctly receives the member data merged with user, which includes the organization membership role.

This inconsistency makes it impossible to validate organization-level permissions in the beforeCreateInvitation hook without a separate database query.

  import { betterAuth } from 'better-auth';
  import { organization } from 'better-auth/plugins';

  export const auth = betterAuth({
    // ... other config
    plugins: [
      organization({
        organizationHooks: {
          beforeCreateInvitation: async ({ invitation, inviter }) => {
            // inviter.role is User.role (platform role), NOT Member.role (org role)
            console.log('Inviter role:', inviter.role);

            // This check fails because inviter.role comes from User table,
            // not the Member table's organization role
            if (inviter.role === 'admin' && invitation.role === 'admin') {
              throw new Error('Admins cannot invite other admins');
            }

            return { data: invitation };
          },
        },
      }),
    ],
  });

Steps:

  1. Create a user with User.role = 'admin' (platform admin via admin plugin)
  2. Add that user as an owner in an organization (Member.role = 'owner')
  3. Try to invite someone as admin to the organization
  4. The hook receives inviter.role = 'admin' (from User table) instead of 'owner' (from Member table)
  5. Permission check fails incorrectly

Current vs. Expected behavior

inviter from the beforeCreateInvitation should include the inviter role from the organization not from the admin role plugin (platform level)

What version of Better Auth are you using?

1.4.5

System info

n/a

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

Backend

Auth config (if applicable)

Add organization and admin plugin.

Additional context

The sendInvitationEmail callback correctly receives the merged member + user object:

  await ctx.context.orgOptions.sendInvitationEmail?.(
    {
      id: invitation.id,
      role: invitation.role,
      email: invitation.email.toLowerCase(),
      organization: organization,
      inviter: {
        ...(member as Member),  // Correct: includes org membership role
        user: session.user,
      },
      invitation,
    },
    ctx.request,
  );

The member variable is already fetched earlier in the function, so the fix would simply be to pass it to the hook the same way it's passed to sendInvitationEmail.

Originally created by @SawkaDev on GitHub (Dec 8, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/6598 ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce The `beforeCreateInvitation` hook receives session.user as the inviter parameter, which contains the User table's role field (platform-level admin role from the admin plugin). However, `sendInvitationEmail` correctly receives the member data merged with user, which includes the organization membership role. This inconsistency makes it impossible to validate organization-level permissions in the `beforeCreateInvitation` hook without a separate database query. ``` import { betterAuth } from 'better-auth'; import { organization } from 'better-auth/plugins'; export const auth = betterAuth({ // ... other config plugins: [ organization({ organizationHooks: { beforeCreateInvitation: async ({ invitation, inviter }) => { // inviter.role is User.role (platform role), NOT Member.role (org role) console.log('Inviter role:', inviter.role); // This check fails because inviter.role comes from User table, // not the Member table's organization role if (inviter.role === 'admin' && invitation.role === 'admin') { throw new Error('Admins cannot invite other admins'); } return { data: invitation }; }, }, }), ], }); ``` Steps: 1. Create a user with User.role = 'admin' (platform admin via admin plugin) 2. Add that user as an owner in an organization (Member.role = 'owner') 3. Try to invite someone as admin to the organization 4. The hook receives inviter.role = 'admin' (from User table) instead of 'owner' (from Member table) 5. Permission check fails incorrectly ### Current vs. Expected behavior `inviter` from the beforeCreateInvitation should include the inviter role from the organization not from the admin role plugin (platform level) ### What version of Better Auth are you using? 1.4.5 ### System info ```bash n/a ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript Add organization and admin plugin. ``` ### Additional context The sendInvitationEmail callback correctly receives the merged member + user object: ``` await ctx.context.orgOptions.sendInvitationEmail?.( { id: invitation.id, role: invitation.role, email: invitation.email.toLowerCase(), organization: organization, inviter: { ...(member as Member), // Correct: includes org membership role user: session.user, }, invitation, }, ctx.request, ); ``` The member variable is already fetched earlier in the function, so the fix would simply be to pass it to the hook the same way it's passed to sendInvitationEmail.
GiteaMirror added the locked label 2026-04-13 06:47:32 -05:00
Author
Owner

@better-auth-agent[bot] commented on GitHub (Dec 8, 2025):

tag @better-auth-agent if you would like an answer from the Agent

If you need more help, tag @better-auth-agent in a comment so I can respond.

DiagramDiscordGitHub

Diagram Join Star

<!-- gh-comment-id:3624205304 --> @better-auth-agent[bot] commented on GitHub (Dec 8, 2025): tag @better-auth-agent if you would like an answer from the Agent _If you need more help, tag @better-auth-agent in a comment so I can respond._ <!-- bot:webhook reply v1 --> [Diagram](https://repodiagrams.s3.eu-north-1.amazonaws.com/skyvern_ultra_detailed_interactive.html) • [Discord](https://discord.gg/fG2XXEuQX3) • [GitHub](https://github.com/Skyvern-AI/Skyvern) [![Diagram](https://img.shields.io/badge/Diagram-2b3137?style=flat-square)](https://repodiagrams.s3.eu-north-1.amazonaws.com/skyvern_ultra_detailed_interactive.html) [![Join](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&style=flat-square)](https://discord.gg/fG2XXEuQX3) [![Star](https://img.shields.io/badge/star-181717?logo=github&logoColor=white&style=flat-square)](https://github.com/Skyvern-AI/Skyvern)
Author
Owner

@SawkaDev commented on GitHub (Dec 8, 2025):

lets try it out, @better-auth-agent

<!-- gh-comment-id:3624205945 --> @SawkaDev commented on GitHub (Dec 8, 2025): lets try it out, @better-auth-agent
Author
Owner

@better-auth-agent[bot] commented on GitHub (Dec 8, 2025):

It appears the issue is that the beforeCreateInvitation hook is receiving the plain session user (with the platform role) rather than the merged organization member data that includes the organization’s role. The expected behavior is that, as in the sendInvitationEmail callback—which correctly receives the member object merged with the user—the beforeCreateInvitation hook should also use this richer member data to accurately validate organization-level permissions [1].

To troubleshoot this, please verify the hook implementation in your organization plugin setup. Ensure that during invitation creation, the member data (which contains the organization role) is fetched earlier and passed to your hook instead of the basic session.user. As a next step, you might consider patching the hook call to pass the member variable, similar to what is done for the sendInvitationEmail callback. Could you check if this adjustment resolves the permission validation issue on your end?

tag @better-auth-agent if you would like an answer from the Agent

DiagramDiscordGitHub

Diagram Join Star

<!-- gh-comment-id:3624208760 --> @better-auth-agent[bot] commented on GitHub (Dec 8, 2025): It appears the issue is that the beforeCreateInvitation hook is receiving the plain session user (with the platform role) rather than the merged organization member data that includes the organization’s role. The expected behavior is that, as in the sendInvitationEmail callback—which correctly receives the member object merged with the user—the beforeCreateInvitation hook should also use this richer member data to accurately validate organization-level permissions [[1]](https://github.com/better-auth/better-auth/issues/3672). To troubleshoot this, please verify the hook implementation in your organization plugin setup. Ensure that during invitation creation, the member data (which contains the organization role) is fetched earlier and passed to your hook instead of the basic session.user. As a next step, you might consider patching the hook call to pass the member variable, similar to what is done for the sendInvitationEmail callback. Could you check if this adjustment resolves the permission validation issue on your end? tag @better-auth-agent if you would like an answer from the Agent <!-- bot:webhook reply v1 --> [Diagram](https://repodiagrams.s3.eu-north-1.amazonaws.com/skyvern_ultra_detailed_interactive.html) • [Discord](https://discord.gg/fG2XXEuQX3) • [GitHub](https://github.com/Skyvern-AI/Skyvern) [![Diagram](https://img.shields.io/badge/Diagram-2b3137?style=flat-square)](https://repodiagrams.s3.eu-north-1.amazonaws.com/skyvern_ultra_detailed_interactive.html) [![Join](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&style=flat-square)](https://discord.gg/fG2XXEuQX3) [![Star](https://img.shields.io/badge/star-181717?logo=github&logoColor=white&style=flat-square)](https://github.com/Skyvern-AI/Skyvern)
Author
Owner

@bytaesu commented on GitHub (Feb 1, 2026):

Hi @SawkaDev,

The inviter is the User itself, so this is the expected behavior.

With the current structure, you could query the member through the user to implement custom logic. We could add member to the hook, but it might feel inconsistent.. 🤔

+) We're rewriting the Organization plugin and plan to introduce it in v1.5 !!

cc @ping-maxwell

<!-- gh-comment-id:3830654822 --> @bytaesu commented on GitHub (Feb 1, 2026): Hi @SawkaDev, The inviter is the `User` itself, so this is the expected behavior. With the current structure, you could query the member through the user to implement custom logic. We could add member to the hook, but it might feel inconsistent.. 🤔 +) We're rewriting the Organization plugin and plan to introduce it in v1.5 !! cc @ping-maxwell
Author
Owner

@github-actions[bot] commented on GitHub (Apr 1, 2026):

This issue has been locked as it was closed more than 7 days ago. If you're experiencing a similar problem or you have additional context, please open a new issue and reference this one.

<!-- gh-comment-id:4166558869 --> @github-actions[bot] commented on GitHub (Apr 1, 2026): This issue has been locked as it was closed more than 7 days ago. If you're experiencing a similar problem or you have additional context, please open a new issue and reference this one.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#10565