@technomoron/api-server-base 2.0.0-beta.14 → 2.0.0-beta.16

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 (45) hide show
  1. package/README.txt +16 -0
  2. package/dist/cjs/api-module.d.ts +3 -2
  3. package/dist/cjs/api-server-base.cjs +67 -18
  4. package/dist/cjs/api-server-base.d.ts +17 -15
  5. package/dist/cjs/auth-api/auth-module.js +10 -10
  6. package/dist/cjs/auth-api/module.d.ts +1 -1
  7. package/dist/cjs/auth-api/sql-auth-store.d.ts +11 -1
  8. package/dist/cjs/auth-api/sql-auth-store.js +35 -3
  9. package/dist/cjs/oauth/models.d.ts +6 -2
  10. package/dist/cjs/oauth/models.js +17 -6
  11. package/dist/cjs/oauth/sequelize.d.ts +13 -4
  12. package/dist/cjs/oauth/sequelize.js +27 -8
  13. package/dist/cjs/passkey/models.d.ts +6 -2
  14. package/dist/cjs/passkey/models.js +15 -4
  15. package/dist/cjs/passkey/sequelize.d.ts +7 -2
  16. package/dist/cjs/passkey/sequelize.js +22 -6
  17. package/dist/cjs/passkey/service.js +11 -11
  18. package/dist/cjs/token/base.d.ts +2 -1
  19. package/dist/cjs/token/sequelize.d.ts +4 -1
  20. package/dist/cjs/token/sequelize.js +26 -7
  21. package/dist/cjs/user/sequelize.d.ts +7 -2
  22. package/dist/cjs/user/sequelize.js +18 -5
  23. package/dist/esm/api-module.d.ts +3 -2
  24. package/dist/esm/api-server-base.d.ts +17 -15
  25. package/dist/esm/api-server-base.js +67 -18
  26. package/dist/esm/auth-api/auth-module.js +10 -10
  27. package/dist/esm/auth-api/module.d.ts +1 -1
  28. package/dist/esm/auth-api/sql-auth-store.d.ts +11 -1
  29. package/dist/esm/auth-api/sql-auth-store.js +35 -3
  30. package/dist/esm/oauth/models.d.ts +6 -2
  31. package/dist/esm/oauth/models.js +17 -6
  32. package/dist/esm/oauth/sequelize.d.ts +13 -4
  33. package/dist/esm/oauth/sequelize.js +27 -8
  34. package/dist/esm/passkey/models.d.ts +6 -2
  35. package/dist/esm/passkey/models.js +15 -4
  36. package/dist/esm/passkey/sequelize.d.ts +7 -2
  37. package/dist/esm/passkey/sequelize.js +22 -6
  38. package/dist/esm/passkey/service.js +11 -11
  39. package/dist/esm/token/base.d.ts +2 -1
  40. package/dist/esm/token/sequelize.d.ts +4 -1
  41. package/dist/esm/token/sequelize.js +26 -7
  42. package/dist/esm/user/sequelize.d.ts +7 -2
  43. package/dist/esm/user/sequelize.js +18 -5
  44. package/docs/swagger/openapi.json +1 -1
  45. package/package.json +11 -10
package/README.txt CHANGED
@@ -141,6 +141,22 @@ Use your storage adapter's filterUser helper to trim sensitive data before retur
141
141
  Provide your own authorize method to enforce role based access control using the ApiAuthClass enum.
142
142
  Create feature modules by extending ApiModule. Use the optional checkConfig hook to validate prerequisites before routes mount.
143
143
 
144
+ Sequelize Table Prefixes
145
+ ------------------------
146
+ Sequelize-backed stores accept `tablePrefix` to prepend to the built-in table names (`users`, `jwttokens`, `passkey_credentials`, `passkey_challenges`, `oauth_clients`, `oauth_codes`).
147
+
148
+ SqlAuthStore supports both a global prefix (`tablePrefix`) and per-module overrides (`tablePrefixes.user|token|passkey|oauth`). When present, `tokenStoreOptions.tablePrefix` and `oauthStoreOptions.tablePrefix` take precedence.
149
+
150
+ Example:
151
+
152
+ const store = new SqlAuthStore({
153
+ sequelize,
154
+ tablePrefix: 'myapp_'
155
+ });
156
+ // Creates tables like myapp_users, myapp_jwttokens, myapp_oauth_clients, ...
157
+
158
+ If you need a different base name (for example `myapp_tokens` instead of `myapp_jwttokens`), pass a custom model or model factory to the store and set the `tableName` yourself.
159
+
144
160
  Custom Express Endpoints
145
161
  ------------------------
146
162
  ApiModule routes run inside the tuple wrapper (always responding with a standardized JSON envelope). For endpoints that need raw Express control (streaming, webhooks, tus uploads, etc.), mount your own handlers directly.
@@ -1,5 +1,6 @@
1
1
  import type { ApiRequest } from './api-server-base.js';
2
- export type ApiHandler = (apiReq: ApiRequest) => Promise<[number] | [number, any] | [number, any, string]>;
2
+ export type ApiHandlerResult<Data = unknown> = [number] | [number, Data] | [number, Data, string];
3
+ export type ApiHandler<Data = unknown> = (apiReq: ApiRequest) => Promise<ApiHandlerResult<Data>>;
3
4
  export type ApiAuthType = 'none' | 'maybe' | 'yes' | 'strict' | 'apikey';
4
5
  export type ApiAuthClass = 'any' | 'admin';
5
6
  export interface ApiKey {
@@ -14,7 +15,7 @@ export type ApiRoute = {
14
15
  req: ApiAuthClass;
15
16
  };
16
17
  };
17
- export declare class ApiModule<T> {
18
+ export declare class ApiModule<T = unknown> {
18
19
  server: T;
19
20
  namespace: string;
20
21
  mountpath: string;
@@ -49,11 +49,14 @@ function guess_exception_text(error, defMsg = 'Unknown Error') {
49
49
  msg.push(error);
50
50
  }
51
51
  else if (error && typeof error === 'object') {
52
- if (typeof error.message === 'string' && error.message.trim() !== '') {
53
- msg.push(error.message);
52
+ const errorDetails = error;
53
+ if (typeof errorDetails.message === 'string' && errorDetails.message.trim() !== '') {
54
+ msg.push(errorDetails.message);
54
55
  }
55
- if (error.parent && typeof error.parent.message === 'string' && error.parent.message.trim() !== '') {
56
- msg.push(error.parent.message);
56
+ if (errorDetails.parent &&
57
+ typeof errorDetails.parent.message === 'string' &&
58
+ errorDetails.parent.message.trim() !== '') {
59
+ msg.push(errorDetails.parent.message);
57
60
  }
58
61
  }
59
62
  return msg.length > 0 ? msg.join(' / ') : defMsg;
@@ -367,6 +370,7 @@ function fillConfig(config) {
367
370
  class ApiServer {
368
371
  constructor(config = {}) {
369
372
  this.currReq = null;
373
+ this.serverAuthAdapter = null;
370
374
  this.apiNotFoundHandler = null;
371
375
  this.tokenStoreAdapter = null;
372
376
  this.userStoreAdapter = null;
@@ -387,7 +391,7 @@ class ApiServer {
387
391
  this.passkeyServiceAdapter = passkeyService ?? null;
388
392
  this.oauthStoreAdapter = oauthStore ?? null;
389
393
  this.canImpersonateAdapter = canImpersonate ?? null;
390
- this.storageAdapter = this;
394
+ this.storageAdapter = this.getServerAuthAdapter();
391
395
  }
392
396
  this.app = (0, express_1.default)();
393
397
  if (config.uploadPath) {
@@ -428,9 +432,9 @@ class ApiServer {
428
432
  }
429
433
  setTokenStore(store) {
430
434
  this.tokenStoreAdapter = store;
431
- // If using direct stores, expose self as the auth storage.
435
+ // If using direct stores, expose the server-backed auth adapter.
432
436
  if (this.userStoreAdapter) {
433
- this.storageAdapter = this;
437
+ this.storageAdapter = this.getServerAuthAdapter();
434
438
  }
435
439
  return this;
436
440
  }
@@ -467,6 +471,33 @@ class ApiServer {
467
471
  }
468
472
  return this.oauthStoreAdapter;
469
473
  }
474
+ getServerAuthAdapter() {
475
+ if (this.serverAuthAdapter) {
476
+ return this.serverAuthAdapter;
477
+ }
478
+ const server = this;
479
+ this.serverAuthAdapter = {
480
+ getUser: (identifier) => server.getUser(identifier),
481
+ getUserPasswordHash: (user) => server.getUserPasswordHash(user),
482
+ getUserId: (user) => server.getUserId(user),
483
+ filterUser: (user) => server.filterUser(user),
484
+ verifyPassword: (password, hash) => server.verifyPassword(password, hash),
485
+ storeToken: (data) => server.storeToken(data),
486
+ getToken: (query, opts) => server.getToken(query, opts),
487
+ deleteToken: (query) => server.deleteToken(query),
488
+ updateToken: (updates) => server.updateToken(updates),
489
+ createPasskeyChallenge: (params) => server.createPasskeyChallenge(params),
490
+ verifyPasskeyResponse: (params) => server.verifyPasskeyResponse(params),
491
+ listUserCredentials: (userId) => server.listUserCredentials(userId),
492
+ deletePasskeyCredential: (credentialId) => server.deletePasskeyCredential(credentialId),
493
+ getClient: (clientId) => server.getClient(clientId),
494
+ verifyClientSecret: (client, clientSecret) => server.verifyClientSecret(client, clientSecret),
495
+ createAuthCode: (request) => server.createAuthCode(request),
496
+ consumeAuthCode: (code, clientId) => server.consumeAuthCode(code, clientId),
497
+ canImpersonate: (params) => server.canImpersonate(params)
498
+ };
499
+ return this.serverAuthAdapter;
500
+ }
470
501
  // AuthAdapter-compatible helpers (used by AuthModule)
471
502
  async getUser(identifier) {
472
503
  return this.userStoreAdapter ? this.userStoreAdapter.findUser(identifier) : null;
@@ -487,8 +518,9 @@ class ApiServer {
487
518
  if (this.tokenStoreAdapter) {
488
519
  return this.tokenStoreAdapter.save(data);
489
520
  }
490
- if (typeof this.storageAdapter.storeToken === 'function') {
491
- return this.storageAdapter.storeToken(data);
521
+ const storage = this.storageAdapter;
522
+ if (typeof storage.storeToken === 'function') {
523
+ return storage.storeToken(data);
492
524
  }
493
525
  throw new Error('Token store is not configured');
494
526
  }
@@ -501,8 +533,9 @@ class ApiServer {
501
533
  if (this.tokenStoreAdapter) {
502
534
  return this.tokenStoreAdapter.get(normalized, opts);
503
535
  }
504
- if (typeof this.storageAdapter.getToken === 'function') {
505
- return this.storageAdapter.getToken(normalized, opts);
536
+ const storage = this.storageAdapter;
537
+ if (typeof storage.getToken === 'function') {
538
+ return storage.getToken(normalized, opts);
506
539
  }
507
540
  return null;
508
541
  }
@@ -515,8 +548,9 @@ class ApiServer {
515
548
  if (this.tokenStoreAdapter) {
516
549
  return this.tokenStoreAdapter.delete(normalized);
517
550
  }
518
- if (typeof this.storageAdapter.deleteToken === 'function') {
519
- return this.storageAdapter.deleteToken(normalized);
551
+ const storage = this.storageAdapter;
552
+ if (typeof storage.deleteToken === 'function') {
553
+ return storage.deleteToken(normalized);
520
554
  }
521
555
  return 0;
522
556
  }
@@ -589,8 +623,9 @@ class ApiServer {
589
623
  if (this.tokenStoreAdapter) {
590
624
  return this.tokenStoreAdapter.update(updates);
591
625
  }
592
- if (typeof this.storageAdapter.updateToken === 'function') {
593
- return this.storageAdapter.updateToken(updates);
626
+ const storage = this.storageAdapter;
627
+ if (typeof storage.updateToken === 'function') {
628
+ return storage.updateToken(updates);
594
629
  }
595
630
  return false;
596
631
  }
@@ -723,7 +758,7 @@ class ApiServer {
723
758
  if (!this.apiNotFoundHandler) {
724
759
  return;
725
760
  }
726
- const stack = this.app?._router?.stack;
761
+ const stack = this.app._router?.stack;
727
762
  if (!stack) {
728
763
  return;
729
764
  }
@@ -1216,7 +1251,8 @@ class ApiServer {
1216
1251
  api(module) {
1217
1252
  const router = express_1.default.Router();
1218
1253
  module.server = this;
1219
- if (module?.moduleType === 'auth') {
1254
+ const moduleType = module.moduleType;
1255
+ if (moduleType === 'auth') {
1220
1256
  this.authModule(module);
1221
1257
  }
1222
1258
  module.checkConfig();
@@ -1226,7 +1262,20 @@ class ApiServer {
1226
1262
  module.mountpath = mountPath;
1227
1263
  module.defineRoutes().forEach((r) => {
1228
1264
  const handler = this.handle_request(r.handler, r.auth);
1229
- router[r.method](r.path, handler);
1265
+ switch (r.method) {
1266
+ case 'get':
1267
+ router.get(r.path, handler);
1268
+ break;
1269
+ case 'post':
1270
+ router.post(r.path, handler);
1271
+ break;
1272
+ case 'put':
1273
+ router.put(r.path, handler);
1274
+ break;
1275
+ case 'delete':
1276
+ router.delete(r.path, handler);
1277
+ break;
1278
+ }
1230
1279
  if (this.config.debug) {
1231
1280
  console.log(`Adding ${mountPath}${r.path} (${r.method.toUpperCase()})`);
1232
1281
  }
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import { Application, Request, Response, type ErrorRequestHandler, type RequestHandler } from 'express';
8
8
  import { ApiModule } from './api-module.js';
9
- import { TokenStore, type JwtDecodeResult, type JwtSignResult, type JwtVerifyResult } from './token/base.js';
9
+ import { TokenStore, type JwtDecodeResult, type JwtSignPayload, type JwtSignResult, type JwtVerifyResult } from './token/base.js';
10
10
  import type { ApiAuthClass, ApiAuthType, ApiKey } from './api-module.js';
11
11
  import type { AuthProviderModule } from './auth-api/module.js';
12
12
  import type { AuthAdapter, AuthIdentifier } from './auth-api/types.js';
@@ -32,7 +32,7 @@ export interface ApiTokenData extends JwtPayload, Partial<Token> {
32
32
  exp?: number;
33
33
  }
34
34
  export interface ApiRequest {
35
- server: any;
35
+ server: ApiServer;
36
36
  req: ExtendedReq;
37
37
  res: Response;
38
38
  tokenData?: ApiTokenData | null;
@@ -64,7 +64,7 @@ export interface ClientInfo extends ClientAgentProfile {
64
64
  ipchain: string[];
65
65
  }
66
66
  export interface ApiServerAuthStores {
67
- userStore: UserStore<any, any>;
67
+ userStore: UserStore<unknown, unknown>;
68
68
  tokenStore: TokenStore;
69
69
  passkeyService?: PasskeyService;
70
70
  oauthStore?: OAuthStore;
@@ -77,13 +77,13 @@ export { ApiModule } from './api-module.js';
77
77
  export type { ApiHandler, ApiAuthType, ApiAuthClass, ApiRoute, ApiKey } from './api-module.js';
78
78
  export interface ApiErrorParams {
79
79
  code?: number;
80
- message?: any;
81
- data?: any;
80
+ message?: unknown;
81
+ data?: unknown;
82
82
  errors?: Record<string, string>;
83
83
  }
84
84
  export declare class ApiError extends Error {
85
85
  code: number;
86
- data: any;
86
+ data: unknown;
87
87
  errors: Record<string, string>;
88
88
  constructor({ code, message, data, errors }: ApiErrorParams);
89
89
  }
@@ -123,6 +123,7 @@ export declare class ApiServer {
123
123
  private readonly apiBasePath;
124
124
  private storageAdapter;
125
125
  private moduleAdapter;
126
+ private serverAuthAdapter;
126
127
  private apiNotFoundHandler;
127
128
  private tokenStoreAdapter;
128
129
  private userStoreAdapter;
@@ -141,8 +142,8 @@ export declare class ApiServer {
141
142
  * @deprecated Use {@link ApiServer.authModule} instead.
142
143
  */
143
144
  useAuthModule<UserRow>(module: AuthProviderModule<UserRow>): this;
144
- getAuthStorage(): AuthAdapter<any, any>;
145
- getAuthModule(): AuthProviderModule<any>;
145
+ getAuthStorage<UserRow = unknown, SafeUser = unknown>(): AuthAdapter<UserRow, SafeUser>;
146
+ getAuthModule<UserRow = unknown>(): AuthProviderModule<UserRow>;
146
147
  setTokenStore(store: TokenStore): this;
147
148
  getTokenStore(): TokenStore | null;
148
149
  private ensureUserStore;
@@ -151,10 +152,11 @@ export declare class ApiServer {
151
152
  listUserCredentials(userId: AuthIdentifier): Promise<StoredPasskeyCredential[]>;
152
153
  deletePasskeyCredential(credentialId: Buffer | string): Promise<boolean>;
153
154
  private ensureOAuthStore;
154
- getUser(identifier: AuthIdentifier): Promise<any | null>;
155
- getUserPasswordHash(user: any): string;
156
- getUserId(user: any): AuthIdentifier;
157
- filterUser(user: any): any;
155
+ private getServerAuthAdapter;
156
+ getUser(identifier: AuthIdentifier): Promise<unknown | null>;
157
+ getUserPasswordHash(user: unknown): string;
158
+ getUserId(user: unknown): AuthIdentifier;
159
+ filterUser(user: unknown): unknown;
158
160
  verifyPassword(password: string, hash: string): Promise<boolean>;
159
161
  storeToken(data: Token): Promise<void>;
160
162
  getToken(query: Partial<Token> & {
@@ -177,7 +179,7 @@ export declare class ApiServer {
177
179
  realUserId: AuthIdentifier;
178
180
  effectiveUserId: AuthIdentifier;
179
181
  }): Promise<boolean>;
180
- jwtSign(payload: any, secret: string, expiresInSeconds: number, options?: SignOptions): JwtSignResult;
182
+ jwtSign(payload: JwtSignPayload, secret: string, expiresInSeconds: number, options?: SignOptions): JwtSignResult;
181
183
  jwtVerify<T>(token: string, secret: string, options?: VerifyOptions): JwtVerifyResult<T>;
182
184
  jwtDecode<T>(token: string, options?: import('jsonwebtoken').DecodeOptions): JwtDecodeResult<T>;
183
185
  getApiKey<T = ApiKey>(token: string): Promise<T | null>;
@@ -188,7 +190,7 @@ export declare class ApiServer {
188
190
  updateToken(updates: Partial<Token> & {
189
191
  refreshToken: string;
190
192
  }): Promise<boolean>;
191
- guessExceptionText(error: any, defMsg?: string): string;
193
+ guessExceptionText(error: unknown, defMsg?: string): string;
192
194
  protected authorize(apiReq: ApiRequest, requiredClass: ApiAuthClass): Promise<void>;
193
195
  private middlewares;
194
196
  private installPingHandler;
@@ -220,7 +222,7 @@ export declare class ApiServer {
220
222
  }): RequestHandler;
221
223
  expressErrorHandler(): ErrorRequestHandler;
222
224
  private handle_request;
223
- api<T extends ApiModule<any>>(module: T): this;
225
+ api<T extends ApiModule<unknown>>(module: T): this;
224
226
  dumpRequest(apiReq: ApiRequest): void;
225
227
  private formatDebugValue;
226
228
  dumpResponse(apiReq: ApiRequest, payload: unknown, status: number): void;
@@ -1052,26 +1052,26 @@ class AuthModule extends module_js_1.BaseAuthModule {
1052
1052
  throw new api_server_base_js_1.ApiError({ code: 401, message: 'Authorization requires user authentication' });
1053
1053
  }
1054
1054
  hasPasskeyService() {
1055
- const storageAny = this.storage;
1056
- if (storageAny.passkeyService || storageAny.passkeyStore) {
1055
+ const storageHints = this.storage;
1056
+ if (storageHints.passkeyService || storageHints.passkeyStore) {
1057
1057
  return true;
1058
1058
  }
1059
- if (storageAny.adapter?.passkeyService || storageAny.adapter?.passkeyStore) {
1059
+ if (storageHints.adapter?.passkeyService || storageHints.adapter?.passkeyStore) {
1060
1060
  return true;
1061
1061
  }
1062
- const serverAny = this.server;
1063
- return !!serverAny.passkeyServiceAdapter;
1062
+ const serverHints = this.server;
1063
+ return !!serverHints.passkeyServiceAdapter;
1064
1064
  }
1065
1065
  hasOAuthStore() {
1066
- const storageAny = this.storage;
1067
- if (storageAny.oauthStore) {
1066
+ const storageHints = this.storage;
1067
+ if (storageHints.oauthStore) {
1068
1068
  return true;
1069
1069
  }
1070
- if (storageAny.adapter?.oauthStore) {
1070
+ if (storageHints.adapter?.oauthStore) {
1071
1071
  return true;
1072
1072
  }
1073
- const serverAny = this.server;
1074
- return !!serverAny.oauthStoreAdapter;
1073
+ const serverHints = this.server;
1074
+ return !!serverHints.oauthStoreAdapter;
1075
1075
  }
1076
1076
  storageImplements(key) {
1077
1077
  const candidate = this.storage[key];
@@ -8,7 +8,7 @@ export interface AuthProviderModule<UserRow = unknown> {
8
8
  expires?: Date;
9
9
  }): Promise<TokenPair>;
10
10
  }
11
- export declare abstract class BaseAuthModule<UserRow = unknown> extends ApiModule<any> implements AuthProviderModule<UserRow> {
11
+ export declare abstract class BaseAuthModule<UserRow = unknown> extends ApiModule implements AuthProviderModule<UserRow> {
12
12
  readonly moduleType: "auth";
13
13
  protected constructor(opts: {
14
14
  namespace: string;
@@ -11,13 +11,23 @@ import type { Token } from '../token/types.js';
11
11
  interface PasskeyOptions extends Partial<PasskeyServiceConfig> {
12
12
  enabled?: boolean;
13
13
  }
14
+ export interface SqlAuthStoreTablePrefixes {
15
+ user?: string;
16
+ token?: string;
17
+ passkey?: string;
18
+ oauth?: string;
19
+ }
14
20
  export interface SqlAuthStoreParams<UserAttributes extends AuthUserAttributes = AuthUserAttributes, PublicUserShape extends Omit<UserAttributes, 'password'> = Omit<UserAttributes, 'password'>> {
15
21
  sequelize: Sequelize;
16
22
  syncOptions?: SyncOptions;
17
23
  bcryptRounds?: number;
18
24
  passwordPepper?: string;
25
+ tablePrefix?: string;
26
+ tablePrefixes?: SqlAuthStoreTablePrefixes;
19
27
  userModel?: GenericUserModelStatic;
20
- userModelFactory?: (sequelize: Sequelize) => GenericUserModelStatic;
28
+ userModelFactory?: (sequelize: Sequelize, options?: {
29
+ tablePrefix?: string;
30
+ }) => GenericUserModelStatic;
21
31
  userRecordMapper?: (model: GenericUserModel) => UserAttributes;
22
32
  publicUserMapper?: (user: UserAttributes) => PublicUserShape;
23
33
  passkeyUserMapper?: (user: UserAttributes) => PasskeyUserDescriptor;
@@ -13,6 +13,22 @@ const DEFAULT_PASSKEY_CONFIG = {
13
13
  timeoutMs: 5 * 60 * 1000,
14
14
  userVerification: 'preferred'
15
15
  };
16
+ function normalizeTablePrefix(prefix) {
17
+ if (!prefix) {
18
+ return undefined;
19
+ }
20
+ const trimmed = prefix.trim();
21
+ return trimmed.length > 0 ? trimmed : undefined;
22
+ }
23
+ function resolveTablePrefix(...prefixes) {
24
+ for (const prefix of prefixes) {
25
+ const normalized = normalizeTablePrefix(prefix);
26
+ if (normalized) {
27
+ return normalized;
28
+ }
29
+ }
30
+ return undefined;
31
+ }
16
32
  function isOriginString(origin) {
17
33
  return typeof origin === 'string' && origin.trim().length > 0;
18
34
  }
@@ -36,6 +52,8 @@ class SqlAuthStore {
36
52
  }
37
53
  this.sequelize = params.sequelize;
38
54
  this.syncOptions = params.syncOptions;
55
+ const moduleTablePrefixes = params.tablePrefixes ?? {};
56
+ const userTablePrefix = resolveTablePrefix(moduleTablePrefixes.user, params.tablePrefix);
39
57
  this.userStore = new sequelize_js_4.SequelizeUserStore({
40
58
  sequelize: this.sequelize,
41
59
  userModel: params.userModel,
@@ -43,18 +61,28 @@ class SqlAuthStore {
43
61
  recordMapper: params.userRecordMapper,
44
62
  toPublic: params.publicUserMapper,
45
63
  bcryptRounds: params.bcryptRounds,
46
- bcryptPepper: params.passwordPepper
64
+ bcryptPepper: params.passwordPepper,
65
+ tablePrefix: userTablePrefix
47
66
  });
67
+ const tokenTablePrefix = resolveTablePrefix(params.tokenStoreOptions?.tablePrefix, moduleTablePrefixes.token, params.tablePrefix);
48
68
  this.tokenStore =
49
- params.tokenStore ?? new sequelize_js_3.SequelizeTokenStore({ sequelize: this.sequelize, ...params.tokenStoreOptions });
69
+ params.tokenStore ??
70
+ new sequelize_js_3.SequelizeTokenStore({
71
+ sequelize: this.sequelize,
72
+ ...params.tokenStoreOptions,
73
+ tablePrefix: tokenTablePrefix
74
+ });
75
+ const oauthTablePrefix = resolveTablePrefix(params.oauthStoreOptions?.tablePrefix, moduleTablePrefixes.oauth, params.tablePrefix);
50
76
  this.oauthStore = new sequelize_js_1.SequelizeOAuthStore({
51
77
  sequelize: this.sequelize,
52
78
  ...params.oauthStoreOptions,
79
+ tablePrefix: oauthTablePrefix,
53
80
  bcryptRounds: params.bcryptRounds
54
81
  });
55
82
  let passkeyStore;
56
83
  let passkeyConfig;
57
84
  if (params.passkeys !== false) {
85
+ const passkeyTablePrefix = resolveTablePrefix(moduleTablePrefixes.passkey, params.tablePrefix);
58
86
  passkeyConfig = normalizePasskeyConfig(params.passkeys ?? {});
59
87
  const resolveUser = async (lookup) => {
60
88
  const found = await this.userStore.findUser(lookup.userId ?? lookup.login ?? '');
@@ -69,7 +97,11 @@ class SqlAuthStore {
69
97
  }));
70
98
  return mapper(found);
71
99
  };
72
- passkeyStore = new sequelize_js_2.SequelizePasskeyStore({ sequelize: this.sequelize, resolveUser });
100
+ passkeyStore = new sequelize_js_2.SequelizePasskeyStore({
101
+ sequelize: this.sequelize,
102
+ resolveUser,
103
+ tablePrefix: passkeyTablePrefix
104
+ });
73
105
  this.passkeyStore = passkeyStore;
74
106
  }
75
107
  this.adapter = new compat_auth_storage_js_1.CompositeAuthAdapter({
@@ -18,7 +18,9 @@ export declare class OAuthClientModel extends Model<OAuthClientAttributes, OAuth
18
18
  metadata: string | null;
19
19
  first_party: boolean;
20
20
  }
21
- export declare function initOAuthClientModel(sequelize: Sequelize): typeof OAuthClientModel;
21
+ export declare function initOAuthClientModel(sequelize: Sequelize, options?: {
22
+ tablePrefix?: string;
23
+ }): typeof OAuthClientModel;
22
24
  export interface OAuthCodeAttributes {
23
25
  code: string;
24
26
  client_id: string;
@@ -42,4 +44,6 @@ export declare class OAuthCodeModel extends Model<OAuthCodeAttributes, OAuthCode
42
44
  expires: Date;
43
45
  metadata: string | null;
44
46
  }
45
- export declare function initOAuthCodeModel(sequelize: Sequelize): typeof OAuthCodeModel;
47
+ export declare function initOAuthCodeModel(sequelize: Sequelize, options?: {
48
+ tablePrefix?: string;
49
+ }): typeof OAuthCodeModel;
@@ -5,11 +5,22 @@ exports.initOAuthClientModel = initOAuthClientModel;
5
5
  exports.initOAuthCodeModel = initOAuthCodeModel;
6
6
  const sequelize_1 = require("sequelize");
7
7
  const DIALECTS_SUPPORTING_UNSIGNED = new Set(['mysql', 'mariadb']);
8
+ function normalizeTablePrefix(prefix) {
9
+ if (!prefix) {
10
+ return undefined;
11
+ }
12
+ const trimmed = prefix.trim();
13
+ return trimmed.length > 0 ? trimmed : undefined;
14
+ }
15
+ function applyTablePrefix(prefix, tableName) {
16
+ const normalized = normalizeTablePrefix(prefix);
17
+ return normalized ? `${normalized}${tableName}` : tableName;
18
+ }
8
19
  function integerIdType(sequelize) {
9
20
  return DIALECTS_SUPPORTING_UNSIGNED.has(sequelize.getDialect()) ? sequelize_1.DataTypes.INTEGER.UNSIGNED : sequelize_1.DataTypes.INTEGER;
10
21
  }
11
- function tableOptions(sequelize, tableName, extra) {
12
- const opts = { sequelize, tableName };
22
+ function tableOptions(sequelize, tableName, tablePrefix, extra) {
23
+ const opts = { sequelize, tableName: applyTablePrefix(tablePrefix, tableName) };
13
24
  if (extra) {
14
25
  Object.assign(opts, extra);
15
26
  }
@@ -22,7 +33,7 @@ function tableOptions(sequelize, tableName, extra) {
22
33
  class OAuthClientModel extends sequelize_1.Model {
23
34
  }
24
35
  exports.OAuthClientModel = OAuthClientModel;
25
- function initOAuthClientModel(sequelize) {
36
+ function initOAuthClientModel(sequelize, options = {}) {
26
37
  OAuthClientModel.init({
27
38
  client_id: { type: sequelize_1.DataTypes.STRING(128), allowNull: false, primaryKey: true },
28
39
  client_secret: { type: sequelize_1.DataTypes.STRING(255), allowNull: false, defaultValue: '' },
@@ -32,14 +43,14 @@ function initOAuthClientModel(sequelize) {
32
43
  metadata: { type: sequelize_1.DataTypes.TEXT, allowNull: true, defaultValue: null },
33
44
  first_party: { type: sequelize_1.DataTypes.BOOLEAN, allowNull: false, defaultValue: false }
34
45
  }, {
35
- ...tableOptions(sequelize, 'oauth_clients', { timestamps: false })
46
+ ...tableOptions(sequelize, 'oauth_clients', options.tablePrefix, { timestamps: false })
36
47
  });
37
48
  return OAuthClientModel;
38
49
  }
39
50
  class OAuthCodeModel extends sequelize_1.Model {
40
51
  }
41
52
  exports.OAuthCodeModel = OAuthCodeModel;
42
- function initOAuthCodeModel(sequelize) {
53
+ function initOAuthCodeModel(sequelize, options = {}) {
43
54
  const idType = integerIdType(sequelize);
44
55
  OAuthCodeModel.init({
45
56
  code: { type: sequelize_1.DataTypes.STRING(128), allowNull: false, primaryKey: true },
@@ -52,7 +63,7 @@ function initOAuthCodeModel(sequelize) {
52
63
  expires: { type: sequelize_1.DataTypes.DATE, allowNull: false },
53
64
  metadata: { type: sequelize_1.DataTypes.TEXT, allowNull: true, defaultValue: null }
54
65
  }, {
55
- ...tableOptions(sequelize, 'oauth_codes', { timestamps: false })
66
+ ...tableOptions(sequelize, 'oauth_codes', options.tablePrefix, { timestamps: false })
56
67
  });
57
68
  return OAuthCodeModel;
58
69
  }
@@ -19,7 +19,9 @@ export declare class OAuthClientModel extends Model<OAuthClientAttributes, OAuth
19
19
  metadata: string | null;
20
20
  first_party: boolean;
21
21
  }
22
- export declare function initOAuthClientModel(sequelize: Sequelize): typeof OAuthClientModel;
22
+ export declare function initOAuthClientModel(sequelize: Sequelize, options?: {
23
+ tablePrefix?: string;
24
+ }): typeof OAuthClientModel;
23
25
  export interface OAuthCodeAttributes {
24
26
  code: string;
25
27
  client_id: string;
@@ -43,13 +45,20 @@ export declare class OAuthCodeModel extends Model<OAuthCodeAttributes, OAuthCode
43
45
  expires: Date;
44
46
  metadata: string | null;
45
47
  }
46
- export declare function initOAuthCodeModel(sequelize: Sequelize): typeof OAuthCodeModel;
48
+ export declare function initOAuthCodeModel(sequelize: Sequelize, options?: {
49
+ tablePrefix?: string;
50
+ }): typeof OAuthCodeModel;
47
51
  export interface SequelizeOAuthStoreOptions {
48
52
  sequelize: Sequelize;
53
+ tablePrefix?: string;
49
54
  clientModel?: typeof OAuthClientModel;
50
55
  codeModel?: typeof OAuthCodeModel;
51
- clientModelFactory?: (sequelize: Sequelize) => typeof OAuthClientModel;
52
- codeModelFactory?: (sequelize: Sequelize) => typeof OAuthCodeModel;
56
+ clientModelFactory?: (sequelize: Sequelize, options?: {
57
+ tablePrefix?: string;
58
+ }) => typeof OAuthClientModel;
59
+ codeModelFactory?: (sequelize: Sequelize, options?: {
60
+ tablePrefix?: string;
61
+ }) => typeof OAuthCodeModel;
53
62
  bcryptRounds?: number;
54
63
  }
55
64
  export declare class SequelizeOAuthStore extends OAuthStore {
@@ -10,11 +10,22 @@ const bcryptjs_1 = __importDefault(require("bcryptjs"));
10
10
  const sequelize_1 = require("sequelize");
11
11
  const base_js_1 = require("./base.js");
12
12
  const DIALECTS_SUPPORTING_UNSIGNED = new Set(['mysql', 'mariadb']);
13
+ function normalizeTablePrefix(prefix) {
14
+ if (!prefix) {
15
+ return undefined;
16
+ }
17
+ const trimmed = prefix.trim();
18
+ return trimmed.length > 0 ? trimmed : undefined;
19
+ }
20
+ function applyTablePrefix(prefix, tableName) {
21
+ const normalized = normalizeTablePrefix(prefix);
22
+ return normalized ? `${normalized}${tableName}` : tableName;
23
+ }
13
24
  function integerIdType(sequelize) {
14
25
  return DIALECTS_SUPPORTING_UNSIGNED.has(sequelize.getDialect()) ? sequelize_1.DataTypes.INTEGER.UNSIGNED : sequelize_1.DataTypes.INTEGER;
15
26
  }
16
- function tableOptions(sequelize, tableName, extra) {
17
- const opts = { sequelize, tableName };
27
+ function tableOptions(sequelize, tableName, tablePrefix, extra) {
28
+ const opts = { sequelize, tableName: applyTablePrefix(tablePrefix, tableName) };
18
29
  if (extra) {
19
30
  Object.assign(opts, extra);
20
31
  }
@@ -27,7 +38,7 @@ function tableOptions(sequelize, tableName, extra) {
27
38
  class OAuthClientModel extends sequelize_1.Model {
28
39
  }
29
40
  exports.OAuthClientModel = OAuthClientModel;
30
- function initOAuthClientModel(sequelize) {
41
+ function initOAuthClientModel(sequelize, options = {}) {
31
42
  OAuthClientModel.init({
32
43
  client_id: { type: sequelize_1.DataTypes.STRING(128), allowNull: false, primaryKey: true },
33
44
  client_secret: { type: sequelize_1.DataTypes.STRING(255), allowNull: false, defaultValue: '' },
@@ -36,13 +47,13 @@ function initOAuthClientModel(sequelize) {
36
47
  scope: { type: sequelize_1.DataTypes.TEXT, allowNull: false, defaultValue: '[]' },
37
48
  metadata: { type: sequelize_1.DataTypes.TEXT, allowNull: true, defaultValue: null },
38
49
  first_party: { type: sequelize_1.DataTypes.BOOLEAN, allowNull: false, defaultValue: false }
39
- }, tableOptions(sequelize, 'oauth_clients', { timestamps: false }));
50
+ }, tableOptions(sequelize, 'oauth_clients', options.tablePrefix, { timestamps: false }));
40
51
  return OAuthClientModel;
41
52
  }
42
53
  class OAuthCodeModel extends sequelize_1.Model {
43
54
  }
44
55
  exports.OAuthCodeModel = OAuthCodeModel;
45
- function initOAuthCodeModel(sequelize) {
56
+ function initOAuthCodeModel(sequelize, options = {}) {
46
57
  const idType = integerIdType(sequelize);
47
58
  OAuthCodeModel.init({
48
59
  code: { type: sequelize_1.DataTypes.STRING(128), allowNull: false, primaryKey: true },
@@ -54,7 +65,7 @@ function initOAuthCodeModel(sequelize) {
54
65
  code_challenge_method: { type: sequelize_1.DataTypes.STRING(10), allowNull: true, defaultValue: null },
55
66
  expires: { type: sequelize_1.DataTypes.DATE, allowNull: false },
56
67
  metadata: { type: sequelize_1.DataTypes.TEXT, allowNull: true, defaultValue: null }
57
- }, tableOptions(sequelize, 'oauth_codes', { timestamps: false }));
68
+ }, tableOptions(sequelize, 'oauth_codes', options.tablePrefix, { timestamps: false }));
58
69
  return OAuthCodeModel;
59
70
  }
60
71
  function encodeStringArray(values) {
@@ -114,8 +125,16 @@ class SequelizeOAuthStore extends base_js_1.OAuthStore {
114
125
  if (!options?.sequelize) {
115
126
  throw new Error('SequelizeOAuthStore requires an initialised Sequelize instance');
116
127
  }
117
- this.clients = options.clientModel ?? (options.clientModelFactory ?? initOAuthClientModel)(options.sequelize);
118
- this.codes = options.codeModel ?? (options.codeModelFactory ?? initOAuthCodeModel)(options.sequelize);
128
+ this.clients =
129
+ options.clientModel ??
130
+ (options.clientModelFactory ?? initOAuthClientModel)(options.sequelize, {
131
+ tablePrefix: options.tablePrefix
132
+ });
133
+ this.codes =
134
+ options.codeModel ??
135
+ (options.codeModelFactory ?? initOAuthCodeModel)(options.sequelize, {
136
+ tablePrefix: options.tablePrefix
137
+ });
119
138
  this.bcryptRounds = options.bcryptRounds ?? 12;
120
139
  }
121
140
  async getClient(clientId) {