Unable to add impersonate permission to a new role

I have a use case where I need three different roles ( user, admin, and manager ). the manager role has to extend the impersonating permissions, but since the admin endpoint always expects the role to be admin, it always fails even tho permissions are set. Is this some sort of a bug, or am I missing some undocumented walkthrough ?
12 Replies
LeMonsalve
LeMonsalve2mo ago
Hello, may be your access control config has mistakes
// permissions.ts
import { createAccessControl } from "better-auth/plugins/access";
import { defaultStatements, adminAc } from "better-auth/plugins/admin/access";

const statement = {
...defaultStatements,
} as const;

export const accessControl = createAccessControl(statement);

export const adminRole = accessControl.newRole({
...adminAc.statements,
});

export const managerRole = accessControl.newRole({
user: ["impersonate"],
session: [],
});
// permissions.ts
import { createAccessControl } from "better-auth/plugins/access";
import { defaultStatements, adminAc } from "better-auth/plugins/admin/access";

const statement = {
...defaultStatements,
} as const;

export const accessControl = createAccessControl(statement);

export const adminRole = accessControl.newRole({
...adminAc.statements,
});

export const managerRole = accessControl.newRole({
user: ["impersonate"],
session: [],
});
// auth.ts
import {
accessControl,
adminRole,
managerRole,
} from "./permissions";

export const auth = betterAuth({
// your code
plugins: [
admin({
impersonationSessionDuration: 60 * 60 * 24,
ac: accessControl,
roles: {
'admin': adminRole,
'manager': managerRole,
},
}),
],
});
// auth.ts
import {
accessControl,
adminRole,
managerRole,
} from "./permissions";

export const auth = betterAuth({
// your code
plugins: [
admin({
impersonationSessionDuration: 60 * 60 * 24,
ac: accessControl,
roles: {
'admin': adminRole,
'manager': managerRole,
},
}),
],
});
Bo3o
Bo3oOP2mo ago
No, actually there was an issue with a check that the admin plugin performs at the endpoint stage. It only checks the adminUserIds array, and it completely ignores adminRoles. https://github.com/better-auth/better-auth/blob/main/packages/better-auth/src/plugins/admin/has-permission.ts
GitHub
better-auth/packages/better-auth/src/plugins/admin/has-permission.t...
The most comprehensive authentication framework for TypeScript - better-auth/better-auth
bekacru
bekacru2mo ago
admin roles should have been depricated. You should define custom roles now with custom permission set.
LeMonsalve
LeMonsalve2mo ago
I check the code and its true, the auth.ap.userHasPermission, returns correct value, but api resquests such listUsers({ headers: await headers() }) returns UNAUTHORIZED:
const res = await auth.api.userHasPermission({
headers: await headers(),
body: {
userId: session.user.id,
permission: {
user: ["list"],
},
},
});

console.log("User " + session.user.name + " with role " + session.user.role);
if (res.success) {
console.log("has permission");
} else {
console.log("has't permission");
}
console.log(res);

const resData = await auth.api.listUsers({
headers: await headers(),
query: {},
});

console.log(resData.users);
const res = await auth.api.userHasPermission({
headers: await headers(),
body: {
userId: session.user.id,
permission: {
user: ["list"],
},
},
});

console.log("User " + session.user.name + " with role " + session.user.role);
if (res.success) {
console.log("has permission");
} else {
console.log("has't permission");
}
console.log(res);

const resData = await auth.api.listUsers({
headers: await headers(),
query: {},
});

console.log(resData.users);
// User Tilin with role seller
// has permission
{ error: null, success: true }
⨯ [Error [APIError]: ] {
status: 'UNAUTHORIZED',
body: undefined,
headers: {},
statusCode: 401,
digest: '5381'
}
GET / 500 in 279ms
GET /favicon.ico 200 in 32ms
// User Tilin with role seller
// has permission
{ error: null, success: true }
⨯ [Error [APIError]: ] {
status: 'UNAUTHORIZED',
body: undefined,
headers: {},
statusCode: 401,
digest: '5381'
}
GET / 500 in 279ms
GET /favicon.ico 200 in 32ms
I have my custom roles with custom permission set:
export const adminRole = accessControl.newRole({
...adminAc.statements,
});

export const sellerRole = accessControl.newRole({
user: ["list"],
session: [],
});

export const stockManagerRole = accessControl.newRole({
user: [],
session: [],
});
export const adminRole = accessControl.newRole({
...adminAc.statements,
});

export const sellerRole = accessControl.newRole({
user: ["list"],
session: [],
});

export const stockManagerRole = accessControl.newRole({
user: [],
session: [],
});
@Bo3o Bro I was trying different things, admin api only works if your role are in adminRoles prop, even if you have granted permissions to list users, delete etc... So, you can add your role as admin and only grant unique permissions required to perform your required admin action
Bo3o
Bo3oOP2mo ago
Yes that's exactly the issue I faced while trying to create a manager role with impersonating permission, tried it by correctly setting the roles and permission, works anywhere except for the admin api, I have modified the mentionned helper that checks the permissions to make it work with predefined adminRoles still documented and there is no mention of deprecation @LeMonsalve admin endpoint does not check adminRoles at all, it only checks if ther user's id exists in the adminUserIds, or if the role is the default adminRole
LeMonsalve
LeMonsalve2mo ago
Yesss We need to wait at least for v1.2.4
Lerio
Lerio2mo ago
mind to share how you did it sir?
bekacru
bekacru2mo ago
should be fixed on 1.2.4-beta.12 @Bo3o @LeMonsalve
Bo3o
Bo3oOP2mo ago
I just modified the has-permission helper in order to consider the roles defined under adminRoles
dx2315x
dx2315x2w ago
this is still not possible, we have bitmasks role structure and we can't integrate it with better-auth because you request your custom roles everywhere. packages/better-auth/src/plugins/access/access.ts user should come here. there is no special role id etc. the only thing that comes is the text ‘admin’ etc. member and user need to be pushed all over the place. member in roleId and user in roleId used. but dont get authorize in roleId :/
dx2315x
dx2315x2w ago
there must be something like this, but I still don't understand the structure.
No description
dx2315x
dx2315x2w ago

Did you find this page helpful?