feat(db): mesh data model — meshes, members, invites, audit log
- pgSchema "mesh" with 4 tables isolating the peer mesh domain - Enums: visibility, transport, tier, role - audit_log is metadata-only (E2E encryption enforced at broker/client) - Cascade on mesh delete, soft-delete via archivedAt/revokedAt Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
---
|
||||
title: RBAC (Roles & Permissions)
|
||||
description: Manage roles, permissions, and access scopes.
|
||||
url: /docs/mobile/organizations/rbac
|
||||
---
|
||||
|
||||
# RBAC (Roles & Permissions)
|
||||
|
||||
Role-based access control (RBAC) lets you define who can do what in an organization.
|
||||
|
||||
<Callout title="New to RBAC?">
|
||||
If you're new to the RBAC concept, a simple mental model is:
|
||||
|
||||
* Users belong to organizations.
|
||||
* Users get roles.
|
||||
* Roles map to permissions on resources.
|
||||
</Callout>
|
||||
|
||||
In TurboStarter, we primarily rely on the [Better Auth plugin](https://www.better-auth.com/docs/plugins/organization) for the heavy lifting—roles, permissions, teams, and member management—while handling critical logic with our own code.
|
||||
|
||||
This provides a flexible access control system, letting you control user access based on their role in the organization. You can also define custom permissions per role.
|
||||
|
||||
<Callout title="Everything is configured out of the box!">
|
||||
TurboStarter ships with the default RBAC system configured out of the box. This setup may be enough if you're not planning a very complex access control system, but you can also easily customize it to your needs.
|
||||
|
||||
On mobile, use conditional UI (disable or hide actions) together with client helpers to match each member's role.
|
||||
</Callout>
|
||||
|
||||
## Roles
|
||||
|
||||
Roles are named bundles of permissions. Keep them few and well-defined. By default, we have the following roles:
|
||||
|
||||
```ts
|
||||
const MemberRole = {
|
||||
MEMBER: "member",
|
||||
ADMIN: "admin",
|
||||
OWNER: "owner",
|
||||
} as const;
|
||||
```
|
||||
|
||||
A user can have multiple roles in an organization. For example, a user can be a member and an admin (if it makes sense for your application).
|
||||
|
||||
<Callout type="warn" title="Don't confuse organization admin with super admin">
|
||||
The organization's `admin` role is different from the user's global `admin` role.
|
||||
|
||||
The organization `admin` governs permissions only inside the organization, whereas the global `admin` controls access to the [super admin dashboard](/docs/web/admin/overview).
|
||||
</Callout>
|
||||
|
||||
To create additional roles with custom permissions, see the [official documentation](https://www.better-auth.com/docs/plugins/organization#create-access-control) for more details.
|
||||
|
||||
## Permissions
|
||||
|
||||
Permissions represent what actions a role can perform on which resources.
|
||||
|
||||
To check if the current user has permission to perform an action on mobile, use the client helper and handle the boolean result in your component logic.
|
||||
|
||||
```tsx title="create-project.tsx"
|
||||
import { useQuery, useMutation } from "@tanstack/react-query";
|
||||
import { authClient } from "~/lib/auth";
|
||||
|
||||
export function CreateProject() {
|
||||
const { data: canCreate } = useQuery({
|
||||
queryKey: ["permission", "project", "create"],
|
||||
queryFn: () =>
|
||||
authClient.organization.hasPermission({
|
||||
permissions: { project: ["create"] },
|
||||
}),
|
||||
});
|
||||
|
||||
const { mutate, isPending } = useMutation({
|
||||
mutationFn: async () => {
|
||||
// perform the create action
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<Button
|
||||
disabled={canCreate === false}
|
||||
loading={isPending}
|
||||
onPress={() => canCreate && mutate()}
|
||||
>
|
||||
Create
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
When you already have the active member's role, prefer the client-side `checkRolePermission` to avoid extra API calls.
|
||||
|
||||
```tsx title="update-project.tsx"
|
||||
import { authClient } from "~/lib/auth";
|
||||
|
||||
export function UpdateProject() {
|
||||
const activeMember = authClient.useActiveMember();
|
||||
|
||||
const canUpdate = authClient.organization.checkRolePermission({
|
||||
permission: {
|
||||
project: ["update"],
|
||||
},
|
||||
role: activeMember.role,
|
||||
});
|
||||
|
||||
return <Button disabled={!canUpdate}>Update</Button>;
|
||||
}
|
||||
```
|
||||
|
||||
We leverage the existing hook to retrieve the active member role within the [active organization](/docs/mobile/organizations/active-organization) context. That way, you can easily check whether a member has permission to perform an action without a server round trip.
|
||||
|
||||
<Callout type="warn">
|
||||
This does not include any dynamic roles or permissions because everything runs synchronously on the client-side. Use the `hasPermission` APIs to include checks for dynamic roles and permissions.
|
||||
</Callout>
|
||||
|
||||
If you need to add more granular permissions to existing roles, or create new ones, use the [`createAccessControl`](https://www.better-auth.com/docs/plugins/organization#custom-permissions) API.
|
||||
|
||||
For further customization—such as dynamic access control, lifecycle hooks, or team management—see the guidance in the [official documentation](https://www.better-auth.com/docs/plugins/organization) and the [web guide](/docs/web/organizations/rbac).
|
||||
Reference in New Issue
Block a user