[GH-ISSUE #5254] userHasPermission didn show on auth.api (server side) #10195

Closed
opened 2026-04-13 06:10:34 -05:00 by GiteaMirror · 4 comments
Owner

Originally created by @mendoanjoe on GitHub (Oct 12, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/5254

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

this my better-auth config

{
  "system": {
    "platform": "darwin",
    "arch": "arm64",
    "version": "Darwin Kernel Version 25.0.0: Mon Aug 25 21:17:53 PDT 2025; root:xnu-12377.1.9~3/RELEASE_ARM64_T8112",
    "release": "25.0.0",
    "cpuCount": 8,
    "cpuModel": "Apple M2",
    "totalMemory": "8.00 GB",
    "freeMemory": "0.09 GB"
  },
  "node": {
    "version": "v22.18.0",
    "env": "development"
  },
  "packageManager": {
    "name": "bun",
    "version": "1.2.19"
  },
  "frameworks": [
    {
      "name": "hono",
      "version": "^4.8.2"
    }
  ],
  "databases": [
    {
      "name": "@libsql/client",
      "version": "^0.15.9"
    },
    {
      "name": "drizzle",
      "version": "^0.44.2"
    }
  ],
  "betterAuth": {
    "version": "^1.3.23",
    "config": {
      "baseURL": "http://localhost:3000",
      "secret": "[REDACTED]",
      "trustedOrigins": [
        "http://localhost:3001"
      ],
      "emailAndPassword": {
        "enabled": true
      },
      "advanced": {
        "defaultCookieAttributes": {
          "sameSite": "none",
          "secure": true,
          "httpOnly": true
        }
      },
      "socialProviders": {
        "google": {
          "enabled": true,
          "prompt": "select_account",
          "clientId": "[REDACTED]",
          "clientSecret": "[REDACTED]"
        }
      },
      "plugins": [
        {
          "name": "admin",
          "config": {
            "id": "admin",
            "hooks": {
              "after": [
                {}
              ]
            },
            "endpoints": {},
            "$ERROR_CODES": {
              "FAILED_TO_CREATE_USER": "Failed to create user",
              "USER_ALREADY_EXISTS": "User already exists.",
              "USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL": "User already exists. Use another email.",
              "YOU_CANNOT_BAN_YOURSELF": "You cannot ban yourself",
              "YOU_ARE_NOT_ALLOWED_TO_CHANGE_USERS_ROLE": "You are not allowed to change users role",
              "YOU_ARE_NOT_ALLOWED_TO_CREATE_USERS": "You are not allowed to create users",
              "YOU_ARE_NOT_ALLOWED_TO_LIST_USERS": "You are not allowed to list users",
              "YOU_ARE_NOT_ALLOWED_TO_LIST_USERS_SESSIONS": "You are not allowed to list users sessions",
              "YOU_ARE_NOT_ALLOWED_TO_BAN_USERS": "You are not allowed to ban users",
              "YOU_ARE_NOT_ALLOWED_TO_IMPERSONATE_USERS": "You are not allowed to impersonate users",
              "YOU_ARE_NOT_ALLOWED_TO_REVOKE_USERS_SESSIONS": "You are not allowed to revoke users sessions",
              "YOU_ARE_NOT_ALLOWED_TO_DELETE_USERS": "You are not allowed to delete users",
              "YOU_ARE_NOT_ALLOWED_TO_SET_USERS_PASSWORD": "[REDACTED]",
              "BANNED_USER": "You have been banned from this application",
              "YOU_ARE_NOT_ALLOWED_TO_GET_USER": "You are not allowed to get user",
              "NO_DATA_TO_UPDATE": "No data to update",
              "YOU_ARE_NOT_ALLOWED_TO_UPDATE_USERS": "You are not allowed to update users",
              "YOU_CANNOT_REMOVE_YOURSELF": "You cannot remove yourself"
            },
            "schema": {
              "user": {
                "fields": {
                  "role": {
                    "type": "string",
                    "required": false,
                    "input": false
                  },
                  "banned": {
                    "type": "boolean",
                    "defaultValue": false,
                    "required": false,
                    "input": false
                  },
                  "banReason": {
                    "type": "string",
                    "required": false,
                    "input": false
                  },
                  "banExpires": {
                    "type": "date",
                    "required": false,
                    "input": false
                  }
                }
              },
              "session": {
                "fields": {
                  "impersonatedBy": {
                    "type": "string",
                    "required": false
                  }
                }
              }
            },
            "options": {
              "ac": {
                "statements": {
                  "user": [
                    "create",
                    "list",
                    "set-role",
                    "ban",
                    "impersonate",
                    "delete",
                    "set-password",
                    "get",
                    "update",
                    "get-self"
                  ],
                  "session": [
                    "list",
                    "revoke",
                    "delete"
                  ],
                  "health": [
                    "read"
                  ]
                }
              },
              "roles": {
                "user": {
                  "statements": {
                    "user": [],
                    "session": [],
                  }
                },
                "outletadmin": {
                  "statements": {
                    "category": [
                      "create",
                      "read",
                      "update",
                      "delete"
                    ],
                  }
                },
                "admin": {
                  "statements": {
                    "user": [
                      "get",
                      "update",
                      "ban"
                    ],
                  }
                },
                "superadmin": {
                  "statements": {
                    "health": [
                      "read"
                    ]
                  }
                }
              }
            }
          }
        }
      ]
    }
  }
}

Current vs. Expected behavior

I expected i can access auth.api.userHasPermission

What version of Better Auth are you using?

1.3.22

System info

this my better-auth config


{
  "system": {
    "platform": "darwin",
    "arch": "arm64",
    "version": "Darwin Kernel Version 25.0.0: Mon Aug 25 21:17:53 PDT 2025; root:xnu-12377.1.9~3/RELEASE_ARM64_T8112",
    "release": "25.0.0",
    "cpuCount": 8,
    "cpuModel": "Apple M2",
    "totalMemory": "8.00 GB",
    "freeMemory": "0.09 GB"
  },
  "node": {
    "version": "v22.18.0",
    "env": "development"
  },
  "packageManager": {
    "name": "bun",
    "version": "1.2.19"
  },
  "frameworks": [
    {
      "name": "hono",
      "version": "^4.8.2"
    }
  ],
  "databases": [
    {
      "name": "@libsql/client",
      "version": "^0.15.9"
    },
    {
      "name": "drizzle",
      "version": "^0.44.2"
    }
  ],
  "betterAuth": {
    "version": "^1.3.23",
    "config": {
      "baseURL": "http://localhost:3000",
      "secret": "[REDACTED]",
      "trustedOrigins": [
        "http://localhost:3001"
      ],
      "emailAndPassword": {
        "enabled": true
      },
      "advanced": {
        "defaultCookieAttributes": {
          "sameSite": "none",
          "secure": true,
          "httpOnly": true
        }
      },
      "socialProviders": {
        "google": {
          "enabled": true,
          "prompt": "select_account",
          "clientId": "[REDACTED]",
          "clientSecret": "[REDACTED]"
        }
      },
      "plugins": [
        {
          "name": "admin",
          "config": {
            "id": "admin",
            "hooks": {
              "after": [
                {}
              ]
            },
            "endpoints": {},
            "$ERROR_CODES": {
              "FAILED_TO_CREATE_USER": "Failed to create user",
              "USER_ALREADY_EXISTS": "User already exists.",
              "USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL": "User already exists. Use another email.",
              "YOU_CANNOT_BAN_YOURSELF": "You cannot ban yourself",
              "YOU_ARE_NOT_ALLOWED_TO_CHANGE_USERS_ROLE": "You are not allowed to change users role",
              "YOU_ARE_NOT_ALLOWED_TO_CREATE_USERS": "You are not allowed to create users",
              "YOU_ARE_NOT_ALLOWED_TO_LIST_USERS": "You are not allowed to list users",
              "YOU_ARE_NOT_ALLOWED_TO_LIST_USERS_SESSIONS": "You are not allowed to list users sessions",
              "YOU_ARE_NOT_ALLOWED_TO_BAN_USERS": "You are not allowed to ban users",
              "YOU_ARE_NOT_ALLOWED_TO_IMPERSONATE_USERS": "You are not allowed to impersonate users",
              "YOU_ARE_NOT_ALLOWED_TO_REVOKE_USERS_SESSIONS": "You are not allowed to revoke users sessions",
              "YOU_ARE_NOT_ALLOWED_TO_DELETE_USERS": "You are not allowed to delete users",
              "YOU_ARE_NOT_ALLOWED_TO_SET_USERS_PASSWORD": "[REDACTED]",
              "BANNED_USER": "You have been banned from this application",
              "YOU_ARE_NOT_ALLOWED_TO_GET_USER": "You are not allowed to get user",
              "NO_DATA_TO_UPDATE": "No data to update",
              "YOU_ARE_NOT_ALLOWED_TO_UPDATE_USERS": "You are not allowed to update users",
              "YOU_CANNOT_REMOVE_YOURSELF": "You cannot remove yourself"
            },
            "schema": {
              "user": {
                "fields": {
                  "role": {
                    "type": "string",
                    "required": false,
                    "input": false
                  },
                  "banned": {
                    "type": "boolean",
                    "defaultValue": false,
                    "required": false,
                    "input": false
                  },
                  "banReason": {
                    "type": "string",
                    "required": false,
                    "input": false
                  },
                  "banExpires": {
                    "type": "date",
                    "required": false,
                    "input": false
                  }
                }
              },
              "session": {
                "fields": {
                  "impersonatedBy": {
                    "type": "string",
                    "required": false
                  }
                }
              }
            },
            "options": {
              "ac": {
                "statements": {
                  "user": [
                    "create",
                    "list",
                    "set-role",
                    "ban",
                    "impersonate",
                    "delete",
                    "set-password",
                    "get",
                    "update",
                    "get-self"
                  ],
                  "session": [
                    "list",
                    "revoke",
                    "delete"
                  ],
                  "health": [
                    "read"
                  ]
                }
              },
              "roles": {
                "user": {
                  "statements": {
                    "user": [],
                    "session": [],
                  }
                },
                "outletadmin": {
                  "statements": {
                    "category": [
                      "create",
                      "read",
                      "update",
                      "delete"
                    ],
                  }
                },
                "admin": {
                  "statements": {
                    "user": [
                      "get",
                      "update",
                      "ban"
                    ],
                  }
                },
                "superadmin": {
                  "statements": {
                    "health": [
                      "read"
                    ]
                  }
                }
              }
            }
          }
        }
      ]
    }
  }
}

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

Backend

Auth config (if applicable)

/** biome-ignore-all lint/style/useNamingConvention: false positive */
import { env } from "cloudflare:workers";
import { type BetterAuthOptions, betterAuth } from "better-auth";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { admin as adminPlugin } from "better-auth/plugins";
import {
  account,
  session,
  user as userSchema,
  verification,
} from "@/db/schema/auth";
import {
  accountRelations,
  sessionRelations,
  userRelations,
} from "@/db/schema/relations";
import { db } from "../../db";
import { ac, admin, outletadmin, superadmin, user } from "./permissions";

const defaultConfig: BetterAuthOptions = {
  baseURL: env.BETTER_AUTH_URL,
  secret: env.BETTER_AUTH_SECRET,
  trustedOrigins: [env.CORS_ORIGIN],
  emailAndPassword: {
    enabled: true,
  },
  // uncomment cookieCache setting when ready to deploy to Cloudflare using *.workers.dev domains
  // session: {
  //   cookieCache: {
  //     enabled: true,
  //     maxAge: 60,
  //   },
  // },
  advanced: {
    defaultCookieAttributes: {
      sameSite: "none",
      secure: true,
      httpOnly: true,
    },
    // uncomment crossSubDomainCookies setting when ready to deploy and replace <your-workers-subdomain> with your actual workers subdomain
    // https://developers.cloudflare.com/workers/wrangler/configuration/#workersdev
    // crossSubDomainCookies: {
    //   enabled: true,
    //   domain: "<your-workers-subdomain>",
    // },
  },
};

const socialConfig: BetterAuthOptions = {
  socialProviders: {
    google: {
      enabled: true,
      prompt: "select_account",
      clientId: env.GOOGLE_CLIENT_ID,
      clientSecret: env.GOOGLE_CLIENT_SECRET,
    },
  },
};

const pluginConfig: BetterAuthOptions = {
  plugins: [
    // myPlugin(),
    adminPlugin({
      defaultRole: "user",
      adminRoles: ["superadmin", "admin", "outletadmin"],
      ac,
      roles: {
        user,
        outletadmin,
        admin,
        superadmin,
      },
    }),
  ],
};

export const auth = betterAuth<BetterAuthOptions>({
  database: drizzleAdapter(db, {
    provider: "sqlite",
    schema: {
      user: userSchema,
      userRelations,
      session,
      sessionRelations,
      account,
      accountRelations,
      verification,
    },
  }),

  ...defaultConfig,
  ...socialConfig,
  ...pluginConfig,
});

Additional context

No response

Originally created by @mendoanjoe on GitHub (Oct 12, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/5254 ### Is this suited for github? - [ ] Yes, this is suited for github ### To Reproduce this my better-auth config ```json { "system": { "platform": "darwin", "arch": "arm64", "version": "Darwin Kernel Version 25.0.0: Mon Aug 25 21:17:53 PDT 2025; root:xnu-12377.1.9~3/RELEASE_ARM64_T8112", "release": "25.0.0", "cpuCount": 8, "cpuModel": "Apple M2", "totalMemory": "8.00 GB", "freeMemory": "0.09 GB" }, "node": { "version": "v22.18.0", "env": "development" }, "packageManager": { "name": "bun", "version": "1.2.19" }, "frameworks": [ { "name": "hono", "version": "^4.8.2" } ], "databases": [ { "name": "@libsql/client", "version": "^0.15.9" }, { "name": "drizzle", "version": "^0.44.2" } ], "betterAuth": { "version": "^1.3.23", "config": { "baseURL": "http://localhost:3000", "secret": "[REDACTED]", "trustedOrigins": [ "http://localhost:3001" ], "emailAndPassword": { "enabled": true }, "advanced": { "defaultCookieAttributes": { "sameSite": "none", "secure": true, "httpOnly": true } }, "socialProviders": { "google": { "enabled": true, "prompt": "select_account", "clientId": "[REDACTED]", "clientSecret": "[REDACTED]" } }, "plugins": [ { "name": "admin", "config": { "id": "admin", "hooks": { "after": [ {} ] }, "endpoints": {}, "$ERROR_CODES": { "FAILED_TO_CREATE_USER": "Failed to create user", "USER_ALREADY_EXISTS": "User already exists.", "USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL": "User already exists. Use another email.", "YOU_CANNOT_BAN_YOURSELF": "You cannot ban yourself", "YOU_ARE_NOT_ALLOWED_TO_CHANGE_USERS_ROLE": "You are not allowed to change users role", "YOU_ARE_NOT_ALLOWED_TO_CREATE_USERS": "You are not allowed to create users", "YOU_ARE_NOT_ALLOWED_TO_LIST_USERS": "You are not allowed to list users", "YOU_ARE_NOT_ALLOWED_TO_LIST_USERS_SESSIONS": "You are not allowed to list users sessions", "YOU_ARE_NOT_ALLOWED_TO_BAN_USERS": "You are not allowed to ban users", "YOU_ARE_NOT_ALLOWED_TO_IMPERSONATE_USERS": "You are not allowed to impersonate users", "YOU_ARE_NOT_ALLOWED_TO_REVOKE_USERS_SESSIONS": "You are not allowed to revoke users sessions", "YOU_ARE_NOT_ALLOWED_TO_DELETE_USERS": "You are not allowed to delete users", "YOU_ARE_NOT_ALLOWED_TO_SET_USERS_PASSWORD": "[REDACTED]", "BANNED_USER": "You have been banned from this application", "YOU_ARE_NOT_ALLOWED_TO_GET_USER": "You are not allowed to get user", "NO_DATA_TO_UPDATE": "No data to update", "YOU_ARE_NOT_ALLOWED_TO_UPDATE_USERS": "You are not allowed to update users", "YOU_CANNOT_REMOVE_YOURSELF": "You cannot remove yourself" }, "schema": { "user": { "fields": { "role": { "type": "string", "required": false, "input": false }, "banned": { "type": "boolean", "defaultValue": false, "required": false, "input": false }, "banReason": { "type": "string", "required": false, "input": false }, "banExpires": { "type": "date", "required": false, "input": false } } }, "session": { "fields": { "impersonatedBy": { "type": "string", "required": false } } } }, "options": { "ac": { "statements": { "user": [ "create", "list", "set-role", "ban", "impersonate", "delete", "set-password", "get", "update", "get-self" ], "session": [ "list", "revoke", "delete" ], "health": [ "read" ] } }, "roles": { "user": { "statements": { "user": [], "session": [], } }, "outletadmin": { "statements": { "category": [ "create", "read", "update", "delete" ], } }, "admin": { "statements": { "user": [ "get", "update", "ban" ], } }, "superadmin": { "statements": { "health": [ "read" ] } } } } } } ] } } } ``` ### Current vs. Expected behavior I expected i can access auth.api.userHasPermission ### What version of Better Auth are you using? 1.3.22 ### System info ```bash this my better-auth config { "system": { "platform": "darwin", "arch": "arm64", "version": "Darwin Kernel Version 25.0.0: Mon Aug 25 21:17:53 PDT 2025; root:xnu-12377.1.9~3/RELEASE_ARM64_T8112", "release": "25.0.0", "cpuCount": 8, "cpuModel": "Apple M2", "totalMemory": "8.00 GB", "freeMemory": "0.09 GB" }, "node": { "version": "v22.18.0", "env": "development" }, "packageManager": { "name": "bun", "version": "1.2.19" }, "frameworks": [ { "name": "hono", "version": "^4.8.2" } ], "databases": [ { "name": "@libsql/client", "version": "^0.15.9" }, { "name": "drizzle", "version": "^0.44.2" } ], "betterAuth": { "version": "^1.3.23", "config": { "baseURL": "http://localhost:3000", "secret": "[REDACTED]", "trustedOrigins": [ "http://localhost:3001" ], "emailAndPassword": { "enabled": true }, "advanced": { "defaultCookieAttributes": { "sameSite": "none", "secure": true, "httpOnly": true } }, "socialProviders": { "google": { "enabled": true, "prompt": "select_account", "clientId": "[REDACTED]", "clientSecret": "[REDACTED]" } }, "plugins": [ { "name": "admin", "config": { "id": "admin", "hooks": { "after": [ {} ] }, "endpoints": {}, "$ERROR_CODES": { "FAILED_TO_CREATE_USER": "Failed to create user", "USER_ALREADY_EXISTS": "User already exists.", "USER_ALREADY_EXISTS_USE_ANOTHER_EMAIL": "User already exists. Use another email.", "YOU_CANNOT_BAN_YOURSELF": "You cannot ban yourself", "YOU_ARE_NOT_ALLOWED_TO_CHANGE_USERS_ROLE": "You are not allowed to change users role", "YOU_ARE_NOT_ALLOWED_TO_CREATE_USERS": "You are not allowed to create users", "YOU_ARE_NOT_ALLOWED_TO_LIST_USERS": "You are not allowed to list users", "YOU_ARE_NOT_ALLOWED_TO_LIST_USERS_SESSIONS": "You are not allowed to list users sessions", "YOU_ARE_NOT_ALLOWED_TO_BAN_USERS": "You are not allowed to ban users", "YOU_ARE_NOT_ALLOWED_TO_IMPERSONATE_USERS": "You are not allowed to impersonate users", "YOU_ARE_NOT_ALLOWED_TO_REVOKE_USERS_SESSIONS": "You are not allowed to revoke users sessions", "YOU_ARE_NOT_ALLOWED_TO_DELETE_USERS": "You are not allowed to delete users", "YOU_ARE_NOT_ALLOWED_TO_SET_USERS_PASSWORD": "[REDACTED]", "BANNED_USER": "You have been banned from this application", "YOU_ARE_NOT_ALLOWED_TO_GET_USER": "You are not allowed to get user", "NO_DATA_TO_UPDATE": "No data to update", "YOU_ARE_NOT_ALLOWED_TO_UPDATE_USERS": "You are not allowed to update users", "YOU_CANNOT_REMOVE_YOURSELF": "You cannot remove yourself" }, "schema": { "user": { "fields": { "role": { "type": "string", "required": false, "input": false }, "banned": { "type": "boolean", "defaultValue": false, "required": false, "input": false }, "banReason": { "type": "string", "required": false, "input": false }, "banExpires": { "type": "date", "required": false, "input": false } } }, "session": { "fields": { "impersonatedBy": { "type": "string", "required": false } } } }, "options": { "ac": { "statements": { "user": [ "create", "list", "set-role", "ban", "impersonate", "delete", "set-password", "get", "update", "get-self" ], "session": [ "list", "revoke", "delete" ], "health": [ "read" ] } }, "roles": { "user": { "statements": { "user": [], "session": [], } }, "outletadmin": { "statements": { "category": [ "create", "read", "update", "delete" ], } }, "admin": { "statements": { "user": [ "get", "update", "ban" ], } }, "superadmin": { "statements": { "health": [ "read" ] } } } } } } ] } } } ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript /** biome-ignore-all lint/style/useNamingConvention: false positive */ import { env } from "cloudflare:workers"; import { type BetterAuthOptions, betterAuth } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; import { admin as adminPlugin } from "better-auth/plugins"; import { account, session, user as userSchema, verification, } from "@/db/schema/auth"; import { accountRelations, sessionRelations, userRelations, } from "@/db/schema/relations"; import { db } from "../../db"; import { ac, admin, outletadmin, superadmin, user } from "./permissions"; const defaultConfig: BetterAuthOptions = { baseURL: env.BETTER_AUTH_URL, secret: env.BETTER_AUTH_SECRET, trustedOrigins: [env.CORS_ORIGIN], emailAndPassword: { enabled: true, }, // uncomment cookieCache setting when ready to deploy to Cloudflare using *.workers.dev domains // session: { // cookieCache: { // enabled: true, // maxAge: 60, // }, // }, advanced: { defaultCookieAttributes: { sameSite: "none", secure: true, httpOnly: true, }, // uncomment crossSubDomainCookies setting when ready to deploy and replace <your-workers-subdomain> with your actual workers subdomain // https://developers.cloudflare.com/workers/wrangler/configuration/#workersdev // crossSubDomainCookies: { // enabled: true, // domain: "<your-workers-subdomain>", // }, }, }; const socialConfig: BetterAuthOptions = { socialProviders: { google: { enabled: true, prompt: "select_account", clientId: env.GOOGLE_CLIENT_ID, clientSecret: env.GOOGLE_CLIENT_SECRET, }, }, }; const pluginConfig: BetterAuthOptions = { plugins: [ // myPlugin(), adminPlugin({ defaultRole: "user", adminRoles: ["superadmin", "admin", "outletadmin"], ac, roles: { user, outletadmin, admin, superadmin, }, }), ], }; export const auth = betterAuth<BetterAuthOptions>({ database: drizzleAdapter(db, { provider: "sqlite", schema: { user: userSchema, userRelations, session, sessionRelations, account, accountRelations, verification, }, }), ...defaultConfig, ...socialConfig, ...pluginConfig, }); ``` ### Additional context _No response_
GiteaMirror added the lockedbug labels 2026-04-13 06:10:34 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Oct 12, 2025):

The admin plugin's userHasPermission method should be available on auth.api when the plugin is configured correctly. If it's missing, the most common cause is the way the endpoints property is set in your admin plugin config. When you set "endpoints": {} (an empty object), it overrides the default endpoints, which means none—including userHasPermission—will be registered. To fix this, either remove the endpoints property entirely from your admin plugin config, or make sure it includes all the endpoints you want to expose, like:

"endpoints": {
  "userHasPermission": true
}

or just omit "endpoints" to get all defaults.

This is a known behavior and has been discussed in issues and tests confirming that userHasPermission is part of the admin plugin's API surface and is used for permission checks on the server side [source][source][source].

After updating your config, restart your server and you should see auth.api.userHasPermission available as expected.

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:3395009101 --> @dosubot[bot] commented on GitHub (Oct 12, 2025): <!-- Answer --> The admin plugin's `userHasPermission` method should be available on `auth.api` when the plugin is configured correctly. If it's missing, the most common cause is the way the `endpoints` property is set in your admin plugin config. When you set `"endpoints": {}` (an empty object), it overrides the default endpoints, which means none—including `userHasPermission`—will be registered. To fix this, either remove the `endpoints` property entirely from your admin plugin config, or make sure it includes all the endpoints you want to expose, like: ```json "endpoints": { "userHasPermission": true } ``` or just omit `"endpoints"` to get all defaults. This is a known behavior and has been discussed in issues and tests confirming that `userHasPermission` is part of the admin plugin's API surface and is used for permission checks on the server side [[source](https://github.com/better-auth/better-auth/issues/1672)][[source](https://github.com/better-auth/better-auth/pull/4649)][[source](https://github.com/better-auth/better-auth/issues/3645#issuecomment-3228484214)]. After updating your config, restart your server and you should see `auth.api.userHasPermission` available as expected. <!-- 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/0b713a6c-ca15-4a24-a9ad-157705886193?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/0b713a6c-ca15-4a24-a9ad-157705886193?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/0b713a6c-ca15-4a24-a9ad-157705886193?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/0b713a6c-ca15-4a24-a9ad-157705886193?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/0b713a6c-ca15-4a24-a9ad-157705886193?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/0b713a6c-ca15-4a24-a9ad-157705886193?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/0b713a6c-ca15-4a24-a9ad-157705886193?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/5254)
Author
Owner

@mendoanjoe commented on GitHub (Oct 12, 2025):

I didn overide endpoints like "endpoints": {}. but my endpoints always empty {} @dosubot

<!-- gh-comment-id:3395160955 --> @mendoanjoe commented on GitHub (Oct 12, 2025): I didn overide endpoints like `"endpoints": {}`. but my endpoints always empty `{}` @dosubot
Author
Owner

@dosubot[bot] commented on GitHub (Oct 12, 2025):

If your admin plugin config always ends up with "endpoints": {} (an empty object), even though you didn't set it, something in your codebase or config merging logic is likely overwriting the plugin's default endpoints. The admin plugin, when instantiated via its factory (like adminPlugin({...})), always provides all endpoints—including userHasPermission—unless an empty endpoints object is merged onto it from outside your plugin setup. This can happen if you spread or merge an empty endpoints object into your plugin config, or if you assemble the plugin object manually without using the factory function. In normal usage, you should omit the endpoints property entirely to get all defaults, and auth.api.userHasPermission will be available as expected. If you see an empty endpoints object, check your config merging or plugin instantiation code for anything that could be clobbering the plugin's endpoints property with {}. source

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:3395178694 --> @dosubot[bot] commented on GitHub (Oct 12, 2025): <!-- Answer --> If your admin plugin config always ends up with `"endpoints": {}` (an empty object), even though you didn't set it, something in your codebase or config merging logic is likely overwriting the plugin's default endpoints. The admin plugin, when instantiated via its factory (like `adminPlugin({...})`), always provides all endpoints—including `userHasPermission`—unless an empty endpoints object is merged onto it from outside your plugin setup. This can happen if you spread or merge an empty `endpoints` object into your plugin config, or if you assemble the plugin object manually without using the factory function. In normal usage, you should omit the `endpoints` property entirely to get all defaults, and `auth.api.userHasPermission` will be available as expected. If you see an empty endpoints object, check your config merging or plugin instantiation code for anything that could be clobbering the plugin's endpoints property with `{}`. [source](https://github.com/better-auth/better-auth/blob/63c8f4dd7356a587e4666f1b8004601250f9294d/packages/better-auth/src/plugins/admin/admin.ts) <!-- 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/a5b4aede-12d5-4c78-a7ca-7336e3e34d39?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/a5b4aede-12d5-4c78-a7ca-7336e3e34d39?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/a5b4aede-12d5-4c78-a7ca-7336e3e34d39?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/a5b4aede-12d5-4c78-a7ca-7336e3e34d39?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/a5b4aede-12d5-4c78-a7ca-7336e3e34d39?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/a5b4aede-12d5-4c78-a7ca-7336e3e34d39?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/a5b4aede-12d5-4c78-a7ca-7336e3e34d39?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/5254)
Author
Owner

@mendoanjoe commented on GitHub (Oct 12, 2025):

i think that because cant sparate the config like baseConfig, socialMediaConfig

<!-- gh-comment-id:3395480575 --> @mendoanjoe commented on GitHub (Oct 12, 2025): i think that because cant sparate the config like baseConfig, socialMediaConfig
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#10195