@selvajs/server 2.0.0-beta.18 → 2.0.0-beta.20

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 (68) hide show
  1. package/dist/access/guards.d.ts +20 -3
  2. package/dist/access/guards.d.ts.map +1 -1
  3. package/dist/access/guards.js +44 -7
  4. package/dist/access/guards.js.map +1 -1
  5. package/dist/api/deps.d.ts +8 -1
  6. package/dist/api/deps.d.ts.map +1 -1
  7. package/dist/api/deps.js +2 -1
  8. package/dist/api/deps.js.map +1 -1
  9. package/dist/api/index.d.ts +1 -1
  10. package/dist/api/index.d.ts.map +1 -1
  11. package/dist/api/index.js +1 -1
  12. package/dist/api/index.js.map +1 -1
  13. package/dist/api/rate-limit.d.ts +6 -0
  14. package/dist/api/rate-limit.d.ts.map +1 -1
  15. package/dist/api/rate-limit.js +15 -0
  16. package/dist/api/rate-limit.js.map +1 -1
  17. package/dist/config/index.d.ts +2 -0
  18. package/dist/config/index.d.ts.map +1 -0
  19. package/dist/config/index.js +2 -0
  20. package/dist/config/index.js.map +1 -0
  21. package/dist/config/settings.d.ts +2 -0
  22. package/dist/config/settings.d.ts.map +1 -0
  23. package/dist/config/settings.js +261 -0
  24. package/dist/config/settings.js.map +1 -0
  25. package/dist/handlers/me.d.ts.map +1 -1
  26. package/dist/handlers/me.js +4 -0
  27. package/dist/handlers/me.js.map +1 -1
  28. package/dist/handlers/projects.d.ts.map +1 -1
  29. package/dist/handlers/projects.js +14 -27
  30. package/dist/handlers/projects.js.map +1 -1
  31. package/dist/handlers/shareLinks.d.ts.map +1 -1
  32. package/dist/handlers/shareLinks.js +5 -2
  33. package/dist/handlers/shareLinks.js.map +1 -1
  34. package/dist/http/api-token.d.ts.map +1 -1
  35. package/dist/http/api-token.js +6 -2
  36. package/dist/http/api-token.js.map +1 -1
  37. package/dist/notifications/smtp-config.d.ts.map +1 -1
  38. package/dist/notifications/smtp-config.js +4 -2
  39. package/dist/notifications/smtp-config.js.map +1 -1
  40. package/dist/oauth/index.d.ts +8 -5
  41. package/dist/oauth/index.d.ts.map +1 -1
  42. package/dist/oauth/index.js +11 -5
  43. package/dist/oauth/index.js.map +1 -1
  44. package/dist/ops/deploymentConfig.d.ts +1 -0
  45. package/dist/ops/deploymentConfig.d.ts.map +1 -1
  46. package/dist/ops/deploymentConfig.js +15 -0
  47. package/dist/ops/deploymentConfig.js.map +1 -1
  48. package/dist/testing/harness.d.ts +2 -2
  49. package/dist/testing/harness.d.ts.map +1 -1
  50. package/dist/testing/harness.js +2 -1
  51. package/dist/testing/harness.js.map +1 -1
  52. package/dist/tokens/api-token-codec.d.ts +2 -2
  53. package/dist/tokens/api-token-codec.d.ts.map +1 -1
  54. package/dist/tokens/api-token-codec.js +7 -3
  55. package/dist/tokens/api-token-codec.js.map +1 -1
  56. package/dist/tokens/api-token-holder.d.ts +2 -0
  57. package/dist/tokens/api-token-holder.d.ts.map +1 -1
  58. package/dist/tokens/api-token-holder.js +18 -5
  59. package/dist/tokens/api-token-holder.js.map +1 -1
  60. package/dist/tokens/index.d.ts +1 -1
  61. package/dist/tokens/index.d.ts.map +1 -1
  62. package/dist/tokens/index.js +3 -3
  63. package/dist/tokens/index.js.map +1 -1
  64. package/dist/tokens/token-codec.d.ts +23 -18
  65. package/dist/tokens/token-codec.d.ts.map +1 -1
  66. package/dist/tokens/token-codec.js +37 -24
  67. package/dist/tokens/token-codec.js.map +1 -1
  68. package/package.json +10 -5
@@ -1,28 +1,23 @@
1
- import { randomBytes, createHmac, timingSafeEqual } from 'node:crypto';
1
+ import { randomBytes, createHash, createHmac, timingSafeEqual } from 'node:crypto';
2
2
  /**
3
- * HMAC token codec — the primitive behind capability-URL tokens (share links,
4
- * invites, and anything shaped like them).
3
+ * Token codec — the primitive behind capability-URL tokens (share links,
4
+ * invites, API tokens, and anything shaped like them).
5
5
  *
6
6
  * ## Format
7
7
  * raw = `<prefix><base64url(32 random bytes)>`
8
- * hash = base64url( HMAC-SHA256(secret, raw) )
8
+ * hash = `sha256:` + base64url( SHA-256(raw) )
9
9
  *
10
- * ## Two secrets per token
10
+ * The raw token is handed back once at mint time and never stored; the store
11
+ * holds only the hash. With 256 bits of entropy in the raw token, an unkeyed
12
+ * hash is as unreplayable from a database leak as a keyed one, and rotating
13
+ * the instance secret no longer invalidates stored tokens.
11
14
  *
12
- * 1. **Per-token entropy** — the 32 random bytes in `mintRawToken()`, handed
13
- * back exactly once at mint time and never stored. This is what end-users
14
- * carry in `?token=…`.
15
+ * ## Legacy hashes
15
16
  *
16
- * 2. **Instance-wide HMAC secret** (`config.secret`) — hashes tokens for
17
- * storage, so the store holds only `HMAC-SHA256(secret, raw)`. A DB-only
18
- * leak can't be replayed: the attacker also needs the secret, and it lives
19
- * in env, not the database.
20
- *
21
- * Resolution hashes the supplied token with the same secret and looks up an
22
- * exact match against the stored hash.
23
- *
24
- * Rotating the secret invalidates every token minted under it — give each token
25
- * family (each prefix) its own secret so they rotate independently.
17
+ * Tokens minted before this format were stored as
18
+ * `base64url(HMAC-SHA256(secret, raw))`. `lookupHashes` returns those forms
19
+ * too, under the current and the previous secret, so they keep resolving
20
+ * through a rotation until they expire.
26
21
  */
27
22
  /**
28
23
  * Minimum accepted secret length, matching the "random ≥32-byte string"
@@ -31,18 +26,23 @@ import { randomBytes, createHmac, timingSafeEqual } from 'node:crypto';
31
26
  */
32
27
  export const MIN_TOKEN_SECRET_LENGTH = 32;
33
28
  export function createTokenCodec(config) {
34
- const { prefix, secret } = config;
35
- if (secret.length < MIN_TOKEN_SECRET_LENGTH) {
36
- throw new Error(`Secret for "${prefix}" tokens must be at least ${MIN_TOKEN_SECRET_LENGTH} characters ` +
37
- `(got ${secret.length}). Generate one with: openssl rand -base64 32`);
29
+ const { prefix, secret, previousSecret } = config;
30
+ for (const s of [secret, ...(previousSecret ? [previousSecret] : [])]) {
31
+ if (s.length < MIN_TOKEN_SECRET_LENGTH) {
32
+ throw new Error(`Secret for "${prefix}" tokens must be at least ${MIN_TOKEN_SECRET_LENGTH} characters ` +
33
+ `(got ${s.length}). Generate one with: openssl rand -base64 32`);
34
+ }
38
35
  }
36
+ const hashToken = (raw) => 'sha256:' + createHash('sha256').update(raw).digest('base64url');
39
37
  return {
40
38
  prefix,
41
39
  mintRawToken() {
42
40
  return prefix + randomBytes(32).toString('base64url');
43
41
  },
44
- hashToken(raw) {
45
- return createHmac('sha256', secret).update(raw).digest('base64url');
42
+ hashToken,
43
+ lookupHashes(raw) {
44
+ const legacy = [secret, ...(previousSecret ? [previousSecret] : [])].map((key) => createHmac('sha256', key).update(raw).digest('base64url'));
45
+ return [hashToken(raw), ...legacy];
46
46
  },
47
47
  hashesEqual(a, b) {
48
48
  const ab = Buffer.from(a);
@@ -56,4 +56,17 @@ export function createTokenCodec(config) {
56
56
  }
57
57
  };
58
58
  }
59
+ /**
60
+ * Look a raw token up by each hash its record may carry. Sequential rather than
61
+ * one `IN` query so stores keep their single-hash interface; records are never
62
+ * rewritten here, so there is no rewrite for a lookup to race.
63
+ */
64
+ export async function findByToken(codec, raw, lookup) {
65
+ for (const hash of codec.lookupHashes(raw)) {
66
+ const found = await lookup(hash);
67
+ if (found)
68
+ return found;
69
+ }
70
+ return null;
71
+ }
59
72
  //# sourceMappingURL=token-codec.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"token-codec.js","sourceRoot":"","sources":["../../src/tokens/token-codec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEvE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,CAAC;AAsB1C,MAAM,UAAU,gBAAgB,CAAC,MAAwB;IACxD,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAClC,IAAI,MAAM,CAAC,MAAM,GAAG,uBAAuB,EAAE,CAAC;QAC7C,MAAM,IAAI,KAAK,CACd,eAAe,MAAM,6BAA6B,uBAAuB,cAAc;YACtF,QAAQ,MAAM,CAAC,MAAM,+CAA+C,CACrE,CAAC;IACH,CAAC;IAED,OAAO;QACN,MAAM;QACN,YAAY;YACX,OAAO,MAAM,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QACvD,CAAC;QACD,SAAS,CAAC,GAAW;YACpB,OAAO,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QACrE,CAAC;QACD,WAAW,CAAC,CAAS,EAAE,CAAS;YAC/B,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC1B,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC1B,IAAI,EAAE,CAAC,MAAM,KAAK,EAAE,CAAC,MAAM;gBAAE,OAAO,KAAK,CAAC;YAC1C,OAAO,eAAe,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QAChC,CAAC;QACD,cAAc,CAAC,KAAa;YAC3B,OAAO,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QACjC,CAAC;KACD,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"token-codec.js","sourceRoot":"","sources":["../../src/tokens/token-codec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnF;;;;;;;;;;;;;;;;;;;GAmBG;AAEH;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,CAAC;AA0B1C,MAAM,UAAU,gBAAgB,CAAC,MAAwB;IACxD,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,CAAC;IAClD,KAAK,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;QACvE,IAAI,CAAC,CAAC,MAAM,GAAG,uBAAuB,EAAE,CAAC;YACxC,MAAM,IAAI,KAAK,CACd,eAAe,MAAM,6BAA6B,uBAAuB,cAAc;gBACtF,QAAQ,CAAC,CAAC,MAAM,+CAA+C,CAChE,CAAC;QACH,CAAC;IACF,CAAC;IAED,MAAM,SAAS,GAAG,CAAC,GAAW,EAAU,EAAE,CACzC,SAAS,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IAElE,OAAO;QACN,MAAM;QACN,YAAY;YACX,OAAO,MAAM,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QACvD,CAAC;QACD,SAAS;QACT,YAAY,CAAC,GAAW;YACvB,MAAM,MAAM,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAChF,UAAU,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,CACzD,CAAC;YACF,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,GAAG,MAAM,CAAC,CAAC;QACpC,CAAC;QACD,WAAW,CAAC,CAAS,EAAE,CAAS;YAC/B,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC1B,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAC1B,IAAI,EAAE,CAAC,MAAM,KAAK,EAAE,CAAC,MAAM;gBAAE,OAAO,KAAK,CAAC;YAC1C,OAAO,eAAe,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QAChC,CAAC;QACD,cAAc,CAAC,KAAa;YAC3B,OAAO,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QACjC,CAAC;KACD,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAChC,KAAuC,EACvC,GAAW,EACX,MAA2C;IAE3C,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC;QAC5C,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,KAAK;YAAE,OAAO,KAAK,CAAC;IACzB,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@selvajs/server",
3
- "version": "2.0.0-beta.18",
3
+ "version": "2.0.0-beta.20",
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",
@@ -31,6 +31,11 @@
31
31
  "types": "./dist/compute/index.d.ts",
32
32
  "import": "./dist/compute/index.js"
33
33
  },
34
+ "./config": {
35
+ "selva-source": "./src/config/index.ts",
36
+ "types": "./dist/config/index.d.ts",
37
+ "import": "./dist/config/index.js"
38
+ },
34
39
  "./definitions": {
35
40
  "selva-source": "./src/definitions/index.ts",
36
41
  "types": "./dist/definitions/index.d.ts",
@@ -116,9 +121,9 @@
116
121
  "dependencies": {
117
122
  "zod": "^4.6.7",
118
123
  "@selvajs/compute": "^5.0.0-beta.9",
119
- "@selvajs/notifications": "^0.3.0-beta.6",
120
- "@selvajs/platform": "^1.0.0-beta.6",
121
- "@selvajs/schemas": "^5.3.0-beta.2"
124
+ "@selvajs/notifications": "^0.3.0-beta.9",
125
+ "@selvajs/schemas": "^5.3.0-beta.3",
126
+ "@selvajs/platform": "^1.0.0-beta.9"
122
127
  },
123
128
  "peerDependencies": {
124
129
  "@sentry/node": ">=8",
@@ -144,7 +149,7 @@
144
149
  "typescript": "~6.0.3",
145
150
  "vitest": "^5.0.5",
146
151
  "@selvajs/config": "0.0.4",
147
- "@selvajs/local-provider": "0.16.0-beta.3"
152
+ "@selvajs/local-provider": "0.16.0-beta.5"
148
153
  },
149
154
  "scripts": {
150
155
  "build": "tsc",