Add a new permission end-to-end — server constant + endpoint gate, and (admin app) mirror it into the permissions catalog + route guard. Use when a new endpoint needs authorization. See modules/identity.md + frontend/admin.md.
73
91%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
A permission spans server + the admin app. The dashboard app does not mirror permissions — it reads them from the JWT and relies on the server's 403.
Modules.{X}.Contracts/Authorization/{X}Permissions.cs)Add the constant to the resource group and ensure it's in the module's All collection. Convention:
Permissions.{Resource}.{Action}.
public static class {X}Permissions
{
public static class {Resources}
{
public const string View = "Permissions.{Resources}.View";
public const string Create = "Permissions.{Resources}.Create"; // ← new
}
public static IReadOnlyList<FshPermission> All { get; } = [ /* … include the new one … */ ];
}The module already calls PermissionConstants.Register({X}Permissions.All) in ConfigureServices, so a new entry in All is picked up automatically.
.RequirePermission({X}Permissions.{Resources}.Create);⚠️ RequiredPermissionAttribute implements IRequiredPermissionMetadata. Never let a second/duplicate of that interface exist — it silently disables all .RequirePermission() gates app-wide. (See .agents/rules/modules/identity.md.)
clients/admin/src/lib/permissions.ts — add the matching string to the frozen tree (no runtime catalog endpoint exists; mirror by hand):
export const {Module}Permissions = Object.freeze({
{Resources}: { View: "Permissions.{Resources}.View", Create: "Permissions.{Resources}.Create" },
} as const);If it should appear in the Role editor UI, add a PERMISSION_CATALOG entry ({ name, description, root?, basic? } under the right category group).
{ path: "{resources}/new",
element: <RouteGuard perms={[{Module}Permissions.{Resources}.Create]}><Create{Resource}Page /></RouteGuard> },So RouteGuard passes on first paint, add the new permission to the test seed set (ADMIN_PERMS in clients/admin/tests/helpers/shell-mocks.ts, used by seedAuthedSession).
No mirror, no RouteGuard. The JWT carries only role names — the app fetches the permission list from GET /api/v1/identity/permissions at hydration and the server enforces access; a missing permission yields a 403 the UI surfaces. Routes aren't permission-gated; to hide a nav entry, set perm/anyPerm on the item in src/components/layout/nav-data.ts. Permission-gated specs mock GET /identity/permissions with the grants they need (shell mocks stub it to []).
{X}Permissions and its All collection.RequirePermission(...); no duplicate IRequiredPermissionMetadatalib/permissions.ts (+ PERMISSION_CATALOG if role-editor-visible)<RouteGuard perms={[…]}>; permission added to ADMIN_PERMS test seedtest:e2e green3f2959e
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.