@technomoron/api-server-base 2.0.0-beta.21 → 2.0.0-beta.23

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 (177) hide show
  1. package/dist/cjs/common/types.cjs +10 -0
  2. package/dist/cjs/common/types.d.ts +137 -0
  3. package/dist/cjs/{api-module.cjs → server/src/api-module.cjs} +8 -0
  4. package/dist/{esm → cjs/server/src}/api-module.d.ts +15 -0
  5. package/dist/cjs/{api-server-base.cjs → server/src/api-server-base.cjs} +669 -627
  6. package/dist/{esm → cjs/server/src}/api-server-base.d.ts +105 -78
  7. package/dist/cjs/{auth-api/auth-module.js → server/src/auth-api/auth-module.cjs} +96 -76
  8. package/dist/cjs/{auth-api → server/src/auth-api}/auth-module.d.ts +1 -1
  9. package/dist/cjs/{auth-api/compat-auth-storage.js → server/src/auth-api/compat-auth-storage.cjs} +4 -4
  10. package/dist/cjs/{auth-api/mem-auth-store.js → server/src/auth-api/mem-auth-store.cjs} +7 -7
  11. package/dist/cjs/{auth-api/module.js → server/src/auth-api/module.cjs} +1 -1
  12. package/dist/cjs/server/src/auth-api/schemas.cjs +171 -0
  13. package/dist/cjs/server/src/auth-api/schemas.d.ts +21 -0
  14. package/dist/cjs/{auth-api/sql-auth-store.js → server/src/auth-api/sql-auth-store.cjs} +8 -8
  15. package/dist/cjs/{auth-api/user-id.js → server/src/auth-api/user-id.cjs} +12 -3
  16. package/dist/{esm → cjs/server/src}/auth-cookie-options.d.ts +5 -3
  17. package/dist/cjs/server/src/base/client-info.cjs +285 -0
  18. package/dist/cjs/server/src/base/client-info.d.ts +27 -0
  19. package/dist/cjs/server/src/base/error-utils.cjs +50 -0
  20. package/dist/cjs/server/src/base/error-utils.d.ts +16 -0
  21. package/dist/cjs/server/src/base/request-utils.cjs +27 -0
  22. package/dist/cjs/server/src/base/request-utils.d.ts +8 -0
  23. package/dist/cjs/{index.cjs → server/src/index.cjs} +24 -15
  24. package/dist/{esm → cjs/server/src}/index.d.ts +7 -0
  25. package/dist/cjs/server/src/limiter/auth-rate-limiter.cjs +35 -0
  26. package/dist/cjs/server/src/limiter/auth-rate-limiter.d.ts +12 -0
  27. package/dist/cjs/server/src/limiter/fixed-window.cjs +41 -0
  28. package/dist/cjs/server/src/limiter/fixed-window.d.ts +11 -0
  29. package/dist/cjs/{oauth/base.js → server/src/oauth/base.cjs} +1 -0
  30. package/dist/cjs/{oauth → server/src/oauth}/base.d.ts +8 -1
  31. package/dist/cjs/{oauth/memory.js → server/src/oauth/memory.cjs} +7 -4
  32. package/dist/{esm → cjs/server/src}/oauth/memory.d.ts +1 -1
  33. package/dist/cjs/{oauth/models.js → server/src/oauth/models.cjs} +2 -2
  34. package/dist/cjs/{oauth/sequelize.js → server/src/oauth/sequelize.cjs} +11 -7
  35. package/dist/{esm → cjs/server/src}/oauth/sequelize.d.ts +1 -1
  36. package/dist/cjs/{passkey/base.js → server/src/passkey/base.cjs} +1 -0
  37. package/dist/{esm → cjs/server/src}/passkey/base.d.ts +11 -0
  38. package/dist/cjs/{passkey/memory.js → server/src/passkey/memory.cjs} +2 -2
  39. package/dist/cjs/{passkey/models.js → server/src/passkey/models.cjs} +1 -1
  40. package/dist/cjs/{passkey/sequelize.js → server/src/passkey/sequelize.cjs} +3 -3
  41. package/dist/cjs/{passkey/service.js → server/src/passkey/service.cjs} +17 -3
  42. package/dist/{esm → cjs/server/src}/passkey/service.d.ts +1 -1
  43. package/dist/cjs/{sequelize-utils.js → server/src/sequelize-utils.cjs} +4 -5
  44. package/dist/cjs/{token/base.js → server/src/token/base.cjs} +4 -0
  45. package/dist/{esm → cjs/server/src}/token/base.d.ts +7 -0
  46. package/dist/cjs/{token/memory.js → server/src/token/memory.cjs} +15 -20
  47. package/dist/cjs/{token/sequelize.js → server/src/token/sequelize.cjs} +25 -11
  48. package/dist/cjs/server/src/upload/memory.cjs +92 -0
  49. package/dist/cjs/server/src/upload/memory.d.ts +17 -0
  50. package/dist/cjs/server/src/upload/tus-module.cjs +270 -0
  51. package/dist/cjs/server/src/upload/tus-module.d.ts +38 -0
  52. package/dist/cjs/server/src/upload/types.d.ts +8 -0
  53. package/dist/cjs/{user/base.js → server/src/user/base.cjs} +1 -0
  54. package/dist/cjs/{user → server/src/user}/base.d.ts +9 -0
  55. package/dist/cjs/{user/memory.js → server/src/user/memory.cjs} +29 -7
  56. package/dist/cjs/{user/sequelize.js → server/src/user/sequelize.cjs} +33 -8
  57. package/dist/cjs/server/src/user/types.cjs +2 -0
  58. package/dist/esm/common/types.d.ts +137 -0
  59. package/dist/esm/common/types.js +9 -0
  60. package/dist/{cjs → esm/server/src}/api-module.d.ts +15 -0
  61. package/dist/esm/{api-module.js → server/src/api-module.js} +8 -0
  62. package/dist/{cjs → esm/server/src}/api-server-base.d.ts +105 -78
  63. package/dist/esm/{api-server-base.js → server/src/api-server-base.js} +658 -616
  64. package/dist/esm/{auth-api → server/src/auth-api}/auth-module.d.ts +1 -1
  65. package/dist/esm/{auth-api → server/src/auth-api}/auth-module.js +92 -72
  66. package/dist/esm/{auth-api → server/src/auth-api}/compat-auth-storage.js +3 -3
  67. package/dist/esm/server/src/auth-api/schemas.d.ts +21 -0
  68. package/dist/esm/server/src/auth-api/schemas.js +168 -0
  69. package/dist/esm/{auth-api → server/src/auth-api}/user-id.js +12 -3
  70. package/dist/{cjs → esm/server/src}/auth-cookie-options.d.ts +5 -3
  71. package/dist/esm/server/src/base/client-info.d.ts +27 -0
  72. package/dist/esm/server/src/base/client-info.js +282 -0
  73. package/dist/esm/server/src/base/error-utils.d.ts +16 -0
  74. package/dist/esm/server/src/base/error-utils.js +44 -0
  75. package/dist/esm/server/src/base/request-utils.d.ts +8 -0
  76. package/dist/esm/server/src/base/request-utils.js +23 -0
  77. package/dist/{cjs → esm/server/src}/index.d.ts +7 -0
  78. package/dist/esm/{index.js → server/src/index.js} +4 -0
  79. package/dist/esm/server/src/limiter/auth-rate-limiter.d.ts +12 -0
  80. package/dist/esm/server/src/limiter/auth-rate-limiter.js +32 -0
  81. package/dist/esm/server/src/limiter/fixed-window.d.ts +11 -0
  82. package/dist/esm/server/src/limiter/fixed-window.js +37 -0
  83. package/dist/esm/{oauth → server/src/oauth}/base.d.ts +8 -1
  84. package/dist/esm/server/src/oauth/base.js +3 -0
  85. package/dist/{cjs → esm/server/src}/oauth/memory.d.ts +1 -1
  86. package/dist/esm/{oauth → server/src/oauth}/memory.js +5 -2
  87. package/dist/{cjs → esm/server/src}/oauth/sequelize.d.ts +1 -1
  88. package/dist/esm/{oauth → server/src/oauth}/sequelize.js +6 -2
  89. package/dist/{cjs → esm/server/src}/passkey/base.d.ts +11 -0
  90. package/dist/esm/server/src/passkey/base.js +3 -0
  91. package/dist/{cjs → esm/server/src}/passkey/service.d.ts +1 -1
  92. package/dist/esm/{passkey → server/src/passkey}/service.js +17 -3
  93. package/dist/esm/{sequelize-utils.js → server/src/sequelize-utils.js} +4 -5
  94. package/dist/{cjs → esm/server/src}/token/base.d.ts +7 -0
  95. package/dist/esm/{token → server/src/token}/base.js +4 -0
  96. package/dist/esm/{token → server/src/token}/memory.js +14 -19
  97. package/dist/esm/{token → server/src/token}/sequelize.js +22 -8
  98. package/dist/esm/server/src/upload/memory.d.ts +17 -0
  99. package/dist/esm/server/src/upload/memory.js +86 -0
  100. package/dist/esm/server/src/upload/tus-module.d.ts +38 -0
  101. package/dist/esm/server/src/upload/tus-module.js +266 -0
  102. package/dist/esm/server/src/upload/types.d.ts +8 -0
  103. package/dist/esm/{user → server/src/user}/base.d.ts +9 -0
  104. package/dist/esm/{user → server/src/user}/base.js +1 -0
  105. package/dist/esm/{user → server/src/user}/memory.js +27 -5
  106. package/dist/esm/{user → server/src/user}/sequelize.js +30 -5
  107. package/dist/esm/server/src/user/types.js +1 -0
  108. package/docs/swagger/openapi.json +411 -125
  109. package/package.json +129 -134
  110. package/README.txt +0 -213
  111. package/dist/esm/oauth/base.js +0 -2
  112. package/dist/esm/passkey/base.js +0 -2
  113. /package/dist/cjs/{auth-api → server/src/auth-api}/compat-auth-storage.d.ts +0 -0
  114. /package/dist/cjs/{auth-api → server/src/auth-api}/mem-auth-store.d.ts +0 -0
  115. /package/dist/cjs/{auth-api → server/src/auth-api}/module.d.ts +0 -0
  116. /package/dist/cjs/{auth-api → server/src/auth-api}/sql-auth-store.d.ts +0 -0
  117. /package/dist/cjs/{auth-api/storage.js → server/src/auth-api/storage.cjs} +0 -0
  118. /package/dist/cjs/{auth-api → server/src/auth-api}/storage.d.ts +0 -0
  119. /package/dist/cjs/{auth-api/types.js → server/src/auth-api/types.cjs} +0 -0
  120. /package/dist/cjs/{auth-api → server/src/auth-api}/types.d.ts +0 -0
  121. /package/dist/cjs/{auth-api → server/src/auth-api}/user-id.d.ts +0 -0
  122. /package/dist/cjs/{auth-cookie-options.js → server/src/auth-cookie-options.cjs} +0 -0
  123. /package/dist/cjs/{oauth → server/src/oauth}/models.d.ts +0 -0
  124. /package/dist/cjs/{oauth/types.js → server/src/oauth/types.cjs} +0 -0
  125. /package/dist/cjs/{oauth → server/src/oauth}/types.d.ts +0 -0
  126. /package/dist/cjs/{passkey/config.js → server/src/passkey/config.cjs} +0 -0
  127. /package/dist/cjs/{passkey → server/src/passkey}/config.d.ts +0 -0
  128. /package/dist/cjs/{passkey → server/src/passkey}/memory.d.ts +0 -0
  129. /package/dist/cjs/{passkey → server/src/passkey}/models.d.ts +0 -0
  130. /package/dist/cjs/{passkey → server/src/passkey}/sequelize.d.ts +0 -0
  131. /package/dist/cjs/{passkey/types.js → server/src/passkey/types.cjs} +0 -0
  132. /package/dist/cjs/{passkey → server/src/passkey}/types.d.ts +0 -0
  133. /package/dist/cjs/{sequelize-utils.d.ts → server/src/sequelize-utils.d.ts} +0 -0
  134. /package/dist/cjs/{token → server/src/token}/memory.d.ts +0 -0
  135. /package/dist/cjs/{token → server/src/token}/sequelize.d.ts +0 -0
  136. /package/dist/cjs/{token/types.js → server/src/token/types.cjs} +0 -0
  137. /package/dist/cjs/{token → server/src/token}/types.d.ts +0 -0
  138. /package/dist/cjs/{user/types.js → server/src/upload/types.cjs} +0 -0
  139. /package/dist/cjs/{user → server/src/user}/memory.d.ts +0 -0
  140. /package/dist/cjs/{user → server/src/user}/sequelize.d.ts +0 -0
  141. /package/dist/cjs/{user → server/src/user}/types.d.ts +0 -0
  142. /package/dist/esm/{auth-api → server/src/auth-api}/compat-auth-storage.d.ts +0 -0
  143. /package/dist/esm/{auth-api → server/src/auth-api}/mem-auth-store.d.ts +0 -0
  144. /package/dist/esm/{auth-api → server/src/auth-api}/mem-auth-store.js +0 -0
  145. /package/dist/esm/{auth-api → server/src/auth-api}/module.d.ts +0 -0
  146. /package/dist/esm/{auth-api → server/src/auth-api}/module.js +0 -0
  147. /package/dist/esm/{auth-api → server/src/auth-api}/sql-auth-store.d.ts +0 -0
  148. /package/dist/esm/{auth-api → server/src/auth-api}/sql-auth-store.js +0 -0
  149. /package/dist/esm/{auth-api → server/src/auth-api}/storage.d.ts +0 -0
  150. /package/dist/esm/{auth-api → server/src/auth-api}/storage.js +0 -0
  151. /package/dist/esm/{auth-api → server/src/auth-api}/types.d.ts +0 -0
  152. /package/dist/esm/{auth-api → server/src/auth-api}/types.js +0 -0
  153. /package/dist/esm/{auth-api → server/src/auth-api}/user-id.d.ts +0 -0
  154. /package/dist/esm/{auth-cookie-options.js → server/src/auth-cookie-options.js} +0 -0
  155. /package/dist/esm/{oauth → server/src/oauth}/models.d.ts +0 -0
  156. /package/dist/esm/{oauth → server/src/oauth}/models.js +0 -0
  157. /package/dist/esm/{oauth → server/src/oauth}/types.d.ts +0 -0
  158. /package/dist/esm/{oauth → server/src/oauth}/types.js +0 -0
  159. /package/dist/esm/{passkey → server/src/passkey}/config.d.ts +0 -0
  160. /package/dist/esm/{passkey → server/src/passkey}/config.js +0 -0
  161. /package/dist/esm/{passkey → server/src/passkey}/memory.d.ts +0 -0
  162. /package/dist/esm/{passkey → server/src/passkey}/memory.js +0 -0
  163. /package/dist/esm/{passkey → server/src/passkey}/models.d.ts +0 -0
  164. /package/dist/esm/{passkey → server/src/passkey}/models.js +0 -0
  165. /package/dist/esm/{passkey → server/src/passkey}/sequelize.d.ts +0 -0
  166. /package/dist/esm/{passkey → server/src/passkey}/sequelize.js +0 -0
  167. /package/dist/esm/{passkey → server/src/passkey}/types.d.ts +0 -0
  168. /package/dist/esm/{passkey → server/src/passkey}/types.js +0 -0
  169. /package/dist/esm/{sequelize-utils.d.ts → server/src/sequelize-utils.d.ts} +0 -0
  170. /package/dist/esm/{token → server/src/token}/memory.d.ts +0 -0
  171. /package/dist/esm/{token → server/src/token}/sequelize.d.ts +0 -0
  172. /package/dist/esm/{token → server/src/token}/types.d.ts +0 -0
  173. /package/dist/esm/{token → server/src/token}/types.js +0 -0
  174. /package/dist/esm/{user → server/src/upload}/types.js +0 -0
  175. /package/dist/esm/{user → server/src/user}/memory.d.ts +0 -0
  176. /package/dist/esm/{user → server/src/user}/sequelize.d.ts +0 -0
  177. /package/dist/esm/{user → server/src/user}/types.d.ts +0 -0
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FixedWindowRateLimiter = void 0;
4
+ class FixedWindowRateLimiter {
5
+ constructor(maxKeys = 10000) {
6
+ this.maxKeys = maxKeys;
7
+ this.buckets = new Map();
8
+ }
9
+ check(key, max, windowMs) {
10
+ if (!key || max <= 0 || windowMs <= 0) {
11
+ return { allowed: true, retryAfterSec: 0 };
12
+ }
13
+ const now = Date.now();
14
+ const bucket = this.buckets.get(key);
15
+ if (!bucket || now - bucket.windowStartMs >= windowMs) {
16
+ this.buckets.delete(key);
17
+ this.buckets.set(key, { windowStartMs: now, count: 1 });
18
+ this.prune();
19
+ return { allowed: true, retryAfterSec: 0 };
20
+ }
21
+ bucket.count += 1;
22
+ // Refresh insertion order to keep active entries at the end for pruning.
23
+ this.buckets.delete(key);
24
+ this.buckets.set(key, bucket);
25
+ if (bucket.count <= max) {
26
+ return { allowed: true, retryAfterSec: 0 };
27
+ }
28
+ const retryAfterSec = Math.max(1, Math.ceil((bucket.windowStartMs + windowMs - now) / 1000));
29
+ return { allowed: false, retryAfterSec };
30
+ }
31
+ prune() {
32
+ while (this.buckets.size > this.maxKeys) {
33
+ const oldest = this.buckets.keys().next().value;
34
+ if (!oldest) {
35
+ break;
36
+ }
37
+ this.buckets.delete(oldest);
38
+ }
39
+ }
40
+ }
41
+ exports.FixedWindowRateLimiter = FixedWindowRateLimiter;
@@ -0,0 +1,11 @@
1
+ export type RateLimitDecision = {
2
+ allowed: boolean;
3
+ retryAfterSec: number;
4
+ };
5
+ export declare class FixedWindowRateLimiter {
6
+ private readonly maxKeys;
7
+ private readonly buckets;
8
+ constructor(maxKeys?: number);
9
+ check(key: string, max: number, windowMs: number): RateLimitDecision;
10
+ private prune;
11
+ }
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.OAuthStore = void 0;
4
+ /** Base contract for OAuth client/auth-code persistence backends. */
4
5
  class OAuthStore {
5
6
  }
6
7
  exports.OAuthStore = OAuthStore;
@@ -1,10 +1,17 @@
1
1
  import type { AuthCode, OAuthClient } from './types.js';
2
+ /** Base contract for OAuth client/auth-code persistence backends. */
2
3
  export declare abstract class OAuthStore {
4
+ /** Fetch an OAuth client by id. */
3
5
  abstract getClient(clientId: string): Promise<OAuthClient | null>;
6
+ /** Create an OAuth client. */
4
7
  abstract createClient(input: OAuthClient): Promise<OAuthClient>;
8
+ /** Verify an OAuth client secret. */
5
9
  abstract verifyClientSecret(clientId: string, secret: string | null): Promise<boolean>;
10
+ /** Persist an authorization code. */
6
11
  abstract createAuthCode(code: AuthCode): Promise<void>;
7
- abstract consumeAuthCode(code: string): Promise<AuthCode | null>;
12
+ /** Consume an authorization code once. When clientId is provided, only consume if it matches. */
13
+ abstract consumeAuthCode(code: string, clientId?: string): Promise<AuthCode | null>;
14
+ /** Close underlying resources. */
8
15
  abstract close(): Promise<void>;
9
16
  }
10
17
  export type { OAuthClient, AuthCode } from './types.js';
@@ -5,8 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.MemoryOAuthStore = void 0;
7
7
  const bcryptjs_1 = __importDefault(require("bcryptjs"));
8
- const user_id_js_1 = require("../auth-api/user-id.js");
9
- const base_js_1 = require("./base.js");
8
+ const user_id_js_1 = require("../auth-api/user-id.cjs");
9
+ const base_js_1 = require("./base.cjs");
10
10
  function cloneClient(client) {
11
11
  if (!client) {
12
12
  return null;
@@ -17,7 +17,7 @@ function cloneClient(client) {
17
17
  name: client.name,
18
18
  redirectUris: [...client.redirectUris],
19
19
  scope: client.scope ? [...client.scope] : undefined,
20
- metadata: client.metadata ? { ...client.metadata } : undefined,
20
+ metadata: client.metadata ? JSON.parse(JSON.stringify(client.metadata)) : undefined,
21
21
  firstParty: client.firstParty
22
22
  };
23
23
  }
@@ -89,11 +89,14 @@ class MemoryOAuthStore extends base_js_1.OAuthStore {
89
89
  this.codes.set(record.code, record);
90
90
  this.enforceCodeCapacity();
91
91
  }
92
- async consumeAuthCode(code) {
92
+ async consumeAuthCode(code, clientId) {
93
93
  const record = this.codes.get(code);
94
94
  if (!record) {
95
95
  return null;
96
96
  }
97
+ if (clientId && record.clientId !== clientId) {
98
+ return null;
99
+ }
97
100
  if (record.expiresAt.getTime() <= Date.now()) {
98
101
  this.codes.delete(code);
99
102
  return null;
@@ -15,7 +15,7 @@ export declare class MemoryOAuthStore extends OAuthStore {
15
15
  createClient(input: OAuthClient): Promise<OAuthClient>;
16
16
  verifyClientSecret(clientId: string, secret: string | null): Promise<boolean>;
17
17
  createAuthCode(code: AuthCode): Promise<void>;
18
- consumeAuthCode(code: string): Promise<AuthCode | null>;
18
+ consumeAuthCode(code: string, clientId?: string): Promise<AuthCode | null>;
19
19
  close(): Promise<void>;
20
20
  private enforceClientCapacity;
21
21
  private enforceCodeCapacity;
@@ -4,8 +4,8 @@ exports.OAuthCodeModel = exports.OAuthClientModel = exports.tableOptions = expor
4
4
  exports.initOAuthClientModel = initOAuthClientModel;
5
5
  exports.initOAuthCodeModel = initOAuthCodeModel;
6
6
  const sequelize_1 = require("sequelize");
7
- const sequelize_utils_js_1 = require("../sequelize-utils.js");
8
- var sequelize_utils_js_2 = require("../sequelize-utils.js");
7
+ const sequelize_utils_js_1 = require("../sequelize-utils.cjs");
8
+ var sequelize_utils_js_2 = require("../sequelize-utils.cjs");
9
9
  Object.defineProperty(exports, "integerIdType", { enumerable: true, get: function () { return sequelize_utils_js_2.integerIdType; } });
10
10
  Object.defineProperty(exports, "tableOptions", { enumerable: true, get: function () { return sequelize_utils_js_2.tableOptions; } });
11
11
  class OAuthClientModel extends sequelize_1.Model {
@@ -6,11 +6,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.SequelizeOAuthStore = exports.initOAuthCodeModel = exports.initOAuthClientModel = exports.OAuthCodeModel = exports.OAuthClientModel = void 0;
7
7
  const bcryptjs_1 = __importDefault(require("bcryptjs"));
8
8
  const sequelize_1 = require("sequelize");
9
- const user_id_js_1 = require("../auth-api/user-id.js");
10
- const sequelize_utils_js_1 = require("../sequelize-utils.js");
11
- const base_js_1 = require("./base.js");
12
- const models_js_1 = require("./models.js");
13
- var models_js_2 = require("./models.js");
9
+ const user_id_js_1 = require("../auth-api/user-id.cjs");
10
+ const sequelize_utils_js_1 = require("../sequelize-utils.cjs");
11
+ const base_js_1 = require("./base.cjs");
12
+ const models_js_1 = require("./models.cjs");
13
+ var models_js_2 = require("./models.cjs");
14
14
  Object.defineProperty(exports, "OAuthClientModel", { enumerable: true, get: function () { return models_js_2.OAuthClientModel; } });
15
15
  Object.defineProperty(exports, "OAuthCodeModel", { enumerable: true, get: function () { return models_js_2.OAuthCodeModel; } });
16
16
  Object.defineProperty(exports, "initOAuthClientModel", { enumerable: true, get: function () { return models_js_2.initOAuthClientModel; } });
@@ -107,13 +107,17 @@ class SequelizeOAuthStore extends base_js_1.OAuthStore {
107
107
  metadata: serializeMetadata(code.metadata)
108
108
  });
109
109
  }
110
- async consumeAuthCode(code) {
110
+ async consumeAuthCode(code, clientId) {
111
111
  const sequelize = this.codes.sequelize;
112
112
  if (!sequelize) {
113
113
  throw new Error('Code model is not bound to a Sequelize instance');
114
114
  }
115
115
  return sequelize.transaction({ isolationLevel: sequelize_1.Transaction.ISOLATION_LEVELS.READ_COMMITTED }, async (transaction) => {
116
- const model = await this.codes.findByPk(code, { transaction, lock: true });
116
+ const where = { code };
117
+ if (clientId) {
118
+ where.client_id = clientId;
119
+ }
120
+ const model = await this.codes.findOne({ where, transaction, lock: true });
117
121
  if (!model) {
118
122
  return null;
119
123
  }
@@ -23,7 +23,7 @@ export declare class SequelizeOAuthStore extends OAuthStore {
23
23
  createClient(input: OAuthClient): Promise<OAuthClient>;
24
24
  verifyClientSecret(clientId: string, clientSecret: string | null): Promise<boolean>;
25
25
  createAuthCode(code: AuthCode): Promise<void>;
26
- consumeAuthCode(code: string): Promise<AuthCode | null>;
26
+ consumeAuthCode(code: string, clientId?: string): Promise<AuthCode | null>;
27
27
  close(): Promise<void>;
28
28
  private toOAuthClient;
29
29
  private toAuthCode;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PasskeyStore = void 0;
4
+ /** Base contract for passkey credential/challenge persistence backends. */
4
5
  class PasskeyStore {
5
6
  }
6
7
  exports.PasskeyStore = PasskeyStore;
@@ -1,17 +1,28 @@
1
1
  import type { PasskeyChallengeRecord, PasskeyStorageAdapter, PasskeyUserDescriptor, StoredPasskeyCredential } from './types.js';
2
2
  import type { AuthIdentifier } from '../auth-api/types.js';
3
+ /** Base contract for passkey credential/challenge persistence backends. */
3
4
  export declare abstract class PasskeyStore implements PasskeyStorageAdapter {
5
+ /** Resolve a passkey user descriptor by id/login. */
4
6
  abstract resolveUser(params: {
5
7
  userId?: AuthIdentifier;
6
8
  login?: string;
7
9
  }): Promise<PasskeyUserDescriptor | null>;
10
+ /** List passkey credentials for a user. */
8
11
  abstract listUserCredentials(userId: AuthIdentifier): Promise<StoredPasskeyCredential[]>;
12
+ /** Delete a credential by binary/base64url id. */
9
13
  abstract deleteCredential(credentialId: Buffer | string): Promise<boolean>;
14
+ /** Find a credential by binary id. */
10
15
  abstract findCredentialById(credentialId: Buffer): Promise<StoredPasskeyCredential | null>;
16
+ /** Save a credential record. */
11
17
  abstract saveCredential(record: StoredPasskeyCredential): Promise<void>;
18
+ /** Update signature counter for a credential. */
12
19
  abstract updateCredentialCounter(credentialId: Buffer, counter: number): Promise<void>;
20
+ /** Save a challenge record. */
13
21
  abstract saveChallenge(record: PasskeyChallengeRecord): Promise<void>;
22
+ /** Read a challenge without consuming it. */
14
23
  abstract getChallenge(challenge: string): Promise<PasskeyChallengeRecord | null>;
24
+ /** Consume and return a challenge. */
15
25
  abstract consumeChallenge(challenge: string): Promise<PasskeyChallengeRecord | null>;
26
+ /** Cleanup expired challenges. */
16
27
  abstract cleanupChallenges(now: Date): Promise<void>;
17
28
  }
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MemoryPasskeyStore = void 0;
4
- const user_id_js_1 = require("../auth-api/user-id.js");
5
- const base_js_1 = require("./base.js");
4
+ const user_id_js_1 = require("../auth-api/user-id.cjs");
5
+ const base_js_1 = require("./base.cjs");
6
6
  function encodeCredentialId(value) {
7
7
  return Buffer.isBuffer(value) ? value.toString('base64') : value;
8
8
  }
@@ -4,7 +4,7 @@ exports.PasskeyChallengeModel = exports.PasskeyCredentialModel = void 0;
4
4
  exports.initPasskeyCredentialModel = initPasskeyCredentialModel;
5
5
  exports.initPasskeyChallengeModel = initPasskeyChallengeModel;
6
6
  const sequelize_1 = require("sequelize");
7
- const sequelize_utils_js_1 = require("../sequelize-utils.js");
7
+ const sequelize_utils_js_1 = require("../sequelize-utils.cjs");
8
8
  class PasskeyCredentialModel extends sequelize_1.Model {
9
9
  }
10
10
  exports.PasskeyCredentialModel = PasskeyCredentialModel;
@@ -2,9 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SequelizePasskeyStore = void 0;
4
4
  const sequelize_1 = require("sequelize");
5
- const user_id_js_1 = require("../auth-api/user-id.js");
6
- const base_js_1 = require("./base.js");
7
- const models_js_1 = require("./models.js");
5
+ const user_id_js_1 = require("../auth-api/user-id.cjs");
6
+ const base_js_1 = require("./base.cjs");
7
+ const models_js_1 = require("./models.cjs");
8
8
  function encodeCredentialId(value) {
9
9
  return Buffer.isBuffer(value) ? value.toString('base64') : value;
10
10
  }
@@ -161,7 +161,19 @@ class PasskeyService {
161
161
  async listUserCredentials(userId) {
162
162
  return this.adapter.listUserCredentials(userId);
163
163
  }
164
- async deleteCredential(credentialId) {
164
+ async deleteCredential(credentialId, userId) {
165
+ if (userId !== undefined) {
166
+ const credentials = await this.adapter.listUserCredentials(userId);
167
+ const target = Buffer.isBuffer(credentialId) ? credentialId : Buffer.from(String(credentialId), 'base64');
168
+ const owns = credentials.some((c) => {
169
+ const stored = Buffer.isBuffer(c.credentialId)
170
+ ? c.credentialId
171
+ : Buffer.from(String(c.credentialId), 'base64');
172
+ return stored.equals(target);
173
+ });
174
+ if (!owns)
175
+ return false;
176
+ }
165
177
  return this.adapter.deleteCredential(credentialId);
166
178
  }
167
179
  async createChallenge(params) {
@@ -287,7 +299,9 @@ class PasskeyService {
287
299
  const attestationResponse = params.response.response;
288
300
  const credentialIdPrimary = toBufferOrNull(registrationInfo.credentialID);
289
301
  const credentialIdFallback = toBufferOrNull(params.response.id);
290
- const credentialId = credentialIdPrimary && credentialIdPrimary.length > 0 ? credentialIdPrimary : credentialIdFallback;
302
+ const credentialId = credentialIdPrimary && credentialIdPrimary.length > 0
303
+ ? credentialIdPrimary
304
+ : (credentialIdFallback ?? Buffer.alloc(0));
291
305
  const publicKeyPrimary = toBufferOrNull(registrationInfo.credentialPublicKey);
292
306
  let publicKeyFallback = toBufferOrNull(attestationResponse?.publicKey);
293
307
  if ((!publicKeyPrimary || publicKeyPrimary.length === 0) && attestationResponse?.attestationObject) {
@@ -330,7 +344,7 @@ class PasskeyService {
330
344
  publicKey: storedPublicKey,
331
345
  counter: registrationInfo.counter ?? 0,
332
346
  transports: sanitizeTransports(params.response.transports),
333
- backedUp: registrationInfo.credentialDeviceType === 'multiDevice',
347
+ backedUp: registrationInfo.credentialBackedUp ?? registrationInfo.credentialDeviceType === 'multiDevice',
334
348
  deviceType: registrationInfo.credentialDeviceType,
335
349
  label: toOptionalString(params.label),
336
350
  createdDomain: toOptionalString(params.domain),
@@ -8,7 +8,7 @@ export declare class PasskeyService {
8
8
  private readonly logger;
9
9
  constructor(config: PasskeyServiceConfig, adapter: PasskeyStorageAdapter, logger?: Logger);
10
10
  listUserCredentials(userId: AuthIdentifier): Promise<StoredPasskeyCredential[]>;
11
- deleteCredential(credentialId: Buffer | string): Promise<boolean>;
11
+ deleteCredential(credentialId: Buffer | string, userId?: AuthIdentifier): Promise<boolean>;
12
12
  createChallenge(params: PasskeyChallengeParams): Promise<PasskeyChallenge>;
13
13
  verifyResponse(params: PasskeyVerificationParams): Promise<PasskeyVerificationResult>;
14
14
  private createRegistrationChallenge;
@@ -48,10 +48,9 @@ function decodeStringArray(raw) {
48
48
  }
49
49
  }
50
50
  catch {
51
- // ignore malformed values
51
+ // Malformed JSON — return empty rather than guessing via whitespace split.
52
+ // A whitespace-split fallback could silently grant unintended permissions
53
+ // if a scope/role column contains a corrupted value like "admin user".
52
54
  }
53
- return raw
54
- .split(/\s+/)
55
- .map((entry) => entry.trim())
56
- .filter((entry) => entry.length > 0);
55
+ return [];
57
56
  }
@@ -42,6 +42,9 @@ function normalizeTokenInternal(input) {
42
42
  const userId = String(input.userId);
43
43
  const ruid = input.ruid === undefined || input.ruid === null ? undefined : String(input.ruid);
44
44
  const expires = input.expires ? new Date(input.expires) : new Date(Date.now() + 30 * 24 * 60 * 60 * 1000);
45
+ if (Number.isNaN(expires.getTime())) {
46
+ throw new Error(`Invalid token expiry value: ${String(input.expires)}`);
47
+ }
45
48
  const issuedAt = input.issuedAt ? new Date(input.issuedAt) : new Date();
46
49
  const lastSeenAt = input.lastSeenAt ? new Date(input.lastSeenAt) : issuedAt;
47
50
  const scope = normalizeScope(input.scope);
@@ -71,6 +74,7 @@ function normalizeTokenInternal(input) {
71
74
  sessionCookie
72
75
  };
73
76
  }
77
+ /** Base contract for token/session persistence backends plus shared JWT helpers. */
74
78
  class TokenStore {
75
79
  // Instance helpers
76
80
  normalizeToken(token) {
@@ -17,20 +17,27 @@ export interface JwtDecodeResult<T> {
17
17
  data?: T;
18
18
  error?: string;
19
19
  }
20
+ /** Base contract for token/session persistence backends plus shared JWT helpers. */
20
21
  export declare abstract class TokenStore {
22
+ /** Create/persist a token record. */
21
23
  abstract save(record: Token): Promise<void>;
24
+ /** Read a token record by partial query. */
22
25
  abstract get(query: Partial<Token>, opts?: {
23
26
  includeExpired?: boolean;
24
27
  }): Promise<Token | null>;
28
+ /** Delete token records matching a partial query. */
25
29
  abstract delete(query: Partial<Token>): Promise<number>;
30
+ /** Update a token identified by refresh token. */
26
31
  abstract update(update: Partial<Token> & {
27
32
  refreshToken: string;
28
33
  }): Promise<boolean>;
34
+ /** List tokens for a specific user. */
29
35
  abstract list(userId: string | number, opts?: {
30
36
  limit?: number;
31
37
  offset?: number;
32
38
  includeExpired?: boolean;
33
39
  }): Promise<Token[]>;
40
+ /** Close underlying resources. */
34
41
  abstract close(): Promise<void>;
35
42
  normalizeToken(token: Partial<Token>): Token;
36
43
  jwtSign(payload: JwtSignPayload, secret: string, expiresInSeconds: number, options?: SignOptions): JwtSignResult;
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MemoryTokenStore = void 0;
4
- const base_js_1 = require("./base.js");
4
+ const base_js_1 = require("./base.cjs");
5
5
  function comparableUserId(value) {
6
6
  if (value === undefined) {
7
7
  return undefined;
@@ -18,16 +18,16 @@ function cloneToken(record) {
18
18
  };
19
19
  }
20
20
  function matchesQuery(record, query, includeExpired) {
21
- if (query.refreshToken && record.refreshToken !== query.refreshToken) {
21
+ if (query.refreshToken !== undefined && record.refreshToken !== query.refreshToken) {
22
22
  return false;
23
23
  }
24
- if (query.accessToken && record.accessToken !== query.accessToken) {
24
+ if (query.accessToken !== undefined && record.accessToken !== query.accessToken) {
25
25
  return false;
26
26
  }
27
27
  if (query.userId !== undefined && comparableUserId(record.userId) !== comparableUserId(query.userId)) {
28
28
  return false;
29
29
  }
30
- if (query.clientId && record.clientId !== query.clientId) {
30
+ if (query.clientId !== undefined && record.clientId !== query.clientId) {
31
31
  return false;
32
32
  }
33
33
  if (query.domain !== undefined && (record.domain ?? '') !== (query.domain ?? '')) {
@@ -39,11 +39,14 @@ function matchesQuery(record, query, includeExpired) {
39
39
  if (query.loginType !== undefined && record.loginType !== (query.loginType ?? undefined)) {
40
40
  return false;
41
41
  }
42
- if (query.label && record.label !== query.label) {
42
+ if (query.label !== undefined && record.label !== query.label) {
43
43
  return false;
44
44
  }
45
- if (!includeExpired && (record.expires ?? new Date(0)).getTime() < Date.now()) {
46
- return false;
45
+ if (!includeExpired) {
46
+ const expiresTime = record.expires ? record.expires.getTime() : Infinity;
47
+ if (expiresTime < Date.now()) {
48
+ return false;
49
+ }
47
50
  }
48
51
  return true;
49
52
  }
@@ -166,16 +169,14 @@ class MemoryTokenStore extends base_js_1.TokenStore {
166
169
  const merged = { ...token };
167
170
  const maybeAssign = (key) => {
168
171
  const value = params[key];
169
- if (value !== undefined) {
172
+ if (value !== undefined && value !== null) {
170
173
  merged[key] = value;
171
174
  }
172
175
  };
173
- if (params.accessToken !== undefined && params.accessToken !== null) {
174
- merged.accessToken = params.accessToken;
175
- }
176
- if (params.expires !== undefined && params.expires !== null) {
177
- merged.expires = params.expires;
178
- }
176
+ maybeAssign('accessToken');
177
+ maybeAssign('expires');
178
+ maybeAssign('issuedAt');
179
+ maybeAssign('lastSeenAt');
179
180
  maybeAssign('scope');
180
181
  maybeAssign('label');
181
182
  maybeAssign('domain');
@@ -186,12 +187,6 @@ class MemoryTokenStore extends base_js_1.TokenStore {
186
187
  maybeAssign('os');
187
188
  maybeAssign('refreshTtlSeconds');
188
189
  maybeAssign('loginType');
189
- if (params.issuedAt !== undefined && params.issuedAt !== null) {
190
- merged.issuedAt = params.issuedAt;
191
- }
192
- if (params.lastSeenAt !== undefined && params.lastSeenAt !== null) {
193
- merged.lastSeenAt = params.lastSeenAt;
194
- }
195
190
  maybeAssign('sessionCookie');
196
191
  const normalized = this.normalizeToken(merged);
197
192
  const previousUserId = token.userId;
@@ -2,9 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SequelizeTokenStore = void 0;
4
4
  const sequelize_1 = require("sequelize");
5
- const user_id_js_1 = require("../auth-api/user-id.js");
6
- const sequelize_utils_js_1 = require("../sequelize-utils.js");
7
- const base_js_1 = require("./base.js");
5
+ const user_id_js_1 = require("../auth-api/user-id.cjs");
6
+ const sequelize_utils_js_1 = require("../sequelize-utils.cjs");
7
+ const base_js_1 = require("./base.cjs");
8
8
  class TokenModel extends sequelize_1.Model {
9
9
  }
10
10
  function initTokenModel(sequelize, options = {}) {
@@ -167,12 +167,20 @@ class SequelizeTokenStore extends base_js_1.TokenStore {
167
167
  await this.Tokens.destroy({ where: removalWhere, transaction });
168
168
  // Access/refresh columns are unique. Remove stale collisions before insert to avoid
169
169
  // transient uniqueness failures during retries/rotation edge-cases.
170
- await this.Tokens.destroy({
171
- where: {
172
- [sequelize_1.Op.or]: [{ access: normalized.accessToken ?? '' }, { refresh: normalized.refreshToken }]
173
- },
174
- transaction
175
- });
170
+ // Only include non-empty token values to prevent matching unrelated rows.
171
+ const collisionConditions = [];
172
+ if (normalized.accessToken) {
173
+ collisionConditions.push({ access: normalized.accessToken });
174
+ }
175
+ if (normalized.refreshToken) {
176
+ collisionConditions.push({ refresh: normalized.refreshToken });
177
+ }
178
+ if (collisionConditions.length > 0) {
179
+ await this.Tokens.destroy({
180
+ where: { [sequelize_1.Op.or]: collisionConditions },
181
+ transaction
182
+ });
183
+ }
176
184
  await this.Tokens.create({
177
185
  user_id: resolvedUserId,
178
186
  real_user_id: resolvedRealUserId,
@@ -313,8 +321,14 @@ class SequelizeTokenStore extends base_js_1.TokenStore {
313
321
  if (Object.keys(updates).length === 0) {
314
322
  return false;
315
323
  }
316
- const [updated] = await this.Tokens.update(updates, { where });
317
- return updated > 0;
324
+ const sequelize = this.Tokens.sequelize;
325
+ if (!sequelize) {
326
+ throw new Error('Token model is not bound to a Sequelize instance');
327
+ }
328
+ return sequelize.transaction(async (transaction) => {
329
+ const [updated] = await this.Tokens.update(updates, { where, transaction });
330
+ return updated > 0;
331
+ });
318
332
  }
319
333
  async list(userId, opts = {}) {
320
334
  const where = { user_id: this.normalizeUserId(userId) };
@@ -0,0 +1,92 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MemoryTusUploadStore = exports.TusUploadExceedsLengthError = exports.TusUploadOffsetError = void 0;
4
+ const node_crypto_1 = require("node:crypto");
5
+ function cloneRecord(record) {
6
+ return {
7
+ ...record,
8
+ metadata: { ...record.metadata },
9
+ createdAt: new Date(record.createdAt),
10
+ updatedAt: new Date(record.updatedAt),
11
+ ...(record.completedAt ? { completedAt: new Date(record.completedAt) } : {})
12
+ };
13
+ }
14
+ class TusUploadOffsetError extends Error {
15
+ constructor(currentOffset) {
16
+ super('Upload offset does not match current offset');
17
+ this.currentOffset = currentOffset;
18
+ }
19
+ }
20
+ exports.TusUploadOffsetError = TusUploadOffsetError;
21
+ class TusUploadExceedsLengthError extends Error {
22
+ constructor() {
23
+ super('Upload exceeds declared length');
24
+ }
25
+ }
26
+ exports.TusUploadExceedsLengthError = TusUploadExceedsLengthError;
27
+ class MemoryTusUploadStore {
28
+ constructor() {
29
+ this.uploads = new Map();
30
+ this.chunks = new Map();
31
+ }
32
+ async createUpload(input) {
33
+ const id = input.id?.trim() || (0, node_crypto_1.randomUUID)();
34
+ if (this.uploads.has(id)) {
35
+ throw new Error(`Upload ${id} already exists`);
36
+ }
37
+ const now = new Date();
38
+ const record = {
39
+ id,
40
+ length: Math.max(0, Math.floor(input.length)),
41
+ offset: 0,
42
+ metadata: { ...input.metadata },
43
+ ...(input.userId ? { userId: input.userId } : {}),
44
+ createdAt: now,
45
+ updatedAt: now
46
+ };
47
+ this.uploads.set(id, record);
48
+ this.chunks.set(id, []);
49
+ return cloneRecord(record);
50
+ }
51
+ async getUpload(uploadId) {
52
+ const found = this.uploads.get(uploadId);
53
+ return found ? cloneRecord(found) : null;
54
+ }
55
+ async appendUpload(input) {
56
+ const current = this.uploads.get(input.uploadId);
57
+ if (!current) {
58
+ throw new Error('Upload not found');
59
+ }
60
+ if (input.offset !== current.offset) {
61
+ throw new TusUploadOffsetError(current.offset);
62
+ }
63
+ const nextOffset = current.offset + input.chunk.length;
64
+ if (nextOffset > current.length) {
65
+ throw new TusUploadExceedsLengthError();
66
+ }
67
+ const now = new Date();
68
+ const updated = {
69
+ ...current,
70
+ offset: nextOffset,
71
+ updatedAt: now,
72
+ ...(nextOffset === current.length ? { completedAt: now } : {})
73
+ };
74
+ this.uploads.set(input.uploadId, updated);
75
+ if (input.chunk.length > 0) {
76
+ this.chunks.get(input.uploadId)?.push(Buffer.from(input.chunk));
77
+ }
78
+ return cloneRecord(updated);
79
+ }
80
+ async deleteUpload(uploadId) {
81
+ this.chunks.delete(uploadId);
82
+ return this.uploads.delete(uploadId);
83
+ }
84
+ readUpload(uploadId) {
85
+ const chunks = this.chunks.get(uploadId);
86
+ if (!chunks) {
87
+ return null;
88
+ }
89
+ return Buffer.concat(chunks);
90
+ }
91
+ }
92
+ exports.MemoryTusUploadStore = MemoryTusUploadStore;
@@ -0,0 +1,17 @@
1
+ import type { TusAppendInput, TusCreateUploadInput, TusUploadRecord, TusUploadStore } from './types.js';
2
+ export declare class TusUploadOffsetError extends Error {
3
+ readonly currentOffset: number;
4
+ constructor(currentOffset: number);
5
+ }
6
+ export declare class TusUploadExceedsLengthError extends Error {
7
+ constructor();
8
+ }
9
+ export declare class MemoryTusUploadStore implements TusUploadStore {
10
+ private readonly uploads;
11
+ private readonly chunks;
12
+ createUpload(input: TusCreateUploadInput): Promise<TusUploadRecord>;
13
+ getUpload(uploadId: string): Promise<TusUploadRecord | null>;
14
+ appendUpload(input: TusAppendInput): Promise<TusUploadRecord>;
15
+ deleteUpload(uploadId: string): Promise<boolean>;
16
+ readUpload(uploadId: string): Buffer | null;
17
+ }