@selvajs/server 1.5.0-beta.5 → 1.5.0-beta.7

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 (64) hide show
  1. package/README.md +1 -1
  2. package/dist/api/bodies.d.ts +0 -2
  3. package/dist/api/bodies.d.ts.map +1 -1
  4. package/dist/api/idempotency-http.d.ts.map +1 -0
  5. package/dist/api/idempotency-http.js.map +1 -0
  6. package/dist/api/idempotency.d.ts +7 -7
  7. package/dist/api/idempotency.d.ts.map +1 -1
  8. package/dist/api/idempotency.js +8 -8
  9. package/dist/api/idempotency.js.map +1 -1
  10. package/dist/api/respond.js +1 -1
  11. package/dist/api/respond.js.map +1 -1
  12. package/dist/api/scope.d.ts +3 -0
  13. package/dist/api/scope.d.ts.map +1 -1
  14. package/dist/api/scope.js +8 -2
  15. package/dist/api/scope.js.map +1 -1
  16. package/dist/compute/index.d.ts +0 -1
  17. package/dist/compute/index.d.ts.map +1 -1
  18. package/dist/compute/index.js +0 -1
  19. package/dist/compute/index.js.map +1 -1
  20. package/dist/handlers/apiTokens.d.ts +13 -3
  21. package/dist/handlers/apiTokens.d.ts.map +1 -1
  22. package/dist/handlers/apiTokens.js +25 -18
  23. package/dist/handlers/apiTokens.js.map +1 -1
  24. package/dist/handlers/definitions.d.ts.map +1 -1
  25. package/dist/handlers/definitions.js +4 -2
  26. package/dist/handlers/definitions.js.map +1 -1
  27. package/dist/handlers/index.d.ts +1 -1
  28. package/dist/handlers/index.d.ts.map +1 -1
  29. package/dist/handlers/index.js +1 -1
  30. package/dist/handlers/index.js.map +1 -1
  31. package/dist/handlers/me.d.ts.map +1 -1
  32. package/dist/handlers/me.js +5 -1
  33. package/dist/handlers/me.js.map +1 -1
  34. package/dist/http/api-token.d.ts +5 -6
  35. package/dist/http/api-token.d.ts.map +1 -1
  36. package/dist/http/api-token.js +6 -8
  37. package/dist/http/api-token.js.map +1 -1
  38. package/dist/http/csrf.d.ts +18 -0
  39. package/dist/http/csrf.d.ts.map +1 -0
  40. package/dist/http/csrf.js +37 -0
  41. package/dist/http/csrf.js.map +1 -0
  42. package/dist/http/index.d.ts +1 -0
  43. package/dist/http/index.d.ts.map +1 -1
  44. package/dist/http/index.js +1 -0
  45. package/dist/http/index.js.map +1 -1
  46. package/dist/oauth/index.d.ts +135 -0
  47. package/dist/oauth/index.d.ts.map +1 -0
  48. package/dist/oauth/index.js +415 -0
  49. package/dist/oauth/index.js.map +1 -0
  50. package/dist/tokens/api-token-codec.d.ts +2 -2
  51. package/dist/tokens/api-token-codec.js +2 -2
  52. package/dist/tokens/api-token-holder.d.ts +8 -6
  53. package/dist/tokens/api-token-holder.d.ts.map +1 -1
  54. package/dist/tokens/api-token-holder.js +5 -1
  55. package/dist/tokens/api-token-holder.js.map +1 -1
  56. package/dist/tokens/index.d.ts +1 -1
  57. package/dist/tokens/index.d.ts.map +1 -1
  58. package/dist/tokens/index.js +1 -0
  59. package/dist/tokens/index.js.map +1 -1
  60. package/package.json +9 -4
  61. package/dist/compute/idempotency-http.d.ts.map +0 -1
  62. package/dist/compute/idempotency-http.js.map +0 -1
  63. /package/dist/{compute → api}/idempotency-http.d.ts +0 -0
  64. /package/dist/{compute → api}/idempotency-http.js +0 -0
@@ -0,0 +1,415 @@
1
+ /**
2
+ * OAuth 2.1 for MCP clients, with this instance as its own authorization server.
3
+ *
4
+ * The access token is an ordinary API token. The host mints it while the user is
5
+ * signed in, at consent, through the same path as Settings → API tokens; this
6
+ * module seals the raw key into the authorization code and hands it over at the
7
+ * token endpoint. So `/mcp` and `/api/v1` keep one credential type.
8
+ *
9
+ * Nothing here is stored. A registered client's id is its own signed metadata,
10
+ * and a code is its own encrypted grant, bound to the issuing origin. The only
11
+ * state is the set of redeemed codes, in the host's memory, so on several
12
+ * replicas a code could be redeemed once per replica within its minute, and only
13
+ * by whoever holds the PKCE verifier.
14
+ */
15
+ import { createCipheriv, createDecipheriv, createHash, createHmac, hkdfSync, randomBytes, timingSafeEqual } from 'node:crypto';
16
+ /** The scopes a grant can carry. Each maps to one org-wide API scope. */
17
+ export const OAUTH_SCOPES = ['read', 'solve'];
18
+ export const MCP_PATH = '/mcp';
19
+ const CODE_TTL_MS = 60_000;
20
+ const CLIENT_NAME_MAX = 100;
21
+ const REDIRECT_URIS_MAX = 10;
22
+ const CIMD_TTL_MS = 60 * 60_000;
23
+ // A failing metadata URL is not refetched for this long: each fetch is outbound
24
+ // work an anonymous caller could otherwise trigger at will.
25
+ const CIMD_FAILURE_TTL_MS = 60_000;
26
+ const CIMD_CACHE_MAX = 500;
27
+ const LOOPBACK_HOSTS = new Set(['localhost', '127.0.0.1', '[::1]']);
28
+ export class OAuthError extends Error {
29
+ error;
30
+ status;
31
+ constructor(
32
+ /** RFC 6749 / 7591 error code. */
33
+ error, description, status = 400) {
34
+ super(description);
35
+ this.error = error;
36
+ this.status = status;
37
+ this.name = 'OAuthError';
38
+ }
39
+ toBody() {
40
+ return { error: this.error, error_description: this.message };
41
+ }
42
+ }
43
+ // ============================================================================
44
+ // Discovery documents
45
+ // ============================================================================
46
+ export function mcpResourceUrl(origin) {
47
+ return origin + MCP_PATH;
48
+ }
49
+ export function resourceMetadataUrl(origin) {
50
+ return `${origin}/.well-known/oauth-protected-resource${MCP_PATH}`;
51
+ }
52
+ /** RFC 9728. Claude uses only the first authorization server. */
53
+ export function protectedResourceMetadata(origin) {
54
+ return {
55
+ resource: mcpResourceUrl(origin),
56
+ authorization_servers: [origin],
57
+ scopes_supported: [...OAUTH_SCOPES],
58
+ bearer_methods_supported: ['header']
59
+ };
60
+ }
61
+ /**
62
+ * RFC 8414. Claude picks Client ID Metadata Documents only when both
63
+ * `client_id_metadata_document_supported` and the `none` auth method appear.
64
+ */
65
+ export function authorizationServerMetadata(origin) {
66
+ return {
67
+ issuer: origin,
68
+ authorization_endpoint: `${origin}/oauth/authorize`,
69
+ token_endpoint: `${origin}/oauth/token`,
70
+ registration_endpoint: `${origin}/oauth/register`,
71
+ response_types_supported: ['code'],
72
+ grant_types_supported: ['authorization_code'],
73
+ code_challenge_methods_supported: ['S256'],
74
+ token_endpoint_auth_methods_supported: ['none'],
75
+ scopes_supported: [...OAUTH_SCOPES],
76
+ client_id_metadata_document_supported: true,
77
+ authorization_response_iss_parameter_supported: true
78
+ };
79
+ }
80
+ // ============================================================================
81
+ // Server
82
+ // ============================================================================
83
+ export function createOAuthServer(options) {
84
+ const { origin } = options;
85
+ const now = options.now ?? Date.now;
86
+ const signKey = derive(options.secret, 'selva-oauth-client');
87
+ const codeKey = derive(options.secret, `selva-oauth-code:${origin}`);
88
+ const redeemed = options.redeemedCodes ?? new Map();
89
+ const metadataCache = new Map();
90
+ // ==========================================================================
91
+ // Clients
92
+ // ==========================================================================
93
+ /** RFC 7591. The client id carries the metadata, signed, so nothing is stored. */
94
+ function register(input) {
95
+ try {
96
+ const meta = (input ?? {});
97
+ const redirectUris = checkRedirectUris(meta.redirect_uris);
98
+ const name = typeof meta.client_name === 'string' ? meta.client_name.trim() : '';
99
+ const payload = b64(JSON.stringify({ n: name.slice(0, CLIENT_NAME_MAX), r: redirectUris, t: now() }));
100
+ const clientId = `dcr_${payload}.${sign(signKey, payload)}`;
101
+ return {
102
+ status: 201,
103
+ body: {
104
+ client_id: clientId,
105
+ client_id_issued_at: Math.floor(now() / 1000),
106
+ client_name: name || undefined,
107
+ redirect_uris: redirectUris,
108
+ grant_types: ['authorization_code'],
109
+ response_types: ['code'],
110
+ token_endpoint_auth_method: 'none'
111
+ }
112
+ };
113
+ }
114
+ catch (err) {
115
+ if (err instanceof OAuthError)
116
+ return { status: err.status, body: err.toBody() };
117
+ throw err;
118
+ }
119
+ }
120
+ async function resolveClient(clientId) {
121
+ if (clientId.startsWith('dcr_'))
122
+ return registeredClient(clientId);
123
+ if (clientId.startsWith('https://'))
124
+ return metadataClient(clientId);
125
+ throw new OAuthError('invalid_client', 'Unknown client.');
126
+ }
127
+ function registeredClient(clientId) {
128
+ const [payload, signature] = clientId.slice('dcr_'.length).split('.');
129
+ if (!payload || !signature || !safeEqual(signature, sign(signKey, payload))) {
130
+ throw new OAuthError('invalid_client', 'Unknown client.');
131
+ }
132
+ const meta = JSON.parse(unb64(payload));
133
+ return { clientId, name: meta.n || 'An MCP client', redirectUris: meta.r };
134
+ }
135
+ async function metadataClient(url) {
136
+ const cached = metadataCache.get(url);
137
+ if (cached) {
138
+ if ('client' in cached && now() - cached.at < CIMD_TTL_MS)
139
+ return cached.client;
140
+ if ('failure' in cached && now() - cached.at < CIMD_FAILURE_TTL_MS)
141
+ throw cached.failure;
142
+ }
143
+ if (!options.fetchClientMetadata) {
144
+ throw new OAuthError('invalid_client', 'This server does not accept URL client ids.');
145
+ }
146
+ try {
147
+ const client = await readMetadata(url, options.fetchClientMetadata);
148
+ remember(url, { at: now(), client });
149
+ return client;
150
+ }
151
+ catch (err) {
152
+ if (!(err instanceof OAuthError))
153
+ throw err;
154
+ remember(url, { at: now(), failure: err });
155
+ throw err;
156
+ }
157
+ }
158
+ function remember(url, entry) {
159
+ if (metadataCache.size >= CIMD_CACHE_MAX) {
160
+ metadataCache.delete(metadataCache.keys().next().value);
161
+ }
162
+ metadataCache.set(url, entry);
163
+ }
164
+ // ==========================================================================
165
+ // Authorization
166
+ // ==========================================================================
167
+ /**
168
+ * Validates the query `/oauth/authorize` arrives with. An `OAuthError` from a
169
+ * bad client or redirect URI must be shown, not redirected: the redirect target
170
+ * is exactly what can't be trusted.
171
+ */
172
+ async function parseAuthorizeRequest(query) {
173
+ const client = await resolveClient(query.get('client_id') ?? '');
174
+ const redirectUri = query.get('redirect_uri') ?? '';
175
+ if (!client.redirectUris.some((r) => redirectMatches(r, redirectUri))) {
176
+ throw new OAuthError('invalid_request', 'The redirect URI is not registered for this client.');
177
+ }
178
+ if (query.get('response_type') !== 'code') {
179
+ throw new OAuthError('unsupported_response_type', 'Only response_type=code is supported.');
180
+ }
181
+ const codeChallenge = query.get('code_challenge') ?? '';
182
+ if (!codeChallenge || query.get('code_challenge_method') !== 'S256') {
183
+ throw new OAuthError('invalid_request', 'PKCE with code_challenge_method=S256 is required.');
184
+ }
185
+ const resource = query.get('resource') ?? mcpResourceUrl(origin);
186
+ if (!isThisResource(resource)) {
187
+ throw new OAuthError('invalid_target', 'The resource is not this server.');
188
+ }
189
+ return {
190
+ client,
191
+ redirectUri,
192
+ state: query.get('state') ?? undefined,
193
+ codeChallenge,
194
+ scopes: parseScopes(query.get('scope')),
195
+ resource
196
+ };
197
+ }
198
+ /** Where to send the browser after consent: the client's redirect URI with a code. */
199
+ function approve(request, grant) {
200
+ const code = seal(codeKey, {
201
+ c: request.client.clientId,
202
+ r: request.redirectUri,
203
+ p: request.codeChallenge,
204
+ x: now() + CODE_TTL_MS,
205
+ g: grant
206
+ });
207
+ return withParams(request.redirectUri, { code, state: request.state, iss: origin });
208
+ }
209
+ function deny(request) {
210
+ return withParams(request.redirectUri, {
211
+ error: 'access_denied',
212
+ state: request.state,
213
+ iss: origin
214
+ });
215
+ }
216
+ // ==========================================================================
217
+ // Token endpoint
218
+ // ==========================================================================
219
+ function exchange(form) {
220
+ try {
221
+ if (form.get('grant_type') !== 'authorization_code') {
222
+ throw new OAuthError('unsupported_grant_type', 'Only authorization_code is supported.');
223
+ }
224
+ const code = form.get('code') ?? '';
225
+ const sealed = open(codeKey, code);
226
+ if (!sealed || sealed.x < now()) {
227
+ throw new OAuthError('invalid_grant', 'The code is invalid or expired.');
228
+ }
229
+ if (sealed.c !== form.get('client_id') || sealed.r !== form.get('redirect_uri')) {
230
+ throw new OAuthError('invalid_grant', 'The code was issued to another client.');
231
+ }
232
+ const verifier = form.get('code_verifier') ?? '';
233
+ if (!safeEqual(createHash('sha256').update(verifier).digest('base64url'), sealed.p)) {
234
+ throw new OAuthError('invalid_grant', 'The code_verifier does not match.');
235
+ }
236
+ const resource = form.get('resource');
237
+ if (resource !== null && !isThisResource(resource)) {
238
+ throw new OAuthError('invalid_target', 'The resource is not this server.');
239
+ }
240
+ if (!markRedeemed(code, sealed.x)) {
241
+ throw new OAuthError('invalid_grant', 'The code was already used.');
242
+ }
243
+ const { g } = sealed;
244
+ return {
245
+ status: 200,
246
+ body: {
247
+ access_token: g.secret,
248
+ token_type: 'Bearer',
249
+ expires_in: Math.max(0, Math.floor((Date.parse(g.expiresAt) - now()) / 1000)),
250
+ scope: g.scopes.join(' ')
251
+ }
252
+ };
253
+ }
254
+ catch (err) {
255
+ if (err instanceof OAuthError)
256
+ return { status: err.status, body: err.toBody() };
257
+ throw err;
258
+ }
259
+ }
260
+ function markRedeemed(code, expiresAt) {
261
+ const t = now();
262
+ for (const [k, x] of redeemed)
263
+ if (x < t)
264
+ redeemed.delete(k);
265
+ const id = createHash('sha256').update(code).digest('base64url');
266
+ if (redeemed.has(id))
267
+ return false;
268
+ redeemed.set(id, expiresAt);
269
+ return true;
270
+ }
271
+ function isThisResource(resource) {
272
+ const r = resource.replace(/\/$/, '');
273
+ return r === origin || r === mcpResourceUrl(origin);
274
+ }
275
+ return {
276
+ register,
277
+ resolveClient,
278
+ parseAuthorizeRequest,
279
+ approve,
280
+ deny,
281
+ exchange,
282
+ protectedResourceMetadata: () => protectedResourceMetadata(origin),
283
+ authorizationServerMetadata: () => authorizationServerMetadata(origin)
284
+ };
285
+ }
286
+ /** OAuth scopes to the API scopes a minted token gets. */
287
+ export function apiScopesFor(scopes) {
288
+ return scopes.map((s) => `${s}:all`);
289
+ }
290
+ // ============================================================================
291
+ // Helpers
292
+ // ============================================================================
293
+ async function readMetadata(url, fetchClientMetadata) {
294
+ let doc;
295
+ try {
296
+ doc = (await fetchClientMetadata(url));
297
+ }
298
+ catch {
299
+ throw new OAuthError('invalid_client', 'The client metadata document could not be read.');
300
+ }
301
+ if (doc?.client_id !== url) {
302
+ throw new OAuthError('invalid_client', 'The metadata document names a different client_id.');
303
+ }
304
+ return {
305
+ clientId: url,
306
+ name: typeof doc.client_name === 'string' && doc.client_name.trim()
307
+ ? doc.client_name.trim().slice(0, CLIENT_NAME_MAX)
308
+ : new URL(url).host,
309
+ redirectUris: checkRedirectUris(doc.redirect_uris)
310
+ };
311
+ }
312
+ /**
313
+ * Every scope when the client asks for none; unknown scopes are dropped. `read`
314
+ * is always in: `solve` includes it at /api/v1, and a grant without it is empty.
315
+ */
316
+ function parseScopes(raw) {
317
+ const asked = (raw ?? '').split(' ').filter(Boolean);
318
+ if (!OAUTH_SCOPES.some((s) => asked.includes(s)))
319
+ return [...OAUTH_SCOPES];
320
+ return OAUTH_SCOPES.filter((s) => s === 'read' || asked.includes(s));
321
+ }
322
+ function checkRedirectUris(value) {
323
+ if (!Array.isArray(value) || !value.length || value.length > REDIRECT_URIS_MAX) {
324
+ throw new OAuthError('invalid_redirect_uri', 'Give between 1 and 10 redirect_uris.');
325
+ }
326
+ for (const uri of value) {
327
+ if (typeof uri !== 'string' || !isAllowedRedirect(uri)) {
328
+ throw new OAuthError('invalid_redirect_uri', 'Redirect URIs must be https, or http on a loopback address.');
329
+ }
330
+ }
331
+ return value;
332
+ }
333
+ function isAllowedRedirect(uri) {
334
+ let url;
335
+ try {
336
+ url = new URL(uri);
337
+ }
338
+ catch {
339
+ return false;
340
+ }
341
+ if (url.hash)
342
+ return false;
343
+ return (url.protocol === 'https:' || (url.protocol === 'http:' && LOOPBACK_HOSTS.has(url.hostname)));
344
+ }
345
+ /** A redirect to a program on the user's own machine, which any local process can claim. */
346
+ export function isLoopbackRedirect(uri) {
347
+ try {
348
+ return LOOPBACK_HOSTS.has(new URL(uri).hostname);
349
+ }
350
+ catch {
351
+ return false;
352
+ }
353
+ }
354
+ /** Exact, except a loopback redirect matches on any port (RFC 8252 §7.3). */
355
+ export function redirectMatches(registered, requested) {
356
+ if (registered === requested)
357
+ return true;
358
+ let a, b;
359
+ try {
360
+ a = new URL(registered);
361
+ b = new URL(requested);
362
+ }
363
+ catch {
364
+ return false;
365
+ }
366
+ return (a.protocol === 'http:' &&
367
+ b.protocol === 'http:' &&
368
+ LOOPBACK_HOSTS.has(a.hostname) &&
369
+ a.hostname === b.hostname &&
370
+ a.pathname === b.pathname &&
371
+ a.search === b.search);
372
+ }
373
+ function withParams(uri, params) {
374
+ const url = new URL(uri);
375
+ for (const [k, v] of Object.entries(params))
376
+ if (v !== undefined)
377
+ url.searchParams.set(k, v);
378
+ return url.toString();
379
+ }
380
+ function derive(secret, info) {
381
+ return Buffer.from(hkdfSync('sha256', secret, '', info, 32));
382
+ }
383
+ function sign(key, data) {
384
+ return createHmac('sha256', key).update(data).digest('base64url');
385
+ }
386
+ function seal(key, value) {
387
+ const iv = randomBytes(12);
388
+ const cipher = createCipheriv('aes-256-gcm', key, iv);
389
+ const body = Buffer.concat([cipher.update(JSON.stringify(value)), cipher.final()]);
390
+ return Buffer.concat([iv, cipher.getAuthTag(), body]).toString('base64url');
391
+ }
392
+ function open(key, sealed) {
393
+ try {
394
+ const raw = Buffer.from(sealed, 'base64url');
395
+ const decipher = createDecipheriv('aes-256-gcm', key, raw.subarray(0, 12));
396
+ decipher.setAuthTag(raw.subarray(12, 28));
397
+ const body = Buffer.concat([decipher.update(raw.subarray(28)), decipher.final()]);
398
+ return JSON.parse(body.toString('utf8'));
399
+ }
400
+ catch {
401
+ return null;
402
+ }
403
+ }
404
+ function b64(text) {
405
+ return Buffer.from(text).toString('base64url');
406
+ }
407
+ function unb64(text) {
408
+ return Buffer.from(text, 'base64url').toString('utf8');
409
+ }
410
+ function safeEqual(a, b) {
411
+ const x = Buffer.from(a);
412
+ const y = Buffer.from(b);
413
+ return x.length === y.length && timingSafeEqual(x, y);
414
+ }
415
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/oauth/index.ts"],"names":[],"mappings":"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"}
@@ -3,8 +3,8 @@ import { type TokenCodec } from './token-codec.js';
3
3
  * API token format: `selva_` + 30 base62 random chars + 6 base62 chars of
4
4
  * CRC32 over the random part. 42 chars total.
5
5
  *
6
- * - The prefix is what secret scanners match on; `sk_` was dropped because
7
- * Stripe and OpenAI keys share it.
6
+ * - The prefix is what secret scanners match on. Not `sk_`: Stripe and OpenAI
7
+ * keys use it.
8
8
  * - 30 base62 chars carry ~178 bits of entropy.
9
9
  * - The checksum lets the resolver reject a typo or a truncated paste without
10
10
  * a store lookup. It is not a MAC: the HMAC at rest is what authenticates.
@@ -5,8 +5,8 @@ import { createTokenCodec } from './token-codec.js';
5
5
  * API token format: `selva_` + 30 base62 random chars + 6 base62 chars of
6
6
  * CRC32 over the random part. 42 chars total.
7
7
  *
8
- * - The prefix is what secret scanners match on; `sk_` was dropped because
9
- * Stripe and OpenAI keys share it.
8
+ * - The prefix is what secret scanners match on. Not `sk_`: Stripe and OpenAI
9
+ * keys use it.
10
10
  * - 30 base62 chars carry ~178 bits of entropy.
11
11
  * - The checksum lets the resolver reject a typo or a truncated paste without
12
12
  * a store lookup. It is not a MAC: the HMAC at rest is what authenticates.
@@ -1,12 +1,14 @@
1
- import type { RequestContext } from '@selvajs/platform';
1
+ import { type RequestContext } from '@selvajs/platform';
2
2
  /**
3
3
  * Who may hold API tokens in the context's acting org, decided by the host.
4
+ * Without one, holding a key needs the platform permission `manage_api_tokens`.
4
5
  *
5
- * Without one, minting needs the platform permission `manage_api_tokens`. With
6
- * one, it replaces that check at mint, and the resolver re-asks it on every
7
- * request against the owner's live context in the token's org, so a key stops
8
- * working (403 `API_TOKEN_HOLDER_REFUSED`) the moment its owner no longer
9
- * qualifies, without being revoked.
6
+ * Asked at mint, and again by the resolver on every request against the
7
+ * owner's live context in the token's org, so a key stops working (403
8
+ * `API_TOKEN_HOLDER_REFUSED`) the moment its owner no longer qualifies,
9
+ * without being revoked.
10
10
  */
11
11
  export type ApiTokenHolderPolicy = (ctx: RequestContext) => boolean;
12
+ /** The one place the "no policy means `manage_api_tokens`" default lives. */
13
+ export declare function canHoldApiTokens(ctx: RequestContext, policy?: ApiTokenHolderPolicy): boolean;
12
14
  //# sourceMappingURL=api-token-holder.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"api-token-holder.d.ts","sourceRoot":"","sources":["../../src/tokens/api-token-holder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAExD;;;;;;;;GAQG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC"}
1
+ {"version":3,"file":"api-token-holder.d.ts","sourceRoot":"","sources":["../../src/tokens/api-token-holder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEvE;;;;;;;;GAQG;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC;AAEpE,6EAA6E;AAC7E,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,cAAc,EAAE,MAAM,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAE5F"}
@@ -1,2 +1,6 @@
1
- export {};
1
+ import { hasPermission } from '@selvajs/platform';
2
+ /** The one place the "no policy means `manage_api_tokens`" default lives. */
3
+ export function canHoldApiTokens(ctx, policy) {
4
+ return policy ? policy(ctx) : hasPermission(ctx, 'manage_api_tokens');
5
+ }
2
6
  //# sourceMappingURL=api-token-holder.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"api-token-holder.js","sourceRoot":"","sources":["../../src/tokens/api-token-holder.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"api-token-holder.js","sourceRoot":"","sources":["../../src/tokens/api-token-holder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAuB,MAAM,mBAAmB,CAAC;AAavE,6EAA6E;AAC7E,MAAM,UAAU,gBAAgB,CAAC,GAAmB,EAAE,MAA6B;IAClF,OAAO,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,GAAG,EAAE,mBAAmB,CAAC,CAAC;AACvE,CAAC"}
@@ -1,4 +1,4 @@
1
1
  export { createTokenCodec, MIN_TOKEN_SECRET_LENGTH, type TokenCodec, type TokenCodecConfig } from './token-codec.js';
2
2
  export { createApiTokenCodec, API_TOKEN_PREFIX, looksLikeApiToken, type ApiTokenCodec } from './api-token-codec.js';
3
- export type { ApiTokenHolderPolicy } from './api-token-holder.js';
3
+ export { canHoldApiTokens, type ApiTokenHolderPolicy } from './api-token-holder.js';
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tokens/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACN,gBAAgB,EAChB,uBAAuB,EACvB,KAAK,UAAU,EACf,KAAK,gBAAgB,EACrB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACN,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,KAAK,aAAa,EAClB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tokens/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACN,gBAAgB,EAChB,uBAAuB,EACvB,KAAK,UAAU,EACf,KAAK,gBAAgB,EACrB,MAAM,kBAAkB,CAAC;AAG1B,OAAO,EACN,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,KAAK,aAAa,EAClB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,gBAAgB,EAAE,KAAK,oBAAoB,EAAE,MAAM,uBAAuB,CAAC"}
@@ -2,4 +2,5 @@
2
2
  export { createTokenCodec, MIN_TOKEN_SECRET_LENGTH } from './token-codec.js';
3
3
  // API tokens (`selva_`): the same HMAC-at-rest scheme, plus a checksum.
4
4
  export { createApiTokenCodec, API_TOKEN_PREFIX, looksLikeApiToken } from './api-token-codec.js';
5
+ export { canHoldApiTokens } from './api-token-holder.js';
5
6
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tokens/index.ts"],"names":[],"mappings":"AAAA,wEAAwE;AAExE,OAAO,EACN,gBAAgB,EAChB,uBAAuB,EAGvB,MAAM,kBAAkB,CAAC;AAE1B,wEAAwE;AACxE,OAAO,EACN,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EAEjB,MAAM,sBAAsB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tokens/index.ts"],"names":[],"mappings":"AAAA,wEAAwE;AAExE,OAAO,EACN,gBAAgB,EAChB,uBAAuB,EAGvB,MAAM,kBAAkB,CAAC;AAE1B,wEAAwE;AACxE,OAAO,EACN,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EAEjB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,gBAAgB,EAA6B,MAAM,uBAAuB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@selvajs/server",
3
- "version": "1.5.0-beta.5",
3
+ "version": "1.5.0-beta.7",
4
4
  "description": "Transport-agnostic server building blocks for apps built on the Selva engine (limits, rate limiting, SSRF guard, remote-definition fetch, definition service + render loader); the solve core lives in @selvajs/solve/server",
5
5
  "license": "MIT",
6
6
  "author": "VektorNode",
@@ -41,6 +41,11 @@
41
41
  "types": "./dist/providers/index.d.ts",
42
42
  "import": "./dist/providers/index.js"
43
43
  },
44
+ "./oauth": {
45
+ "selva-source": "./src/oauth/index.ts",
46
+ "types": "./dist/oauth/index.d.ts",
47
+ "import": "./dist/oauth/index.js"
48
+ },
44
49
  "./tokens": {
45
50
  "selva-source": "./src/tokens/index.ts",
46
51
  "types": "./dist/tokens/index.d.ts",
@@ -111,8 +116,8 @@
111
116
  "dependencies": {
112
117
  "zod": "^4.6.5",
113
118
  "@selvajs/compute": "^4.2.2-beta.0",
114
- "@selvajs/platform": "^0.21.0-beta.1",
115
- "@selvajs/notifications": "^0.3.0-beta.1",
119
+ "@selvajs/notifications": "^0.3.0-beta.2",
120
+ "@selvajs/platform": "^0.21.0-beta.2",
116
121
  "@selvajs/schemas": "^5.2.0"
117
122
  },
118
123
  "peerDependencies": {
@@ -138,7 +143,7 @@
138
143
  "typescript": "~6.0.3",
139
144
  "vitest": "^5.0.2",
140
145
  "@selvajs/config": "0.0.4",
141
- "@selvajs/local-provider": "0.16.0-beta.0"
146
+ "@selvajs/local-provider": "0.16.0-beta.1"
142
147
  },
143
148
  "scripts": {
144
149
  "build": "tsc",
@@ -1 +0,0 @@
1
- {"version":3,"file":"idempotency-http.d.ts","sourceRoot":"","sources":["../../src/compute/idempotency-http.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,8EAA8E;AAC9E,eAAO,MAAM,2BAA2B,yBAAyB,CAAC;AAElE;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;IAC5B,IAAI,EAAE,WAAW,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAE1E;AAED,mEAAmE;AACnE,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,cAAc,CAAC,CAM7E;AAED,iFAAiF;AACjF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,cAAc,EAAE,QAAQ,EAAE,OAAO,GAAG,QAAQ,CAItF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"idempotency-http.js","sourceRoot":"","sources":["../../src/compute/idempotency-http.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,8EAA8E;AAC9E,MAAM,CAAC,MAAM,2BAA2B,GAAG,sBAAsB,CAAC;AAYlE;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,QAAgB,EAAE,SAAiB;IACjE,OAAO,GAAG,QAAQ,IAAI,SAAS,EAAE,CAAC;AACnC,CAAC;AAED,mEAAmE;AACnE,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,GAAa;IACnD,OAAO;QACN,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,OAAO,EAAE,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QACnC,IAAI,EAAE,MAAM,GAAG,CAAC,WAAW,EAAE;KAC7B,CAAC;AACH,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,kBAAkB,CAAC,MAAsB,EAAE,QAAiB;IAC3E,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAC5C,IAAI,QAAQ;QAAE,OAAO,CAAC,GAAG,CAAC,2BAA2B,EAAE,MAAM,CAAC,CAAC;IAC/D,OAAO,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;AACtE,CAAC"}
File without changes
File without changes