@nocobase/app-plugin-api-keys 0.0.0-stage → 1.0.0-beta.14
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.
- package/CHANGELOG.md +468 -0
- package/README.md +162 -2
- package/dist/client/api-keys-client.d.ts +19 -0
- package/dist/client/api-keys-client.d.ts.map +1 -0
- package/dist/client/api-keys-client.js +7 -0
- package/dist/client/api-keys-client.js.map +1 -0
- package/dist/client/expiry.d.ts +23 -0
- package/dist/client/expiry.d.ts.map +1 -0
- package/dist/client/expiry.js +40 -0
- package/dist/client/expiry.js.map +1 -0
- package/dist/client/index.d.ts +4 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +6 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/plugin.d.ts +8 -0
- package/dist/client/plugin.d.ts.map +1 -0
- package/dist/client/plugin.js +10 -0
- package/dist/client/plugin.js.map +1 -0
- package/dist/database/migrations/.manifest.json +9 -0
- package/dist/database/migrations/202609150001_create_api_key_table.js +58 -0
- package/dist/database/migrations/202609150001_create_api_key_table.js.map +1 -0
- package/dist/server/api-docs.d.ts +24 -0
- package/dist/server/api-docs.d.ts.map +1 -0
- package/dist/server/api-docs.js +73 -0
- package/dist/server/api-docs.js.map +1 -0
- package/dist/server/api-keys.d.ts +52 -0
- package/dist/server/api-keys.d.ts.map +1 -0
- package/dist/server/api-keys.js +199 -0
- package/dist/server/api-keys.js.map +1 -0
- package/dist/server/index.d.ts +23 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +22 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/key-sessions.d.ts +35 -0
- package/dist/server/key-sessions.d.ts.map +1 -0
- package/dist/server/key-sessions.js +50 -0
- package/dist/server/key-sessions.js.map +1 -0
- package/dist/server/plugin.d.ts +4 -0
- package/dist/server/plugin.d.ts.map +1 -0
- package/dist/server/plugin.js +15 -0
- package/dist/server/plugin.js.map +1 -0
- package/dist/server/providers/index.d.ts +19 -0
- package/dist/server/providers/index.d.ts.map +1 -0
- package/dist/server/providers/index.js +72 -0
- package/dist/server/providers/index.js.map +1 -0
- package/dist/server/routes/errors.d.ts +8 -0
- package/dist/server/routes/errors.d.ts.map +1 -0
- package/dist/server/routes/errors.js +48 -0
- package/dist/server/routes/errors.js.map +1 -0
- package/dist/server/routes/index.d.ts +10 -0
- package/dist/server/routes/index.d.ts.map +1 -0
- package/dist/server/routes/index.js +177 -0
- package/dist/server/routes/index.js.map +1 -0
- package/dist/server/routes/schemas.d.ts +25 -0
- package/dist/server/routes/schemas.d.ts.map +1 -0
- package/dist/server/routes/schemas.js +127 -0
- package/dist/server/routes/schemas.js.map +1 -0
- package/dist/server/scoped-keys.d.ts +116 -0
- package/dist/server/scoped-keys.d.ts.map +1 -0
- package/dist/server/scoped-keys.js +451 -0
- package/dist/server/scoped-keys.js.map +1 -0
- package/dist/server/scopes.d.ts +70 -0
- package/dist/server/scopes.d.ts.map +1 -0
- package/dist/server/scopes.js +165 -0
- package/dist/server/scopes.js.map +1 -0
- package/dist/server/service.d.ts +28 -0
- package/dist/server/service.d.ts.map +1 -0
- package/dist/server/service.js +69 -0
- package/dist/server/service.js.map +1 -0
- package/dist/server/tokens.d.ts +6 -0
- package/dist/server/tokens.d.ts.map +1 -0
- package/dist/server/tokens.js +5 -0
- package/dist/server/tokens.js.map +1 -0
- package/dist/shared/scopes.d.ts +160 -0
- package/dist/shared/scopes.d.ts.map +1 -0
- package/dist/shared/scopes.js +83 -0
- package/dist/shared/scopes.js.map +1 -0
- package/package.json +76 -3
- package/skills/nocobase-app-plugin-api-keys/SKILL.md +161 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { apiKey as betterAuthApiKey, } from '@better-auth/api-key';
|
|
2
|
+
import { isServiceAccount } from '@nocobase/app-plugin-authentication/server';
|
|
3
|
+
import { APIError, createAuthEndpoint, createAuthMiddleware, getSessionFromCtx, } from 'better-auth/api';
|
|
4
|
+
import { isDeviceApprovalPath, isKeyManagementPath } from './key-sessions.js';
|
|
5
|
+
/**
|
|
6
|
+
* Where the plugin's provider puts the application's own-key policy (`ScopedApiKeys.setOwnKeyPolicy`), so the hook on
|
|
7
|
+
* Better Auth's own `/api-key/create` asks the same question `/api/apiKeys` does.
|
|
8
|
+
*/
|
|
9
|
+
export const OWN_KEY_POLICY_SLOT = Symbol.for('@nocobase/app-plugin-api-keys/own-key-policy');
|
|
10
|
+
/** The slot of a plugin `apiKey()` made, or undefined for any other plugin. */
|
|
11
|
+
export function ownKeyPolicySlotOf(plugin) {
|
|
12
|
+
if (typeof plugin !== 'object' || plugin === null)
|
|
13
|
+
return undefined;
|
|
14
|
+
return Reflect.get(plugin, OWN_KEY_POLICY_SLOT);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Better Auth's API Key plugin with the defaults a NocoBase application wants.
|
|
18
|
+
*
|
|
19
|
+
* `enableSessionForAPIKeys` is the one that has to be set: Better Auth
|
|
20
|
+
* defaults it off, and with it off a key authenticates nothing — the Settings
|
|
21
|
+
* page still issues keys, and every request carrying one answers 401.
|
|
22
|
+
*
|
|
23
|
+
* Every option is overridable, `enableSessionForAPIKeys` included, so passing
|
|
24
|
+
* all three back reproduces Better Auth's own behaviour exactly.
|
|
25
|
+
*/
|
|
26
|
+
function createApiKeysPlugin(options = {}) {
|
|
27
|
+
const configurations = (Array.isArray(options) ? options : [options]).map((configuration) => ({
|
|
28
|
+
enableSessionForAPIKeys: true,
|
|
29
|
+
rateLimit: { enabled: false },
|
|
30
|
+
requireName: true,
|
|
31
|
+
...configuration,
|
|
32
|
+
}));
|
|
33
|
+
const plugin = betterAuthApiKey(Array.isArray(options) ? configurations : configurations[0]);
|
|
34
|
+
const managedQuery = plugin.endpoints.getApiKey.options.query.partial();
|
|
35
|
+
const managedBody = plugin.endpoints.deleteApiKey.options.body;
|
|
36
|
+
function requireDatabaseConfiguration(configId) {
|
|
37
|
+
const id = configId ?? 'default';
|
|
38
|
+
const config = configurations.find((item) => (item.configId ?? 'default') === id);
|
|
39
|
+
if (!config)
|
|
40
|
+
throw new Error(`API key configuration "${id}" is not registered.`);
|
|
41
|
+
if (config.customStorage ||
|
|
42
|
+
(config.storage && config.storage !== 'database'))
|
|
43
|
+
throw new Error('Server API key management requires the default database schema and storage.');
|
|
44
|
+
return id;
|
|
45
|
+
}
|
|
46
|
+
const headers = [
|
|
47
|
+
...new Set(configurations.flatMap((configuration) => configuration.apiKeyHeaders === undefined
|
|
48
|
+
? ['x-api-key']
|
|
49
|
+
: Array.isArray(configuration.apiKeyHeaders)
|
|
50
|
+
? configuration.apiKeyHeaders
|
|
51
|
+
: [configuration.apiKeyHeaders])),
|
|
52
|
+
];
|
|
53
|
+
const policy = { current: null };
|
|
54
|
+
const presentedKey = (requestHeaders) => {
|
|
55
|
+
for (const header of headers) {
|
|
56
|
+
const value = requestHeaders?.get(header);
|
|
57
|
+
if (value)
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
};
|
|
62
|
+
const result = {
|
|
63
|
+
...plugin,
|
|
64
|
+
options: { configurations },
|
|
65
|
+
hooks: {
|
|
66
|
+
...plugin.hooks,
|
|
67
|
+
before: [
|
|
68
|
+
...plugin.hooks.before,
|
|
69
|
+
{
|
|
70
|
+
// Runs after Better Auth has turned the key into a session. No key manages keys: every `/api-key/*`
|
|
71
|
+
// endpoint takes a sign-in, and so does approving a device's sign-in (`/device/*`), which issues a session. A scoped key, and any key of a service account, reaches no account endpoint at
|
|
72
|
+
// all: it cannot change the profile, list sessions or sign out either. Only `/get-session` answers, before
|
|
73
|
+
// this hook runs.
|
|
74
|
+
matcher: (ctx) => presentedKey(ctx.headers) !== null,
|
|
75
|
+
handler: createAuthMiddleware(async (ctx) => {
|
|
76
|
+
const key = presentedKey(ctx.headers);
|
|
77
|
+
const session = ctx.context.session;
|
|
78
|
+
if (!key || !session || session.session.token !== key)
|
|
79
|
+
return;
|
|
80
|
+
if (isKeyManagementPath(ctx.path))
|
|
81
|
+
throw APIError.from('FORBIDDEN', {
|
|
82
|
+
code: 'API_KEY_SESSION_FORBIDDEN',
|
|
83
|
+
message: 'API keys are managed only from a signed-in session.',
|
|
84
|
+
});
|
|
85
|
+
if (isDeviceApprovalPath(ctx.path))
|
|
86
|
+
throw APIError.from('FORBIDDEN', {
|
|
87
|
+
code: 'API_KEY_SESSION_FORBIDDEN',
|
|
88
|
+
message: 'A device sign-in is approved only from a signed-in session.',
|
|
89
|
+
});
|
|
90
|
+
let scoped = isServiceAccount(session.user);
|
|
91
|
+
if (!scoped) {
|
|
92
|
+
const row = await ctx.context.adapter.findOne({
|
|
93
|
+
model: 'apikey',
|
|
94
|
+
where: [{ field: 'id', value: session.session.id }],
|
|
95
|
+
});
|
|
96
|
+
scoped = row?.permissions != null;
|
|
97
|
+
}
|
|
98
|
+
if (scoped)
|
|
99
|
+
throw APIError.from('FORBIDDEN', {
|
|
100
|
+
code: 'SCOPED_KEY_FORBIDDEN',
|
|
101
|
+
message: 'A scoped API key or a service-account key cannot use account endpoints.',
|
|
102
|
+
});
|
|
103
|
+
}),
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
// A person creating a key of their own from a sign-in: the application may say who may
|
|
107
|
+
// (`ScopedApiKeys.setOwnKeyPolicy`). A server call carries no session and is the caller's to authorize.
|
|
108
|
+
matcher: (ctx) => ctx.path === '/api-key/create',
|
|
109
|
+
handler: createAuthMiddleware(async (ctx) => {
|
|
110
|
+
const check = policy.current;
|
|
111
|
+
if (!check || presentedKey(ctx.headers) !== null)
|
|
112
|
+
return;
|
|
113
|
+
const session = await getSessionFromCtx(ctx);
|
|
114
|
+
if (session && !(await check(session.user.id)))
|
|
115
|
+
throw APIError.from('FORBIDDEN', {
|
|
116
|
+
code: 'API_KEY_CREATION_FORBIDDEN',
|
|
117
|
+
message: 'You may not create API keys of your own.',
|
|
118
|
+
});
|
|
119
|
+
}),
|
|
120
|
+
},
|
|
121
|
+
// The added hooks answer nothing, so they fit the upstream list, whose type names only what Better Auth's hook
|
|
122
|
+
// returns.
|
|
123
|
+
],
|
|
124
|
+
},
|
|
125
|
+
endpoints: {
|
|
126
|
+
...plugin.endpoints,
|
|
127
|
+
getServerApiKey: createAuthEndpoint.serverOnly({ method: 'GET', query: managedQuery }, async (ctx) => {
|
|
128
|
+
const configId = requireDatabaseConfiguration(ctx.query.configId);
|
|
129
|
+
if (!ctx.query.id)
|
|
130
|
+
return null;
|
|
131
|
+
const row = await ctx.context.adapter.findOne({
|
|
132
|
+
model: 'apikey',
|
|
133
|
+
where: [
|
|
134
|
+
{ field: 'id', value: ctx.query.id },
|
|
135
|
+
{ field: 'configId', value: configId },
|
|
136
|
+
],
|
|
137
|
+
});
|
|
138
|
+
if (!row)
|
|
139
|
+
return null;
|
|
140
|
+
const { key: _hash, ...key } = row;
|
|
141
|
+
return {
|
|
142
|
+
...key,
|
|
143
|
+
permissions: typeof key.permissions === 'string'
|
|
144
|
+
? JSON.parse(key.permissions)
|
|
145
|
+
: (key.permissions ?? null),
|
|
146
|
+
};
|
|
147
|
+
}),
|
|
148
|
+
deleteServerApiKey: createAuthEndpoint.serverOnly({ method: 'POST', body: managedBody }, async (ctx) => {
|
|
149
|
+
const configId = requireDatabaseConfiguration(ctx.body.configId);
|
|
150
|
+
await ctx.context.adapter.deleteMany({
|
|
151
|
+
model: 'apikey',
|
|
152
|
+
where: [
|
|
153
|
+
{ field: 'id', value: ctx.body.keyId },
|
|
154
|
+
{ field: 'configId', value: configId },
|
|
155
|
+
],
|
|
156
|
+
});
|
|
157
|
+
return { success: true };
|
|
158
|
+
}),
|
|
159
|
+
},
|
|
160
|
+
};
|
|
161
|
+
Object.defineProperty(result, OWN_KEY_POLICY_SLOT, { value: policy });
|
|
162
|
+
return result;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* The API key a request carries, read the way the plugin reads it to authenticate a request: from the header each
|
|
166
|
+
* configuration that turns keys into sessions names, `x-api-key` unless configured. A configuration with a
|
|
167
|
+
* `customAPIKeyGetter` is skipped, because that getter needs a Better Auth endpoint context this caller does not have.
|
|
168
|
+
*/
|
|
169
|
+
export function findRequestApiKey(plugin, headers) {
|
|
170
|
+
for (const name of requestApiKeyHeaders(plugin)) {
|
|
171
|
+
const value = headers.get(name);
|
|
172
|
+
if (value)
|
|
173
|
+
return value;
|
|
174
|
+
}
|
|
175
|
+
return undefined;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* The headers a request may carry its API key in, in the order `findRequestApiKey` reads them: those of each
|
|
179
|
+
* configuration that turns keys into sessions, `x-api-key` unless configured, without one that has a
|
|
180
|
+
* `customAPIKeyGetter`.
|
|
181
|
+
*/
|
|
182
|
+
export function requestApiKeyHeaders(plugin) {
|
|
183
|
+
const headers = [];
|
|
184
|
+
for (const configuration of plugin.options.configurations) {
|
|
185
|
+
if (!configuration.enableSessionForAPIKeys)
|
|
186
|
+
continue;
|
|
187
|
+
if (configuration.customAPIKeyGetter)
|
|
188
|
+
continue;
|
|
189
|
+
const names = configuration.apiKeyHeaders ?? 'x-api-key';
|
|
190
|
+
for (const name of Array.isArray(names) ? names : [names])
|
|
191
|
+
if (!headers.includes(name))
|
|
192
|
+
headers.push(name);
|
|
193
|
+
}
|
|
194
|
+
return headers;
|
|
195
|
+
}
|
|
196
|
+
export function apiKey(options = {}) {
|
|
197
|
+
return createApiKeysPlugin(options);
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=api-keys.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-keys.js","sourceRoot":"","sources":["../../server/api-keys.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export { default } from './plugin.js';
|
|
2
|
+
/**
|
|
3
|
+
* Better Auth's API Key plugin with NocoBase defaults and trusted server operations.
|
|
4
|
+
*
|
|
5
|
+
* It carries Better Auth's name, options, and behaviour, so Better Auth's
|
|
6
|
+
* documentation applies unchanged. It is exported from here rather than
|
|
7
|
+
* installed separately by each application because this package ships the
|
|
8
|
+
* `apikey` table migration, and that table has to match the schema this
|
|
9
|
+
* version of `@better-auth/api-key` declares.
|
|
10
|
+
*/
|
|
11
|
+
export { apiKey, findRequestApiKey, OWN_KEY_POLICY_SLOT, ownKeyPolicySlotOf, type ApiKeysPlugin, type ApiKeysPluginOptions, type OwnKeyPolicySlot, } from './api-keys.js';
|
|
12
|
+
export { createApiKeyApiDocsAccess } from './api-docs.js';
|
|
13
|
+
export { API_KEY_ERROR_CODES, API_KEY_TABLE_NAME } from '@better-auth/api-key';
|
|
14
|
+
export type { ApiKey, ApiKeyConfigurationOptions, ApiKeyOptions, } from '@better-auth/api-key';
|
|
15
|
+
export { ApiKeyService, removeUserApiKeys, type ServerApiKeySummary, type CreateServerApiKeyInput, } from './service.js';
|
|
16
|
+
export { ApiKeyScopeError, createApiKeyScopes, type AccessMapping, type MappedAccess, type ApiKeyScopes, type KeyScopeObjectSource, } from './scopes.js';
|
|
17
|
+
export { API_KEY_HEADER, DEVICE_APPROVAL_PATHS, KEY_MANAGEMENT_PATH_PREFIXES, isApiKeySession, isDeviceApprovalPath, isKeyManagementPath, requireSignInSession, } from './key-sessions.js';
|
|
18
|
+
export { ApiKeyRequestError, connectOwnKeyPolicy, DEFAULT_SCOPED_KEY_DAYS, ScopedApiKeys, type ApiKeysConfig, type CheckedApiKey, type OwnKeyPolicy, type ScopedApiKeysOptions, } from './scoped-keys.js';
|
|
19
|
+
export { apiKeyScopesToken, scopedApiKeysToken } from './tokens.js';
|
|
20
|
+
export { API_KEYS_ERROR_DOMAIN, toApiKeysApiError } from './routes/errors.js';
|
|
21
|
+
export { ApiKeysProvider } from './providers/index.js';
|
|
22
|
+
export * from '../shared/scopes.js';
|
|
23
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtC;;;;;;;;GAQG;AACH,OAAO,EACL,MAAM,EACN,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,KAAK,aAAa,EAClB,KAAK,oBAAoB,EACzB,KAAK,gBAAgB,GACtB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAE1D,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAE/E,YAAY,EACV,MAAM,EACN,0BAA0B,EAC1B,aAAa,GACd,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,GAC7B,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,oBAAoB,GAC1B,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,4BAA4B,EAC5B,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,uBAAuB,EACvB,aAAa,EACb,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,oBAAoB,GAC1B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,cAAc,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export { default } from './plugin.js';
|
|
2
|
+
/**
|
|
3
|
+
* Better Auth's API Key plugin with NocoBase defaults and trusted server operations.
|
|
4
|
+
*
|
|
5
|
+
* It carries Better Auth's name, options, and behaviour, so Better Auth's
|
|
6
|
+
* documentation applies unchanged. It is exported from here rather than
|
|
7
|
+
* installed separately by each application because this package ships the
|
|
8
|
+
* `apikey` table migration, and that table has to match the schema this
|
|
9
|
+
* version of `@better-auth/api-key` declares.
|
|
10
|
+
*/
|
|
11
|
+
export { apiKey, findRequestApiKey, OWN_KEY_POLICY_SLOT, ownKeyPolicySlotOf, } from './api-keys.js';
|
|
12
|
+
export { createApiKeyApiDocsAccess } from './api-docs.js';
|
|
13
|
+
export { API_KEY_ERROR_CODES, API_KEY_TABLE_NAME } from '@better-auth/api-key';
|
|
14
|
+
export { ApiKeyService, removeUserApiKeys, } from './service.js';
|
|
15
|
+
export { ApiKeyScopeError, createApiKeyScopes, } from './scopes.js';
|
|
16
|
+
export { API_KEY_HEADER, DEVICE_APPROVAL_PATHS, KEY_MANAGEMENT_PATH_PREFIXES, isApiKeySession, isDeviceApprovalPath, isKeyManagementPath, requireSignInSession, } from './key-sessions.js';
|
|
17
|
+
export { ApiKeyRequestError, connectOwnKeyPolicy, DEFAULT_SCOPED_KEY_DAYS, ScopedApiKeys, } from './scoped-keys.js';
|
|
18
|
+
export { apiKeyScopesToken, scopedApiKeysToken } from './tokens.js';
|
|
19
|
+
export { API_KEYS_ERROR_DOMAIN, toApiKeysApiError } from './routes/errors.js';
|
|
20
|
+
export { ApiKeysProvider } from './providers/index.js';
|
|
21
|
+
export * from '../shared/scopes.js';
|
|
22
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtC;;;;;;;;GAQG;AACH,OAAO,EACL,MAAM,EACN,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,GAInB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAE1D,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAO/E,OAAO,EACL,aAAa,EACb,iBAAiB,GAGlB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,gBAAgB,EAChB,kBAAkB,GAKnB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,4BAA4B,EAC5B,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACnB,oBAAoB,GACrB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACnB,uBAAuB,EACvB,aAAa,GAKd,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,cAAc,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telling a session made from an API key apart from a sign-in. Managing API keys takes a sign-in: no key, scoped or
|
|
3
|
+
* not, creates, rotates or revokes keys, so a leaked key cannot mint its own successors or remove the evidence. For the
|
|
4
|
+
* same reason no key approves another device's sign-in (Better Auth's device authorization), which issues a session.
|
|
5
|
+
*/
|
|
6
|
+
import type { AuthEnv } from '@nocobase/app-plugin-authentication/server';
|
|
7
|
+
import type { MiddlewareHandler } from 'hono';
|
|
8
|
+
/** The header a key arrives in; Better Auth's default `apiKeyHeaders`. */
|
|
9
|
+
export declare const API_KEY_HEADER = "x-api-key";
|
|
10
|
+
/** The Better Auth endpoints no API key may reach, whatever its scope: every `/api-key/*` endpoint. */
|
|
11
|
+
export declare const KEY_MANAGEMENT_PATH_PREFIXES: readonly string[];
|
|
12
|
+
/** Whether a Better Auth endpoint path manages API keys. */
|
|
13
|
+
export declare function isKeyManagementPath(path: string): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Whether a session was made from an API key: Better Auth's key session carries the key as its token, which a
|
|
16
|
+
* sign-in session never matches.
|
|
17
|
+
*/
|
|
18
|
+
export declare function isApiKeySession(session: {
|
|
19
|
+
readonly session: {
|
|
20
|
+
readonly token: string;
|
|
21
|
+
};
|
|
22
|
+
}, headers: Headers, headerNames?: readonly string[]): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* The Better Auth endpoints that review, approve or deny a device authorization (`deviceAuthorization()`): approving
|
|
25
|
+
* issues the device a session, so no API key reaches them.
|
|
26
|
+
*/
|
|
27
|
+
export declare const DEVICE_APPROVAL_PATHS: readonly string[];
|
|
28
|
+
/** Whether a Better Auth endpoint path reviews, approves or denies a device authorization. */
|
|
29
|
+
export declare function isDeviceApprovalPath(path: string): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Refuses a request whose session came from an API key with 403 `API_KEY_SESSION_FORBIDDEN`. Install after
|
|
32
|
+
* `auth.required()` on every route that manages keys, a service account's included.
|
|
33
|
+
*/
|
|
34
|
+
export declare function requireSignInSession(): MiddlewareHandler<AuthEnv>;
|
|
35
|
+
//# sourceMappingURL=key-sessions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"key-sessions.d.ts","sourceRoot":"","sources":["../../server/key-sessions.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,4CAA4C,CAAC;AAE1E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAE9C,0EAA0E;AAC1E,eAAO,MAAM,cAAc,cAAc,CAAC;AAE1C,uGAAuG;AACvG,eAAO,MAAM,4BAA4B,EAAE,SAAS,MAAM,EAAkB,CAAC;AAE7E,4DAA4D;AAC5D,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE;IAAE,QAAQ,CAAC,OAAO,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,EACzD,OAAO,EAAE,OAAO,EAChB,WAAW,GAAE,SAAS,MAAM,EAAqB,GAChD,OAAO,CAKT;AAED;;;GAGG;AACH,eAAO,MAAM,qBAAqB,EAAE,SAAS,MAAM,EAIlD,CAAC;AAEF,8FAA8F;AAC9F,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,iBAAiB,CAAC,OAAO,CAAC,CAejE"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { ApiError, apiErrorHandler } from '@nocobase/app-server/router';
|
|
2
|
+
/** The header a key arrives in; Better Auth's default `apiKeyHeaders`. */
|
|
3
|
+
export const API_KEY_HEADER = 'x-api-key';
|
|
4
|
+
/** The Better Auth endpoints no API key may reach, whatever its scope: every `/api-key/*` endpoint. */
|
|
5
|
+
export const KEY_MANAGEMENT_PATH_PREFIXES = ['/api-key/'];
|
|
6
|
+
/** Whether a Better Auth endpoint path manages API keys. */
|
|
7
|
+
export function isKeyManagementPath(path) {
|
|
8
|
+
return KEY_MANAGEMENT_PATH_PREFIXES.some((prefix) => path.startsWith(prefix));
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Whether a session was made from an API key: Better Auth's key session carries the key as its token, which a
|
|
12
|
+
* sign-in session never matches.
|
|
13
|
+
*/
|
|
14
|
+
export function isApiKeySession(session, headers, headerNames = [API_KEY_HEADER]) {
|
|
15
|
+
return headerNames.some((name) => {
|
|
16
|
+
const key = headers.get(name);
|
|
17
|
+
return Boolean(key) && session.session.token === key;
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The Better Auth endpoints that review, approve or deny a device authorization (`deviceAuthorization()`): approving
|
|
22
|
+
* issues the device a session, so no API key reaches them.
|
|
23
|
+
*/
|
|
24
|
+
export const DEVICE_APPROVAL_PATHS = [
|
|
25
|
+
'/device',
|
|
26
|
+
'/device/approve',
|
|
27
|
+
'/device/deny',
|
|
28
|
+
];
|
|
29
|
+
/** Whether a Better Auth endpoint path reviews, approves or denies a device authorization. */
|
|
30
|
+
export function isDeviceApprovalPath(path) {
|
|
31
|
+
return DEVICE_APPROVAL_PATHS.includes(path);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Refuses a request whose session came from an API key with 403 `API_KEY_SESSION_FORBIDDEN`. Install after
|
|
35
|
+
* `auth.required()` on every route that manages keys, a service account's included.
|
|
36
|
+
*/
|
|
37
|
+
export function requireSignInSession() {
|
|
38
|
+
return async (context, next) => {
|
|
39
|
+
const auth = context.get('auth');
|
|
40
|
+
if (auth && isApiKeySession(auth, context.req.raw.headers))
|
|
41
|
+
return apiErrorHandler(new ApiError({
|
|
42
|
+
status: 'PERMISSION_DENIED',
|
|
43
|
+
reason: 'API_KEY_SESSION_FORBIDDEN',
|
|
44
|
+
domain: 'apiKeys',
|
|
45
|
+
message: 'API keys are managed only from a signed-in session.',
|
|
46
|
+
}), context);
|
|
47
|
+
await next();
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=key-sessions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"key-sessions.js","sourceRoot":"","sources":["../../server/key-sessions.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAGxE,0EAA0E;AAC1E,MAAM,CAAC,MAAM,cAAc,GAAG,WAAW,CAAC;AAE1C,uGAAuG;AACvG,MAAM,CAAC,MAAM,4BAA4B,GAAsB,CAAC,WAAW,CAAC,CAAC;AAE7E,4DAA4D;AAC5D,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,OAAO,4BAA4B,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;AAChF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC7B,OAAyD,EACzD,OAAgB,EAChB,cAAiC,CAAC,cAAc,CAAC;IAEjD,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;QAC/B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC9B,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,KAAK,KAAK,GAAG,CAAC;IACvD,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAsB;IACtD,SAAS;IACT,iBAAiB;IACjB,cAAc;CACf,CAAC;AAEF,8FAA8F;AAC9F,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,OAAO,qBAAqB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AAC9C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB;IAClC,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;QAC7B,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACjC,IAAI,IAAI,IAAI,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC;YACxD,OAAO,eAAe,CACpB,IAAI,QAAQ,CAAC;gBACX,MAAM,EAAE,mBAAmB;gBAC3B,MAAM,EAAE,2BAA2B;gBACnC,MAAM,EAAE,SAAS;gBACjB,OAAO,EAAE,qDAAqD;aAC/D,CAAC,EACF,OAAO,CACR,CAAC;QACJ,MAAM,IAAI,EAAE,CAAC;IACf,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../server/plugin.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,8BAA8B,CAAC;AAKtC,QAAA,MAAM,aAAa,EAAE,eAQnB,CAAC;AAEH,eAAe,aAAa,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { defineServerPlugin, } from '@nocobase/app-server/plugins';
|
|
3
|
+
import serviceProviders from './providers/index.js';
|
|
4
|
+
import routes from './routes/index.js';
|
|
5
|
+
const apiKeysPlugin = defineServerPlugin({
|
|
6
|
+
baseDir: path.resolve(import.meta.dirname, '..'),
|
|
7
|
+
packageName: '@nocobase/app-plugin-api-keys',
|
|
8
|
+
serviceProviders,
|
|
9
|
+
routes,
|
|
10
|
+
database: {
|
|
11
|
+
migrations: './database/migrations',
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
export default apiKeysPlugin;
|
|
15
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../server/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EACL,kBAAkB,GAEnB,MAAM,8BAA8B,CAAC;AAEtC,OAAO,gBAAgB,MAAM,sBAAsB,CAAC;AACpD,OAAO,MAAM,MAAM,mBAAmB,CAAC;AAEvC,MAAM,aAAa,GAAoB,kBAAkB,CAAC;IACxD,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC;IAChD,WAAW,EAAE,+BAA+B;IAC5C,gBAAgB;IAChB,MAAM;IACN,QAAQ,EAAE;QACR,UAAU,EAAE,uBAAuB;KACpC;CACF,CAAC,CAAC;AAEH,eAAe,aAAa,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { AppPluginApplication, AppPluginProviderConstructor } from '@nocobase/app-server/plugins';
|
|
2
|
+
import { ServiceProvider } from '@nocobase/service-provider';
|
|
3
|
+
/**
|
|
4
|
+
* Binds the key-scope registry and the scoped-key service, and at boot teaches authentication and authorization to
|
|
5
|
+
* recognize a scoped key: `auth.required()` refuses one unless the route opts in, and the authorization identity of a
|
|
6
|
+
* request made with one carries its `keyScope`. Also lets a request carrying a valid API key read the application's API
|
|
7
|
+
* documentation, and adds the API key header to the document as a security scheme.
|
|
8
|
+
*/
|
|
9
|
+
export declare class ApiKeysProvider extends ServiceProvider<AppPluginApplication> {
|
|
10
|
+
readonly name: string;
|
|
11
|
+
private readonly releases;
|
|
12
|
+
private booted;
|
|
13
|
+
register(): void;
|
|
14
|
+
boot(): Promise<void>;
|
|
15
|
+
shutdown(): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
export declare const serviceProviders: readonly AppPluginProviderConstructor[];
|
|
18
|
+
export default serviceProviders;
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../server/providers/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,oBAAoB,EACpB,4BAA4B,EAC7B,MAAM,8BAA8B,CAAC;AAGtC,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAc7D;;;;;GAKG;AACH,qBAAa,eAAgB,SAAQ,eAAe,CAAC,oBAAoB,CAAC;IACxE,SAAgB,IAAI,EAAE,MAAM,CAAmC;IAC/D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsB;IAC/C,OAAO,CAAC,MAAM,CAAS;IAEP,QAAQ,IAAI,IAAI;IAmBV,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAuC3B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAI1C;AAED,eAAO,MAAM,gBAAgB,EAAE,SAAS,4BAA4B,EAEnE,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { authenticationToken } from '@nocobase/app-plugin-authentication/server';
|
|
2
|
+
import { authorizationToken } from '@nocobase/app-plugin-authorization';
|
|
3
|
+
import { apiDocsToken } from '@nocobase/app-server/router';
|
|
4
|
+
import { databaseManagerToken } from '@nocobase/db';
|
|
5
|
+
import { ServiceProvider } from '@nocobase/service-provider';
|
|
6
|
+
import { createApiKeyApiDocsAccess, createApiKeySecurityFragment, } from '../api-docs.js';
|
|
7
|
+
import { connectOwnKeyPolicy, ScopedApiKeys, } from '../scoped-keys.js';
|
|
8
|
+
import { apiKeyScopesToken, createApiKeyScopes } from '../scopes.js';
|
|
9
|
+
import { scopedApiKeysToken } from '../tokens.js';
|
|
10
|
+
/**
|
|
11
|
+
* Binds the key-scope registry and the scoped-key service, and at boot teaches authentication and authorization to
|
|
12
|
+
* recognize a scoped key: `auth.required()` refuses one unless the route opts in, and the authorization identity of a
|
|
13
|
+
* request made with one carries its `keyScope`. Also lets a request carrying a valid API key read the application's API
|
|
14
|
+
* documentation, and adds the API key header to the document as a security scheme.
|
|
15
|
+
*/
|
|
16
|
+
export class ApiKeysProvider extends ServiceProvider {
|
|
17
|
+
name = '@nocobase/app-plugin-api-keys';
|
|
18
|
+
releases = [];
|
|
19
|
+
booted = false;
|
|
20
|
+
register() {
|
|
21
|
+
const { container } = this.app;
|
|
22
|
+
container.singleton(apiKeyScopesToken, () => createApiKeyScopes());
|
|
23
|
+
container.singleton(scopedApiKeysToken, (resolver) => new ScopedApiKeys({
|
|
24
|
+
auth: resolver.resolve(authenticationToken),
|
|
25
|
+
connection: () => resolver.resolve(databaseManagerToken).connection(),
|
|
26
|
+
scopes: resolver.resolve(apiKeyScopesToken),
|
|
27
|
+
authorization: () => container.has(authorizationToken)
|
|
28
|
+
? resolver.resolve(authorizationToken)
|
|
29
|
+
: undefined,
|
|
30
|
+
config: () => this.app.config.get('apiKeys'),
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
async boot() {
|
|
34
|
+
const { container } = this.app;
|
|
35
|
+
if (this.booted ||
|
|
36
|
+
!container.has(authenticationToken) ||
|
|
37
|
+
!container.has(databaseManagerToken))
|
|
38
|
+
return;
|
|
39
|
+
this.booted = true;
|
|
40
|
+
if (container.has(apiDocsToken)) {
|
|
41
|
+
const apiDocs = container.resolve(apiDocsToken);
|
|
42
|
+
const resolveAuth = () => container.resolve(authenticationToken);
|
|
43
|
+
apiDocs.addAccess(createApiKeyApiDocsAccess(resolveAuth));
|
|
44
|
+
// Resolved on the first request for the document, like the access check, so starting the application builds nothing.
|
|
45
|
+
apiDocs.addFragment(() => createApiKeySecurityFragment(resolveAuth));
|
|
46
|
+
}
|
|
47
|
+
const keys = container.resolve(scopedApiKeysToken);
|
|
48
|
+
const auth = container.resolve(authenticationToken);
|
|
49
|
+
this.releases.push(await connectOwnKeyPolicy(auth, keys));
|
|
50
|
+
this.releases.push(auth.addScopedCredentialCheck(async (session, request) => (await keys.resolve(session, request)) !== null));
|
|
51
|
+
if (container.has(authorizationToken))
|
|
52
|
+
container.resolve(authorizationToken).use(async (request, next) => {
|
|
53
|
+
const session = request.http.get('auth');
|
|
54
|
+
if (session && typeof session === 'object' && 'session' in session) {
|
|
55
|
+
const scope = await keys.resolve(session, request.http.req.raw);
|
|
56
|
+
if (scope)
|
|
57
|
+
request.keyScope = scope;
|
|
58
|
+
}
|
|
59
|
+
await next();
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
shutdown() {
|
|
63
|
+
for (const release of this.releases.splice(0))
|
|
64
|
+
release();
|
|
65
|
+
return Promise.resolve();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
export const serviceProviders = [
|
|
69
|
+
ApiKeysProvider,
|
|
70
|
+
];
|
|
71
|
+
export default serviceProviders;
|
|
72
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/providers/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** The error domain of the API keys plugin: its URL namespace. */
|
|
2
|
+
export declare const API_KEYS_ERROR_DOMAIN = "apiKeys";
|
|
3
|
+
/**
|
|
4
|
+
* The plugin's own errors as `ApiError`, domain `apiKeys`, with their code as the reason; anything else is returned
|
|
5
|
+
* unchanged for `apiErrorHandler`. Exported so an application's own key routes answer the same way.
|
|
6
|
+
*/
|
|
7
|
+
export declare function toApiKeysApiError(error: unknown): unknown;
|
|
8
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../server/routes/errors.ts"],"names":[],"mappings":"AAKA,kEAAkE;AAClE,eAAO,MAAM,qBAAqB,YAAY,CAAC;AAe/C;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAwBzD"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { ApiError } from '@nocobase/app-server/router';
|
|
2
|
+
import { ApiKeyRequestError } from '../scoped-keys.js';
|
|
3
|
+
import { ApiKeyScopeError } from '../scopes.js';
|
|
4
|
+
/** The error domain of the API keys plugin: its URL namespace. */
|
|
5
|
+
export const API_KEYS_ERROR_DOMAIN = 'apiKeys';
|
|
6
|
+
const FIELD_OF = {
|
|
7
|
+
EXPIRY_REQUIRED: 'expiresInDays',
|
|
8
|
+
INVALID_EXPIRY: 'expiresInDays',
|
|
9
|
+
};
|
|
10
|
+
function statusOf(error) {
|
|
11
|
+
if (error.status === 403)
|
|
12
|
+
return 'PERMISSION_DENIED';
|
|
13
|
+
if (error.status === 404)
|
|
14
|
+
return 'NOT_FOUND';
|
|
15
|
+
return error.code === 'KEY_NOT_SCOPED'
|
|
16
|
+
? 'FAILED_PRECONDITION'
|
|
17
|
+
: 'INVALID_ARGUMENT';
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* The plugin's own errors as `ApiError`, domain `apiKeys`, with their code as the reason; anything else is returned
|
|
21
|
+
* unchanged for `apiErrorHandler`. Exported so an application's own key routes answer the same way.
|
|
22
|
+
*/
|
|
23
|
+
export function toApiKeysApiError(error) {
|
|
24
|
+
if (error instanceof ApiKeyScopeError)
|
|
25
|
+
return new ApiError({
|
|
26
|
+
status: 'INVALID_ARGUMENT',
|
|
27
|
+
reason: error.code,
|
|
28
|
+
domain: API_KEYS_ERROR_DOMAIN,
|
|
29
|
+
message: error.message,
|
|
30
|
+
fieldViolations: [{ field: 'scope', description: error.message }],
|
|
31
|
+
cause: error,
|
|
32
|
+
});
|
|
33
|
+
if (error instanceof ApiKeyRequestError) {
|
|
34
|
+
const field = FIELD_OF[error.code];
|
|
35
|
+
return new ApiError({
|
|
36
|
+
status: statusOf(error),
|
|
37
|
+
reason: error.code,
|
|
38
|
+
domain: API_KEYS_ERROR_DOMAIN,
|
|
39
|
+
message: error.message,
|
|
40
|
+
...(field
|
|
41
|
+
? { fieldViolations: [{ field, description: error.message }] }
|
|
42
|
+
: {}),
|
|
43
|
+
cause: error,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
return error;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../server/routes/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAuB,MAAM,6BAA6B,CAAC;AAE5E,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhD,kEAAkE;AAClE,MAAM,CAAC,MAAM,qBAAqB,GAAG,SAAS,CAAC;AAE/C,MAAM,QAAQ,GAAqC;IACjD,eAAe,EAAE,eAAe;IAChC,cAAc,EAAE,eAAe;CAChC,CAAC;AAEF,SAAS,QAAQ,CAAC,KAAyB;IACzC,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG;QAAE,OAAO,mBAAmB,CAAC;IACrD,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG;QAAE,OAAO,WAAW,CAAC;IAC7C,OAAO,KAAK,CAAC,IAAI,KAAK,gBAAgB;QACpC,CAAC,CAAC,qBAAqB;QACvB,CAAC,CAAC,kBAAkB,CAAC;AACzB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAc;IAC9C,IAAI,KAAK,YAAY,gBAAgB;QACnC,OAAO,IAAI,QAAQ,CAAC;YAClB,MAAM,EAAE,kBAAkB;YAC1B,MAAM,EAAE,KAAK,CAAC,IAAI;YAClB,MAAM,EAAE,qBAAqB;YAC7B,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,eAAe,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;YACjE,KAAK,EAAE,KAAK;SACb,CAAC,CAAC;IACL,IAAI,KAAK,YAAY,kBAAkB,EAAE,CAAC;QACxC,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACnC,OAAO,IAAI,QAAQ,CAAC;YAClB,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC;YACvB,MAAM,EAAE,KAAK,CAAC,IAAI;YAClB,MAAM,EAAE,qBAAqB;YAC7B,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,GAAG,CAAC,KAAK;gBACP,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,EAAE;gBAC9D,CAAC,CAAC,EAAE,CAAC;YACP,KAAK,EAAE,KAAK;SACb,CAAC,CAAC;IACL,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AppPluginApplication } from '@nocobase/app-server/plugins';
|
|
2
|
+
import { type AppApiRouteContribution, type AppRouteContribution } from '@nocobase/app-server/router';
|
|
3
|
+
/**
|
|
4
|
+
* `/api/apiKeys`: a person's own keys, with or without a scope. Self-service: every endpoint acts on the caller's
|
|
5
|
+
* keys only, and only from a sign-in: a request made with any API key is refused (`requireSignInSession`).
|
|
6
|
+
*/
|
|
7
|
+
export declare const apiRoutes: AppApiRouteContribution<AppPluginApplication>;
|
|
8
|
+
declare const routes: readonly AppRouteContribution<AppPluginApplication>[];
|
|
9
|
+
export default routes;
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../server/routes/index.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACzE,OAAO,EAWL,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,EAC1B,MAAM,6BAA6B,CAAC;AAqCrC;;;GAGG;AACH,eAAO,MAAM,SAAS,EAAE,uBAAuB,CAAC,oBAAoB,CA+MhE,CAAC;AAEL,QAAA,MAAM,MAAM,EAAE,SAAS,oBAAoB,CAAC,oBAAoB,CAAC,EAEhE,CAAC;AAEF,eAAe,MAAM,CAAC"}
|