fix(auth): recognize comma-joined multi-role admin in AccessControl's fast-path bypass#4926
Merged
pedrofrxncx merged 1 commit intoJul 21, 2026
Conversation
… fast-path bypass
AccessControl.checkMemberAccess checked `this.role === "admin" || this.role
=== "owner"` with a strict equality match. Better Auth's organization plugin
joins an assigned role array with "," before storing member.role (and
`setRole()` can set `this.role` to the path-resolved org's role, per its own
docstring), so a legitimate multi-role owner/admin (e.g.
"admin,billing-manager") arrives here as that comma-joined string. The
exact-match check misses it and falls through to `boundAuth.hasPermission`,
which — per builtin-role-permission.ts's own docstring — hits a DB-backed
Better Auth round-trip specifically to avoid ("~40ms event-loop hitches").
Same failure mode already fixed for `canAssignRole`'s caller check (#4918)
and the runtime bypass in context-factory.ts (#4923), now fixed here via the
shared `hasAdminRole` helper in auth/roles.ts.
Not a live authz bypass — the downstream Better Auth path still grants
correctly — but this closes the gap so AccessControl's own bypass is
self-contained and multi-role admins take the fast in-memory path instead of
an avoidable network round-trip on every tool call.
This was referenced Jul 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follows #4918/#4923 hardening: same failure mode, third site.
AccessControl.checkMemberAccess(apps/mesh/src/core/access-control.ts) checkedthis.role === "admin" || this.role === "owner"with a strict equality match. Better Auth's organization plugin joins an assigned role array with "," before storingmember.role, andAccessControl.setRole()can setthis.roleto a path-resolved org's role (per its own docstring), so a legitimate multi-role owner/admin (e.g."admin,billing-manager") arrives here as that comma-joined string. The exact-match check misses it and falls through toboundAuth.hasPermission, which — perbuiltin-role-permission.ts's own docstring — hits a DB-backed Better Auth round-trip specifically because the in-memory fast path exists to avoid "~40ms event-loop hitches" per request.This is not a live authz bypass (the downstream Better Auth path still grants correctly for multi-role admins), but it closes the gap so
AccessControl's own bypass is self-contained, matching the sharedhasAdminRolehelper already used bycanAssignRole(#4918) andcontext-factory.ts's runtime bypass (#4923). Multi-role admins now take the fast in-memory path instead of an avoidable DB round-trip on every tool call.Added a regression test asserting the multi-role admin bypasses without ever calling
boundAuth.hasPermission— before the fix it fell through to the mock, which (having no stored permissions) denied.Reviewer check:
bun test apps/mesh/src/core/access-control.test.tsLocally verified:
bun run fmt,cd apps/mesh && bunx tsc --noEmit(clean), and the targeted test file (26/26 pass). Full CI validates the rest.Summary by cubic
Fixes
AccessControlto recognize comma-joined multi-role admins/owners, so they hit the fast in-memory bypass instead of a DB round-trip. This prevents unnecessary permission checks and improves request latency.hasAdminRoleto detect admin/owner incheckMemberAccess, including values like "admin,billing-manager".boundAuth.hasPermission.Written for commit bcb2716. Summary will update on new commits.