@technomoron/api-server-base 1.0.43 → 1.1.1

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.
package/README.txt CHANGED
@@ -27,18 +27,32 @@ All runtime dependencies and `@types/*` packages are bundled with the distributi
27
27
 
28
28
  Quick Start
29
29
  -----------
30
- import { ApiServer, ApiModule, ApiError } from '@technomoron/api-server-base';
30
+ import { ApiServer, ApiModule, ApiError, BaseAuthStorage } from '@technomoron/api-server-base';
31
+
32
+ type DemoUser = { id: string; email: string; password: string };
33
+
34
+ class DemoStorage extends BaseAuthStorage<DemoUser, Omit<DemoUser, 'password'>> {
35
+ private readonly users = new Map<string, DemoUser>([
36
+ ['1', { id: '1', email: 'demo@example.com', password: 'secret' }]
37
+ ]);
31
38
 
32
- class AppServer extends ApiServer {
33
39
  async getUser(uid: unknown) {
34
- return { id: uid, email: 'demo@example.com' };
40
+ return this.users.get(String(uid)) ?? null;
41
+ }
42
+
43
+ getUserPasswordHash(user: DemoUser) {
44
+ return user.password;
35
45
  }
36
46
 
37
- filterUser(user: any) {
38
- return { id: user.id, email: user.email };
47
+ filterUser(user: DemoUser) {
48
+ const { password: _password, ...safe } = user;
49
+ void _password;
50
+ return safe;
39
51
  }
40
52
  }
41
53
 
54
+ class AppServer extends ApiServer {}
55
+
42
56
  class UserModule extends ApiModule<AppServer> {
43
57
  constructor() {
44
58
  super({ namespace: '/users' });
@@ -51,24 +65,30 @@ class UserModule extends ApiModule<AppServer> {
51
65
  path: '/',
52
66
  auth: { type: 'yes', req: 'any' },
53
67
  handler: async ({ server, tokenData }) => {
54
- const user = await server.getUser(tokenData?.uid);
68
+ const storage = server.getAuthStorage();
69
+ const user = tokenData ? await storage.getUser(tokenData.uid) : null;
55
70
  if (!user) {
56
71
  throw new ApiError({ code: 404, message: 'User not found' });
57
72
  }
58
- return [200, server.filterUser(user)];
73
+ return [200, storage.filterUser(user)];
59
74
  },
60
75
  },
61
76
  ];
62
77
  }
63
78
  }
64
79
 
80
+ const yourStorageAdapter = new DemoStorage();
81
+
65
82
  const server = new AppServer({
66
83
  apiPort: 3101,
67
84
  apiHost: '127.0.0.1',
68
- accessSecret: 'replace-me',
69
- });
85
+ accessSecret: 'replace-me'
86
+ })
87
+ .authStorage(yourStorageAdapter)
88
+ .api(new UserModule())
89
+ .start();
70
90
 
71
- server.api(new UserModule()).start();
91
+ Need a dedicated auth module as well? Chain `.authModule(...)` in the same spot.
72
92
 
73
93
  Handlers must return a tuple: [statusCode], [statusCode, data], or [statusCode, data, message]. Throw ApiError for predictable failures.
74
94
 
@@ -111,8 +131,8 @@ Use getClientIp(req) to obtain the most likely client address, skipping loopback
111
131
 
112
132
  Extending the Base Classes
113
133
  --------------------------
114
- Override getApiKey, getUser, authenticateUser, storeToken, getToken, updateToken, deleteToken, and verifyPassword to integrate with your persistence layer.
115
- Customize filterUser to trim sensitive data before sending it to the client.
134
+ Implement the AuthStorage contract (getUser, verifyPassword, storeToken, updateToken, etc.) to integrate with your persistence layer, then supply it via authStorage().
135
+ Use your storage adapter's filterUser helper to trim sensitive data before returning responses.
116
136
  Provide your own authorize method to enforce role based access control using the ApiAuthClass enum.
117
137
  Create feature modules by extending ApiModule. Use the optional checkConfig hook to validate prerequisites before routes mount.
118
138
 
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ApiModule = void 0;
4
+ class ApiModule {
5
+ constructor(opts = {}) {
6
+ this.mountpath = '';
7
+ this.namespace = opts.namespace ?? this.constructor.defaultNamespace ?? '';
8
+ }
9
+ checkConfig() {
10
+ return true;
11
+ }
12
+ defineRoutes() {
13
+ return [];
14
+ }
15
+ }
16
+ exports.ApiModule = ApiModule;
17
+ ApiModule.defaultNamespace = '';
@@ -0,0 +1,27 @@
1
+ import type { ApiRequest } from './api-server-base.js';
2
+ export type ApiHandler = (apiReq: ApiRequest) => Promise<[number] | [number, any] | [number, any, string]>;
3
+ export type ApiAuthType = 'none' | 'maybe' | 'yes';
4
+ export type ApiAuthClass = 'any' | 'admin';
5
+ export interface ApiKey {
6
+ uid: unknown;
7
+ }
8
+ export type ApiRoute = {
9
+ method: 'get' | 'post' | 'put' | 'delete';
10
+ path: string;
11
+ handler: ApiHandler;
12
+ auth: {
13
+ type: ApiAuthType;
14
+ req: ApiAuthClass;
15
+ };
16
+ };
17
+ export declare class ApiModule<T> {
18
+ server: T;
19
+ namespace: string;
20
+ mountpath: string;
21
+ static defaultNamespace: string;
22
+ constructor(opts?: {
23
+ namespace?: string;
24
+ });
25
+ checkConfig(): boolean;
26
+ defineRoutes(): ApiRoute[];
27
+ }
@@ -15,20 +15,10 @@ const cors_1 = __importDefault(require("cors"));
15
15
  const express_1 = __importDefault(require("express"));
16
16
  const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
17
17
  const multer_1 = __importDefault(require("multer"));
18
- class ApiModule {
19
- constructor(opts = {}) {
20
- this.mountpath = '';
21
- this.namespace = opts.namespace ?? this.constructor.defaultNamespace ?? '';
22
- }
23
- checkConfig() {
24
- return true;
25
- }
26
- defineRoutes() {
27
- return [];
28
- }
29
- }
30
- exports.ApiModule = ApiModule;
31
- ApiModule.defaultNamespace = '';
18
+ const auth_module_js_1 = require("./auth-module.cjs");
19
+ const auth_storage_js_1 = require("./auth-storage.cjs");
20
+ var api_module_js_1 = require("./api-module.cjs");
21
+ Object.defineProperty(exports, "ApiModule", { enumerable: true, get: function () { return api_module_js_1.ApiModule; } });
32
22
  function guess_exception_text(error, defMsg = 'Unknown Error') {
33
23
  const msg = [];
34
24
  if (typeof error === 'string' && error.trim() !== '') {
@@ -168,6 +158,8 @@ class ApiServer {
168
158
  constructor(config = {}) {
169
159
  this.currReq = null;
170
160
  this.config = fillConfig(config);
161
+ this.storageAdapter = auth_storage_js_1.nullAuthStorage;
162
+ this.moduleAdapter = auth_module_js_1.nullAuthModule;
171
163
  this.app = (0, express_1.default)();
172
164
  if (config.uploadPath) {
173
165
  const upload = (0, multer_1.default)({ dest: config.uploadPath });
@@ -176,6 +168,32 @@ class ApiServer {
176
168
  this.middlewares();
177
169
  // addSwaggerUi(this.app);
178
170
  }
171
+ authStorage(storage) {
172
+ this.storageAdapter = storage;
173
+ return this;
174
+ }
175
+ /**
176
+ * @deprecated Use {@link ApiServer.authStorage} instead.
177
+ */
178
+ useAuthStorage(storage) {
179
+ return this.authStorage(storage);
180
+ }
181
+ authModule(module) {
182
+ this.moduleAdapter = module;
183
+ return this;
184
+ }
185
+ /**
186
+ * @deprecated Use {@link ApiServer.authModule} instead.
187
+ */
188
+ useAuthModule(module) {
189
+ return this.authModule(module);
190
+ }
191
+ getAuthStorage() {
192
+ return this.storageAdapter;
193
+ }
194
+ getAuthModule() {
195
+ return this.moduleAdapter;
196
+ }
179
197
  jwtSign(payload, secret, expiresInSeconds, options) {
180
198
  options || (options = {});
181
199
  const opts = { ...options, expiresIn: expiresInSeconds };
@@ -247,37 +265,28 @@ class ApiServer {
247
265
  void token;
248
266
  return null;
249
267
  }
250
- async getUser(uid) {
251
- void uid;
252
- throw new Error('getUser() not implemented');
253
- }
254
268
  async authenticateUser(params) {
255
- void params;
256
- throw new Error('authenticateUser() not implemented');
257
- }
258
- async storeToken(params) {
259
- void params;
260
- throw new Error('storeToken() not implemented');
261
- }
262
- async getToken(params) {
263
- void params;
264
- throw new Error('getToken() not implemented');
265
- }
266
- async updateToken(params) {
267
- void params;
268
- throw new Error('updateToken() not implemented');
269
- }
270
- async deleteToken(params) {
271
- void params;
272
- throw new Error('deleteToken() not implemented');
273
- }
274
- async verifyPassword(password, hash) {
275
- void password;
276
- void hash;
277
- throw new Error('verifyPassword() not implemented');
269
+ if (!params?.login || !params?.password) {
270
+ return false;
271
+ }
272
+ const user = await this.storageAdapter.getUser(params.login);
273
+ if (!user) {
274
+ return false;
275
+ }
276
+ const hash = this.storageAdapter.getUserPasswordHash(user);
277
+ return this.storageAdapter.verifyPassword(params.password, hash);
278
278
  }
279
- filterUser(fullUser) {
280
- return fullUser;
279
+ async updateToken(updates) {
280
+ if (typeof this.storageAdapter.updateToken !== 'function') {
281
+ return false;
282
+ }
283
+ return this.storageAdapter.updateToken({
284
+ refreshToken: updates.refreshToken,
285
+ access: updates.accessToken,
286
+ expires: updates.expires,
287
+ clientId: updates.clientId,
288
+ scope: updates.scope
289
+ });
281
290
  }
282
291
  guessExceptionText(error, defMsg = 'Unkown Error') {
283
292
  return guess_exception_text(error, defMsg);
@@ -495,6 +504,9 @@ class ApiServer {
495
504
  api(module) {
496
505
  const router = express_1.default.Router();
497
506
  module.server = this;
507
+ if (module?.moduleType === 'auth') {
508
+ this.authModule(module);
509
+ }
498
510
  module.checkConfig();
499
511
  const base = this.config.apiBasePath ?? '/api';
500
512
  const ns = module.namespace;
@@ -6,6 +6,10 @@
6
6
  */
7
7
  import { Application, Request, Response } from 'express';
8
8
  import jwt, { JwtPayload, SignOptions, VerifyOptions } from 'jsonwebtoken';
9
+ import { ApiModule } from './api-module.js';
10
+ import type { ApiAuthClass, ApiKey } from './api-module.js';
11
+ import type { AuthProviderModule } from './auth-module.js';
12
+ import type { AuthStorage } from './auth-storage.js';
9
13
  export type { Application, Request, Response, NextFunction, Router } from 'express';
10
14
  export type { Multer } from 'multer';
11
15
  export type { JwtPayload, SignOptions, VerifyOptions } from 'jsonwebtoken';
@@ -43,32 +47,8 @@ export interface ApiRequest {
43
47
  tokenData?: ApiTokenData | null;
44
48
  token?: string;
45
49
  }
46
- export type ApiHandler = (apiReq: ApiRequest) => Promise<[number] | [number, any] | [number, any, string]>;
47
- export type ApiAuthType = 'none' | 'maybe' | 'yes';
48
- export type ApiAuthClass = 'any' | 'admin';
49
- export interface ApiKey {
50
- uid: unknown;
51
- }
52
- export type ApiRoute = {
53
- method: 'get' | 'post' | 'put' | 'delete';
54
- path: string;
55
- handler: ApiHandler;
56
- auth: {
57
- type: ApiAuthType;
58
- req: ApiAuthClass;
59
- };
60
- };
61
- export declare class ApiModule<T> {
62
- server: T;
63
- namespace: string;
64
- mountpath: string;
65
- static defaultNamespace: string;
66
- constructor(opts?: {
67
- namespace?: string;
68
- });
69
- checkConfig(): boolean;
70
- defineRoutes(): ApiRoute[];
71
- }
50
+ export { ApiModule } from './api-module.js';
51
+ export type { ApiHandler, ApiAuthType, ApiAuthClass, ApiRoute, ApiKey } from './api-module.js';
72
52
  export interface ApiErrorParams {
73
53
  code?: number;
74
54
  message?: any;
@@ -104,41 +84,36 @@ export declare class ApiServer {
104
84
  app: Application;
105
85
  currReq: ApiRequest | null;
106
86
  readonly config: ApiServerConf;
87
+ private storageAdapter;
88
+ private moduleAdapter;
107
89
  constructor(config?: Partial<ApiServerConf>);
90
+ authStorage<UserRow, SafeUser>(storage: AuthStorage<UserRow, SafeUser>): this;
91
+ /**
92
+ * @deprecated Use {@link ApiServer.authStorage} instead.
93
+ */
94
+ useAuthStorage<UserRow, SafeUser>(storage: AuthStorage<UserRow, SafeUser>): this;
95
+ authModule<UserRow>(module: AuthProviderModule<UserRow>): this;
96
+ /**
97
+ * @deprecated Use {@link ApiServer.authModule} instead.
98
+ */
99
+ useAuthModule<UserRow>(module: AuthProviderModule<UserRow>): this;
100
+ getAuthStorage(): AuthStorage<any, any>;
101
+ getAuthModule(): AuthProviderModule<any>;
108
102
  jwtSign(payload: any, secret: string, expiresInSeconds: number, options?: SignOptions): JwtSignResult;
109
103
  jwtVerify<T>(token: string, secret: string, options?: VerifyOptions): JwtVerifyResult<T>;
110
104
  jwtDecode<T>(token: string, options?: jwt.DecodeOptions): JwtDecodeResult<T>;
111
105
  getApiKey<T = ApiKey>(token: string): Promise<T | null>;
112
- getUser(uid: unknown): Promise<unknown>;
113
- authenticateUser(params: unknown): Promise<boolean>;
114
- storeToken(params: {
115
- access: string;
116
- refresh: string;
117
- userId: unknown;
118
- domain?: string;
119
- fingerprint?: string;
120
- label?: string;
121
- }): Promise<void>;
122
- getToken(params: {
123
- accessToken?: string;
124
- refreshToken?: string;
125
- userId?: unknown;
126
- }): Promise<unknown>;
127
- updateToken(params: {
106
+ authenticateUser(params: {
107
+ login: string;
108
+ password: string;
109
+ }): Promise<boolean>;
110
+ updateToken(updates: {
128
111
  accessToken: string;
129
112
  refreshToken: string;
130
113
  expires?: Date;
114
+ clientId?: string;
115
+ scope?: string[];
131
116
  }): Promise<boolean>;
132
- deleteToken(params: {
133
- refreshToken?: string;
134
- accessToken?: string;
135
- userId?: unknown;
136
- domain?: string;
137
- fingerprint?: string;
138
- label?: string;
139
- }): Promise<number>;
140
- verifyPassword(password: string, hash: string): Promise<boolean>;
141
- filterUser<T = any, U = any>(fullUser: T): U;
142
117
  guessExceptionText(error: any, defMsg?: string): string;
143
118
  protected authorize(apiReq: ApiRequest, requiredClass: ApiAuthClass): Promise<void>;
144
119
  private middlewares;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.nullAuthModule = exports.BaseAuthModule = void 0;
4
+ const api_module_js_1 = require("./api-module.cjs");
5
+ // Handy base that you can extend when wiring a real auth module. Subclasses
6
+ // must supply a namespace via the constructor and implement token issuance.
7
+ class BaseAuthModule extends api_module_js_1.ApiModule {
8
+ constructor(opts) {
9
+ super(opts);
10
+ this.moduleType = 'auth';
11
+ }
12
+ }
13
+ exports.BaseAuthModule = BaseAuthModule;
14
+ class NullAuthModule extends BaseAuthModule {
15
+ constructor() {
16
+ super({ namespace: '__null__' });
17
+ }
18
+ async issueTokens(apiReq, user, metadata) {
19
+ void apiReq;
20
+ void user;
21
+ void metadata;
22
+ throw new Error('Auth module not configured. Inject a real auth module before issuing tokens.');
23
+ }
24
+ }
25
+ exports.nullAuthModule = new NullAuthModule();
@@ -0,0 +1,20 @@
1
+ import { ApiModule } from './api-module.js';
2
+ import type { ApiRequest } from './api-server-base.js';
3
+ import type { AuthTokenMetadata, AuthTokenPair } from './auth-storage.js';
4
+ export interface AuthProviderModule<UserRow = unknown> {
5
+ readonly moduleType: 'auth';
6
+ readonly namespace: string;
7
+ issueTokens(apiReq: ApiRequest, user: UserRow, metadata?: AuthTokenMetadata & {
8
+ expires?: Date;
9
+ }): Promise<AuthTokenPair>;
10
+ }
11
+ export declare abstract class BaseAuthModule<UserRow = unknown> extends ApiModule<any> implements AuthProviderModule<UserRow> {
12
+ readonly moduleType: "auth";
13
+ protected constructor(opts: {
14
+ namespace: string;
15
+ });
16
+ abstract issueTokens(apiReq: ApiRequest, user: UserRow, metadata?: AuthTokenMetadata & {
17
+ expires?: Date;
18
+ }): Promise<AuthTokenPair>;
19
+ }
20
+ export declare const nullAuthModule: AuthProviderModule<unknown>;
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ // Numeric database id or lookup string such as username/email.
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.nullAuthStorage = exports.BaseAuthStorage = void 0;
5
+ // Handy base you can extend when wiring a real storage adapter. Every method
6
+ // throws by default so unimplemented hooks fail loudly.
7
+ class BaseAuthStorage {
8
+ // Override to load a user record by identifier
9
+ async getUser(identifier) {
10
+ void identifier;
11
+ return null;
12
+ }
13
+ // Override to return the stored password hash for the user
14
+ getUserPasswordHash(user) {
15
+ void user;
16
+ throw new Error('Auth storage not configured');
17
+ }
18
+ // Override to expose the canonical user identifier
19
+ getUserId(user) {
20
+ void user;
21
+ throw new Error('Auth storage not configured');
22
+ }
23
+ // Override to strip sensitive fields from the user record
24
+ filterUser(user) {
25
+ return user;
26
+ }
27
+ // Override to validate a raw password against the stored hash
28
+ async verifyPassword(password, hash) {
29
+ void password;
30
+ void hash;
31
+ throw new Error('Auth storage not configured');
32
+ }
33
+ // Override to persist newly issued tokens
34
+ async storeToken(data) {
35
+ void data;
36
+ throw new Error('Auth storage not configured');
37
+ }
38
+ // Override to look up a stored token by query
39
+ async getToken(query) {
40
+ void query;
41
+ return null;
42
+ }
43
+ // Override to remove stored tokens that match the query
44
+ async deleteToken(query) {
45
+ void query;
46
+ return 0;
47
+ }
48
+ // Override to update metadata for an existing refresh token
49
+ async updateToken(updates) {
50
+ void updates;
51
+ return false;
52
+ }
53
+ // Override to create a new passkey challenge record
54
+ async createPasskeyChallenge(params) {
55
+ void params;
56
+ throw new Error('Auth storage not configured');
57
+ }
58
+ // Override to verify an incoming WebAuthn response
59
+ async verifyPasskeyResponse(params) {
60
+ void params;
61
+ throw new Error('Auth storage not configured');
62
+ }
63
+ // Override to fetch an OAuth client by identifier
64
+ async getClient(clientId) {
65
+ void clientId;
66
+ return null;
67
+ }
68
+ // Override to compare a provided client secret against storage
69
+ async verifyClientSecret(client, clientSecret) {
70
+ void client;
71
+ void clientSecret;
72
+ throw new Error('Auth storage not configured');
73
+ }
74
+ // Override to create a new authorization code entry
75
+ async createAuthCode(request) {
76
+ void request;
77
+ throw new Error('Auth storage not configured');
78
+ }
79
+ // Override to consume and invalidate an authorization code
80
+ async consumeAuthCode(code, clientId) {
81
+ void code;
82
+ void clientId;
83
+ return null;
84
+ }
85
+ }
86
+ exports.BaseAuthStorage = BaseAuthStorage;
87
+ exports.nullAuthStorage = new BaseAuthStorage();
@@ -0,0 +1,114 @@
1
+ export type AuthIdentifier = string | number;
2
+ export interface AuthTokenMetadata {
3
+ clientId?: string;
4
+ domain?: string;
5
+ fingerprint?: string;
6
+ label?: string;
7
+ scope?: string | string[];
8
+ }
9
+ export interface AuthTokenData extends AuthTokenMetadata {
10
+ access: string;
11
+ expires?: Date;
12
+ refresh: string;
13
+ userId: AuthIdentifier;
14
+ }
15
+ export interface AuthTokenQuery extends AuthTokenMetadata {
16
+ accessToken?: string;
17
+ refreshToken?: string;
18
+ userId?: AuthIdentifier;
19
+ }
20
+ export interface AuthTokenPair {
21
+ accessToken: string;
22
+ refreshToken: string;
23
+ }
24
+ export interface AuthTokenPayload extends AuthTokenMetadata {
25
+ exp?: number;
26
+ iat?: number;
27
+ uid: AuthIdentifier;
28
+ }
29
+ export interface PasskeyChallengeParams extends AuthTokenMetadata {
30
+ action: 'register' | 'authenticate';
31
+ login?: string;
32
+ userAgent?: string;
33
+ userId?: AuthIdentifier;
34
+ }
35
+ export interface PasskeyChallenge extends Record<string, unknown> {
36
+ challenge: string;
37
+ expiresAt?: string | number | Date;
38
+ userId?: AuthIdentifier;
39
+ }
40
+ export interface PasskeyVerificationParams extends AuthTokenMetadata {
41
+ expectedChallenge: string;
42
+ login?: string;
43
+ response: Record<string, unknown>;
44
+ userId?: AuthIdentifier;
45
+ }
46
+ export interface PasskeyVerificationResult extends Record<string, unknown> {
47
+ login?: string;
48
+ tokens?: AuthTokenPair;
49
+ userId?: AuthIdentifier;
50
+ verified: boolean;
51
+ }
52
+ export interface OAuthClient {
53
+ clientId: string;
54
+ clientSecret: string;
55
+ firstParty?: boolean;
56
+ metadata?: Record<string, unknown>;
57
+ name?: string;
58
+ redirectUris: string[];
59
+ scope?: string[];
60
+ }
61
+ export interface AuthCodeData {
62
+ code: string;
63
+ clientId: string;
64
+ codeChallenge?: string;
65
+ codeChallengeMethod?: 'plain' | 'S256';
66
+ expiresAt: Date;
67
+ metadata?: Record<string, unknown>;
68
+ redirectUri: string;
69
+ scope: string[];
70
+ userId: AuthIdentifier;
71
+ }
72
+ export type AuthCodeRequest = Omit<AuthCodeData, 'code' | 'expiresAt'> & {
73
+ code?: string;
74
+ expiresInSeconds?: number;
75
+ };
76
+ export interface AuthStorage<UserRow, SafeUser> {
77
+ getUser(identifier: AuthIdentifier): Promise<UserRow | null>;
78
+ getUserPasswordHash(user: UserRow): string;
79
+ getUserId(user: UserRow): AuthIdentifier;
80
+ filterUser(user: UserRow): SafeUser;
81
+ verifyPassword(password: string, hash: string): Promise<boolean>;
82
+ storeToken(data: AuthTokenData): Promise<void>;
83
+ getToken(query: AuthTokenQuery): Promise<AuthTokenData | null>;
84
+ deleteToken(query: AuthTokenQuery): Promise<number>;
85
+ updateToken?(updates: Partial<AuthTokenData> & {
86
+ refreshToken: string;
87
+ }): Promise<boolean>;
88
+ createPasskeyChallenge?(params: PasskeyChallengeParams): Promise<PasskeyChallenge>;
89
+ verifyPasskeyResponse?(params: PasskeyVerificationParams): Promise<PasskeyVerificationResult>;
90
+ getClient?(clientId: string): Promise<OAuthClient | null>;
91
+ verifyClientSecret?(client: OAuthClient, clientSecret: string | null): Promise<boolean>;
92
+ createAuthCode?(request: AuthCodeRequest): Promise<AuthCodeData>;
93
+ consumeAuthCode?(code: string, clientId: string): Promise<AuthCodeData | null>;
94
+ }
95
+ export declare class BaseAuthStorage<UserRow = unknown, SafeUser = unknown> implements AuthStorage<UserRow, SafeUser> {
96
+ getUser(identifier: AuthIdentifier): Promise<UserRow | null>;
97
+ getUserPasswordHash(user: UserRow): string;
98
+ getUserId(user: UserRow): AuthIdentifier;
99
+ filterUser(user: UserRow): SafeUser;
100
+ verifyPassword(password: string, hash: string): Promise<boolean>;
101
+ storeToken(data: AuthTokenData): Promise<void>;
102
+ getToken(query: AuthTokenQuery): Promise<AuthTokenData | null>;
103
+ deleteToken(query: AuthTokenQuery): Promise<number>;
104
+ updateToken(updates: Partial<AuthTokenData> & {
105
+ refreshToken: string;
106
+ }): Promise<boolean>;
107
+ createPasskeyChallenge(params: PasskeyChallengeParams): Promise<PasskeyChallenge>;
108
+ verifyPasskeyResponse(params: PasskeyVerificationParams): Promise<PasskeyVerificationResult>;
109
+ getClient(clientId: string): Promise<OAuthClient | null>;
110
+ verifyClientSecret(client: OAuthClient, clientSecret: string | null): Promise<boolean>;
111
+ createAuthCode(request: AuthCodeRequest): Promise<AuthCodeData>;
112
+ consumeAuthCode(code: string, clientId: string): Promise<AuthCodeData | null>;
113
+ }
114
+ export declare const nullAuthStorage: AuthStorage<unknown, unknown>;
@@ -3,9 +3,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.ApiError = exports.ApiModule = exports.ApiServer = void 0;
6
+ exports.BaseAuthModule = exports.nullAuthModule = exports.BaseAuthStorage = exports.nullAuthStorage = exports.ApiModule = exports.ApiError = exports.ApiServer = void 0;
7
7
  var api_server_base_js_1 = require("./api-server-base.cjs");
8
8
  Object.defineProperty(exports, "ApiServer", { enumerable: true, get: function () { return __importDefault(api_server_base_js_1).default; } });
9
9
  var api_server_base_js_2 = require("./api-server-base.cjs");
10
- Object.defineProperty(exports, "ApiModule", { enumerable: true, get: function () { return api_server_base_js_2.ApiModule; } });
11
10
  Object.defineProperty(exports, "ApiError", { enumerable: true, get: function () { return api_server_base_js_2.ApiError; } });
11
+ var api_module_js_1 = require("./api-module.cjs");
12
+ Object.defineProperty(exports, "ApiModule", { enumerable: true, get: function () { return api_module_js_1.ApiModule; } });
13
+ var auth_storage_js_1 = require("./auth-storage.cjs");
14
+ Object.defineProperty(exports, "nullAuthStorage", { enumerable: true, get: function () { return auth_storage_js_1.nullAuthStorage; } });
15
+ Object.defineProperty(exports, "BaseAuthStorage", { enumerable: true, get: function () { return auth_storage_js_1.BaseAuthStorage; } });
16
+ var auth_module_js_1 = require("./auth-module.cjs");
17
+ Object.defineProperty(exports, "nullAuthModule", { enumerable: true, get: function () { return auth_module_js_1.nullAuthModule; } });
18
+ Object.defineProperty(exports, "BaseAuthModule", { enumerable: true, get: function () { return auth_module_js_1.BaseAuthModule; } });
@@ -1,3 +1,8 @@
1
1
  export { default as ApiServer } from './api-server-base.js';
2
- export { ApiModule, ApiError } from './api-server-base.js';
2
+ export { ApiError } from './api-server-base.js';
3
+ export { ApiModule } from './api-module.js';
3
4
  export type { ApiErrorParams, ApiHandler, ApiKey, ApiServerConf, ApiRequest, ApiRoute, ApiAuthType, ApiAuthClass, ApiTokenData, RequestWithStuff } from './api-server-base.js';
5
+ export type { AuthIdentifier, AuthTokenMetadata, AuthTokenData, AuthTokenQuery, AuthTokenPair, AuthTokenPayload, PasskeyChallengeParams, PasskeyChallenge, PasskeyVerificationParams, PasskeyVerificationResult, OAuthClient, AuthCodeData, AuthCodeRequest, AuthStorage } from './auth-storage.js';
6
+ export type { AuthProviderModule } from './auth-module.js';
7
+ export { nullAuthStorage, BaseAuthStorage } from './auth-storage.js';
8
+ export { nullAuthModule, BaseAuthModule } from './auth-module.js';
@@ -0,0 +1,27 @@
1
+ import type { ApiRequest } from './api-server-base.js';
2
+ export type ApiHandler = (apiReq: ApiRequest) => Promise<[number] | [number, any] | [number, any, string]>;
3
+ export type ApiAuthType = 'none' | 'maybe' | 'yes';
4
+ export type ApiAuthClass = 'any' | 'admin';
5
+ export interface ApiKey {
6
+ uid: unknown;
7
+ }
8
+ export type ApiRoute = {
9
+ method: 'get' | 'post' | 'put' | 'delete';
10
+ path: string;
11
+ handler: ApiHandler;
12
+ auth: {
13
+ type: ApiAuthType;
14
+ req: ApiAuthClass;
15
+ };
16
+ };
17
+ export declare class ApiModule<T> {
18
+ server: T;
19
+ namespace: string;
20
+ mountpath: string;
21
+ static defaultNamespace: string;
22
+ constructor(opts?: {
23
+ namespace?: string;
24
+ });
25
+ checkConfig(): boolean;
26
+ defineRoutes(): ApiRoute[];
27
+ }
@@ -0,0 +1,13 @@
1
+ export class ApiModule {
2
+ constructor(opts = {}) {
3
+ this.mountpath = '';
4
+ this.namespace = opts.namespace ?? this.constructor.defaultNamespace ?? '';
5
+ }
6
+ checkConfig() {
7
+ return true;
8
+ }
9
+ defineRoutes() {
10
+ return [];
11
+ }
12
+ }
13
+ ApiModule.defaultNamespace = '';
@@ -6,6 +6,10 @@
6
6
  */
7
7
  import { Application, Request, Response } from 'express';
8
8
  import jwt, { JwtPayload, SignOptions, VerifyOptions } from 'jsonwebtoken';
9
+ import { ApiModule } from './api-module.js';
10
+ import type { ApiAuthClass, ApiKey } from './api-module.js';
11
+ import type { AuthProviderModule } from './auth-module.js';
12
+ import type { AuthStorage } from './auth-storage.js';
9
13
  export type { Application, Request, Response, NextFunction, Router } from 'express';
10
14
  export type { Multer } from 'multer';
11
15
  export type { JwtPayload, SignOptions, VerifyOptions } from 'jsonwebtoken';
@@ -43,32 +47,8 @@ export interface ApiRequest {
43
47
  tokenData?: ApiTokenData | null;
44
48
  token?: string;
45
49
  }
46
- export type ApiHandler = (apiReq: ApiRequest) => Promise<[number] | [number, any] | [number, any, string]>;
47
- export type ApiAuthType = 'none' | 'maybe' | 'yes';
48
- export type ApiAuthClass = 'any' | 'admin';
49
- export interface ApiKey {
50
- uid: unknown;
51
- }
52
- export type ApiRoute = {
53
- method: 'get' | 'post' | 'put' | 'delete';
54
- path: string;
55
- handler: ApiHandler;
56
- auth: {
57
- type: ApiAuthType;
58
- req: ApiAuthClass;
59
- };
60
- };
61
- export declare class ApiModule<T> {
62
- server: T;
63
- namespace: string;
64
- mountpath: string;
65
- static defaultNamespace: string;
66
- constructor(opts?: {
67
- namespace?: string;
68
- });
69
- checkConfig(): boolean;
70
- defineRoutes(): ApiRoute[];
71
- }
50
+ export { ApiModule } from './api-module.js';
51
+ export type { ApiHandler, ApiAuthType, ApiAuthClass, ApiRoute, ApiKey } from './api-module.js';
72
52
  export interface ApiErrorParams {
73
53
  code?: number;
74
54
  message?: any;
@@ -104,41 +84,36 @@ export declare class ApiServer {
104
84
  app: Application;
105
85
  currReq: ApiRequest | null;
106
86
  readonly config: ApiServerConf;
87
+ private storageAdapter;
88
+ private moduleAdapter;
107
89
  constructor(config?: Partial<ApiServerConf>);
90
+ authStorage<UserRow, SafeUser>(storage: AuthStorage<UserRow, SafeUser>): this;
91
+ /**
92
+ * @deprecated Use {@link ApiServer.authStorage} instead.
93
+ */
94
+ useAuthStorage<UserRow, SafeUser>(storage: AuthStorage<UserRow, SafeUser>): this;
95
+ authModule<UserRow>(module: AuthProviderModule<UserRow>): this;
96
+ /**
97
+ * @deprecated Use {@link ApiServer.authModule} instead.
98
+ */
99
+ useAuthModule<UserRow>(module: AuthProviderModule<UserRow>): this;
100
+ getAuthStorage(): AuthStorage<any, any>;
101
+ getAuthModule(): AuthProviderModule<any>;
108
102
  jwtSign(payload: any, secret: string, expiresInSeconds: number, options?: SignOptions): JwtSignResult;
109
103
  jwtVerify<T>(token: string, secret: string, options?: VerifyOptions): JwtVerifyResult<T>;
110
104
  jwtDecode<T>(token: string, options?: jwt.DecodeOptions): JwtDecodeResult<T>;
111
105
  getApiKey<T = ApiKey>(token: string): Promise<T | null>;
112
- getUser(uid: unknown): Promise<unknown>;
113
- authenticateUser(params: unknown): Promise<boolean>;
114
- storeToken(params: {
115
- access: string;
116
- refresh: string;
117
- userId: unknown;
118
- domain?: string;
119
- fingerprint?: string;
120
- label?: string;
121
- }): Promise<void>;
122
- getToken(params: {
123
- accessToken?: string;
124
- refreshToken?: string;
125
- userId?: unknown;
126
- }): Promise<unknown>;
127
- updateToken(params: {
106
+ authenticateUser(params: {
107
+ login: string;
108
+ password: string;
109
+ }): Promise<boolean>;
110
+ updateToken(updates: {
128
111
  accessToken: string;
129
112
  refreshToken: string;
130
113
  expires?: Date;
114
+ clientId?: string;
115
+ scope?: string[];
131
116
  }): Promise<boolean>;
132
- deleteToken(params: {
133
- refreshToken?: string;
134
- accessToken?: string;
135
- userId?: unknown;
136
- domain?: string;
137
- fingerprint?: string;
138
- label?: string;
139
- }): Promise<number>;
140
- verifyPassword(password: string, hash: string): Promise<boolean>;
141
- filterUser<T = any, U = any>(fullUser: T): U;
142
117
  guessExceptionText(error: any, defMsg?: string): string;
143
118
  protected authorize(apiReq: ApiRequest, requiredClass: ApiAuthClass): Promise<void>;
144
119
  private middlewares;
@@ -9,19 +9,9 @@ import cors from 'cors';
9
9
  import express from 'express';
10
10
  import jwt from 'jsonwebtoken';
11
11
  import multer from 'multer';
12
- export class ApiModule {
13
- constructor(opts = {}) {
14
- this.mountpath = '';
15
- this.namespace = opts.namespace ?? this.constructor.defaultNamespace ?? '';
16
- }
17
- checkConfig() {
18
- return true;
19
- }
20
- defineRoutes() {
21
- return [];
22
- }
23
- }
24
- ApiModule.defaultNamespace = '';
12
+ import { nullAuthModule } from './auth-module.js';
13
+ import { nullAuthStorage } from './auth-storage.js';
14
+ export { ApiModule } from './api-module.js';
25
15
  function guess_exception_text(error, defMsg = 'Unknown Error') {
26
16
  const msg = [];
27
17
  if (typeof error === 'string' && error.trim() !== '') {
@@ -160,6 +150,8 @@ export class ApiServer {
160
150
  constructor(config = {}) {
161
151
  this.currReq = null;
162
152
  this.config = fillConfig(config);
153
+ this.storageAdapter = nullAuthStorage;
154
+ this.moduleAdapter = nullAuthModule;
163
155
  this.app = express();
164
156
  if (config.uploadPath) {
165
157
  const upload = multer({ dest: config.uploadPath });
@@ -168,6 +160,32 @@ export class ApiServer {
168
160
  this.middlewares();
169
161
  // addSwaggerUi(this.app);
170
162
  }
163
+ authStorage(storage) {
164
+ this.storageAdapter = storage;
165
+ return this;
166
+ }
167
+ /**
168
+ * @deprecated Use {@link ApiServer.authStorage} instead.
169
+ */
170
+ useAuthStorage(storage) {
171
+ return this.authStorage(storage);
172
+ }
173
+ authModule(module) {
174
+ this.moduleAdapter = module;
175
+ return this;
176
+ }
177
+ /**
178
+ * @deprecated Use {@link ApiServer.authModule} instead.
179
+ */
180
+ useAuthModule(module) {
181
+ return this.authModule(module);
182
+ }
183
+ getAuthStorage() {
184
+ return this.storageAdapter;
185
+ }
186
+ getAuthModule() {
187
+ return this.moduleAdapter;
188
+ }
171
189
  jwtSign(payload, secret, expiresInSeconds, options) {
172
190
  options || (options = {});
173
191
  const opts = { ...options, expiresIn: expiresInSeconds };
@@ -239,37 +257,28 @@ export class ApiServer {
239
257
  void token;
240
258
  return null;
241
259
  }
242
- async getUser(uid) {
243
- void uid;
244
- throw new Error('getUser() not implemented');
245
- }
246
260
  async authenticateUser(params) {
247
- void params;
248
- throw new Error('authenticateUser() not implemented');
249
- }
250
- async storeToken(params) {
251
- void params;
252
- throw new Error('storeToken() not implemented');
253
- }
254
- async getToken(params) {
255
- void params;
256
- throw new Error('getToken() not implemented');
257
- }
258
- async updateToken(params) {
259
- void params;
260
- throw new Error('updateToken() not implemented');
261
- }
262
- async deleteToken(params) {
263
- void params;
264
- throw new Error('deleteToken() not implemented');
265
- }
266
- async verifyPassword(password, hash) {
267
- void password;
268
- void hash;
269
- throw new Error('verifyPassword() not implemented');
261
+ if (!params?.login || !params?.password) {
262
+ return false;
263
+ }
264
+ const user = await this.storageAdapter.getUser(params.login);
265
+ if (!user) {
266
+ return false;
267
+ }
268
+ const hash = this.storageAdapter.getUserPasswordHash(user);
269
+ return this.storageAdapter.verifyPassword(params.password, hash);
270
270
  }
271
- filterUser(fullUser) {
272
- return fullUser;
271
+ async updateToken(updates) {
272
+ if (typeof this.storageAdapter.updateToken !== 'function') {
273
+ return false;
274
+ }
275
+ return this.storageAdapter.updateToken({
276
+ refreshToken: updates.refreshToken,
277
+ access: updates.accessToken,
278
+ expires: updates.expires,
279
+ clientId: updates.clientId,
280
+ scope: updates.scope
281
+ });
273
282
  }
274
283
  guessExceptionText(error, defMsg = 'Unkown Error') {
275
284
  return guess_exception_text(error, defMsg);
@@ -487,6 +496,9 @@ export class ApiServer {
487
496
  api(module) {
488
497
  const router = express.Router();
489
498
  module.server = this;
499
+ if (module?.moduleType === 'auth') {
500
+ this.authModule(module);
501
+ }
490
502
  module.checkConfig();
491
503
  const base = this.config.apiBasePath ?? '/api';
492
504
  const ns = module.namespace;
@@ -0,0 +1,20 @@
1
+ import { ApiModule } from './api-module.js';
2
+ import type { ApiRequest } from './api-server-base.js';
3
+ import type { AuthTokenMetadata, AuthTokenPair } from './auth-storage.js';
4
+ export interface AuthProviderModule<UserRow = unknown> {
5
+ readonly moduleType: 'auth';
6
+ readonly namespace: string;
7
+ issueTokens(apiReq: ApiRequest, user: UserRow, metadata?: AuthTokenMetadata & {
8
+ expires?: Date;
9
+ }): Promise<AuthTokenPair>;
10
+ }
11
+ export declare abstract class BaseAuthModule<UserRow = unknown> extends ApiModule<any> implements AuthProviderModule<UserRow> {
12
+ readonly moduleType: "auth";
13
+ protected constructor(opts: {
14
+ namespace: string;
15
+ });
16
+ abstract issueTokens(apiReq: ApiRequest, user: UserRow, metadata?: AuthTokenMetadata & {
17
+ expires?: Date;
18
+ }): Promise<AuthTokenPair>;
19
+ }
20
+ export declare const nullAuthModule: AuthProviderModule<unknown>;
@@ -0,0 +1,21 @@
1
+ import { ApiModule } from './api-module.js';
2
+ // Handy base that you can extend when wiring a real auth module. Subclasses
3
+ // must supply a namespace via the constructor and implement token issuance.
4
+ export class BaseAuthModule extends ApiModule {
5
+ constructor(opts) {
6
+ super(opts);
7
+ this.moduleType = 'auth';
8
+ }
9
+ }
10
+ class NullAuthModule extends BaseAuthModule {
11
+ constructor() {
12
+ super({ namespace: '__null__' });
13
+ }
14
+ async issueTokens(apiReq, user, metadata) {
15
+ void apiReq;
16
+ void user;
17
+ void metadata;
18
+ throw new Error('Auth module not configured. Inject a real auth module before issuing tokens.');
19
+ }
20
+ }
21
+ export const nullAuthModule = new NullAuthModule();
@@ -0,0 +1,114 @@
1
+ export type AuthIdentifier = string | number;
2
+ export interface AuthTokenMetadata {
3
+ clientId?: string;
4
+ domain?: string;
5
+ fingerprint?: string;
6
+ label?: string;
7
+ scope?: string | string[];
8
+ }
9
+ export interface AuthTokenData extends AuthTokenMetadata {
10
+ access: string;
11
+ expires?: Date;
12
+ refresh: string;
13
+ userId: AuthIdentifier;
14
+ }
15
+ export interface AuthTokenQuery extends AuthTokenMetadata {
16
+ accessToken?: string;
17
+ refreshToken?: string;
18
+ userId?: AuthIdentifier;
19
+ }
20
+ export interface AuthTokenPair {
21
+ accessToken: string;
22
+ refreshToken: string;
23
+ }
24
+ export interface AuthTokenPayload extends AuthTokenMetadata {
25
+ exp?: number;
26
+ iat?: number;
27
+ uid: AuthIdentifier;
28
+ }
29
+ export interface PasskeyChallengeParams extends AuthTokenMetadata {
30
+ action: 'register' | 'authenticate';
31
+ login?: string;
32
+ userAgent?: string;
33
+ userId?: AuthIdentifier;
34
+ }
35
+ export interface PasskeyChallenge extends Record<string, unknown> {
36
+ challenge: string;
37
+ expiresAt?: string | number | Date;
38
+ userId?: AuthIdentifier;
39
+ }
40
+ export interface PasskeyVerificationParams extends AuthTokenMetadata {
41
+ expectedChallenge: string;
42
+ login?: string;
43
+ response: Record<string, unknown>;
44
+ userId?: AuthIdentifier;
45
+ }
46
+ export interface PasskeyVerificationResult extends Record<string, unknown> {
47
+ login?: string;
48
+ tokens?: AuthTokenPair;
49
+ userId?: AuthIdentifier;
50
+ verified: boolean;
51
+ }
52
+ export interface OAuthClient {
53
+ clientId: string;
54
+ clientSecret: string;
55
+ firstParty?: boolean;
56
+ metadata?: Record<string, unknown>;
57
+ name?: string;
58
+ redirectUris: string[];
59
+ scope?: string[];
60
+ }
61
+ export interface AuthCodeData {
62
+ code: string;
63
+ clientId: string;
64
+ codeChallenge?: string;
65
+ codeChallengeMethod?: 'plain' | 'S256';
66
+ expiresAt: Date;
67
+ metadata?: Record<string, unknown>;
68
+ redirectUri: string;
69
+ scope: string[];
70
+ userId: AuthIdentifier;
71
+ }
72
+ export type AuthCodeRequest = Omit<AuthCodeData, 'code' | 'expiresAt'> & {
73
+ code?: string;
74
+ expiresInSeconds?: number;
75
+ };
76
+ export interface AuthStorage<UserRow, SafeUser> {
77
+ getUser(identifier: AuthIdentifier): Promise<UserRow | null>;
78
+ getUserPasswordHash(user: UserRow): string;
79
+ getUserId(user: UserRow): AuthIdentifier;
80
+ filterUser(user: UserRow): SafeUser;
81
+ verifyPassword(password: string, hash: string): Promise<boolean>;
82
+ storeToken(data: AuthTokenData): Promise<void>;
83
+ getToken(query: AuthTokenQuery): Promise<AuthTokenData | null>;
84
+ deleteToken(query: AuthTokenQuery): Promise<number>;
85
+ updateToken?(updates: Partial<AuthTokenData> & {
86
+ refreshToken: string;
87
+ }): Promise<boolean>;
88
+ createPasskeyChallenge?(params: PasskeyChallengeParams): Promise<PasskeyChallenge>;
89
+ verifyPasskeyResponse?(params: PasskeyVerificationParams): Promise<PasskeyVerificationResult>;
90
+ getClient?(clientId: string): Promise<OAuthClient | null>;
91
+ verifyClientSecret?(client: OAuthClient, clientSecret: string | null): Promise<boolean>;
92
+ createAuthCode?(request: AuthCodeRequest): Promise<AuthCodeData>;
93
+ consumeAuthCode?(code: string, clientId: string): Promise<AuthCodeData | null>;
94
+ }
95
+ export declare class BaseAuthStorage<UserRow = unknown, SafeUser = unknown> implements AuthStorage<UserRow, SafeUser> {
96
+ getUser(identifier: AuthIdentifier): Promise<UserRow | null>;
97
+ getUserPasswordHash(user: UserRow): string;
98
+ getUserId(user: UserRow): AuthIdentifier;
99
+ filterUser(user: UserRow): SafeUser;
100
+ verifyPassword(password: string, hash: string): Promise<boolean>;
101
+ storeToken(data: AuthTokenData): Promise<void>;
102
+ getToken(query: AuthTokenQuery): Promise<AuthTokenData | null>;
103
+ deleteToken(query: AuthTokenQuery): Promise<number>;
104
+ updateToken(updates: Partial<AuthTokenData> & {
105
+ refreshToken: string;
106
+ }): Promise<boolean>;
107
+ createPasskeyChallenge(params: PasskeyChallengeParams): Promise<PasskeyChallenge>;
108
+ verifyPasskeyResponse(params: PasskeyVerificationParams): Promise<PasskeyVerificationResult>;
109
+ getClient(clientId: string): Promise<OAuthClient | null>;
110
+ verifyClientSecret(client: OAuthClient, clientSecret: string | null): Promise<boolean>;
111
+ createAuthCode(request: AuthCodeRequest): Promise<AuthCodeData>;
112
+ consumeAuthCode(code: string, clientId: string): Promise<AuthCodeData | null>;
113
+ }
114
+ export declare const nullAuthStorage: AuthStorage<unknown, unknown>;
@@ -0,0 +1,83 @@
1
+ // Numeric database id or lookup string such as username/email.
2
+ // Handy base you can extend when wiring a real storage adapter. Every method
3
+ // throws by default so unimplemented hooks fail loudly.
4
+ export class BaseAuthStorage {
5
+ // Override to load a user record by identifier
6
+ async getUser(identifier) {
7
+ void identifier;
8
+ return null;
9
+ }
10
+ // Override to return the stored password hash for the user
11
+ getUserPasswordHash(user) {
12
+ void user;
13
+ throw new Error('Auth storage not configured');
14
+ }
15
+ // Override to expose the canonical user identifier
16
+ getUserId(user) {
17
+ void user;
18
+ throw new Error('Auth storage not configured');
19
+ }
20
+ // Override to strip sensitive fields from the user record
21
+ filterUser(user) {
22
+ return user;
23
+ }
24
+ // Override to validate a raw password against the stored hash
25
+ async verifyPassword(password, hash) {
26
+ void password;
27
+ void hash;
28
+ throw new Error('Auth storage not configured');
29
+ }
30
+ // Override to persist newly issued tokens
31
+ async storeToken(data) {
32
+ void data;
33
+ throw new Error('Auth storage not configured');
34
+ }
35
+ // Override to look up a stored token by query
36
+ async getToken(query) {
37
+ void query;
38
+ return null;
39
+ }
40
+ // Override to remove stored tokens that match the query
41
+ async deleteToken(query) {
42
+ void query;
43
+ return 0;
44
+ }
45
+ // Override to update metadata for an existing refresh token
46
+ async updateToken(updates) {
47
+ void updates;
48
+ return false;
49
+ }
50
+ // Override to create a new passkey challenge record
51
+ async createPasskeyChallenge(params) {
52
+ void params;
53
+ throw new Error('Auth storage not configured');
54
+ }
55
+ // Override to verify an incoming WebAuthn response
56
+ async verifyPasskeyResponse(params) {
57
+ void params;
58
+ throw new Error('Auth storage not configured');
59
+ }
60
+ // Override to fetch an OAuth client by identifier
61
+ async getClient(clientId) {
62
+ void clientId;
63
+ return null;
64
+ }
65
+ // Override to compare a provided client secret against storage
66
+ async verifyClientSecret(client, clientSecret) {
67
+ void client;
68
+ void clientSecret;
69
+ throw new Error('Auth storage not configured');
70
+ }
71
+ // Override to create a new authorization code entry
72
+ async createAuthCode(request) {
73
+ void request;
74
+ throw new Error('Auth storage not configured');
75
+ }
76
+ // Override to consume and invalidate an authorization code
77
+ async consumeAuthCode(code, clientId) {
78
+ void code;
79
+ void clientId;
80
+ return null;
81
+ }
82
+ }
83
+ export const nullAuthStorage = new BaseAuthStorage();
@@ -1,3 +1,8 @@
1
1
  export { default as ApiServer } from './api-server-base.js';
2
- export { ApiModule, ApiError } from './api-server-base.js';
2
+ export { ApiError } from './api-server-base.js';
3
+ export { ApiModule } from './api-module.js';
3
4
  export type { ApiErrorParams, ApiHandler, ApiKey, ApiServerConf, ApiRequest, ApiRoute, ApiAuthType, ApiAuthClass, ApiTokenData, RequestWithStuff } from './api-server-base.js';
5
+ export type { AuthIdentifier, AuthTokenMetadata, AuthTokenData, AuthTokenQuery, AuthTokenPair, AuthTokenPayload, PasskeyChallengeParams, PasskeyChallenge, PasskeyVerificationParams, PasskeyVerificationResult, OAuthClient, AuthCodeData, AuthCodeRequest, AuthStorage } from './auth-storage.js';
6
+ export type { AuthProviderModule } from './auth-module.js';
7
+ export { nullAuthStorage, BaseAuthStorage } from './auth-storage.js';
8
+ export { nullAuthModule, BaseAuthModule } from './auth-module.js';
package/dist/esm/index.js CHANGED
@@ -1,2 +1,5 @@
1
1
  export { default as ApiServer } from './api-server-base.js';
2
- export { ApiModule, ApiError } from './api-server-base.js';
2
+ export { ApiError } from './api-server-base.js';
3
+ export { ApiModule } from './api-module.js';
4
+ export { nullAuthStorage, BaseAuthStorage } from './auth-storage.js';
5
+ export { nullAuthModule, BaseAuthModule } from './auth-module.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@technomoron/api-server-base",
3
- "version": "1.0.43",
3
+ "version": "1.1.1",
4
4
  "description": "Api Server Skeleton / Base Class",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.cjs",