Repository navigation
fix(auth): admin member management works without the admin's own membership - #881
Merged
Merged
Conversation
…ership adminAddMember forwarded the caller's headers into Better Auth's addMember, which checks the caller's ORGANIZATION permissions — a platform admin who was not a member of the target organization (the whole point of an admin route) got FORBIDDEN. The mutation is now a direct write like the rest of AdminOrganizationsService: membership-limit policy enforced, duplicate memberships refused. Tests now cover adding a member to an organization the admin does not belong to — the case the old fixtures masked by creating every organization owned by the admin. - adminUpdateMemberRole refuses to demote an organization's only owner, matching adminRemoveMember's last-owner guard - getOrganizationForAdmin uses the standard requireAdmin middleware - crossSubDomainCookies gains config-time warnings for the two silent browser failures: a cookie domain that does not cover the baseUrl host, and localhost/loopback base URLs - README documents the full multi-app session recipe: shared cookie domain + shared BETTER_AUTH_SECRET + shared postgres:// AUTH_DATABASE_URL (ADR 0027)
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.
Summary
Follow-ups to #876/#880 (admin organization routes). The admin mutation set is supposed to operate on any organization without requiring membership — but one mutation still leaned on Better Auth's organization-permission model:
A platform admin administering an organization they don't belong to — the whole point of an admin route — got FORBIDDEN. The tests masked it: every fixture organization was owned by the admin, so Better Auth's permission check passed.
Rides along:
adminUpdateMemberRolerefuses to demote an organization's only owner, matchingadminRemoveMember's existing last-owner guardgetOrganizationForAdminuses the standardrequireAdminmiddleware instead of a hand-rolled role check (same pattern as the feat(auth): admin organization management routes #880 routes)crossSubDomainCookies(Auth plugin: expose cross-subdomain cookie configuration (cookie domain) in the auth config schema #668) gains config-time warnings for the two ways a shared cookie domain fails silently at the browser: adomainthat doesn't cover the app's ownbaseUrlhost (cookie dropped outright → sign-in breaks) and localhost/loopback base URLs (browsers refuseDomainattributes)BETTER_AUTH_SECRET+ sharedpostgres://AUTH_DATABASE_URL(ADR 0027's decomposition)Evidence
Before — the unmasked case (admin not in the org) failed under Better Auth's permission check; no test covered it. After —
plugins/auth/tests/integration/admin-organizations.test.ts:adds a member without the admin belonging to the organization— admin adds to an org owned by someone else; member row verified in DB; duplicate add and unknown user still rejectedrefuses to demote the only owner of an organization— demote rejected while sole owner (role unchanged in DB); succeeds after a second owner is addedenforces the organization membership limit when adding members— limit now enforced by the plugin's own policy rather than Better Auth internalsorganizations.test.tsadmin-rejection message updated to therequireAdminmiddleware'sFull auth plugin suite: 43 files, 486 tests passed.
Merge Danger
Door: two-way — straight revert, no data migrations.
Blast radius: auth plugin API.
adminAddMembergains two refusals Better Auth previously provided implicitly (duplicate member, full org) — same outcomes, now surfaced asBAD_REQUESTfrom the plugin itself.getOrganizationForAdmin's rejection message changes (Admin access required→Requires role: admin); consumers asserting the old string must match the middleware's. Cookie-domain warnings are advisory; behavior is unchanged withoutcrossSubDomainCookiesconfig.