[GH-ISSUE #3693] List Team Members doesn't work for Organization Owner/Admins #18321

Closed
opened 2026-04-15 16:44:53 -05:00 by GiteaMirror · 4 comments
Owner

Originally created by @TheUntraceable on GitHub (Jul 29, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/3693

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

  1. Create new team inside of an organization
    2 const teamMembers = await auth.api.listTeamMembers({
    headers: h,
    query: { teamId },
    });

Current vs. Expected behavior

Expected: Returns the list of members inside of the team as you are the Owner
Current: Errors as you are not part of that team.

What version of Better Auth are you using?

1.3.4

Provide environment information

- OS: Windows 11
- Browser: Brave Browser

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

Backend

Auth config (if applicable)

import { client } from '@/lib/db';
import { reverify } from '@better-auth-kit/reverify';
import { sso } from '@better-auth/sso';
import { betterAuth } from 'better-auth';
import { mongodbAdapter } from 'better-auth/adapters/mongodb';
import { haveIBeenPwned } from 'better-auth/plugins';
import { admin } from 'better-auth/plugins/admin';
import { emailOTP } from 'better-auth/plugins/email-otp';
import { organization } from 'better-auth/plugins/organization';
import { passkey } from 'better-auth/plugins/passkey';
import { twoFactor } from 'better-auth/plugins/two-factor';
import { Resend } from 'resend';

const db = client.db('auth');

const emailOtpConfig = {
    sendVerificationOTP: async ({
        email,
        otp,
        type,
    }: {
        email: string;
        otp: string;
        type: 'forget-password' | 'sign-in' | 'email-verification';
    }) => {
          ...
        }
    },
};

export const auth = betterAuth({
    appName: 'Better Auth Template',
    baseURL: 'http:127.0.0.1:3000',
    user: {
        changeEmail: {
            enabled: true,
            async sendChangeEmailVerification({ newEmail, url, user }) {...},
        },
        deleteUser: {
            enabled: true,
            async sendDeleteAccountVerification({ token, user }) {...},
        },
    },
    database: mongodbAdapter(db),
    trustedOrigins: [
        process.env.BETTER_AUTH_URL,
        'http://127.0.0.1:3000',
    ],
    emailAndPassword: {
        enabled: true,
        requireEmailVerification: true,
    },
    emailVerification: {
        autoSignInAfterVerification: true,
    },
    plugins: [
        sso({
            organizationProvisioning: {
                defaultRole: 'member',
                disabled: false,
            },
        }),
        admin(),
        haveIBeenPwned({
            customPasswordCompromisedMessage:
                'This password has been compromised in a data breach. Please choose a different password.',
        }),
        reverify(),
        organization({
            teams: {
                enabled: true,
                defaultTeam: {
                    enabled: false,
                },
            },
        }),
        emailOTP(emailOtpConfig),
        twoFactor({
            issuer: 'My App',
            backupCodeOptions: {
                amount: 12,
                length: 10,
            },
        }),
        passkey({
            rpID: "google.com"
            rpName: 'Totally Google',
            origin:
                process.env.BETTER_AUTH_URL || 'https://my-app.com',
        }),
    ],
    logger: {
        level: "info",
        disabled: false,
        log: (level, message, ...args) => {
            console.log(`[${level}] ${message}`, ...args);
        }
    }
});

export type Session = typeof auth.$Infer.Session;

Additional context

No response

Originally created by @TheUntraceable on GitHub (Jul 29, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/3693 ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce 1. Create new team inside of an organization 2 const teamMembers = await auth.api.listTeamMembers({ headers: h, query: { teamId }, }); ### Current vs. Expected behavior Expected: Returns the list of members inside of the team as you are the Owner Current: Errors as you are not part of that team. ### What version of Better Auth are you using? 1.3.4 ### Provide environment information ```bash - OS: Windows 11 - Browser: Brave Browser ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript import { client } from '@/lib/db'; import { reverify } from '@better-auth-kit/reverify'; import { sso } from '@better-auth/sso'; import { betterAuth } from 'better-auth'; import { mongodbAdapter } from 'better-auth/adapters/mongodb'; import { haveIBeenPwned } from 'better-auth/plugins'; import { admin } from 'better-auth/plugins/admin'; import { emailOTP } from 'better-auth/plugins/email-otp'; import { organization } from 'better-auth/plugins/organization'; import { passkey } from 'better-auth/plugins/passkey'; import { twoFactor } from 'better-auth/plugins/two-factor'; import { Resend } from 'resend'; const db = client.db('auth'); const emailOtpConfig = { sendVerificationOTP: async ({ email, otp, type, }: { email: string; otp: string; type: 'forget-password' | 'sign-in' | 'email-verification'; }) => { ... } }, }; export const auth = betterAuth({ appName: 'Better Auth Template', baseURL: 'http:127.0.0.1:3000', user: { changeEmail: { enabled: true, async sendChangeEmailVerification({ newEmail, url, user }) {...}, }, deleteUser: { enabled: true, async sendDeleteAccountVerification({ token, user }) {...}, }, }, database: mongodbAdapter(db), trustedOrigins: [ process.env.BETTER_AUTH_URL, 'http://127.0.0.1:3000', ], emailAndPassword: { enabled: true, requireEmailVerification: true, }, emailVerification: { autoSignInAfterVerification: true, }, plugins: [ sso({ organizationProvisioning: { defaultRole: 'member', disabled: false, }, }), admin(), haveIBeenPwned({ customPasswordCompromisedMessage: 'This password has been compromised in a data breach. Please choose a different password.', }), reverify(), organization({ teams: { enabled: true, defaultTeam: { enabled: false, }, }, }), emailOTP(emailOtpConfig), twoFactor({ issuer: 'My App', backupCodeOptions: { amount: 12, length: 10, }, }), passkey({ rpID: "google.com" rpName: 'Totally Google', origin: process.env.BETTER_AUTH_URL || 'https://my-app.com', }), ], logger: { level: "info", disabled: false, log: (level, message, ...args) => { console.log(`[${level}] ${message}`, ...args); } } }); export type Session = typeof auth.$Infer.Session; ``` ### Additional context _No response_
GiteaMirror added the locked label 2026-04-15 16:44:53 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Jul 29, 2025):

Hi @TheUntraceable! I'm Dosu and I’m helping the better-auth team.

This is the intended behavior in Better Auth v1.3.4: organization Owners/Admins cannot list team members unless they are actually members of that team. The API enforces this strictly and does not check for org-level roles when listing team members—only direct team membership is considered, even for Owners/Admins. This is by design to keep team data boundaries strict and is not currently configurable or overrideable via plugin options, hooks, or role settings see source.

If you need Owners/Admins to have this access, you’d need to either add them as members to all teams programmatically, or modify the code for the listTeamMembers endpoint to check org-level roles and permissions. Alternatively, you can use the organization-level listMembers API to get all org members, but that won’t give you team-specific membership.

There’s no built-in way to override this in the current version. If you need this to be more flexible, a code change or feature request would be needed.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Chat with Dosu Join Discord Share on X

<!-- gh-comment-id:3134097806 --> @dosubot[bot] commented on GitHub (Jul 29, 2025): <!-- Greeting --> Hi @TheUntraceable! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> This is the intended behavior in Better Auth v1.3.4: organization Owners/Admins cannot list team members unless they are actually members of that team. The API enforces this strictly and does not check for org-level roles when listing team members—only direct team membership is considered, even for Owners/Admins. This is by design to keep team data boundaries strict and is not currently configurable or overrideable via plugin options, hooks, or role settings [see source](https://github.com/better-auth/better-auth/blob/978b28588e4708d802e598b0b491f8437c4741fe/packages/better-auth/src/plugins/organization/routes/crud-team.ts). If you need Owners/Admins to have this access, you’d need to either add them as members to all teams programmatically, or modify the code for the listTeamMembers endpoint to check org-level roles and permissions. Alternatively, you can use the organization-level `listMembers` API to get all org members, but that won’t give you team-specific membership. There’s no built-in way to override this in the current version. If you need this to be more flexible, a code change or feature request would be needed. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/e38d3d39-394b-4b06-8697-14d48b44457b?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/e38d3d39-394b-4b06-8697-14d48b44457b?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/e38d3d39-394b-4b06-8697-14d48b44457b?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/e38d3d39-394b-4b06-8697-14d48b44457b?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/e38d3d39-394b-4b06-8697-14d48b44457b?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/e38d3d39-394b-4b06-8697-14d48b44457b?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/e38d3d39-394b-4b06-8697-14d48b44457b?feedback_type=other)</sup>&nbsp;&nbsp;[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/3693)
Author
Owner

@TheUntraceable commented on GitHub (Jul 31, 2025):

From an implementation basis do you think making it so that users with the admin role or the permission to delete or create teams should implicitly be able list the members in a team? @dosu

<!-- gh-comment-id:3139485085 --> @TheUntraceable commented on GitHub (Jul 31, 2025): From an implementation basis do you think making it so that users with the admin role or the permission to delete or create teams should implicitly be able list the members in a team? @dosu
Author
Owner

@dosubot[bot] commented on GitHub (Jul 31, 2025):

From an implementation and security perspective, it's generally best practice to keep permissions explicit and granular—especially for sensitive operations like listing team members, which can expose internal structure or sensitive membership info. In Better Auth, even admins or users with team management rights are not implicitly allowed to list team members unless they're direct members of the team. This is intentional to maintain strict data boundaries between teams, and aligns with conservative RBAC patterns where higher roles don't automatically get all possible permissions unless explicitly granted example.

Allowing implicit access for admins or users with team management permissions could make some workflows easier, but it also increases the risk of privilege creep and accidental data exposure, especially in larger or multi-tenant orgs. If you want to support this, you'd need to update the permission checks in the listTeamMembers endpoint to recognize org-level roles or specific permissions, which is not currently supported out of the box example.

If your use case requires it, you could extend the access control logic to allow certain roles (like admin or owner) to list team members, but it's important to weigh the operational convenience against the potential for overexposure of team data. Most systems err on the side of explicit grants for this reason.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Chat with Dosu Join Discord Share on X

<!-- gh-comment-id:3139493206 --> @dosubot[bot] commented on GitHub (Jul 31, 2025): <!-- Answer --> From an implementation and security perspective, it's generally best practice to keep permissions explicit and granular—especially for sensitive operations like listing team members, which can expose internal structure or sensitive membership info. In Better Auth, even admins or users with team management rights are not implicitly allowed to list team members unless they're direct members of the team. This is intentional to maintain strict data boundaries between teams, and aligns with conservative RBAC patterns where higher roles don't automatically get all possible permissions unless explicitly granted [example](https://github.com/better-auth/better-auth/issues/2762). Allowing implicit access for admins or users with team management permissions could make some workflows easier, but it also increases the risk of privilege creep and accidental data exposure, especially in larger or multi-tenant orgs. If you want to support this, you'd need to update the permission checks in the listTeamMembers endpoint to recognize org-level roles or specific permissions, which is not currently supported out of the box [example](https://github.com/better-auth/better-auth/pull/1425). If your use case requires it, you could extend the access control logic to allow certain roles (like admin or owner) to list team members, but it's important to weigh the operational convenience against the potential for overexposure of team data. Most systems err on the side of explicit grants for this reason. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/59c29e14-27c5-448e-b16d-971b72903e88?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/59c29e14-27c5-448e-b16d-971b72903e88?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/59c29e14-27c5-448e-b16d-971b72903e88?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/59c29e14-27c5-448e-b16d-971b72903e88?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/59c29e14-27c5-448e-b16d-971b72903e88?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/59c29e14-27c5-448e-b16d-971b72903e88?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/59c29e14-27c5-448e-b16d-971b72903e88?feedback_type=other)</sup>&nbsp;&nbsp;[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/3693)
Author
Owner

@Kinfe123 commented on GitHub (Aug 8, 2025):

May be we could add a flag for opt-in to this feature but right now as dosu mention that is what actually make sense and should be done. but it should be something that we should keep in mind.

<!-- gh-comment-id:3169183531 --> @Kinfe123 commented on GitHub (Aug 8, 2025): May be we could add a flag for opt-in to this feature but right now as dosu mention that is what actually make sense and should be done. but it should be something that we should keep in mind.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#18321