@nocobase/app-plugin-api-keys 0.0.0-stage → 1.0.0-beta.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/CHANGELOG.md +418 -0
  2. package/README.md +165 -2
  3. package/dist/client/api-keys-client.d.ts +19 -0
  4. package/dist/client/api-keys-client.d.ts.map +1 -0
  5. package/dist/client/api-keys-client.js +7 -0
  6. package/dist/client/api-keys-client.js.map +1 -0
  7. package/dist/client/components/page-container.d.ts +4 -0
  8. package/dist/client/components/page-container.d.ts.map +1 -0
  9. package/dist/client/components/page-container.js +6 -0
  10. package/dist/client/components/page-container.js.map +1 -0
  11. package/dist/client/components/page-header.d.ts +8 -0
  12. package/dist/client/components/page-header.d.ts.map +1 -0
  13. package/dist/client/components/page-header.js +5 -0
  14. package/dist/client/components/page-header.js.map +1 -0
  15. package/dist/client/components/ui/alert-dialog.d.ts +19 -0
  16. package/dist/client/components/ui/alert-dialog.d.ts.map +1 -0
  17. package/dist/client/components/ui/alert-dialog.js +42 -0
  18. package/dist/client/components/ui/alert-dialog.js.map +1 -0
  19. package/dist/client/components/ui/badge.d.ts +6 -0
  20. package/dist/client/components/ui/badge.d.ts.map +1 -0
  21. package/dist/client/components/ui/badge.js +8 -0
  22. package/dist/client/components/ui/badge.js.map +1 -0
  23. package/dist/client/components/ui/button.d.ts +8 -0
  24. package/dist/client/components/ui/button.d.ts.map +1 -0
  25. package/dist/client/components/ui/button.js +27 -0
  26. package/dist/client/components/ui/button.js.map +1 -0
  27. package/dist/client/components/ui/dialog.d.ts +9 -0
  28. package/dist/client/components/ui/dialog.d.ts.map +1 -0
  29. package/dist/client/components/ui/dialog.js +29 -0
  30. package/dist/client/components/ui/dialog.js.map +1 -0
  31. package/dist/client/components/ui/input.d.ts +4 -0
  32. package/dist/client/components/ui/input.d.ts.map +1 -0
  33. package/dist/client/components/ui/input.js +8 -0
  34. package/dist/client/components/ui/input.js.map +1 -0
  35. package/dist/client/components/ui/label.d.ts +3 -0
  36. package/dist/client/components/ui/label.d.ts.map +1 -0
  37. package/dist/client/components/ui/label.js +6 -0
  38. package/dist/client/components/ui/label.js.map +1 -0
  39. package/dist/client/components/ui/select.d.ts +8 -0
  40. package/dist/client/components/ui/select.d.ts.map +1 -0
  41. package/dist/client/components/ui/select.js +19 -0
  42. package/dist/client/components/ui/select.js.map +1 -0
  43. package/dist/client/components/ui/spinner.d.ts +4 -0
  44. package/dist/client/components/ui/spinner.d.ts.map +1 -0
  45. package/dist/client/components/ui/spinner.js +11 -0
  46. package/dist/client/components/ui/spinner.js.map +1 -0
  47. package/dist/client/components/ui/table.d.ts +8 -0
  48. package/dist/client/components/ui/table.d.ts.map +1 -0
  49. package/dist/client/components/ui/table.js +21 -0
  50. package/dist/client/components/ui/table.js.map +1 -0
  51. package/dist/client/expiry.d.ts +23 -0
  52. package/dist/client/expiry.d.ts.map +1 -0
  53. package/dist/client/expiry.js +40 -0
  54. package/dist/client/expiry.js.map +1 -0
  55. package/dist/client/index.d.ts +6 -0
  56. package/dist/client/index.d.ts.map +1 -0
  57. package/dist/client/index.js +7 -0
  58. package/dist/client/index.js.map +1 -0
  59. package/dist/client/lib/utils.d.ts +2 -0
  60. package/dist/client/lib/utils.d.ts.map +1 -0
  61. package/dist/client/lib/utils.js +2 -0
  62. package/dist/client/lib/utils.js.map +1 -0
  63. package/dist/client/locales/en-US.d.ts +71 -0
  64. package/dist/client/locales/en-US.d.ts.map +1 -0
  65. package/dist/client/locales/en-US.js +62 -0
  66. package/dist/client/locales/en-US.js.map +1 -0
  67. package/dist/client/locales/index.d.ts +4 -0
  68. package/dist/client/locales/index.d.ts.map +1 -0
  69. package/dist/client/locales/index.js +7 -0
  70. package/dist/client/locales/index.js.map +1 -0
  71. package/dist/client/locales/zh-CN.d.ts +4 -0
  72. package/dist/client/locales/zh-CN.d.ts.map +1 -0
  73. package/dist/client/locales/zh-CN.js +62 -0
  74. package/dist/client/locales/zh-CN.js.map +1 -0
  75. package/dist/client/pages/api-keys-page.d.ts +3 -0
  76. package/dist/client/pages/api-keys-page.d.ts.map +1 -0
  77. package/dist/client/pages/api-keys-page.js +161 -0
  78. package/dist/client/pages/api-keys-page.js.map +1 -0
  79. package/dist/client/plugin.d.ts +9 -0
  80. package/dist/client/plugin.d.ts.map +1 -0
  81. package/dist/client/plugin.js +10 -0
  82. package/dist/client/plugin.js.map +1 -0
  83. package/dist/client/routes.d.ts +22 -0
  84. package/dist/client/routes.d.ts.map +1 -0
  85. package/dist/client/routes.js +42 -0
  86. package/dist/client/routes.js.map +1 -0
  87. package/dist/database/migrations/.manifest.json +9 -0
  88. package/dist/database/migrations/202609150001_create_api_key_table.js +58 -0
  89. package/dist/database/migrations/202609150001_create_api_key_table.js.map +1 -0
  90. package/dist/server/api-docs.d.ts +24 -0
  91. package/dist/server/api-docs.d.ts.map +1 -0
  92. package/dist/server/api-docs.js +73 -0
  93. package/dist/server/api-docs.js.map +1 -0
  94. package/dist/server/api-keys.d.ts +52 -0
  95. package/dist/server/api-keys.d.ts.map +1 -0
  96. package/dist/server/api-keys.js +199 -0
  97. package/dist/server/api-keys.js.map +1 -0
  98. package/dist/server/index.d.ts +23 -0
  99. package/dist/server/index.d.ts.map +1 -0
  100. package/dist/server/index.js +22 -0
  101. package/dist/server/index.js.map +1 -0
  102. package/dist/server/key-sessions.d.ts +35 -0
  103. package/dist/server/key-sessions.d.ts.map +1 -0
  104. package/dist/server/key-sessions.js +50 -0
  105. package/dist/server/key-sessions.js.map +1 -0
  106. package/dist/server/plugin.d.ts +4 -0
  107. package/dist/server/plugin.d.ts.map +1 -0
  108. package/dist/server/plugin.js +15 -0
  109. package/dist/server/plugin.js.map +1 -0
  110. package/dist/server/providers/index.d.ts +19 -0
  111. package/dist/server/providers/index.d.ts.map +1 -0
  112. package/dist/server/providers/index.js +72 -0
  113. package/dist/server/providers/index.js.map +1 -0
  114. package/dist/server/routes/errors.d.ts +8 -0
  115. package/dist/server/routes/errors.d.ts.map +1 -0
  116. package/dist/server/routes/errors.js +48 -0
  117. package/dist/server/routes/errors.js.map +1 -0
  118. package/dist/server/routes/index.d.ts +10 -0
  119. package/dist/server/routes/index.d.ts.map +1 -0
  120. package/dist/server/routes/index.js +177 -0
  121. package/dist/server/routes/index.js.map +1 -0
  122. package/dist/server/routes/schemas.d.ts +25 -0
  123. package/dist/server/routes/schemas.d.ts.map +1 -0
  124. package/dist/server/routes/schemas.js +127 -0
  125. package/dist/server/routes/schemas.js.map +1 -0
  126. package/dist/server/scoped-keys.d.ts +116 -0
  127. package/dist/server/scoped-keys.d.ts.map +1 -0
  128. package/dist/server/scoped-keys.js +451 -0
  129. package/dist/server/scoped-keys.js.map +1 -0
  130. package/dist/server/scopes.d.ts +70 -0
  131. package/dist/server/scopes.d.ts.map +1 -0
  132. package/dist/server/scopes.js +165 -0
  133. package/dist/server/scopes.js.map +1 -0
  134. package/dist/server/service.d.ts +28 -0
  135. package/dist/server/service.d.ts.map +1 -0
  136. package/dist/server/service.js +69 -0
  137. package/dist/server/service.js.map +1 -0
  138. package/dist/server/tokens.d.ts +6 -0
  139. package/dist/server/tokens.d.ts.map +1 -0
  140. package/dist/server/tokens.js +5 -0
  141. package/dist/server/tokens.js.map +1 -0
  142. package/dist/shared/scopes.d.ts +160 -0
  143. package/dist/shared/scopes.d.ts.map +1 -0
  144. package/dist/shared/scopes.js +83 -0
  145. package/dist/shared/scopes.js.map +1 -0
  146. package/package.json +91 -3
  147. package/skills/nocobase-app-plugin-api-keys/SKILL.md +166 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scopes.js","sourceRoot":"","sources":["../../shared/scopes.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,94 @@
1
1
  {
2
2
  "name": "@nocobase/app-plugin-api-keys",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
3
+ "displayName": "API Keys App Plugin",
4
+ "description": "API key authentication for NocoBase applications, built on Better Auth.",
5
+ "version": "1.0.0-beta.13",
6
+ "type": "module",
7
+ "prettier": "@nocobase/dev-config/prettier",
8
+ "engines": {
9
+ "node": ">=24.0.0"
10
+ },
11
+ "sideEffects": false,
12
+ "exports": {
13
+ "./server": {
14
+ "types": "./dist/server/index.d.ts",
15
+ "import": "./dist/server/index.js"
16
+ },
17
+ "./client": {
18
+ "types": "./dist/client/index.d.ts",
19
+ "import": "./dist/client/index.js"
20
+ },
21
+ "./client/plugin": {
22
+ "types": "./dist/client/plugin.d.ts",
23
+ "import": "./dist/client/plugin.js"
24
+ },
25
+ "./client/routes": {
26
+ "types": "./dist/client/routes.d.ts",
27
+ "import": "./dist/client/routes.js"
28
+ },
29
+ "./shared/scopes": {
30
+ "types": "./dist/shared/scopes.d.ts",
31
+ "import": "./dist/shared/scopes.js"
32
+ },
33
+ "./package.json": "./package.json"
34
+ },
35
+ "files": [
36
+ "dist",
37
+ "README.md",
38
+ "CHANGELOG.md",
39
+ "skills"
40
+ ],
41
+ "publishConfig": {
42
+ "access": "public"
43
+ },
44
+ "dependencies": {
45
+ "@better-auth/api-key": "1.7.5",
46
+ "@better-auth/core": "1.7.5",
47
+ "@better-auth/utils": "0.4.2",
48
+ "better-auth": "1.7.5",
49
+ "better-call": "1.4.0",
50
+ "hono": "^4.13.2",
51
+ "zod": "^4.6.5"
52
+ },
53
+ "peerDependencies": {
54
+ "@base-ui/react": "^1.6.0",
55
+ "class-variance-authority": "^0.7.1",
56
+ "cn": "^0.4.0",
57
+ "lucide-react": "^0.487.0",
58
+ "react": "^19.0.0",
59
+ "@nocobase/app-client": "^2.0.0-beta.2",
60
+ "@nocobase/app-plugin-authentication": "^2.0.0-beta.2",
61
+ "@nocobase/app-plugin-authorization": "^1.0.0-beta.25",
62
+ "@nocobase/app-server": "^2.0.0-beta.1",
63
+ "@nocobase/authorization": "^1.0.0-beta.12",
64
+ "@nocobase/db": "^1.0.0-beta.18",
65
+ "@nocobase/i18n": "^1.0.0-beta.5",
66
+ "@nocobase/service-provider": "^0.0.2-beta.1"
67
+ },
68
+ "devDependencies": {
69
+ "@testing-library/react": "^16.3.2",
70
+ "@types/node": "^24.0.0",
71
+ "@types/react": "^19.2.0",
72
+ "eslint": "^10.0.0",
73
+ "prettier": "^3.9.6",
74
+ "shadcn": "^4.13.1",
75
+ "tailwindcss": "^4.1.4",
76
+ "tw-animate-css": "^1.2.5",
77
+ "typescript": "^6.0.3",
78
+ "vitest": "^4.1.10",
79
+ "@nocobase/app-testing": "0.1.0-beta.1",
80
+ "@nocobase/dev-config": "0.1.0-beta.19"
81
+ },
82
+ "scripts": {
83
+ "clean": "node ./scripts/clean-dist.mjs",
84
+ "build": "pnpm clean && tsc -p tsconfig.json && tsc -p tsconfig.migrations.json && nocobase-db-manifests",
85
+ "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.migrations.json --noEmit",
86
+ "test": "vitest run --passWithNoTests",
87
+ "lint": "eslint . --max-warnings 0",
88
+ "lint:fix": "eslint . --fix --max-warnings 0",
89
+ "format": "prettier . --write",
90
+ "format:check": "prettier . --check",
91
+ "check": "pnpm lint && pnpm format:check && pnpm typecheck && pnpm test && pnpm build",
92
+ "fix": "pnpm lint:fix && pnpm format"
93
+ }
6
94
  }
@@ -0,0 +1,166 @@
1
+ ---
2
+ name: nocobase-app-plugin-api-keys
3
+ description: Register the API Keys plugin in a NocoBase App, let scripts and integrations call the App's API with an x-api-key header, place or restrict the key management page, or work out why a key is rejected.
4
+ metadata:
5
+ short-description: Authenticate API requests with user-owned keys
6
+ ---
7
+
8
+ # API Keys App Plugin
9
+
10
+ Use this Skill when an App needs non-interactive callers — a script, a cron job,
11
+ a third-party integration — to reach its API as a real user, or when a key the
12
+ App issued is being rejected. The default configuration resolves a key into its owner’s Session and carries that user’s roles. Applications needing scoped credentials must use a separate configuration with `enableSessionForAPIKeys: false` and enforce their business rules on the server.
13
+
14
+ This package is Better Auth's
15
+ [API Key plugin](https://www.better-auth.com/docs/plugins/api-key) plus the
16
+ `apikey` migration, the Settings page, and the locales. `apiKey` is wrapped
17
+ only to supply three defaults, all overridable; its name, options and behaviour
18
+ are Better Auth's, and Better Auth's documentation applies unchanged.
19
+
20
+ ## Public surfaces
21
+
22
+ - Server plugin: default export of `@nocobase/app-plugin-api-keys/server`.
23
+ Contributes the `apikey` table migration and nothing else.
24
+ - `apiKey(options?)`, plus `API_KEY_TABLE_NAME`, `API_KEY_ERROR_CODES` and the
25
+ option types, from the same entry. Use these rather than adding
26
+ `@better-auth/api-key` to the App: the migration here matches the schema this
27
+ version declares, and taking both from one package keeps them in step.
28
+ - Client registration factory and `ApiKeysClientOptions`:
29
+ `@nocobase/app-plugin-api-keys/client`.
30
+ - `apiKeyClient`, re-exported from `@better-auth/api-key/client` by the same
31
+ entry.
32
+ - `ApiKeySummary`, `API_KEYS_PAGE_ACCESS`, `API_KEYS_ROUTE_ID`, and the expiry
33
+ helpers, for an App that builds its own page instead of using the one here.
34
+ The page itself reads `useAuthenticationClient().apiKey`, typed through the
35
+ authentication plugin's `AuthClientPluginRegistry`.
36
+ - HTTP API: `POST /api/auth/api-key/create`, `GET /api/auth/api-key/list`,
37
+ `GET /api/auth/api-key/get`, `POST /api/auth/api-key/update`, and
38
+ `POST /api/auth/api-key/delete`. All five act only on the caller's own keys.
39
+
40
+ The plugin contributes no NocoBase route of its own. The endpoints above are
41
+ Better Auth's, mounted by Authentication's `/api/auth/*` handler. Its server registers one thing: the access check that lets a valid key read the App's API document.
42
+
43
+ ## Register it
44
+
45
+ Both halves are required, and each fails differently on its own.
46
+
47
+ 1. Add the default export to the App's `server/plugins.ts` and
48
+ `client/plugins.ts`. Configure the page with
49
+ `apiKeys({ path: '/api-keys' })`; it mounts under `/settings`.
50
+ 2. Add `apiKey()` to `plugins` in `server/config/auth.ts`, and `apiKeyClient()`
51
+ to `plugins` in `client/config/auth.ts`.
52
+ 3. Grant `page:api-keys/access` to the roles that may manage keys. Keys are
53
+ self-service and every endpoint acts only on the caller's own, so this is
54
+ normally granted to all authenticated users.
55
+ 4. Run `pnpm nocobase db apply`.
56
+
57
+ Only step 1 leaves the `apikey` table created and no endpoints mounted. Only
58
+ step 2 mounts endpoints against a table that does not exist, and every call
59
+ fails at the database.
60
+
61
+ ### The three defaults
62
+
63
+ `apiKey()` supplies `enableSessionForAPIKeys: true`, `rateLimit: { enabled:
64
+ false }` and `requireName: true`, so an App normally passes nothing.
65
+
66
+ `enableSessionForAPIKeys` is the one that has to be set. Better Auth defaults
67
+ it off, and with it off a key authenticates nothing: the Settings page still
68
+ issues keys, and every request carrying one answers 401. Nothing points at the
69
+ configuration, so do not override it without meaning to.
70
+
71
+ `rateLimit` is off because Better Auth's own default is 10 requests per key per
72
+ day, which is a quota for issuing keys rather than for using them. Pass
73
+ `{ enabled: true, maxRequests: 1000, timeWindow: 60_000 }` for a quota the App
74
+ actually wants.
75
+
76
+ All three are overridable, and passing all three back reproduces Better Auth's
77
+ own behaviour exactly.
78
+
79
+ ## Call the API with a key
80
+
81
+ ```bash
82
+ curl -H 'x-api-key: <key>' https://example.com/api/orders
83
+ ```
84
+
85
+ An App route needs no change. `auth.required()` and `auth.optional()` resolve
86
+ the key into the owner's Session, and Authorization then evaluates that user's
87
+ roles. Read `context.get('auth')` exactly as for a cookie Session.
88
+
89
+ Pass `apiKeyHeaders: ['x-api-key', 'authorization-key']` to accept a different
90
+ header.
91
+
92
+ To find out what the App's API offers, read its OpenAPI document with the same key: `GET /api/swagger` returns the JSON document and `GET /api/swagger/docs` the Swagger UI page. A signed-in session or a valid key may read it; anything else gets `401` with `error.reason` `API_DOCS_UNAUTHENTICATED`. The API key endpoints are listed there under `/api/auth/api-key/...`, tagged `Authentication`.
93
+
94
+ ## What a key can reach, and what it cannot
95
+
96
+ A key is its owner, so it reaches the Better Auth endpoints a Session reaches.
97
+ Verified against `@better-auth/api-key` 1.7.1 with only an `x-api-key` header:
98
+
99
+ | Endpoint | Result |
100
+ | -------------------------------------------------------- | -------------------------- |
101
+ | `POST /api-key/create`, `GET /api-key/list` | reachable |
102
+ | `POST /update-user` | reachable |
103
+ | `GET /list-sessions` | reachable |
104
+ | `POST /sign-out` | reachable |
105
+ | `POST /delete-user`, `/change-email`, `/revoke-sessions` | 401 |
106
+ | `POST /change-password` | requires `currentPassword` |
107
+
108
+ Two consequences to plan for:
109
+
110
+ - **A key mints keys.** The successor holds its own expiry and revocation, and
111
+ nothing links it to the key that made it. Revoking a leaked key means
112
+ reviewing the owner's whole list, not deleting the one key you know about.
113
+ - **`/list-sessions` returns the unsigned session token.** The cookie is
114
+ `token.signature`, so replaying the token alone does not authenticate; it
115
+ becomes a takeover only if the auth secret leaks too or cookie signing is
116
+ off.
117
+
118
+ An App that wants either closed adds its own Better Auth `before` hook
119
+ rejecting those paths when the API key header is present. Match on the header
120
+ rather than on the resolved Session, and place the hook before the plugin's
121
+ own, or the Session is minted and returned first.
122
+
123
+ ## Other constraints
124
+
125
+ - **A rejected key is answered with Better Auth's own status, and its code as `error.reason`** (domain `authentication`) in the standard error body, so a guarded route tells the caller why: `401 KEY_EXPIRED`, `401 KEY_NOT_FOUND`, `429 USAGE_EXCEEDED`. `getSession()` throws Better Auth's `APIError` for a
126
+ refused key, as Better Auth itself does; a caller that only asks who is
127
+ signed in catches it.
128
+ - **Disabling a user disables that user's keys immediately**, because
129
+ Authentication re-reads `user.disabledAt` on every request.
130
+ - **The key is shown once.** Only a hash is stored, alongside the first few
131
+ characters used to identify it in a list. A lost key is replaced, not
132
+ recovered.
133
+ - **A client may set only `name`, `expiresIn`, `prefix`, and `metadata`.**
134
+ Better Auth refuses `remaining`, `permissions`, and the rate-limit fields on
135
+ a request that carries headers, so an App that needs those sets them
136
+ server-side.
137
+ - `expiresIn` is seconds, validated in whole days between 1 and 365.
138
+
139
+ ## Ownership
140
+
141
+ The plugin owns the `apikey` table, the `@better-auth/api-key` version, the
142
+ three defaults, and the Settings page. The App owns its two `config/auth.ts`
143
+ files, any option it overrides there, where the page is mounted, and which
144
+ roles may reach it.
145
+
146
+ Never write the `apikey` table directly. Its `key` column holds a hash, and a
147
+ row inserted by hand authenticates nothing.
148
+
149
+ ## Verification
150
+
151
+ 1. Grant `page:api-keys/access`, sign in, open the Settings page, create a key,
152
+ and copy it.
153
+ 2. `curl -H 'x-api-key: <key>' <app>/api/<a route behind auth.required()>` —
154
+ expect the same response the signed-in user gets.
155
+ 3. Revoke the key in the page, repeat step 2 — expect 401, not 500.
156
+ 4. Disable the owning user, repeat step 2 with a second key — expect 401.
157
+
158
+ ## Trusted server extensions
159
+
160
+ `apiKey()` also accepts a configuration array, each with a unique `configId`. The three NocoBase defaults apply to each entry and remain overridable. A non-Session configuration must never share the default Session configuration’s identity. Verification checks the actual stored configuration, so presenting such a key in `x-api-key` does not grant its owner’s Session.
161
+
162
+ `new ApiKeyService(authentication, configId)` provides trusted database-backed `create({ userId, name, expiresIn? })`, `get(id)`, `verify(secret)`, `disable(id)` and `remove(id)` operations. Create returns `{ key, secret }` once; summaries never include the stored hash. Get, disable and remove are configuration-bound; disable and remove are repeatable. Missing configurations fail closed. The service uses the registered Better Auth configuration, endpoints and formal Authentication plugin API. Secondary storage is not supported by this service.
163
+
164
+ These methods are server-only capabilities, not authorization checks. Callers must authorize the actor, bind their own resources, and enforce scopes before allowing the operation. Keep application-specific rules outside this plugin. Applications that reserve a configuration for their own management API must also reject public Better Auth management requests for that configuration; otherwise its owner can update its enabled state or expiration through the self-service endpoints. Better Auth may prune expired rows during normal API Key operations.
165
+
166
+ Server calls run through `Auth.pluginApi()` and the normal Better Auth hooks; `withConnection(connection)` binds all credential writes to a caller-owned transaction. The added get/delete operations are declared with `createAuthEndpoint.serverOnly` and cannot be reached over HTTP.