mirror of
https://github.com/nocobase/nocobase.git
synced 2026-10-13 07:47:55 +08:00
* feat: add API keys plugin built on Better Auth Add @nocobase/app-plugin-api-keys: the apikey table migration, a self-service Settings page, and Better Auth's apiKey/apiKeyClient re-exported with the defaults a NocoBase application needs (enableSessionForAPIKeys on, rate limiting off, requireName on — all overridable). A key resolves to a session for its owner, so auth.required(), the route guards and Authorization see the owning user and that user's roles unchanged. In app-plugin-authentication: - AuthClientPluginRegistry, an augmentable interface, so AuthClient is typed by the client plugins an application registers instead of being pinned to usernameClient. - required()/optional() forward Better Auth's own status and body when a credential is present but refused (401 KEY_EXPIRED, 429 USAGE_EXCEEDED) instead of letting the APIError escape as a 500; getSession() folds it into null for callers that only ask who is signed in. Register the plugin in app-template-default and app-template-hub. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * fix(authentication): keep getSession as the single resolution seam required() and optional() had been routed through a private authenticate() so they could forward Better Auth's rejection, which bypassed getSession — the method tests replace to fake a signed-in user. Every authenticated request in app-plugin-repository-example's suite answered 401. Fold the resolution back into getSession and let the middleware ask for the reason with { throwOnRejected: true }. A test double that replaces getSession is honoured again, programmatic callers still read a refused credential as null, and the middleware still forwards 401 KEY_EXPIRED and 429 USAGE_EXCEEDED. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> * refactor(authentication): follow Better Auth's getSession contract getSession() returns the session, null when nobody is signed in, and throws Better Auth's APIError when a credential is refused — Better Auth's own semantics, with no option and no second method. required() and optional() translate that APIError into a response with its status and body, which is the one thing the wrapper was missing. Callers that only ask who is signed in catch it themselves. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>