mcpbeat Sign in

Nocobase App Plugin Users Agent Skill

Integrate the Users plugin into a NocoBase App, build a user management page on its API and configure its permissions, or add an application-owned role scope through the public Server contracts.

2.8k tokens
context cost
SKILL.md, read when the skill is used
1
files
instructions only
0
copies elsewhere
how many repositories repackaged it
24 822
stars on the repo
on the repository, not the skill itself

Install

one command, takes just this skill from the repository
npx skills add https://github.com/nocobase/nocobase --skill nocobase-app-plugin-users

What it tells the agent to use

found in the instruction text
Read reads your files

The instruction itself

9 sections, as written by the author

User Management App Plugin

Use this Skill when an application needs user management, or when another

plugin needs to expose application-specific roles in user management. Do

not use it to modify the Users plugin source or to replace Authentication's

user, account, or Session storage.

Public surfaces

  • Client registration factory and UsersClientOptions:

@nocobase/app-plugin-users/client. It contributes only the public

invitation page at /invite/:token; there is no user management page.

  • Typed API client: UsersClient and its types from

@nocobase/app-plugin-users/client/user-client.

  • Server contracts: userManagementServiceToken,

userRoleScopeRegistryToken, UserRoleScope, and related types from

@nocobase/app-plugin-users/server/tokens.

  • HTTP API: GET /api/users (page, pageSize, q, status, roleScope, role, sort=name, order=asc|desc; answers { data, meta: { page, pageSize, total } }), GET /api/users/options, GET /api/users/:userId (latest managed profile and role scopes, requires user/read, 404 for absent accounts), POST /api/users, PATCH /api/users/:userId, DELETE /api/users/:userId?confirm=true, POST /api/users/:userId/disable|enable|resetPassword|revokeSessions, and PUT /api/users/:userId/roleScopes/:scope. The running application documents each one, with request and response schemas, at /api/swagger/docs (JSON at /api/swagger) under the Users tag. Failures use the standard error body; branch on error.reason, such as USER_NOT_FOUND, SELF_DELETE_NOT_ALLOWED, LAST_ASSIGNMENT or USER_EMAIL_CONFLICT.

UsersClient.update(userId, { name?, username?, email?, roleScopes? }) supports one atomic editor Save. Profile fields require update; submitted roleScopes additionally require assign-role. A roles-only payload requires assign-role without update. The service validates scope values, runs each applicable target-account guard, and commits profile changes and all submitted scope replacements in the caller-owned transaction, notifying permission clients only after commit. Omitted scopes are preserved. Keep the existing per-scope PUT for callers needing only one scope. Do not implement a combined Save as sequential profile and role requests: a later failure would leave a partially saved account.

Every HTTP route requires Authentication and Authorization. Routes check the user record type with the applicable action from read, create, update, disable, enable, assign-role, reset-password, revoke-sessions or delete. Account creation checks both create and assign-role. delete is permitted only while an application role scope can clean a deleted user up.

Register the plugin

  • Register Authentication, Authorization, and then Users in the App's Client

and Server plugin arrays. The Client factory takes no placement options:

users().

  • Grant only the user actions those roles need, on { type: 'user', id: '*' } because user is a record type. The plugin creates no roles and grants no access by itself.
  • For a user management page, build it in the App on UsersClient and declare it among the App's own routes with the page access it needs; the plugin ships none.

The invitation page reports results through useToaster() from @nocobase/app-client, so the App needs the @nocobase/app-client that exports it and registers a toaster service, as the templates do: client/lib/toaster.ts from the template, and this.app.container.instance(toasterToken, createToaster()) in the register() of client/service-provider.ts, with the Toaster component mounted in client/react-providers.ts. Without the registration nothing throws, but its toasts are only logged to the browser console.

The default app permission-set scope is supplied by Users whenever the Authorization plugin's authorizationToken is available. Do not copy a user-roles Provider into an application. Set users.permissionSets: false to replace the default with an application-owned scope.

Add an application role scope

Resolve userRoleScopeRegistryToken in an application or business plugin

ServiceProvider and register one UserRoleScope during boot(). The scope owns

its available options, current assignment lookup, role filtering, atomic

replacement, and any disable guard. Unregister it during shutdown().

Use the DatabaseConnection passed to each scope method. This is the

caller-owned transaction used to keep account creation or state changes atomic

with role assignments. Do not open an unrelated transaction and do not write

Authentication's internal tables.

Guard who may act on whom with assertCanAdminister(userId, actorId, action, connection): the management API calls it for update, disable, enable, reset-password, revoke-sessions and assign-role with the authenticated actor, in the action's transaction and before anything changes, after authorization of the action itself has passed. Throw a UserRoleScopeError to refuse, for example when someone who is not an application owner resets an owner's password; the route answers FAILED_PRECONDITION with the scope's reason. Service callers that pass no actorId are the application's own server-side work and are not asked. Deletion has its own assertCanDelete(userId, actorId, connection).

For a required single-role scope, set selection: 'single' and

requiredOnCreate: true. Reject invalid values in the scope and enforce

business invariants such as the last-administrator rule on the server.

Set assignable: false or removable: false on an option when user management

must show a protected assignment but must not add or revoke it. Enforce the

same rule in replace() because these flags only control the Client. Set

hasAuthenticatedDefaultAccess: true when the scope lists direct assignments

but all signed-in users also inherit separately configured default access, so a

user management page can explain that distinction instead of treating the

default as a role.

Use labelI18nKey with labelI18nNs on a scope or option when its owner has

registered Client locale resources; keep label as the readable fallback.

Implement optional getMany() when assignments can be read as a batch. Users

uses it for list pages and falls back to get() for existing scopes.

Invite users

Holders of invite on user can create invitations through POST /api/users/invitations or UsersClient; choosing roles also requires assign-role. Results include a shareable link even when email succeeds. A queued invitation closed or rotated by another request returns emailSent=false without a link, while the other recipients still complete. Original inviters can renew with sendEmail=false, which invalidates the old link. Plugin-specific invitations must use their domain endpoint, which rechecks its own grants. Other managers may resend email but receive no link. Tokens last seven days; only hashes are stored.

New accounts register directly from the invitation link with a name and password; acceptance leaves emailVerified=false. Email delivery is optional: authorized original inviters can privately forward the link when the email channel is absent or fails. Links use app.publicOrigin when configured, otherwise the invitation request origin. Existing accounts must sign in with the invited email. Acceptance consumes only the supplied invitation and applies only its grants; handlers run in its transaction and roll back account creation on failure.

Ownership

  • Authentication owns user identity, credentials, account state, password

hashing, and Sessions.

  • Authorization owns Permission Sets, grants, and assignments.
  • Users owns the management API, the invitation page, orchestration transaction,

user authorization handler, and role-scope registry.

  • The App or business plugin owns role definitions, role grants, assignments,

the user management page, and role-specific invariants.

  • The plugin's skills/ source is authoritative. .agents/skills/ is a

synchronized copy and must not be edited.

Permissions and constraints

  • Browser route access is only navigation control. The Server independently

authenticates and authorizes every request.

  • A conditional grant is not accepted as an unrestricted user-management

grant; use explicit static grants for this resource.

  • Disabled users are rejected by Authentication and lose their existing HTTP

Sessions and Realtime connections.

  • Password hashes, Session tokens, reset tokens, and submitted passwords are

never returned by the service or included in security events.

Verification

  • Anonymous API requests return 401; authenticated requests without the

requested user action return 403.

  • Creating a user with a required role scope creates both records, while role

assignment failure rolls back the user.

  • Duplicate emails or usernames return a stable 409 conflict. Password reset

and database Session revocation commit or roll back together.

  • Disabling a user invalidates HTTP Sessions and Realtime connections; enabling

the user requires a new login.

  • Role changes become visible after transaction commit and do not alter

assignments outside the registered scope.

  • The target App passes its relevant tests, typecheck, and build. Skill

synchronization alone proves only that the copy matches this source.

Deletion uses DELETE /api/users/:userId?confirm=true and user/delete authorization. Obtain an explicit user deletion request before calling it. Application role scopes can guard deletion and clean dependent credentials transactionally. Historical user identities are retained but cannot sign in or appear in management lists.

Invitation emails use notificationService.sendTransient() so credentials never enter notification message snapshots or job storage. This performs one bounded attempt without durable retries or deduplication. Failures retain safe status/category diagnostics without raw provider messages. The inviter can generate and privately deliver a new link if delivery fails. Historical notification snapshots are not rewritten.

Creation defaults and existing applications

Apply an option scope’s defaultValue only when creating a new account. An explicit empty array takes precedence; editing uses saved roles and never reapplies defaults. Member is a visible direct role that can be removed. A roleless person retains authenticated account basics but no role-granted business access. Upgrade Authentication, Authorization, and Users together, stop old writers before applying database tasks, and merge the template user pages and translations separately: upgrading plugins does not replace application-owned source. The new Authorization seed preserves existing users before removing Member’s global assignment; never recreate that assignment in application seeds or startup reconciliation.

How to use it

Copy the folder

Take nocobase/nocobase-app-plugin-users from the repository into ~/.claude/skills for personal use, or into .claude/skills inside a project.

Check the name does not clash

The agent identifies a skill by the name field in its header. Two skills with the same name cannot sit side by side — one of them will be ignored.