[GH-ISSUE #4557] Dynamic Roles and Permissions for Admin Plugin #27304

Open
opened 2026-04-17 18:14:20 -05:00 by GiteaMirror · 13 comments
Owner

Originally created by @its4lion on GitHub (Sep 10, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/4557

Originally assigned to: @ping-maxwell on GitHub.

Is this suited for github?

  • Yes, this is suited for github

Right now, better-auth uses fixed roles and permissions, making it hard for admins to change access rights without updating code or planning everything ahead.

Describe the solution you'd like

Add a way for admins to create and change roles and permissions easily, like the organization plugin, using a simple dashboard or API. Store the settings in a database.

Describe alternatives you've considered

We looked into using a different access control system, but it would require starting from scratch and wouldn’t fit well with better-auth.

Additional context

Adopted as tracking issue: 2026-04-14

Scope clarification

This issue tracks dynamic roles and their lifecycle for the admin plugin: create, rename, remove defaults, validate custom roles, and signal client-side staleness. It does not track platform admin scope, impersonation, team-role assignment, or permission-union math; those items belong in tracking: admin plugin: super-admin scope, impersonation, permission math.

In scope

  • Dynamic role create / update / delete.
  • Role rename consistency across the member table (#7727).
  • Removing defaults (#6772, #8604 owner-role missing).
  • Custom role validation (#9156 dash plugin flagging).
  • Client-side invalidation of role state on sign-out and role change (#8595 cross-ref to session cookie cache).
  • TS surface for dynamic-role operations (the invite-endpoint variant #9135 and the admin-plugin-wide pattern).

Out of scope

  • Super-admin vs org-admin authority (#1595), impersonation (#3056, #7796), team-role (#4493), permission-union math (#3011): tracking: admin plugin: super-admin scope, impersonation, permission math.
  • General TypeScript inference gaps that surface at role-adjacent endpoints: tracking: TypeScript inference (#5666).

Resolution criteria

  • Dynamic roles create and rename propagate to member rows.
  • Built-in roles can be disabled entirely.
  • Custom role validation reports cleanly to all plugins that inspect roles.
  • Client-side role state invalidates on sign-out without manual refetch.
  • Dynamic-role operations typecheck across all endpoints that accept a role input.
Originally created by @its4lion on GitHub (Sep 10, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/4557 Originally assigned to: @ping-maxwell on GitHub. ### Is this suited for github? - [x] Yes, this is suited for github ### Is your feature request related to a problem? Please describe. Right now, better-auth uses fixed roles and permissions, making it hard for admins to change access rights without updating code or planning everything ahead. ### Describe the solution you'd like Add a way for admins to create and change roles and permissions easily, like the organization plugin, using a simple dashboard or API. Store the settings in a database. ### Describe alternatives you've considered We looked into using a different access control system, but it would require starting from scratch and wouldn’t fit well with better-auth. ### Additional context related discussion: https://www.answeroverflow.com/m/1357026607033880727 --- ## Adopted as tracking issue: 2026-04-14 ## Scope clarification This issue tracks dynamic roles and their lifecycle for the admin plugin: create, rename, remove defaults, validate custom roles, and signal client-side staleness. It does not track platform admin scope, impersonation, team-role assignment, or permission-union math; those items belong in `tracking: admin plugin: super-admin scope, impersonation, permission math`. ## In scope - Dynamic role create / update / delete. - Role rename consistency across the member table (#7727). - Removing defaults (#6772, #8604 owner-role missing). - Custom role validation (#9156 `dash` plugin flagging). - Client-side invalidation of role state on sign-out and role change (#8595 cross-ref to session cookie cache). - TS surface for dynamic-role operations (the invite-endpoint variant #9135 and the admin-plugin-wide pattern). ## Out of scope - Super-admin vs org-admin authority (#1595), impersonation (#3056, #7796), team-role (#4493), permission-union math (#3011): `tracking: admin plugin: super-admin scope, impersonation, permission math`. - General TypeScript inference gaps that surface at role-adjacent endpoints: `tracking: TypeScript inference` (#5666). ## Resolution criteria - Dynamic roles create and rename propagate to member rows. - Built-in roles can be disabled entirely. - Custom role validation reports cleanly to all plugins that inspect roles. - Client-side role state invalidates on sign-out without manual refetch. - Dynamic-role operations typecheck across all endpoints that accept a role input.
GiteaMirror added the trackingorganization labels 2026-04-17 18:14:20 -05:00
Author
Owner

@frectonz commented on GitHub (Sep 10, 2025):

This is already supported for the organization plugin. [Dynamic Access Control]

<!-- gh-comment-id:3274077402 --> @frectonz commented on GitHub (Sep 10, 2025): This is already supported for the `organization` plugin. [[Dynamic Access Control](https://www.better-auth.com/docs/plugins/organization#dynamic-access-control)]
Author
Owner

@its4lion commented on GitHub (Sep 10, 2025):

This is already supported for the organization plugin. [Dynamic Access Control]

Yes, I saw that but I need a solution for admin use, not the org plugin.

<!-- gh-comment-id:3274222984 --> @its4lion commented on GitHub (Sep 10, 2025): > This is already supported for the `organization` plugin. [[Dynamic Access Control](https://www.better-auth.com/docs/plugins/organization#dynamic-access-control)] Yes, I saw that but I need a solution for admin use, not the org plugin.
Author
Owner

@TheUntraceable commented on GitHub (Sep 13, 2025):

In my opinion, the dynamic access control plugin should be it's own plugin, and along with that I think that for plugin registration (in auth.ts), functions returning arrays should work (and deduplicate duplicate plugins). For example the organization plugin depends on it, so the access control plugin comes with it (it returns [{id: "organization"}, {id: "access-control"}])). The admin plugin also depends on it, and thus would return [{id: "admin"}, {id: "access-control"}]) at the time of registration. The betterAuth function should recognise the duplicate id usage, and just ignore one of them. One concern that occurs to me is if the developer uses the access control plugin themselves, and adds configuration to it, which should be ignored? I suppose you could make the organization and admin plugin (and any other dependent plugins) check if it already exists before trying to add it, removing the need for the deduplication at registration time.

<!-- gh-comment-id:3288764663 --> @TheUntraceable commented on GitHub (Sep 13, 2025): In my opinion, the dynamic access control plugin should be it's own plugin, and along with that I think that for plugin registration (in auth.ts), functions returning arrays should work (and deduplicate duplicate plugins). For example the organization plugin depends on it, so the access control plugin comes with it (it returns `[{id: "organization"}, {id: "access-control"}])`). The admin plugin also depends on it, and thus would return `[{id: "admin"}, {id: "access-control"}])` at the time of registration. The betterAuth function should recognise the duplicate id usage, and just ignore one of them. One concern that occurs to me is if the developer uses the access control plugin themselves, and adds configuration to it, which should be ignored? I suppose you could make the organization and admin plugin (and any other dependent plugins) check if it already exists before trying to add it, removing the need for the deduplication at registration time.
Author
Owner

@hieudien14310 commented on GitHub (Sep 22, 2025):

Is there any plan for this yet?

<!-- gh-comment-id:3318953818 --> @hieudien14310 commented on GitHub (Sep 22, 2025): Is there any plan for this yet?
Author
Owner

@iserdmi commented on GitHub (Oct 10, 2025):

Hello! I've implemented a solution for this feature request. 🎉

PR: #5203

Overview

This PR enables granular per-user permissions that bypass role definitions while maintaining full backward compatibility with the existing role-based system.

Key Concept

You can continue using traditional roles as before, but now you also have the option to assign "special roles" to users. When a user has a special role, their permissions are read directly from their personal data in the database (specialPermissions field) instead of from role definitions.

Example Usage

const ac = createAccessControl({
  user: ["create", "read", "update", "delete", "list", "set-role"],
  order: ["create", "read", "update", "delete", "update-many"],
});

// Define special roles with empty permissions
const adminSpecialAc = ac.newRole({
  user: [],
  order: [],
});

export const auth = betterAuth({
  plugins: [
    admin({
      ac,
      roles: {
        admin: adminAc,
        user: userAc,
        adminSpecial: adminSpecialAc, // special role
      },
      specialRoles: ["adminSpecial"],
    }),
  ],
});

// Assign custom permissions to a specific user
await client.admin.setRole({
  userId: "user-123",
  role: "adminSpecial",
  specialPermissions: {
    user: ["create", "read", "update"], // granular permissions
    order: ["read"],
  },
}, { headers: adminHeaders });

Benefits

  • Granular control: Set unique permissions for individual users
  • Backward compatible: Existing role-based permissions work as before
  • Flexible: Choose between roles, special permissions, or both
  • Type-safe: Full TypeScript support based on your access control configuration
  • Database-driven: Permissions stored in user data, can be changed dynamically

Would appreciate feedback! 🙏

<!-- gh-comment-id:3387987891 --> @iserdmi commented on GitHub (Oct 10, 2025): Hello! I've implemented a solution for this feature request. 🎉 **PR:** #5203 ## Overview This PR enables **granular per-user permissions** that bypass role definitions while maintaining full backward compatibility with the existing role-based system. ## Key Concept You can continue using traditional roles as before, but now you also have the option to assign "special roles" to users. When a user has a special role, their permissions are read directly from their personal data in the database (`specialPermissions` field) instead of from role definitions. ## Example Usage ```typescript const ac = createAccessControl({ user: ["create", "read", "update", "delete", "list", "set-role"], order: ["create", "read", "update", "delete", "update-many"], }); // Define special roles with empty permissions const adminSpecialAc = ac.newRole({ user: [], order: [], }); export const auth = betterAuth({ plugins: [ admin({ ac, roles: { admin: adminAc, user: userAc, adminSpecial: adminSpecialAc, // special role }, specialRoles: ["adminSpecial"], }), ], }); // Assign custom permissions to a specific user await client.admin.setRole({ userId: "user-123", role: "adminSpecial", specialPermissions: { user: ["create", "read", "update"], // granular permissions order: ["read"], }, }, { headers: adminHeaders }); ``` ## Benefits - ✅ **Granular control**: Set unique permissions for individual users - ✅ **Backward compatible**: Existing role-based permissions work as before - ✅ **Flexible**: Choose between roles, special permissions, or both - ✅ **Type-safe**: Full TypeScript support based on your access control configuration - ✅ **Database-driven**: Permissions stored in user data, can be changed dynamically Would appreciate feedback! 🙏
Author
Owner

@dosubot[bot] commented on GitHub (Jan 9, 2026):

Hi, @its4lion. I'm Dosu, and I'm helping the better-auth team manage their backlog and am marking this issue as stale.

Issue Summary:

  • You requested dynamic roles and permissions in the admin plugin for flexible, database-stored access control without code changes.
  • It was noted that dynamic access control exists for the organization plugin but not specifically for admin.
  • A suggestion was made to create a standalone plugin for dynamic access control shared by admin and organization plugins.
  • A backward-compatible solution was implemented via PR #5203, introducing "special roles" with per-user granular permissions stored in the database.
  • This solution effectively enabled dynamic roles and permissions in the admin plugin and received positive community feedback.

Next Steps:

  • Please confirm if this solution still addresses your needs with the latest version of better-auth by commenting on this issue.
  • If I do not hear from you within 7 days, I will automatically close this issue.

Thank you for your understanding and contribution!

<!-- gh-comment-id:3729601653 --> @dosubot[bot] commented on GitHub (Jan 9, 2026): Hi, @its4lion. I'm [Dosu](https://dosu.dev), and I'm helping the better-auth team manage their backlog and am marking this issue as stale. **Issue Summary:** - You requested dynamic roles and permissions in the admin plugin for flexible, database-stored access control without code changes. - It was noted that dynamic access control exists for the organization plugin but not specifically for admin. - A suggestion was made to create a standalone plugin for dynamic access control shared by admin and organization plugins. - A backward-compatible solution was implemented via PR #5203, introducing "special roles" with per-user granular permissions stored in the database. - This solution effectively enabled dynamic roles and permissions in the admin plugin and received positive community feedback. **Next Steps:** - Please confirm if this solution still addresses your needs with the latest version of better-auth by commenting on this issue. - If I do not hear from you within 7 days, I will automatically close this issue. Thank you for your understanding and contribution!
Author
Owner

@scottrepreneur commented on GitHub (Jan 9, 2026):

  • still active until #5203 is implemented
<!-- gh-comment-id:3729852767 --> @scottrepreneur commented on GitHub (Jan 9, 2026): - still active until #5203 is implemented
Author
Owner

@bgervan commented on GitHub (Jan 9, 2026):

In the organization plugin, the dynamic role is possible via DB table https://www.better-auth.com/docs/plugins/organization#organization-role-optional

// To use custom resources or permissions,
// make sure they are defined in the `ac` instance of your organization config.
const permission = {
  project: ["create", "update", "delete"]
}
await auth.api.createOrgRole({
    body: {
        role: "my-unique-role", // required
        permission: permission,
        organizationId: "organization-id",
    },
    // This endpoint requires session cookies.
    headers: await headers(),
});

Here in this PR, with a simple json field you are creating custom roles for each user. So if I would create as admin a role "publisher" for example, then I would need to define all the permissions for each user I am assigning that. Moreover, updating a single custom roles means updates on all user that has this role.

I propose to replicate the feature set created by org plugin, following the same approaches, to keep it consistent.
Also the naming looks weird to me, why not just permissions field in the user table?

<!-- gh-comment-id:3730314368 --> @bgervan commented on GitHub (Jan 9, 2026): > * still active until [feat(admin): add special permissions support for dynamic user permissions #5203](https://github.com/better-auth/better-auth/pull/5203) is implemented In the organization plugin, the dynamic role is possible via DB table https://www.better-auth.com/docs/plugins/organization#organization-role-optional ``` // To use custom resources or permissions, // make sure they are defined in the `ac` instance of your organization config. const permission = { project: ["create", "update", "delete"] } await auth.api.createOrgRole({ body: { role: "my-unique-role", // required permission: permission, organizationId: "organization-id", }, // This endpoint requires session cookies. headers: await headers(), }); ``` Here in this PR, with a simple json field you are creating custom roles for each user. So if I would create as admin a role "publisher" for example, then I would need to define all the permissions for each user I am assigning that. Moreover, updating a single custom roles means updates on all user that has this role. I propose to replicate the feature set created by org plugin, following the same approaches, to keep it consistent. Also the naming looks weird to me, why not just `permissions` field in the user table?
Author
Owner

@carlos-duran commented on GitHub (Jan 12, 2026):

I'm also using Admin plugin for backoffice and I would like to simply create many roles like Sales, Logistics, Financial, Support, etc. and assign them to multiple people. These roles are based on the admin plugin permission statement.

Our customers are already using the organization plugin, so they do manage their custom roles based on organization plugin permission statement.

<!-- gh-comment-id:3737723766 --> @carlos-duran commented on GitHub (Jan 12, 2026): I'm also using Admin plugin for backoffice and I would like to simply create many roles like Sales, Logistics, Financial, Support, etc. and assign them to multiple people. These roles are based on the admin plugin permission statement. Our customers are already using the organization plugin, so they do manage their custom roles based on organization plugin permission statement.
Author
Owner

@satyam-bhosale commented on GitHub (Jan 12, 2026):

@carlos-duran But what if the user wants to create multiple roles, permissions and then attach the roles and permissions and then assign them tot he users, this would way more granular access control right ?

<!-- gh-comment-id:3740984179 --> @satyam-bhosale commented on GitHub (Jan 12, 2026): @carlos-duran But what if the user wants to create multiple roles, permissions and then attach the roles and permissions and then assign them tot he users, this would way more granular access control right ?
Author
Owner

@carlos-duran commented on GitHub (Jan 13, 2026):

@carlos-duran But what if the user wants to create multiple roles, permissions and then attach the roles and permissions and then assign them tot he users, this would way more granular access control right ?

With the admin plugin we can have fixed permissions (they are defined in the code and are attached to the system logic). With the admin plugin, users can not create roles since those are hardcoded.

If you're asking about assigning roles and permissions (both) to a user, that is not possible right now, but I think we shouldn't go too deep. It would be nice if we start having something similar to organization plugin dynamicAccessControl.

Just dropping my thoughts here. (And sorry for my english, I'm beginner)

<!-- gh-comment-id:3741854023 --> @carlos-duran commented on GitHub (Jan 13, 2026): > [@carlos-duran](https://github.com/carlos-duran) But what if the user wants to create multiple roles, permissions and then attach the roles and permissions and then assign them tot he users, this would way more granular access control right ? With the admin plugin we can have fixed permissions (they are defined in the code and are attached to the system logic). With the admin plugin, users can not create roles since those are hardcoded. If you're asking about assigning roles and permissions (both) to a user, that is not possible right now, but I think we shouldn't go too deep. It would be nice if we start having something similar to organization plugin dynamicAccessControl. Just dropping my thoughts here. (And sorry for my english, I'm beginner)
Author
Owner

@satyam-bhosale commented on GitHub (Jan 13, 2026):

No I said the users with the admin can CRUD over roles and permissions, then assign permissions to the roles and later assign those roles to the user, which will be easy rather changing the code for any RBAC changes.

<!-- gh-comment-id:3744345673 --> @satyam-bhosale commented on GitHub (Jan 13, 2026): No I said the users with the admin can CRUD over roles and permissions, then assign permissions to the roles and later assign those roles to the user, which will be easy rather changing the code for any RBAC changes.
Author
Owner

@pranavgoel29 commented on GitHub (Jan 30, 2026):

I think we should just replicate something like org plugin in the admin plugin that gives us the ability to opt in to dynamic roles if we want to, and gives us granular control as well.
Also I think the org should have the ability for impersonation of a user as well like in admin plugin, but check for org role permissions

Only thing that I would change is to use role id in the user table instead of role name, this will overcome so many pitfalls there are currently with the org dynamic role managment

<!-- gh-comment-id:3824341903 --> @pranavgoel29 commented on GitHub (Jan 30, 2026): I think we should just replicate something like org plugin in the admin plugin that gives us the ability to opt in to dynamic roles if we want to, and gives us granular control as well. Also I think the org should have the ability for impersonation of a user as well like in admin plugin, but check for org role permissions Only thing that I would change is to use role id in the user table instead of role name, this will overcome so many pitfalls there are currently with the org dynamic role managment
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#27304