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

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/README.txt +81 -28
  2. package/dist/cjs/api-module.cjs +9 -0
  3. package/dist/cjs/api-module.d.ts +7 -4
  4. package/dist/cjs/api-server-base.cjs +607 -99
  5. package/dist/cjs/api-server-base.d.ts +80 -23
  6. package/dist/cjs/auth-api/auth-module.d.ts +23 -3
  7. package/dist/cjs/auth-api/auth-module.js +320 -124
  8. package/dist/cjs/auth-api/compat-auth-storage.d.ts +7 -5
  9. package/dist/cjs/auth-api/compat-auth-storage.js +15 -3
  10. package/dist/cjs/auth-api/mem-auth-store.d.ts +5 -3
  11. package/dist/cjs/auth-api/mem-auth-store.js +14 -28
  12. package/dist/cjs/auth-api/module.d.ts +1 -1
  13. package/dist/cjs/auth-api/sql-auth-store.d.ts +16 -4
  14. package/dist/cjs/auth-api/sql-auth-store.js +43 -30
  15. package/dist/cjs/auth-api/storage.d.ts +6 -4
  16. package/dist/cjs/auth-api/storage.js +15 -5
  17. package/dist/cjs/auth-api/types.d.ts +7 -2
  18. package/dist/cjs/auth-api/user-id.d.ts +5 -0
  19. package/dist/cjs/auth-api/user-id.js +38 -0
  20. package/dist/cjs/auth-cookie-options.d.ts +11 -0
  21. package/dist/cjs/auth-cookie-options.js +66 -0
  22. package/dist/cjs/index.cjs +4 -14
  23. package/dist/cjs/index.d.ts +4 -9
  24. package/dist/cjs/oauth/memory.d.ts +6 -0
  25. package/dist/cjs/oauth/memory.js +44 -11
  26. package/dist/cjs/oauth/models.d.ts +7 -2
  27. package/dist/cjs/oauth/models.js +10 -21
  28. package/dist/cjs/oauth/sequelize.d.ts +10 -48
  29. package/dist/cjs/oauth/sequelize.js +44 -99
  30. package/dist/cjs/oauth/types.d.ts +1 -0
  31. package/dist/cjs/passkey/base.d.ts +2 -0
  32. package/dist/cjs/passkey/config.d.ts +2 -0
  33. package/dist/cjs/passkey/config.js +26 -0
  34. package/dist/cjs/passkey/memory.d.ts +8 -0
  35. package/dist/cjs/passkey/memory.js +57 -16
  36. package/dist/cjs/passkey/models.d.ts +13 -4
  37. package/dist/cjs/passkey/models.js +41 -14
  38. package/dist/cjs/passkey/sequelize.d.ts +13 -25
  39. package/dist/cjs/passkey/sequelize.js +68 -153
  40. package/dist/cjs/passkey/service.d.ts +6 -2
  41. package/dist/cjs/passkey/service.js +205 -27
  42. package/dist/cjs/passkey/types.d.ts +18 -9
  43. package/dist/cjs/sequelize-utils.d.ts +8 -0
  44. package/dist/cjs/sequelize-utils.js +57 -0
  45. package/dist/cjs/token/base.d.ts +2 -1
  46. package/dist/cjs/token/base.js +3 -1
  47. package/dist/cjs/token/memory.d.ts +10 -0
  48. package/dist/cjs/token/memory.js +122 -32
  49. package/dist/cjs/token/sequelize.d.ts +4 -4
  50. package/dist/cjs/token/sequelize.js +67 -85
  51. package/dist/cjs/token/types.d.ts +8 -1
  52. package/dist/cjs/user/base.d.ts +1 -0
  53. package/dist/cjs/user/base.js +11 -4
  54. package/dist/cjs/user/memory.d.ts +2 -0
  55. package/dist/cjs/user/memory.js +9 -10
  56. package/dist/cjs/user/sequelize.d.ts +7 -2
  57. package/dist/cjs/user/sequelize.js +19 -32
  58. package/dist/esm/api-module.d.ts +7 -4
  59. package/dist/esm/api-module.js +9 -0
  60. package/dist/esm/api-server-base.d.ts +80 -23
  61. package/dist/esm/api-server-base.js +608 -100
  62. package/dist/esm/auth-api/auth-module.d.ts +23 -3
  63. package/dist/esm/auth-api/auth-module.js +321 -125
  64. package/dist/esm/auth-api/compat-auth-storage.d.ts +7 -5
  65. package/dist/esm/auth-api/compat-auth-storage.js +13 -1
  66. package/dist/esm/auth-api/mem-auth-store.d.ts +5 -3
  67. package/dist/esm/auth-api/mem-auth-store.js +14 -28
  68. package/dist/esm/auth-api/module.d.ts +1 -1
  69. package/dist/esm/auth-api/sql-auth-store.d.ts +16 -4
  70. package/dist/esm/auth-api/sql-auth-store.js +43 -30
  71. package/dist/esm/auth-api/storage.d.ts +6 -4
  72. package/dist/esm/auth-api/storage.js +13 -3
  73. package/dist/esm/auth-api/types.d.ts +7 -2
  74. package/dist/esm/auth-api/user-id.d.ts +5 -0
  75. package/dist/esm/auth-api/user-id.js +32 -0
  76. package/dist/esm/auth-cookie-options.d.ts +11 -0
  77. package/dist/esm/auth-cookie-options.js +63 -0
  78. package/dist/esm/index.d.ts +4 -9
  79. package/dist/esm/index.js +2 -7
  80. package/dist/esm/oauth/memory.d.ts +6 -0
  81. package/dist/esm/oauth/memory.js +44 -11
  82. package/dist/esm/oauth/models.d.ts +7 -2
  83. package/dist/esm/oauth/models.js +6 -19
  84. package/dist/esm/oauth/sequelize.d.ts +10 -48
  85. package/dist/esm/oauth/sequelize.js +32 -87
  86. package/dist/esm/oauth/types.d.ts +1 -0
  87. package/dist/esm/passkey/base.d.ts +2 -0
  88. package/dist/esm/passkey/config.d.ts +2 -0
  89. package/dist/esm/passkey/config.js +23 -0
  90. package/dist/esm/passkey/memory.d.ts +8 -0
  91. package/dist/esm/passkey/memory.js +57 -16
  92. package/dist/esm/passkey/models.d.ts +13 -4
  93. package/dist/esm/passkey/models.js +39 -12
  94. package/dist/esm/passkey/sequelize.d.ts +13 -25
  95. package/dist/esm/passkey/sequelize.js +69 -154
  96. package/dist/esm/passkey/service.d.ts +6 -2
  97. package/dist/esm/passkey/service.js +173 -28
  98. package/dist/esm/passkey/types.d.ts +18 -9
  99. package/dist/esm/sequelize-utils.d.ts +8 -0
  100. package/dist/esm/sequelize-utils.js +48 -0
  101. package/dist/esm/token/base.d.ts +2 -1
  102. package/dist/esm/token/base.js +3 -1
  103. package/dist/esm/token/memory.d.ts +10 -0
  104. package/dist/esm/token/memory.js +122 -32
  105. package/dist/esm/token/sequelize.d.ts +4 -4
  106. package/dist/esm/token/sequelize.js +67 -85
  107. package/dist/esm/token/types.d.ts +8 -1
  108. package/dist/esm/user/base.d.ts +1 -0
  109. package/dist/esm/user/base.js +11 -4
  110. package/dist/esm/user/memory.d.ts +2 -0
  111. package/dist/esm/user/memory.js +9 -10
  112. package/dist/esm/user/sequelize.d.ts +7 -2
  113. package/dist/esm/user/sequelize.js +19 -32
  114. package/docs/swagger/openapi.json +1876 -0
  115. package/package.json +84 -34
@@ -4,16 +4,16 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
- import { Application, Request, Response } from 'express';
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';
10
- import type { ApiAuthClass, ApiKey } from './api-module.js';
9
+ import { TokenStore, type JwtDecodeResult, type JwtSignPayload, type JwtSignResult, type JwtVerifyResult } from './token/base.js';
10
+ import type { ApiAuthClass, ApiAuthType, ApiKey } from './api-module.js';
11
11
  import type { AuthProviderModule } from './auth-api/module.js';
12
- import type { AuthStorage, AuthIdentifier } from './auth-api/types.js';
12
+ import type { AuthAdapter, AuthIdentifier } from './auth-api/types.js';
13
13
  import type { OAuthStore } from './oauth/base.js';
14
14
  import type { AuthCodeData, AuthCodeRequest, OAuthClient } from './oauth/types.js';
15
15
  import type { PasskeyService } from './passkey/service.js';
16
- import type { PasskeyChallenge, PasskeyChallengeParams, PasskeyVerificationParams, PasskeyVerificationResult } from './passkey/types.js';
16
+ import type { PasskeyChallenge, PasskeyChallengeParams, StoredPasskeyCredential, PasskeyVerificationParams, PasskeyVerificationResult } from './passkey/types.js';
17
17
  import type { Token } from './token/types.js';
18
18
  import type { UserStore } from './user/base.js';
19
19
  import type { JwtPayload, SignOptions, VerifyOptions } from 'jsonwebtoken';
@@ -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;
@@ -47,6 +47,12 @@ export interface ApiRequest {
47
47
  getRealUid: () => AuthIdentifier | null;
48
48
  isImpersonating: () => boolean;
49
49
  }
50
+ export interface ExpressApiRequest extends ExtendedReq {
51
+ apiReq?: ApiRequest;
52
+ }
53
+ export interface ExpressApiLocals {
54
+ apiReq?: ApiRequest;
55
+ }
50
56
  export interface ClientAgentProfile {
51
57
  ua: string;
52
58
  browser: string;
@@ -58,7 +64,7 @@ export interface ClientInfo extends ClientAgentProfile {
58
64
  ipchain: string[];
59
65
  }
60
66
  export interface ApiServerAuthStores {
61
- userStore: UserStore<any, any>;
67
+ userStore: UserStore<unknown, unknown>;
62
68
  tokenStore: TokenStore;
63
69
  passkeyService?: PasskeyService;
64
70
  oauthStore?: OAuthStore;
@@ -71,13 +77,13 @@ export { ApiModule } from './api-module.js';
71
77
  export type { ApiHandler, ApiAuthType, ApiAuthClass, ApiRoute, ApiKey } from './api-module.js';
72
78
  export interface ApiErrorParams {
73
79
  code?: number;
74
- message?: any;
75
- data?: any;
80
+ message?: unknown;
81
+ data?: unknown;
76
82
  errors?: Record<string, string>;
77
83
  }
78
84
  export declare class ApiError extends Error {
79
85
  code: number;
80
- data: any;
86
+ data: unknown;
81
87
  errors: Record<string, string>;
82
88
  constructor({ code, message, data, errors }: ApiErrorParams);
83
89
  }
@@ -86,12 +92,29 @@ export interface ApiServerConf {
86
92
  apiHost: string;
87
93
  uploadPath: string;
88
94
  uploadMax: number;
95
+ staticDirs?: Record<string, string>;
89
96
  origins: string[];
90
97
  debug: boolean;
91
98
  apiBasePath: string;
99
+ swaggerEnabled?: boolean;
100
+ swaggerPath?: string;
92
101
  accessSecret: string;
93
102
  refreshSecret: string;
103
+ /** Cookie domain for auth cookies. Prefer leaving empty for localhost/development. */
94
104
  cookieDomain: string;
105
+ /** Cookie path for auth cookies. */
106
+ cookiePath?: string;
107
+ /** Cookie SameSite attribute for auth cookies. */
108
+ cookieSameSite?: 'lax' | 'strict' | 'none';
109
+ /**
110
+ * Cookie Secure attribute for auth cookies.
111
+ * - true: always secure
112
+ * - false: never secure
113
+ * - 'auto': secure when request is HTTPS (or forwarded as HTTPS)
114
+ */
115
+ cookieSecure?: boolean | 'auto';
116
+ /** Cookie HttpOnly attribute for auth cookies. */
117
+ cookieHttpOnly?: boolean;
95
118
  accessCookie: string;
96
119
  refreshCookie: string;
97
120
  accessExpiry: number;
@@ -101,49 +124,68 @@ export interface ApiServerConf {
101
124
  devMode: boolean;
102
125
  hydrateGetBody: boolean;
103
126
  validateTokens: boolean;
127
+ refreshMaybe: boolean;
104
128
  apiVersion: string;
105
129
  minClientVersion: string;
106
130
  tokenStore?: TokenStore;
107
131
  authStores?: ApiServerAuthStores;
132
+ onStartError?: (error: Error) => void;
108
133
  }
109
134
  export declare class ApiServer {
110
135
  app: Application;
111
- currReq: ApiRequest | null;
112
136
  readonly config: ApiServerConf;
113
137
  readonly startedAt: number;
114
138
  private readonly apiBasePath;
139
+ private readonly apiRouter;
140
+ private finalized;
115
141
  private storageAdapter;
116
142
  private moduleAdapter;
143
+ private serverAuthAdapter;
117
144
  private apiNotFoundHandler;
145
+ private apiErrorHandlerInstalled;
118
146
  private tokenStoreAdapter;
119
147
  private userStoreAdapter;
120
148
  private passkeyServiceAdapter;
121
149
  private oauthStoreAdapter;
122
150
  private canImpersonateAdapter;
123
151
  private readonly jwtHelper;
152
+ private currReqDeprecationWarned;
153
+ /**
154
+ * @deprecated ApiServer does not track a global "current request". This value is always null.
155
+ * Use the per-request ApiRequest passed to handlers, or `req.apiReq` / `res.locals.apiReq`
156
+ * when mounting raw Express endpoints.
157
+ */
158
+ get currReq(): ApiRequest | null;
159
+ set currReq(_value: ApiRequest | null);
124
160
  constructor(config?: Partial<ApiServerConf>);
125
- authStorage<UserRow, SafeUser>(storage: AuthStorage<UserRow, SafeUser>): this;
161
+ private assertNotFinalized;
162
+ private toApiRouterPath;
163
+ finalize(): this;
164
+ authStorage<UserRow, SafeUser>(storage: AuthAdapter<UserRow, SafeUser>): this;
126
165
  /**
127
166
  * @deprecated Use {@link ApiServer.authStorage} instead.
128
167
  */
129
- useAuthStorage<UserRow, SafeUser>(storage: AuthStorage<UserRow, SafeUser>): this;
168
+ useAuthStorage<UserRow, SafeUser>(storage: AuthAdapter<UserRow, SafeUser>): this;
130
169
  authModule<UserRow>(module: AuthProviderModule<UserRow>): this;
131
170
  /**
132
171
  * @deprecated Use {@link ApiServer.authModule} instead.
133
172
  */
134
173
  useAuthModule<UserRow>(module: AuthProviderModule<UserRow>): this;
135
- getAuthStorage(): AuthStorage<any, any>;
136
- getAuthModule(): AuthProviderModule<any>;
174
+ getAuthStorage<UserRow = unknown, SafeUser = unknown>(): AuthAdapter<UserRow, SafeUser>;
175
+ getAuthModule<UserRow = unknown>(): AuthProviderModule<UserRow>;
137
176
  setTokenStore(store: TokenStore): this;
138
177
  getTokenStore(): TokenStore | null;
139
178
  private ensureUserStore;
140
179
  private ensureTokenStore;
141
180
  private ensurePasskeyService;
181
+ listUserCredentials(userId: AuthIdentifier): Promise<StoredPasskeyCredential[]>;
182
+ deletePasskeyCredential(credentialId: Buffer | string): Promise<boolean>;
142
183
  private ensureOAuthStore;
143
- getUser(identifier: AuthIdentifier): Promise<any | null>;
144
- getUserPasswordHash(user: any): string;
145
- getUserId(user: any): AuthIdentifier;
146
- filterUser(user: any): any;
184
+ private getServerAuthAdapter;
185
+ getUser(identifier: AuthIdentifier): Promise<unknown | null>;
186
+ getUserPasswordHash(user: unknown): string;
187
+ getUserId(user: unknown): AuthIdentifier;
188
+ filterUser(user: unknown): unknown;
147
189
  verifyPassword(password: string, hash: string): Promise<boolean>;
148
190
  storeToken(data: Token): Promise<void>;
149
191
  getToken(query: Partial<Token> & {
@@ -166,7 +208,7 @@ export declare class ApiServer {
166
208
  realUserId: AuthIdentifier;
167
209
  effectiveUserId: AuthIdentifier;
168
210
  }): Promise<boolean>;
169
- jwtSign(payload: any, secret: string, expiresInSeconds: number, options?: SignOptions): JwtSignResult;
211
+ jwtSign(payload: JwtSignPayload, secret: string, expiresInSeconds: number, options?: SignOptions): JwtSignResult;
170
212
  jwtVerify<T>(token: string, secret: string, options?: VerifyOptions): JwtVerifyResult<T>;
171
213
  jwtDecode<T>(token: string, options?: import('jsonwebtoken').DecodeOptions): JwtDecodeResult<T>;
172
214
  getApiKey<T = ApiKey>(token: string): Promise<T | null>;
@@ -177,16 +219,23 @@ export declare class ApiServer {
177
219
  updateToken(updates: Partial<Token> & {
178
220
  refreshToken: string;
179
221
  }): Promise<boolean>;
180
- guessExceptionText(error: any, defMsg?: string): string;
222
+ guessExceptionText(error: unknown, defMsg?: string): string;
181
223
  protected authorize(apiReq: ApiRequest, requiredClass: ApiAuthClass): Promise<void>;
182
224
  private middlewares;
225
+ private installStaticDirs;
183
226
  private installPingHandler;
227
+ private loadSwaggerSpec;
228
+ private installSwaggerHandler;
184
229
  private normalizeApiBasePath;
185
230
  private installApiNotFoundHandler;
186
- private ensureApiNotFoundOrdering;
231
+ private installApiErrorHandler;
187
232
  private describeMissingEndpoint;
188
233
  start(): this;
234
+ private internalServerErrorMessage;
189
235
  private verifyJWT;
236
+ private jwtCookieOptions;
237
+ private setAccessCookie;
238
+ private tryRefreshAccessToken;
190
239
  private authenticate;
191
240
  private tryAuthenticateApiKey;
192
241
  private requiresAuthToken;
@@ -195,8 +244,16 @@ export declare class ApiServer {
195
244
  private normalizeAuthIdentifier;
196
245
  private extractTokenUserId;
197
246
  private resolveRealUserId;
247
+ useExpress(path: string, ...handlers: Array<RequestHandler | ErrorRequestHandler>): this;
248
+ useExpress(...handlers: Array<RequestHandler | ErrorRequestHandler>): this;
249
+ private createApiRequest;
250
+ expressAuth(auth: {
251
+ type: ApiAuthType;
252
+ req: ApiAuthClass;
253
+ }): RequestHandler;
254
+ expressErrorHandler(): ErrorRequestHandler;
198
255
  private handle_request;
199
- api<T extends ApiModule<any>>(module: T): this;
256
+ api<T extends ApiModule<unknown>>(module: T): this;
200
257
  dumpRequest(apiReq: ApiRequest): void;
201
258
  private formatDebugValue;
202
259
  dumpResponse(apiReq: ApiRequest, payload: unknown, status: number): void;
@@ -1,6 +1,6 @@
1
1
  import { type ApiRequest, type ApiRoute, type ApiServer } from '../api-server-base.js';
2
2
  import { BaseAuthModule, type AuthProviderModule } from './module.js';
3
- import type { AuthIdentifier, AuthStorage } from './types.js';
3
+ import type { AuthAdapter, AuthIdentifier } from './types.js';
4
4
  import type { OAuthCallbackParams, OAuthCallbackResult, OAuthStartParams, OAuthStartResult } from '../oauth/types.js';
5
5
  import type { TokenPair, Token } from '../token/types.js';
6
6
  interface CanImpersonateContext<UserEntity> {
@@ -10,10 +10,16 @@ interface CanImpersonateContext<UserEntity> {
10
10
  targetUser: UserEntity;
11
11
  effectiveUserId: AuthIdentifier;
12
12
  }
13
+ type AuthRateLimitEndpoint = 'login' | 'passkey-challenge' | 'oauth-token';
13
14
  interface AuthModuleOptions<UserEntity> {
14
15
  namespace?: string;
15
16
  defaultDomain?: string;
16
17
  canImpersonate?: (context: CanImpersonateContext<UserEntity>) => Promise<boolean> | boolean;
18
+ rateLimit?: (context: {
19
+ apiReq: ApiRequest;
20
+ endpoint: AuthRateLimitEndpoint;
21
+ }) => Promise<void> | void;
22
+ allowInsecurePkcePlain?: boolean;
17
23
  }
18
24
  type TokenMetadata = Partial<Token> & {
19
25
  sessionCookie?: boolean;
@@ -42,11 +48,14 @@ type AuthCapableServer<PublicUser> = ApiServer & {
42
48
  };
43
49
  export default class AuthModule<UserEntity, PublicUser> extends BaseAuthModule<UserEntity> implements AuthProviderModule<UserEntity> {
44
50
  static defaultNamespace: string;
45
- server: AuthCapableServer<PublicUser>;
51
+ get server(): AuthCapableServer<PublicUser>;
52
+ set server(value: AuthCapableServer<PublicUser>);
46
53
  private readonly defaultDomain?;
47
54
  private readonly canImpersonateHook?;
55
+ private readonly rateLimitHook?;
56
+ private readonly allowInsecurePkcePlain;
48
57
  constructor(options?: AuthModuleOptions<UserEntity>);
49
- protected get storage(): AuthStorage<UserEntity, PublicUser>;
58
+ protected get storage(): AuthAdapter<UserEntity, PublicUser>;
50
59
  protected canImpersonate(apiReq: ApiRequest, realUser: UserEntity, targetUser: UserEntity): Promise<boolean>;
51
60
  protected ensureImpersonationAllowed(apiReq: ApiRequest, realUser: UserEntity, targetUser: UserEntity): Promise<void>;
52
61
  protected buildTokenPayload(user: UserEntity, metadata?: TokenMetadata): TokenClaims;
@@ -57,6 +66,9 @@ export default class AuthModule<UserEntity, PublicUser> extends BaseAuthModule<U
57
66
  private resolveSessionPreferences;
58
67
  private mergeSessionPreferences;
59
68
  private sessionPrefsFromRecord;
69
+ private validateCredentialId;
70
+ private normalizeCredentialId;
71
+ private toIsoDate;
60
72
  private cookieOptions;
61
73
  private setJwtCookies;
62
74
  issueTokens(apiReq: ApiRequest, user: UserEntity, metadata?: TokenIssueOptions): Promise<TokenPair>;
@@ -76,6 +88,8 @@ export default class AuthModule<UserEntity, PublicUser> extends BaseAuthModule<U
76
88
  private postWhoAmI;
77
89
  private postPasskeyChallenge;
78
90
  private postPasskeyVerify;
91
+ private getPasskeys;
92
+ private deletePasskey;
79
93
  private postImpersonation;
80
94
  private deleteImpersonation;
81
95
  private getUserFromPasskey;
@@ -91,6 +105,12 @@ export default class AuthModule<UserEntity, PublicUser> extends BaseAuthModule<U
91
105
  private resolveClientAuthentication;
92
106
  private assertRedirectUriAllowed;
93
107
  private resolveUserForOAuth;
108
+ private hasPasskeyService;
109
+ private hasOAuthStore;
110
+ private storageImplements;
111
+ private storageImplementsAll;
112
+ private applyRateLimit;
113
+ private resolvePkceChallengeMethod;
94
114
  defineRoutes(): ApiRoute[];
95
115
  }
96
116
  export {};