@technomoron/api-server-base 1.1.13 → 2.0.0-beta.2

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 (115) hide show
  1. package/dist/cjs/api-server-base.cjs +181 -74
  2. package/dist/cjs/api-server-base.d.ts +66 -29
  3. package/dist/cjs/auth-api/auth-module.d.ts +96 -0
  4. package/dist/cjs/auth-api/auth-module.js +1032 -0
  5. package/dist/cjs/auth-api/compat-auth-storage.d.ts +55 -0
  6. package/dist/cjs/auth-api/compat-auth-storage.js +116 -0
  7. package/dist/cjs/auth-api/mem-auth-store.d.ts +66 -0
  8. package/dist/cjs/auth-api/mem-auth-store.js +135 -0
  9. package/dist/cjs/{auth-module.d.ts → auth-api/module.d.ts} +7 -7
  10. package/dist/cjs/{auth-module.cjs → auth-api/module.js} +1 -1
  11. package/dist/cjs/auth-api/sql-auth-store.d.ts +75 -0
  12. package/dist/cjs/auth-api/sql-auth-store.js +166 -0
  13. package/dist/cjs/auth-api/storage.d.ts +36 -0
  14. package/dist/cjs/{auth-storage.cjs → auth-api/storage.js} +2 -2
  15. package/dist/cjs/auth-api/types.d.ts +29 -0
  16. package/dist/cjs/auth-api/types.js +2 -0
  17. package/dist/cjs/index.cjs +41 -7
  18. package/dist/cjs/index.d.ts +29 -5
  19. package/dist/cjs/oauth/base.d.ts +10 -0
  20. package/dist/cjs/oauth/base.js +6 -0
  21. package/dist/cjs/oauth/memory.d.ts +16 -0
  22. package/dist/cjs/oauth/memory.js +99 -0
  23. package/dist/cjs/oauth/models.d.ts +45 -0
  24. package/dist/cjs/oauth/models.js +58 -0
  25. package/dist/cjs/oauth/sequelize.d.ts +68 -0
  26. package/dist/cjs/oauth/sequelize.js +210 -0
  27. package/dist/cjs/oauth/types.d.ts +50 -0
  28. package/dist/cjs/oauth/types.js +3 -0
  29. package/dist/cjs/passkey/base.d.ts +15 -0
  30. package/dist/cjs/passkey/base.js +6 -0
  31. package/dist/cjs/passkey/memory.d.ts +26 -0
  32. package/dist/cjs/passkey/memory.js +82 -0
  33. package/dist/cjs/passkey/models.d.ts +25 -0
  34. package/dist/cjs/passkey/models.js +115 -0
  35. package/dist/cjs/passkey/sequelize.d.ts +54 -0
  36. package/dist/cjs/passkey/sequelize.js +211 -0
  37. package/dist/cjs/passkey/service.d.ts +17 -0
  38. package/dist/cjs/passkey/service.js +221 -0
  39. package/dist/cjs/passkey/types.d.ts +75 -0
  40. package/dist/cjs/passkey/types.js +2 -0
  41. package/dist/cjs/token/base.d.ts +38 -0
  42. package/dist/cjs/token/base.js +114 -0
  43. package/dist/cjs/token/memory.d.ts +19 -0
  44. package/dist/cjs/token/memory.js +149 -0
  45. package/dist/cjs/token/sequelize.d.ts +58 -0
  46. package/dist/cjs/token/sequelize.js +404 -0
  47. package/dist/cjs/token/types.d.ts +27 -0
  48. package/dist/cjs/token/types.js +2 -0
  49. package/dist/cjs/user/base.d.ts +26 -0
  50. package/dist/cjs/user/base.js +45 -0
  51. package/dist/cjs/user/memory.d.ts +35 -0
  52. package/dist/cjs/user/memory.js +173 -0
  53. package/dist/cjs/user/sequelize.d.ts +41 -0
  54. package/dist/cjs/user/sequelize.js +182 -0
  55. package/dist/cjs/user/types.d.ts +11 -0
  56. package/dist/cjs/user/types.js +2 -0
  57. package/dist/esm/api-server-base.d.ts +66 -29
  58. package/dist/esm/api-server-base.js +179 -72
  59. package/dist/esm/auth-api/auth-module.d.ts +96 -0
  60. package/dist/esm/auth-api/auth-module.js +1030 -0
  61. package/dist/esm/auth-api/compat-auth-storage.d.ts +55 -0
  62. package/dist/esm/auth-api/compat-auth-storage.js +112 -0
  63. package/dist/esm/auth-api/mem-auth-store.d.ts +66 -0
  64. package/dist/esm/auth-api/mem-auth-store.js +131 -0
  65. package/dist/esm/{auth-module.d.ts → auth-api/module.d.ts} +7 -7
  66. package/dist/esm/{auth-module.js → auth-api/module.js} +1 -1
  67. package/dist/esm/auth-api/sql-auth-store.d.ts +75 -0
  68. package/dist/esm/auth-api/sql-auth-store.js +162 -0
  69. package/dist/esm/auth-api/storage.d.ts +36 -0
  70. package/dist/esm/{auth-storage.js → auth-api/storage.js} +2 -2
  71. package/dist/esm/auth-api/types.d.ts +29 -0
  72. package/dist/esm/auth-api/types.js +1 -0
  73. package/dist/esm/index.d.ts +29 -5
  74. package/dist/esm/index.js +19 -2
  75. package/dist/esm/oauth/base.d.ts +10 -0
  76. package/dist/esm/oauth/base.js +2 -0
  77. package/dist/esm/oauth/memory.d.ts +16 -0
  78. package/dist/esm/oauth/memory.js +92 -0
  79. package/dist/esm/oauth/models.d.ts +45 -0
  80. package/dist/esm/oauth/models.js +51 -0
  81. package/dist/esm/oauth/sequelize.d.ts +68 -0
  82. package/dist/esm/oauth/sequelize.js +199 -0
  83. package/dist/esm/oauth/types.d.ts +50 -0
  84. package/dist/esm/oauth/types.js +2 -0
  85. package/dist/esm/passkey/base.d.ts +15 -0
  86. package/dist/esm/passkey/base.js +2 -0
  87. package/dist/esm/passkey/memory.d.ts +26 -0
  88. package/dist/esm/passkey/memory.js +78 -0
  89. package/dist/esm/passkey/models.d.ts +25 -0
  90. package/dist/esm/passkey/models.js +108 -0
  91. package/dist/esm/passkey/sequelize.d.ts +54 -0
  92. package/dist/esm/passkey/sequelize.js +207 -0
  93. package/dist/esm/passkey/service.d.ts +17 -0
  94. package/dist/esm/passkey/service.js +217 -0
  95. package/dist/esm/passkey/types.d.ts +75 -0
  96. package/dist/esm/passkey/types.js +1 -0
  97. package/dist/esm/token/base.d.ts +38 -0
  98. package/dist/esm/token/base.js +107 -0
  99. package/dist/esm/token/memory.d.ts +19 -0
  100. package/dist/esm/token/memory.js +145 -0
  101. package/dist/esm/token/sequelize.d.ts +58 -0
  102. package/dist/esm/token/sequelize.js +400 -0
  103. package/dist/esm/token/types.d.ts +27 -0
  104. package/dist/esm/token/types.js +1 -0
  105. package/dist/esm/user/base.d.ts +26 -0
  106. package/dist/esm/user/base.js +38 -0
  107. package/dist/esm/user/memory.d.ts +35 -0
  108. package/dist/esm/user/memory.js +169 -0
  109. package/dist/esm/user/sequelize.d.ts +41 -0
  110. package/dist/esm/user/sequelize.js +176 -0
  111. package/dist/esm/user/types.d.ts +11 -0
  112. package/dist/esm/user/types.js +1 -0
  113. package/package.json +11 -3
  114. package/dist/cjs/auth-storage.d.ts +0 -133
  115. package/dist/esm/auth-storage.d.ts +0 -133
@@ -10,13 +10,34 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.ApiServer = exports.ApiError = exports.ApiModule = void 0;
13
+ const node_crypto_1 = require("node:crypto");
13
14
  const cookie_parser_1 = __importDefault(require("cookie-parser"));
14
15
  const cors_1 = __importDefault(require("cors"));
15
16
  const express_1 = __importDefault(require("express"));
16
- const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
17
17
  const multer_1 = __importDefault(require("multer"));
18
- const auth_module_js_1 = require("./auth-module.cjs");
19
- const auth_storage_js_1 = require("./auth-storage.cjs");
18
+ const module_js_1 = require("./auth-api/module.js");
19
+ const storage_js_1 = require("./auth-api/storage.js");
20
+ const base_js_1 = require("./token/base.js");
21
+ class JwtHelperStore extends base_js_1.TokenStore {
22
+ async save() {
23
+ throw new Error('Token store is not configured');
24
+ }
25
+ async get() {
26
+ throw new Error('Token store is not configured');
27
+ }
28
+ async delete() {
29
+ throw new Error('Token store is not configured');
30
+ }
31
+ async update() {
32
+ throw new Error('Token store is not configured');
33
+ }
34
+ async list() {
35
+ return [];
36
+ }
37
+ async close() {
38
+ return;
39
+ }
40
+ }
20
41
  var api_module_js_1 = require("./api-module.cjs");
21
42
  Object.defineProperty(exports, "ApiModule", { enumerable: true, get: function () { return api_module_js_1.ApiModule; } });
22
43
  function guess_exception_text(error, defMsg = 'Unknown Error') {
@@ -332,18 +353,36 @@ function fillConfig(config) {
332
353
  hydrateGetBody: config.hydrateGetBody ?? true,
333
354
  validateTokens: config.validateTokens ?? false,
334
355
  apiVersion: config.apiVersion ?? '',
335
- minClientVersion: config.minClientVersion ?? ''
356
+ minClientVersion: config.minClientVersion ?? '',
357
+ tokenStore: config.tokenStore,
358
+ authStores: config.authStores
336
359
  };
337
360
  }
338
361
  class ApiServer {
339
362
  constructor(config = {}) {
340
363
  this.currReq = null;
341
364
  this.apiNotFoundHandler = null;
365
+ this.tokenStoreAdapter = null;
366
+ this.userStoreAdapter = null;
367
+ this.passkeyServiceAdapter = null;
368
+ this.oauthStoreAdapter = null;
369
+ this.canImpersonateAdapter = null;
342
370
  this.config = fillConfig(config);
343
371
  this.apiBasePath = this.normalizeApiBasePath(this.config.apiBasePath);
344
372
  this.startedAt = Date.now();
345
- this.storageAdapter = auth_storage_js_1.nullAuthStorage;
346
- this.moduleAdapter = auth_module_js_1.nullAuthModule;
373
+ this.storageAdapter = storage_js_1.nullAuthStorage;
374
+ this.moduleAdapter = module_js_1.nullAuthModule;
375
+ this.jwtHelper = new JwtHelperStore();
376
+ this.tokenStoreAdapter = this.config.tokenStore ?? null;
377
+ if (this.config.authStores) {
378
+ const { userStore, tokenStore, passkeyService, oauthStore, canImpersonate } = this.config.authStores;
379
+ this.userStoreAdapter = userStore;
380
+ this.tokenStoreAdapter = tokenStore;
381
+ this.passkeyServiceAdapter = passkeyService ?? null;
382
+ this.oauthStoreAdapter = oauthStore ?? null;
383
+ this.canImpersonateAdapter = canImpersonate ?? null;
384
+ this.storageAdapter = this;
385
+ }
347
386
  this.app = (0, express_1.default)();
348
387
  if (config.uploadPath) {
349
388
  const upload = (0, multer_1.default)({ dest: config.uploadPath });
@@ -380,72 +419,143 @@ class ApiServer {
380
419
  getAuthModule() {
381
420
  return this.moduleAdapter;
382
421
  }
383
- jwtSign(payload, secret, expiresInSeconds, options) {
384
- options || (options = {});
385
- const opts = { ...options, expiresIn: expiresInSeconds };
386
- try {
387
- const token = jsonwebtoken_1.default.sign(payload, secret, opts);
388
- return {
389
- success: true,
390
- token
391
- };
422
+ setTokenStore(store) {
423
+ this.tokenStoreAdapter = store;
424
+ // If using direct stores, expose self as the auth storage.
425
+ if (this.userStoreAdapter) {
426
+ this.storageAdapter = this;
392
427
  }
393
- catch (error) {
394
- return {
395
- success: false,
396
- error: error instanceof Error ? error.message : String(error)
397
- };
428
+ return this;
429
+ }
430
+ getTokenStore() {
431
+ return this.tokenStoreAdapter;
432
+ }
433
+ ensureUserStore() {
434
+ if (!this.userStoreAdapter) {
435
+ throw new Error('User store is not configured');
398
436
  }
437
+ return this.userStoreAdapter;
399
438
  }
400
- jwtVerify(token, secret, options) {
401
- options || (options = {});
402
- try {
403
- const data = jsonwebtoken_1.default.verify(token, secret, options);
404
- return {
405
- success: true,
406
- data
407
- };
439
+ ensureTokenStore() {
440
+ if (!this.tokenStoreAdapter) {
441
+ throw new Error('Token store is not configured');
408
442
  }
409
- catch (error) {
410
- if (error instanceof jsonwebtoken_1.default.TokenExpiredError) {
411
- return {
412
- success: false,
413
- expired: true,
414
- error: 'Token expired'
415
- };
416
- }
417
- else {
418
- return {
419
- success: false,
420
- expired: false,
421
- error: error instanceof Error ? error.message : String(error)
422
- };
423
- }
443
+ return this.tokenStoreAdapter;
444
+ }
445
+ ensurePasskeyService() {
446
+ if (!this.passkeyServiceAdapter) {
447
+ throw new Error('Passkey service is not configured');
424
448
  }
449
+ return this.passkeyServiceAdapter;
425
450
  }
426
- jwtDecode(token, options) {
427
- options || (options = {});
428
- try {
429
- const data = jsonwebtoken_1.default.decode(token, options);
430
- // jwt.decode returns null for invalid tokens rather than throwing
431
- if (data === null) {
432
- return {
433
- success: false,
434
- error: 'Invalid token format'
435
- };
436
- }
437
- return {
438
- success: true,
439
- data
440
- };
451
+ ensureOAuthStore() {
452
+ if (!this.oauthStoreAdapter) {
453
+ throw new Error('OAuth store is not configured');
441
454
  }
442
- catch (error) {
443
- // jwt.decode rarely throws, but might for severely malformed tokens
444
- return {
445
- success: false,
446
- error: error instanceof Error ? error.message : String(error)
447
- };
455
+ return this.oauthStoreAdapter;
456
+ }
457
+ // AuthStorage-compatible helpers (used by AuthModule)
458
+ async getUser(identifier) {
459
+ return this.userStoreAdapter ? this.userStoreAdapter.findUser(identifier) : null;
460
+ }
461
+ getUserPasswordHash(user) {
462
+ return this.ensureUserStore().getPasswordHash(user) ?? '';
463
+ }
464
+ getUserId(user) {
465
+ return this.ensureUserStore().getUserId(user);
466
+ }
467
+ filterUser(user) {
468
+ return this.ensureUserStore().toPublic(user);
469
+ }
470
+ async verifyPassword(password, hash) {
471
+ return this.ensureUserStore().verifyPassword(password, hash);
472
+ }
473
+ async storeToken(data) {
474
+ if (this.tokenStoreAdapter) {
475
+ return this.tokenStoreAdapter.save(data);
448
476
  }
477
+ if (typeof this.storageAdapter.storeToken === 'function') {
478
+ return this.storageAdapter.storeToken(data);
479
+ }
480
+ throw new Error('Token store is not configured');
481
+ }
482
+ async getToken(query, opts) {
483
+ const normalized = {
484
+ ...query,
485
+ userId: query.userId !== undefined && query.userId !== null ? String(query.userId) : undefined,
486
+ ruid: query.ruid !== undefined && query.ruid !== null ? String(query.ruid) : undefined
487
+ };
488
+ if (this.tokenStoreAdapter) {
489
+ return this.tokenStoreAdapter.get(normalized, opts);
490
+ }
491
+ if (typeof this.storageAdapter.getToken === 'function') {
492
+ return this.storageAdapter.getToken(normalized, opts);
493
+ }
494
+ return null;
495
+ }
496
+ async deleteToken(query) {
497
+ const normalized = {
498
+ ...query,
499
+ userId: query.userId !== undefined && query.userId !== null ? String(query.userId) : undefined,
500
+ ruid: query.ruid !== undefined && query.ruid !== null ? String(query.ruid) : undefined
501
+ };
502
+ if (this.tokenStoreAdapter) {
503
+ return this.tokenStoreAdapter.delete(normalized);
504
+ }
505
+ if (typeof this.storageAdapter.deleteToken === 'function') {
506
+ return this.storageAdapter.deleteToken(normalized);
507
+ }
508
+ return 0;
509
+ }
510
+ async createPasskeyChallenge(params) {
511
+ return this.ensurePasskeyService().createChallenge(params);
512
+ }
513
+ async verifyPasskeyResponse(params) {
514
+ return this.ensurePasskeyService().verifyResponse(params);
515
+ }
516
+ async getClient(clientId) {
517
+ return this.oauthStoreAdapter ? this.oauthStoreAdapter.getClient(clientId) : null;
518
+ }
519
+ async verifyClientSecret(client, clientSecret) {
520
+ return this.ensureOAuthStore().verifyClientSecret(client.clientId, clientSecret);
521
+ }
522
+ async createAuthCode(request) {
523
+ const expiresAt = new Date(Date.now() + (request.expiresInSeconds ?? 300) * 1000);
524
+ const code = request.code ?? (0, node_crypto_1.randomUUID)();
525
+ await this.ensureOAuthStore().createAuthCode({ ...request, code, expiresAt });
526
+ return {
527
+ code,
528
+ clientId: request.clientId,
529
+ userId: request.userId,
530
+ redirectUri: request.redirectUri,
531
+ scope: request.scope ?? [],
532
+ codeChallenge: request.codeChallenge,
533
+ codeChallengeMethod: request.codeChallengeMethod,
534
+ expiresAt,
535
+ metadata: request.metadata
536
+ };
537
+ }
538
+ async consumeAuthCode(code, clientId) {
539
+ const consumed = await this.ensureOAuthStore().consumeAuthCode(code);
540
+ if (!consumed || consumed.clientId !== clientId) {
541
+ return null;
542
+ }
543
+ return consumed;
544
+ }
545
+ async canImpersonate(params) {
546
+ if (this.canImpersonateAdapter) {
547
+ return !!(await this.canImpersonateAdapter(params));
548
+ }
549
+ return params.realUserId === params.effectiveUserId;
550
+ }
551
+ jwtSign(payload, secret, expiresInSeconds, options) {
552
+ return (this.tokenStoreAdapter ?? this.jwtHelper).jwtSign(payload, secret, expiresInSeconds, options);
553
+ }
554
+ jwtVerify(token, secret, options) {
555
+ return (this.tokenStoreAdapter ?? this.jwtHelper).jwtVerify(token, secret, options);
556
+ }
557
+ jwtDecode(token, options) {
558
+ return (this.tokenStoreAdapter ?? this.jwtHelper).jwtDecode(token, options);
449
559
  }
450
560
  async getApiKey(token) {
451
561
  void token;
@@ -463,16 +573,13 @@ class ApiServer {
463
573
  return this.storageAdapter.verifyPassword(params.password, hash);
464
574
  }
465
575
  async updateToken(updates) {
466
- if (typeof this.storageAdapter.updateToken !== 'function') {
467
- return false;
576
+ if (this.tokenStoreAdapter) {
577
+ return this.tokenStoreAdapter.update(updates);
468
578
  }
469
- return this.storageAdapter.updateToken({
470
- refreshToken: updates.refreshToken,
471
- access: updates.accessToken,
472
- expires: updates.expires,
473
- clientId: updates.clientId,
474
- scope: updates.scope
475
- });
579
+ if (typeof this.storageAdapter.updateToken === 'function') {
580
+ return this.storageAdapter.updateToken(updates);
581
+ }
582
+ return false;
476
583
  }
477
584
  guessExceptionText(error, defMsg = 'Unkown Error') {
478
585
  return guess_exception_text(error, defMsg);
@@ -686,7 +793,7 @@ class ApiServer {
686
793
  return this.config.validateTokens || authType === 'strict';
687
794
  }
688
795
  async assertStoredAccessToken(apiReq, token, tokenData) {
689
- const userId = this.extractTokenUserId(tokenData);
796
+ const userId = String(this.extractTokenUserId(tokenData));
690
797
  const stored = await this.storageAdapter.getToken({
691
798
  accessToken: token,
692
799
  userId
@@ -5,48 +5,39 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
  import { Application, Request, Response } from 'express';
8
- import jwt, { JwtPayload, SignOptions, VerifyOptions } from 'jsonwebtoken';
9
8
  import { ApiModule } from './api-module.js';
9
+ import { TokenStore, type JwtDecodeResult, type JwtSignResult, type JwtVerifyResult } from './token/base.js';
10
10
  import type { ApiAuthClass, ApiKey } from './api-module.js';
11
- import type { AuthProviderModule } from './auth-module.js';
12
- import type { AuthStorage, AuthIdentifier, AuthTokenData, AuthTokenMetadata } from './auth-storage.js';
11
+ import type { AuthProviderModule } from './auth-api/module.js';
12
+ import type { AuthStorage, AuthIdentifier } from './auth-api/types.js';
13
+ import type { OAuthStore } from './oauth/base.js';
14
+ import type { AuthCodeData, AuthCodeRequest, OAuthClient } from './oauth/types.js';
15
+ import type { PasskeyService } from './passkey/service.js';
16
+ import type { PasskeyChallenge, PasskeyChallengeParams, PasskeyVerificationParams, PasskeyVerificationResult } from './passkey/types.js';
17
+ import type { Token } from './token/types.js';
18
+ import type { UserStore } from './user/base.js';
19
+ import type { JwtPayload, SignOptions, VerifyOptions } from 'jsonwebtoken';
13
20
  export type { Application, Request, Response, NextFunction, Router } from 'express';
14
21
  export type { Multer } from 'multer';
15
22
  export type { JwtPayload, SignOptions, VerifyOptions } from 'jsonwebtoken';
16
- export interface RequestWithStuff extends Request {
23
+ export interface ExtendedReq extends Request {
17
24
  file?: Express.Multer.File;
18
25
  files?: Express.Multer.File[] | {
19
26
  [fieldname: string]: Express.Multer.File[];
20
27
  };
21
28
  }
22
- interface JwtSignResult {
23
- success: boolean;
24
- token?: string;
25
- error?: string;
26
- }
27
- interface JwtVerifyResult<T> {
28
- success: boolean;
29
- data?: T;
30
- expired?: boolean;
31
- error?: string;
32
- }
33
- interface JwtDecodeResult<T> {
34
- success: boolean;
35
- data?: T;
36
- error?: string;
37
- }
38
- export interface ApiTokenData extends JwtPayload, AuthTokenMetadata {
29
+ export interface ApiTokenData extends JwtPayload, Partial<Token> {
39
30
  uid: unknown;
40
31
  iat?: number;
41
32
  exp?: number;
42
33
  }
43
34
  export interface ApiRequest {
44
35
  server: any;
45
- req: RequestWithStuff;
36
+ req: ExtendedReq;
46
37
  res: Response;
47
38
  tokenData?: ApiTokenData | null;
48
39
  token?: string;
49
- authToken?: AuthTokenData | null;
40
+ authToken?: Token | null;
50
41
  apiKey?: ApiKey | null;
51
42
  clientInfo?: ClientInfo;
52
43
  realUid?: AuthIdentifier | null;
@@ -66,6 +57,16 @@ export interface ClientInfo extends ClientAgentProfile {
66
57
  ip: string | null;
67
58
  ipchain: string[];
68
59
  }
60
+ export interface ApiServerAuthStores {
61
+ userStore: UserStore<any, any>;
62
+ tokenStore: TokenStore;
63
+ passkeyService?: PasskeyService;
64
+ oauthStore?: OAuthStore;
65
+ canImpersonate?: (params: {
66
+ realUserId: AuthIdentifier;
67
+ effectiveUserId: AuthIdentifier;
68
+ }) => boolean | Promise<boolean>;
69
+ }
69
70
  export { ApiModule } from './api-module.js';
70
71
  export type { ApiHandler, ApiAuthType, ApiAuthClass, ApiRoute, ApiKey } from './api-module.js';
71
72
  export interface ApiErrorParams {
@@ -102,6 +103,8 @@ export interface ApiServerConf {
102
103
  validateTokens: boolean;
103
104
  apiVersion: string;
104
105
  minClientVersion: string;
106
+ tokenStore?: TokenStore;
107
+ authStores?: ApiServerAuthStores;
105
108
  }
106
109
  export declare class ApiServer {
107
110
  app: Application;
@@ -112,6 +115,12 @@ export declare class ApiServer {
112
115
  private storageAdapter;
113
116
  private moduleAdapter;
114
117
  private apiNotFoundHandler;
118
+ private tokenStoreAdapter;
119
+ private userStoreAdapter;
120
+ private passkeyServiceAdapter;
121
+ private oauthStoreAdapter;
122
+ private canImpersonateAdapter;
123
+ private readonly jwtHelper;
115
124
  constructor(config?: Partial<ApiServerConf>);
116
125
  authStorage<UserRow, SafeUser>(storage: AuthStorage<UserRow, SafeUser>): this;
117
126
  /**
@@ -125,20 +134,48 @@ export declare class ApiServer {
125
134
  useAuthModule<UserRow>(module: AuthProviderModule<UserRow>): this;
126
135
  getAuthStorage(): AuthStorage<any, any>;
127
136
  getAuthModule(): AuthProviderModule<any>;
137
+ setTokenStore(store: TokenStore): this;
138
+ getTokenStore(): TokenStore | null;
139
+ private ensureUserStore;
140
+ private ensureTokenStore;
141
+ private ensurePasskeyService;
142
+ private ensureOAuthStore;
143
+ getUser(identifier: AuthIdentifier): Promise<any | null>;
144
+ getUserPasswordHash(user: any): string;
145
+ getUserId(user: any): AuthIdentifier;
146
+ filterUser(user: any): any;
147
+ verifyPassword(password: string, hash: string): Promise<boolean>;
148
+ storeToken(data: Token): Promise<void>;
149
+ getToken(query: Partial<Token> & {
150
+ userId?: AuthIdentifier;
151
+ ruid?: AuthIdentifier;
152
+ }, opts?: {
153
+ includeExpired?: boolean;
154
+ }): Promise<Token | null>;
155
+ deleteToken(query: Partial<Token> & {
156
+ userId?: AuthIdentifier;
157
+ ruid?: AuthIdentifier;
158
+ }): Promise<number>;
159
+ createPasskeyChallenge(params: PasskeyChallengeParams): Promise<PasskeyChallenge>;
160
+ verifyPasskeyResponse(params: PasskeyVerificationParams): Promise<PasskeyVerificationResult>;
161
+ getClient(clientId: string): Promise<OAuthClient | null>;
162
+ verifyClientSecret(client: OAuthClient, clientSecret: string | null): Promise<boolean>;
163
+ createAuthCode(request: AuthCodeRequest): Promise<AuthCodeData>;
164
+ consumeAuthCode(code: string, clientId: string): Promise<AuthCodeData | null>;
165
+ canImpersonate(params: {
166
+ realUserId: AuthIdentifier;
167
+ effectiveUserId: AuthIdentifier;
168
+ }): Promise<boolean>;
128
169
  jwtSign(payload: any, secret: string, expiresInSeconds: number, options?: SignOptions): JwtSignResult;
129
170
  jwtVerify<T>(token: string, secret: string, options?: VerifyOptions): JwtVerifyResult<T>;
130
- jwtDecode<T>(token: string, options?: jwt.DecodeOptions): JwtDecodeResult<T>;
171
+ jwtDecode<T>(token: string, options?: import('jsonwebtoken').DecodeOptions): JwtDecodeResult<T>;
131
172
  getApiKey<T = ApiKey>(token: string): Promise<T | null>;
132
173
  authenticateUser(params: {
133
174
  login: string;
134
175
  password: string;
135
176
  }): Promise<boolean>;
136
- updateToken(updates: {
137
- accessToken: string;
177
+ updateToken(updates: Partial<Token> & {
138
178
  refreshToken: string;
139
- expires?: Date;
140
- clientId?: string;
141
- scope?: string[];
142
179
  }): Promise<boolean>;
143
180
  guessExceptionText(error: any, defMsg?: string): string;
144
181
  protected authorize(apiReq: ApiRequest, requiredClass: ApiAuthClass): Promise<void>;
@@ -0,0 +1,96 @@
1
+ import { type ApiRequest, type ApiRoute, type ApiServer } from '../api-server-base.js';
2
+ import { BaseAuthModule, type AuthProviderModule } from './module.js';
3
+ import type { AuthIdentifier, AuthStorage } from './types.js';
4
+ import type { OAuthCallbackParams, OAuthCallbackResult, OAuthStartParams, OAuthStartResult } from '../oauth/types.js';
5
+ import type { TokenPair, Token } from '../token/types.js';
6
+ interface CanImpersonateContext<UserEntity> {
7
+ apiReq: ApiRequest;
8
+ realUser: UserEntity;
9
+ realUserId: AuthIdentifier;
10
+ targetUser: UserEntity;
11
+ effectiveUserId: AuthIdentifier;
12
+ }
13
+ interface AuthModuleOptions<UserEntity> {
14
+ namespace?: string;
15
+ defaultDomain?: string;
16
+ canImpersonate?: (context: CanImpersonateContext<UserEntity>) => Promise<boolean> | boolean;
17
+ }
18
+ type TokenMetadata = Partial<Token> & {
19
+ sessionCookie?: boolean;
20
+ };
21
+ interface TokenIssueOptions extends TokenMetadata {
22
+ expires?: Date;
23
+ sessionCookie?: boolean;
24
+ }
25
+ interface NormalizedTokenMetadata extends TokenMetadata {
26
+ domain: string;
27
+ fingerprint: string;
28
+ label: string;
29
+ browser: string;
30
+ device: string;
31
+ ip: string;
32
+ os: string;
33
+ }
34
+ type TokenClaims = TokenMetadata & {
35
+ uid: string;
36
+ exp?: number;
37
+ iat?: number;
38
+ };
39
+ type AuthCapableServer<PublicUser> = ApiServer & {
40
+ initiateOAuth?: (params: OAuthStartParams) => Promise<OAuthStartResult>;
41
+ completeOAuth?: (params: OAuthCallbackParams) => Promise<OAuthCallbackResult<PublicUser>>;
42
+ };
43
+ export default class AuthModule<UserEntity, PublicUser> extends BaseAuthModule<UserEntity> implements AuthProviderModule<UserEntity> {
44
+ static defaultNamespace: string;
45
+ server: AuthCapableServer<PublicUser>;
46
+ private readonly defaultDomain?;
47
+ private readonly canImpersonateHook?;
48
+ constructor(options?: AuthModuleOptions<UserEntity>);
49
+ protected get storage(): AuthStorage<UserEntity, PublicUser>;
50
+ protected canImpersonate(apiReq: ApiRequest, realUser: UserEntity, targetUser: UserEntity): Promise<boolean>;
51
+ protected ensureImpersonationAllowed(apiReq: ApiRequest, realUser: UserEntity, targetUser: UserEntity): Promise<void>;
52
+ protected buildTokenPayload(user: UserEntity, metadata?: TokenMetadata): TokenClaims;
53
+ protected buildTokenMetadata(metadata?: TokenMetadata): NormalizedTokenMetadata;
54
+ protected enrichTokenMetadata(apiReq: ApiRequest, metadata?: TokenMetadata): TokenMetadata;
55
+ private sessionRefreshTtlSeconds;
56
+ private normalizeRefreshTtlSeconds;
57
+ private resolveSessionPreferences;
58
+ private mergeSessionPreferences;
59
+ private sessionPrefsFromRecord;
60
+ private cookieOptions;
61
+ private setJwtCookies;
62
+ issueTokens(apiReq: ApiRequest, user: UserEntity, metadata?: TokenIssueOptions): Promise<TokenPair>;
63
+ private assertAuthReady;
64
+ private parseLoginBody;
65
+ private parseImpersonationRequest;
66
+ private resolveImpersonationIdentifier;
67
+ private buildImpersonationMetadata;
68
+ private getUserOrThrow;
69
+ private getRealUserIdentifier;
70
+ private resolveActorContext;
71
+ private extractRefreshToken;
72
+ private normalizeScope;
73
+ private postLogin;
74
+ private postRefresh;
75
+ private postLogout;
76
+ private postWhoAmI;
77
+ private postPasskeyChallenge;
78
+ private postPasskeyVerify;
79
+ private postImpersonation;
80
+ private deleteImpersonation;
81
+ private getUserFromPasskey;
82
+ private postOAuthStart;
83
+ private postOAuthCallback;
84
+ private postOAuthAuthorize;
85
+ private postOAuthToken;
86
+ private handleAuthorizationCodeGrant;
87
+ private handleRefreshTokenGrant;
88
+ private clearOAuthCookies;
89
+ private buildTokenResponse;
90
+ private resolveScope;
91
+ private resolveClientAuthentication;
92
+ private assertRedirectUriAllowed;
93
+ private resolveUserForOAuth;
94
+ defineRoutes(): ApiRoute[];
95
+ }
96
+ export {};