Build authentication requirements in a NocoBase 3 application: protect API routes and pages with the session, read and refresh the session in the browser, customize the login, registration and password pages, add sign-in methods such as social login, OIDC or a custom Better Auth plugin, enable official Better Auth plugins (device login for a CLI, bearer tokens, organizations, two-factor, passkeys) with the migration their tables need, manage account lifecycle from server code, and configure secrets, cookies and shared storage for deployment.
npx skills add https://github.com/nocobase/nocobase --skill nocobase-app-plugin-authentication
Use this Skill when an application built on @nocobase/app-plugin-authentication
needs anything about who the caller is: protecting a route or page, reading the
current user, changing the sign-in pages, adding a sign-in method, disabling
accounts, or preparing authentication for deployment.
Do not use it for what a signed-in user may do — that is
nocobase-app-plugin-authorization — or for the user administration page,
which is nocobase-app-plugin-users. Do not modify the plugin's own source,
migrations, or dist/; everything below is done in the application.
The plugin wraps Better Auth. Application configuration is Better Auth
configuration, and every Better Auth plugin, social provider, and hook is
available through it. Read the installed Better Auth version's documentation
for option details; this Skill covers where those options go in a NocoBase
application and what the plugin adds on top.
Enable an official Better Auth plugin before writing anything of your own: device login for a CLI (deviceAuthorization() with bearer()), organizations, two-factor and passkeys all exist upstream, and each needs only configuration, an application migration for its tables, and its UI. Enabling an official Better Auth plugin covers the rule, the schema check and the migration.
Server, from @nocobase/app-plugin-authentication (the root entry is the
server entry) or @nocobase/app-plugin-authentication/server:
authenticationToken resolves the Auth instance: required(),optional(), getSession(headers), handler(request), signInUser(userId, request).
AuthEnv types a Hono router whose routes read context.get('auth').userAdministrationServiceToken resolves UserAdministrationService:list, get, create, update, disable, enable, resetPassword,
revokeSessions, withConnection; errors are UserAdministrationError.
AuthConfig is Better Auth's BetterAuthOptions, the type of the application's server/config/auth.ts. User initialization configuration lives separately under users.initialAdmin.createAuthentication, databaseAdapter, createAuthStorage build aninstance outside the application runtime, mainly in tests.
Client, from @nocobase/app-plugin-authentication/client:
authentication() is the registration factory. It takes no options,contributes no routes, and mounts one React provider.
useAuthentication() returns { client, session, isPending, refresh };useAuthenticationClient() returns the Better Auth client alone.
RequiredAuthentication, GuestAuthentication, andAuthenticationGuard({ mode }) gate a route subtree on the session.
AuthConfig is Better Auth's client options, the type of the application'sclient/config/auth.ts.
authenticationClientToken is the client's service token, used to stub theclient in component tests.
Headless actions, from @nocobase/app-plugin-authentication/client/actions:
usePasswordLogin, usePasswordRegistration, usePasswordResetRequest,
usePasswordReset, each returning { submit, isPending, error }.
HTTP: every Better Auth endpoint is served under /api/auth/* by the plugin's
own route. A Better Auth plugin's endpoints appear there automatically. There
is no other authentication REST surface.
API document: the plugin lets a signed-in session read the App's OpenAPI document at GET /api/swagger and its Swagger UI at GET /api/swagger/docs; an anonymous request gets 401 with error.reason API_DOCS_UNAUTHENTICATED, and @nocobase/app-plugin-api-keys adds API keys as a second way in. The plugin also merges Better Auth's endpoints into that document from Better Auth's own OpenAPI generator, at their full /api/auth/... paths and tagged Authentication, so a Better Auth plugin added to auth.plugins is documented without further work. Browser-only steps are left out: /sign-in/social, /link-social, /callback/{id}, /verify-email, /reset-password/{token}, /delete-user/callback and /error. Do not add Better Auth's openAPI() plugin: it would publish its own unauthenticated /api/auth/reference page and schema route.
| The task is | Read |
| -------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| Require or read the session in an API route, compose with authorization, test it | protecting routes |
| Read the user in a component, sign out, gate a page, change the login pages, add SSO buttons | client session and pages |
| Add GitHub, Google, OIDC, magic link, or another sign-in method | adding sign-in methods |
| Enable any official Better Auth plugin: CLI device login, bearer tokens, organizations, 2FA | Better Auth plugins |
| The identity platform uses a protocol Better Auth cannot express | custom Better Auth plugin |
| Disable or reset an account from server code, add user fields, send reset emails, deploy | user lifecycle and deployment |
Read only the reference the task needs.
user, session, account, andverification collections and their migrations, session validation, the
guards, the headless actions, and the /api/auth/* route.
server/config/auth.ts, client/config/auth.ts, thefour guest routes in client/routes.ts, the pages in client/pages/auth/,
the authentication UI in client/extensions/nocobase-auth-*/, its own migrations for any
schema a sign-in method adds, and every environment variable and secret.
application's own code decides which roles exist.
skills/ source is authoritative. .agents/skills/ is asynchronized copy and must not be edited.
Prefer props and page composition in client/pages/auth/, then new application components, over editing the UI Library blocks in client/extensions/nocobase-auth-*/. Reuse the headless authentication actions in custom forms. Disabling registration or another feature should preserve its pages and components, conditionally disable the route, and hide its entry points so it can be restored. The server must still reject the disabled operation. Read client session and pages for the implementation and verification rules.
/api authenticates nothing. A route is protected only by auth.required() on it. auth.optional() accepts anonymous requests but still rejects invalid credentials and untrusted cookie write origins. Without a configured authentication base URL, cookie write checks derive the trusted origin from each request URL using Better Auth's trusted-proxy-header policy; the Origin header is checked against that origin, never used to establish trust.request independently.
needs is an application migration in database/main/migrations/; never copy or
edit the plugin's migrations, and never let Better Auth alter tables.
token, store a session in localStorage, or resolve identity from a client
claim.
issuer + subject, never by anunverified email. Merging accounts on email is a product decision that has to
be stated explicitly before it is implemented.
client/may read one.
token. Log user ids, request ids, and outcomes.
403 ACCOUNT_DISABLED, andits existing sessions and realtime connections are revoked at once. Do not
add a parallel "active" flag.
401 with the standard error body, error.reason AUTHENTICATION_REQUIRED; the same request with a session returns the route's own response.403 fromauthorization, not 401.
required route redirects an anonymous browser to /login; a guestroute redirects a signed-in browser to /.
rejects an invalid, expired, or reused credential, and creates exactly one
binding under concurrent first sign-ins.
up and down against a realtest database.
lint, typecheck, test, and build. Skillsynchronization alone proves only that the copy matches this source.
Auth.pluginApi<TPlugin>(pluginId) returns only the registered plugin’s API methods through Better Auth’s normal dispatch, including before and after hooks. It does not expose the full authentication context and does not authenticate a caller. Auth.forConnection(connection) binds those operations to a caller-owned transaction. Consumers still enforce authorization; HTTP-specific hooks must explicitly check for request or headers instead of accidentally denying trusted server calls.
Before the first seed run, set users.initialAdmin.username, users.initialAdmin.email and users.initialAdmin.password in application configuration to customize the bootstrap account. Omitting the entire node preserves nocobase / [email protected] / admin123; an explicit node requires a nonempty password and defaults the username to nocobase and the email to [email protected]. Usernames contain 3–30 letters, digits, underscores or dots, emails must be valid addresses, and both are stored lowercase. Initialization only runs when the user table is empty, hashes the password and grants the newly created account root permission through the authorization plugin. After initialization, configuration changes do not reset accounts or rerun the recorded seeds. The root permission seed selects the administrator by the same configured username. Use the user administration service for subsequent account changes. Do not edit historical seeds or add a competing administrator seed.
Auth.signInUser(userId, request, { clientAddress }?) creates an ordinary browser session for an existing enabled person account after trusted server code has authorized that sign-in. It returns a Response; the caller checks its status and forwards its Set-Cookie headers. Better Auth applies the configured session hooks and cookie attributes, without reading or changing the password. There is no HTTP endpoint for this method. The calling plugin owns the access policy: Demo Mode uses it only while public quick sign-in is enabled. This method does not grant permissions or replace authentication for ordinary public login routes.
Take nocobase/nocobase-app-plugin-authentication from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
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.