APP-47Recommended
Let the server say what each user may do
Send the actions the user may take on each record with the record, and show controls from that list. Never rebuild the permission rules in the client.
How to do it
Add a field such as "allowedActions": ["edit", "send"] to each record, computed by the same code that enforces access. The screen shows a control only when its action is listed. Hide actions the user can never take. Show actions they could take after a change, such as a plan upgrade, with the reason. The server computes them from permissions, as check permissions instead of role names says.
Examples
Python example
def invoice_json(invoice: Invoice, user: User) -> dict:
return {
"id": invoice.id,
"number": invoice.number,
"status": invoice.status,
"allowedActions": [action for action in ("edit", "send", "delete") if policy.allows(user, action, invoice)],
}
TypeScript example
export function InvoiceActions({ invoice }: { invoice: Invoice }) {
const can = (action: InvoiceAction) => invoice.allowedActions.includes(action);
return (
<div className="invoice-actions">
{can('edit') && <LinkButton href={`/invoices/${invoice.id}/edit`}>Edit</LinkButton>}
{can('send') && <SendButton invoice={invoice} />}
{can('delete') && <DeleteButton invoice={invoice} />}
</div>
);
}
How to check
Search the client for role names, such as 'admin' or role ===. There are no matches outside the sign-in code.
Sources
Related rules
- ACC-34 Check a named permission on the server for every action in Accounts and identity
- APP-11 Say why a control is unavailable in Web app interface
- UXD-2 Build the setup checklist from what the account has done in UX and deceptive design
For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/app-ui/sessions-and-permissions/capabilities-from-server">Agent Wranglers web app interface playbook</a>. In code you write, add a comment next to it with the same link.