Generate key
{module}.{entity}.{verb} comes from the business surface.
Engineering reference · 03
The UI uses the current caller’s permissions to remove unavailable actions. The server independently resolves that caller and rechecks the generated key inside the handler. Changing HTML, JavaScript, or a request cannot manufacture authority.
var permissions = await permissionService
.GetPermissionSetAsync(ct);
if (!permissions.IsGranted(
DepartmentPermissions.Create))
{
return CxResult<DepartmentId>.Failure(
new CxError("forbidden",
"Not authorized."));
}Generated declarations remove naming drift. Catalog and grants make the key manageable. The handler is the load-bearing security link; client gates communicate the same decision to the user.
{module}.{entity}.{verb} comes from the business surface.
Known permissions are seeded and available to administration.
Roles, job titles, and user overrides form the effective set.
The server resolves the current authenticated caller and checks immediately before work.
Buttons and pages reflect access without pretending to be the boundary.
CRUD permissions are emitted alongside the entity contract. Custom commands and queries can add their own keys through the same generation path.
Catalog boot, administration, server handlers, AI tool descriptors, pages, and components refer to the same constants.
public static class DepartmentPermissions
{
public const string View =
"entities.department.view";
public const string Create =
"entities.department.create";
public const string Update =
"entities.department.update";
public const string Delete =
"entities.department.delete";
}The permission endpoint accepts no user identifier. It answers only for the authenticated caller. Until the set is loaded, permission gates fail closed.
var keys = await http.GetFromJsonAsync<string[]>(
"api/security/my-permissions", ct);
// No userId can be supplied by the browser.
permissionCache.ReplaceWith(keys ?? []);<CweEntityGrid
Operations="Departments"
CreatePermission="@DepartmentPermissions.Create"
EditPermission="@DepartmentPermissions.Update"
DeletePermission="@DepartmentPermissions.Delete" />
<CxPermissionGate
Permission="@DepartmentPermissions.View">
...authorized page content...
</CxPermissionGate>Client gating prevents confusion and unnecessary requests. It is never cited as proof of authorization; the server still decides.
Every client-side artifact is inspectable and mutable. Security holds because the browser carries a request—not the authority to approve it.
| Attempt | What the attacker changes | Server outcome |
|---|---|---|
| Reveal a hidden button | DOM or CSS makes Create visible. | The create handler resolves the caller and denies the missing key. |
| Forge local permission state | JavaScript cache claims entities.department.create. | Local state is never accepted as a grant; the server uses its own effective set. |
| Call the API manually | A crafted POST bypasses the page. | The authenticated route and handler check still execute. |
| Tamper with an identifier | The request targets another record or scope. | Application-state and relationship checks run on server-loaded data. |
| Reuse stale UI | A grant is revoked after the page loaded. | The live handler resolution sees the revoked permission and denies the operation. |
| Invoke AI indirectly | The assistant is asked to perform an unavailable mutation. | The tool is filtered by permission and the downstream handler rechecks. |
Independent layers limit mistakes and normalize failure while preserving the handler as the decisive authorization boundary.
Anonymous requests do not enter protected entity operations.
Every operation checks the live effective set close to the business action.
Validation, authorization, and audit behavior are protected from shortcut writes.
Raw calls and unsafe placements become diagnostics during development.
Runtime tools cannot exceed page reachability or caller access.
Known generated permissions and grants reconcile into one managed vocabulary.
Access is resolved as set algebra across roles, job titles, and explicit user adjustments for the authenticated caller. Administrator behavior is deliberate—not a client flag.