APIError: You are not allowed to list users #1256

Closed
opened 2026-03-13 08:30:13 -05:00 by GiteaMirror · 1 comment
Owner

Originally created by @Fazycks on GitHub (May 23, 2025).

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

1: Checks if the user has access
2: Recovers user list by caching it
3: Display user table
4 : If my id is not configured as admin in auth.ts I get an error

Current vs. Expected behavior

I want to retrieve the user list but I get this error even though everything is set up correctly (at least I think so): APIError: You are not allowed to list users

What version of Better Auth are you using?

1.2.8

Provide environment information

- OS : Windows 11
- Browser : Brave

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

Backend, Other, Types

Auth config (if applicable)

import { betterAuth } from "better-auth";
import { prismaAdapter } from "better-auth/adapters/prisma";
import { nextCookies } from "better-auth/next-js";
import { admin as adminPlugin } from "better-auth/plugins";
import { ResetPasswordEmail } from "../emails/reset-password-email";
import { prisma } from "../prisma";
import { resend } from "../resend";
import { ac, roles } from "./auth-permissions";

export const auth = betterAuth({
    database: prismaAdapter(prisma, {
        provider: "postgresql",
    }),
    emailAndPassword: {
        enabled: true,
        autoSignIn: false,
        sendResetPassword: async ({ user, url }) => {
            await resend.emails.send({
                from: "@fazcore.com",
                to: user.email,
                subject: "Réinitialisation de votre mot de passe",
                react: ResetPasswordEmail({
                    resetPasswordLink: url,
                    userName: user.name,
                }),
            });
        },
    },
    plugins: [
        nextCookies(),
        adminPlugin({
            adminRoles: ["admin", "owner"],
            adminUserIds: ["my id"],
            defaultRole: "member",
            ac,
            roles,
        }),
    ],
});

Additional context

auth-client.ts

import { adminClient } from "better-auth/client/plugins";
import { createAuthClient } from "better-auth/react";
import { ac, roles } from "./auth-permissions";

export const authClient = createAuthClient({
    baseURL: "http://localhost:3000",
    plugins: [
        adminClient({
            ac,
            roles,
        }),
    ],
});

auth-session.ts

import { headers } from "next/headers";
import { auth } from "./auth";

export const getUser = async () => {
    const session = await auth.api.getSession({
        headers: await headers(),
    });

    return session?.user;
};

export const getUsersList = async () => {
    const { users: usersList } = await auth.api.listUsers({
        headers: await headers(),
        query: {
            offset: 0,
        },
    });

    return usersList;
};

auth-permissions.ts

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

export const AuthPermissionSchema = z.object({
    userId: z.string(),
    role: z.enum(["member", "admin", "owner"]),
    permissions: z.object({
        posts: z.array(z.enum(["create", "delete", "list"])).optional(),
        users: z
            .array(z.enum(["create", "delete", "update", "list"]))
            .optional(),
        organization: z.array(z.enum(["update", "delete"])).optional(),
        member: z.array(z.enum(["create", "update", "delete"])).optional(),
        invitation: z.array(z.enum(["create", "cancel"])).optional(),
        team: z.array(z.enum(["create", "update", "delete"])).optional(),
    }),
});

export type AuthPermission = z.infer<typeof AuthPermissionSchema>;

const statement = {
    ...defaultStatements,
    posts: ["create", "delete", "list"],
    users: ["create", "delete", "update", "list"],
} as const satisfies Statements;

export const ac = createAccessControl(statement);

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

const admin = ac.newRole({
    posts: ["create", "list"],
    users: ["create", "list"],
    ...adminAc.statements,
});

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

export const roles = {
    member,
    admin,
    owner,
} as const;

export const RoleKeys = ["member", "admin", "owner"] as const;

export type AuthRole = keyof typeof roles;

Error
Image

Originally created by @Fazycks on GitHub (May 23, 2025). ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce 1: Checks if the user has access 2: Recovers user list by caching it 3: Display user table 4 : If my id is not configured as admin in auth.ts I get an error ### Current vs. Expected behavior I want to retrieve the user list but I get this error even though everything is set up correctly (at least I think so): APIError: You are not allowed to list users ### What version of Better Auth are you using? 1.2.8 ### Provide environment information ```bash - OS : Windows 11 - Browser : Brave ``` ### Which area(s) are affected? (Select all that apply) Backend, Other, Types ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth"; import { prismaAdapter } from "better-auth/adapters/prisma"; import { nextCookies } from "better-auth/next-js"; import { admin as adminPlugin } from "better-auth/plugins"; import { ResetPasswordEmail } from "../emails/reset-password-email"; import { prisma } from "../prisma"; import { resend } from "../resend"; import { ac, roles } from "./auth-permissions"; export const auth = betterAuth({ database: prismaAdapter(prisma, { provider: "postgresql", }), emailAndPassword: { enabled: true, autoSignIn: false, sendResetPassword: async ({ user, url }) => { await resend.emails.send({ from: "@fazcore.com", to: user.email, subject: "Réinitialisation de votre mot de passe", react: ResetPasswordEmail({ resetPasswordLink: url, userName: user.name, }), }); }, }, plugins: [ nextCookies(), adminPlugin({ adminRoles: ["admin", "owner"], adminUserIds: ["my id"], defaultRole: "member", ac, roles, }), ], }); ``` ### Additional context **auth-client.ts** ```tsx import { adminClient } from "better-auth/client/plugins"; import { createAuthClient } from "better-auth/react"; import { ac, roles } from "./auth-permissions"; export const authClient = createAuthClient({ baseURL: "http://localhost:3000", plugins: [ adminClient({ ac, roles, }), ], }); ``` **auth-session.ts** ```tsx import { headers } from "next/headers"; import { auth } from "./auth"; export const getUser = async () => { const session = await auth.api.getSession({ headers: await headers(), }); return session?.user; }; export const getUsersList = async () => { const { users: usersList } = await auth.api.listUsers({ headers: await headers(), query: { offset: 0, }, }); return usersList; }; ``` **auth-permissions.ts** ```tsx import { createAccessControl, Statements } from "better-auth/plugins/access"; import { adminAc, defaultStatements, memberAc, ownerAc, } from "better-auth/plugins/organization/access"; import { z } from "zod"; export const AuthPermissionSchema = z.object({ userId: z.string(), role: z.enum(["member", "admin", "owner"]), permissions: z.object({ posts: z.array(z.enum(["create", "delete", "list"])).optional(), users: z .array(z.enum(["create", "delete", "update", "list"])) .optional(), organization: z.array(z.enum(["update", "delete"])).optional(), member: z.array(z.enum(["create", "update", "delete"])).optional(), invitation: z.array(z.enum(["create", "cancel"])).optional(), team: z.array(z.enum(["create", "update", "delete"])).optional(), }), }); export type AuthPermission = z.infer<typeof AuthPermissionSchema>; const statement = { ...defaultStatements, posts: ["create", "delete", "list"], users: ["create", "delete", "update", "list"], } as const satisfies Statements; export const ac = createAccessControl(statement); const member = ac.newRole({ ...memberAc.statements, }); const admin = ac.newRole({ posts: ["create", "list"], users: ["create", "list"], ...adminAc.statements, }); const owner = ac.newRole({ ...(statement as Statements), ...ownerAc.statements, }); export const roles = { member, admin, owner, } as const; export const RoleKeys = ["member", "admin", "owner"] as const; export type AuthRole = keyof typeof roles; ``` **Error** ![Image](https://github.com/user-attachments/assets/238e5ab9-8de9-4e34-8361-9470660025bf)
Author
Owner

@Kinfe123 commented on GitHub (May 28, 2025):

The ...adminAc.statements is being spread after your custom permissions, which means it's overwriting your custom permissions.

@Kinfe123 commented on GitHub (May 28, 2025): The `...adminAc.statements` is being spread after your custom permissions, which means it's overwriting your custom permissions.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#1256