@technomoron/api-server-base 2.0.0-beta.12 → 2.0.0-beta.15
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/dist/cjs/api-module.d.ts +3 -2
- package/dist/cjs/api-server-base.cjs +125 -18
- package/dist/cjs/api-server-base.d.ts +21 -15
- package/dist/cjs/auth-api/auth-module.js +10 -10
- package/dist/cjs/auth-api/module.d.ts +1 -1
- package/dist/cjs/index.cjs +1 -11
- package/dist/cjs/index.d.ts +0 -5
- package/dist/cjs/passkey/service.js +11 -11
- package/dist/cjs/token/base.d.ts +2 -1
- package/dist/esm/api-module.d.ts +3 -2
- package/dist/esm/api-server-base.d.ts +21 -15
- package/dist/esm/api-server-base.js +125 -18
- package/dist/esm/auth-api/auth-module.js +10 -10
- package/dist/esm/auth-api/module.d.ts +1 -1
- package/dist/esm/index.d.ts +0 -5
- package/dist/esm/index.js +0 -5
- package/dist/esm/passkey/service.js +11 -11
- package/dist/esm/token/base.d.ts +2 -1
- package/docs/swagger/openapi.json +2010 -0
- package/package.json +65 -18
package/dist/cjs/api-module.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ApiRequest } from './api-server-base.js';
|
|
2
|
-
export type
|
|
2
|
+
export type ApiHandlerResult<Data = unknown> = [number] | [number, Data] | [number, Data, string];
|
|
3
|
+
export type ApiHandler<Data = unknown> = (apiReq: ApiRequest) => Promise<ApiHandlerResult<Data>>;
|
|
3
4
|
export type ApiAuthType = 'none' | 'maybe' | 'yes' | 'strict' | 'apikey';
|
|
4
5
|
export type ApiAuthClass = 'any' | 'admin';
|
|
5
6
|
export interface ApiKey {
|
|
@@ -14,7 +15,7 @@ export type ApiRoute = {
|
|
|
14
15
|
req: ApiAuthClass;
|
|
15
16
|
};
|
|
16
17
|
};
|
|
17
|
-
export declare class ApiModule<T> {
|
|
18
|
+
export declare class ApiModule<T = unknown> {
|
|
18
19
|
server: T;
|
|
19
20
|
namespace: string;
|
|
20
21
|
mountpath: string;
|
|
@@ -11,6 +11,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.ApiServer = exports.ApiError = exports.ApiModule = void 0;
|
|
13
13
|
const node_crypto_1 = require("node:crypto");
|
|
14
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
15
|
+
const node_module_1 = require("node:module");
|
|
16
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
14
17
|
const cookie_parser_1 = __importDefault(require("cookie-parser"));
|
|
15
18
|
const cors_1 = __importDefault(require("cors"));
|
|
16
19
|
const express_1 = __importDefault(require("express"));
|
|
@@ -46,11 +49,14 @@ function guess_exception_text(error, defMsg = 'Unknown Error') {
|
|
|
46
49
|
msg.push(error);
|
|
47
50
|
}
|
|
48
51
|
else if (error && typeof error === 'object') {
|
|
49
|
-
|
|
50
|
-
|
|
52
|
+
const errorDetails = error;
|
|
53
|
+
if (typeof errorDetails.message === 'string' && errorDetails.message.trim() !== '') {
|
|
54
|
+
msg.push(errorDetails.message);
|
|
51
55
|
}
|
|
52
|
-
if (
|
|
53
|
-
|
|
56
|
+
if (errorDetails.parent &&
|
|
57
|
+
typeof errorDetails.parent.message === 'string' &&
|
|
58
|
+
errorDetails.parent.message.trim() !== '') {
|
|
59
|
+
msg.push(errorDetails.parent.message);
|
|
54
60
|
}
|
|
55
61
|
}
|
|
56
62
|
return msg.length > 0 ? msg.join(' / ') : defMsg;
|
|
@@ -340,6 +346,8 @@ function fillConfig(config) {
|
|
|
340
346
|
origins: config.origins ?? [],
|
|
341
347
|
debug: config.debug ?? false,
|
|
342
348
|
apiBasePath: config.apiBasePath ?? '/api',
|
|
349
|
+
swaggerEnabled: config.swaggerEnabled ?? false,
|
|
350
|
+
swaggerPath: config.swaggerPath ?? '',
|
|
343
351
|
accessSecret: config.accessSecret ?? '',
|
|
344
352
|
refreshSecret: config.refreshSecret ?? '',
|
|
345
353
|
cookieDomain: config.cookieDomain ?? '.somewhere-over-the-rainbow.com',
|
|
@@ -362,6 +370,7 @@ function fillConfig(config) {
|
|
|
362
370
|
class ApiServer {
|
|
363
371
|
constructor(config = {}) {
|
|
364
372
|
this.currReq = null;
|
|
373
|
+
this.serverAuthAdapter = null;
|
|
365
374
|
this.apiNotFoundHandler = null;
|
|
366
375
|
this.tokenStoreAdapter = null;
|
|
367
376
|
this.userStoreAdapter = null;
|
|
@@ -382,7 +391,7 @@ class ApiServer {
|
|
|
382
391
|
this.passkeyServiceAdapter = passkeyService ?? null;
|
|
383
392
|
this.oauthStoreAdapter = oauthStore ?? null;
|
|
384
393
|
this.canImpersonateAdapter = canImpersonate ?? null;
|
|
385
|
-
this.storageAdapter = this;
|
|
394
|
+
this.storageAdapter = this.getServerAuthAdapter();
|
|
386
395
|
}
|
|
387
396
|
this.app = (0, express_1.default)();
|
|
388
397
|
if (config.uploadPath) {
|
|
@@ -391,6 +400,7 @@ class ApiServer {
|
|
|
391
400
|
}
|
|
392
401
|
this.middlewares();
|
|
393
402
|
this.installPingHandler();
|
|
403
|
+
this.installSwaggerHandler();
|
|
394
404
|
// addSwaggerUi(this.app);
|
|
395
405
|
this.installApiNotFoundHandler();
|
|
396
406
|
}
|
|
@@ -422,9 +432,9 @@ class ApiServer {
|
|
|
422
432
|
}
|
|
423
433
|
setTokenStore(store) {
|
|
424
434
|
this.tokenStoreAdapter = store;
|
|
425
|
-
// If using direct stores, expose
|
|
435
|
+
// If using direct stores, expose the server-backed auth adapter.
|
|
426
436
|
if (this.userStoreAdapter) {
|
|
427
|
-
this.storageAdapter = this;
|
|
437
|
+
this.storageAdapter = this.getServerAuthAdapter();
|
|
428
438
|
}
|
|
429
439
|
return this;
|
|
430
440
|
}
|
|
@@ -461,6 +471,33 @@ class ApiServer {
|
|
|
461
471
|
}
|
|
462
472
|
return this.oauthStoreAdapter;
|
|
463
473
|
}
|
|
474
|
+
getServerAuthAdapter() {
|
|
475
|
+
if (this.serverAuthAdapter) {
|
|
476
|
+
return this.serverAuthAdapter;
|
|
477
|
+
}
|
|
478
|
+
const server = this;
|
|
479
|
+
this.serverAuthAdapter = {
|
|
480
|
+
getUser: (identifier) => server.getUser(identifier),
|
|
481
|
+
getUserPasswordHash: (user) => server.getUserPasswordHash(user),
|
|
482
|
+
getUserId: (user) => server.getUserId(user),
|
|
483
|
+
filterUser: (user) => server.filterUser(user),
|
|
484
|
+
verifyPassword: (password, hash) => server.verifyPassword(password, hash),
|
|
485
|
+
storeToken: (data) => server.storeToken(data),
|
|
486
|
+
getToken: (query, opts) => server.getToken(query, opts),
|
|
487
|
+
deleteToken: (query) => server.deleteToken(query),
|
|
488
|
+
updateToken: (updates) => server.updateToken(updates),
|
|
489
|
+
createPasskeyChallenge: (params) => server.createPasskeyChallenge(params),
|
|
490
|
+
verifyPasskeyResponse: (params) => server.verifyPasskeyResponse(params),
|
|
491
|
+
listUserCredentials: (userId) => server.listUserCredentials(userId),
|
|
492
|
+
deletePasskeyCredential: (credentialId) => server.deletePasskeyCredential(credentialId),
|
|
493
|
+
getClient: (clientId) => server.getClient(clientId),
|
|
494
|
+
verifyClientSecret: (client, clientSecret) => server.verifyClientSecret(client, clientSecret),
|
|
495
|
+
createAuthCode: (request) => server.createAuthCode(request),
|
|
496
|
+
consumeAuthCode: (code, clientId) => server.consumeAuthCode(code, clientId),
|
|
497
|
+
canImpersonate: (params) => server.canImpersonate(params)
|
|
498
|
+
};
|
|
499
|
+
return this.serverAuthAdapter;
|
|
500
|
+
}
|
|
464
501
|
// AuthAdapter-compatible helpers (used by AuthModule)
|
|
465
502
|
async getUser(identifier) {
|
|
466
503
|
return this.userStoreAdapter ? this.userStoreAdapter.findUser(identifier) : null;
|
|
@@ -481,8 +518,9 @@ class ApiServer {
|
|
|
481
518
|
if (this.tokenStoreAdapter) {
|
|
482
519
|
return this.tokenStoreAdapter.save(data);
|
|
483
520
|
}
|
|
484
|
-
|
|
485
|
-
|
|
521
|
+
const storage = this.storageAdapter;
|
|
522
|
+
if (typeof storage.storeToken === 'function') {
|
|
523
|
+
return storage.storeToken(data);
|
|
486
524
|
}
|
|
487
525
|
throw new Error('Token store is not configured');
|
|
488
526
|
}
|
|
@@ -495,8 +533,9 @@ class ApiServer {
|
|
|
495
533
|
if (this.tokenStoreAdapter) {
|
|
496
534
|
return this.tokenStoreAdapter.get(normalized, opts);
|
|
497
535
|
}
|
|
498
|
-
|
|
499
|
-
|
|
536
|
+
const storage = this.storageAdapter;
|
|
537
|
+
if (typeof storage.getToken === 'function') {
|
|
538
|
+
return storage.getToken(normalized, opts);
|
|
500
539
|
}
|
|
501
540
|
return null;
|
|
502
541
|
}
|
|
@@ -509,8 +548,9 @@ class ApiServer {
|
|
|
509
548
|
if (this.tokenStoreAdapter) {
|
|
510
549
|
return this.tokenStoreAdapter.delete(normalized);
|
|
511
550
|
}
|
|
512
|
-
|
|
513
|
-
|
|
551
|
+
const storage = this.storageAdapter;
|
|
552
|
+
if (typeof storage.deleteToken === 'function') {
|
|
553
|
+
return storage.deleteToken(normalized);
|
|
514
554
|
}
|
|
515
555
|
return 0;
|
|
516
556
|
}
|
|
@@ -583,8 +623,9 @@ class ApiServer {
|
|
|
583
623
|
if (this.tokenStoreAdapter) {
|
|
584
624
|
return this.tokenStoreAdapter.update(updates);
|
|
585
625
|
}
|
|
586
|
-
|
|
587
|
-
|
|
626
|
+
const storage = this.storageAdapter;
|
|
627
|
+
if (typeof storage.updateToken === 'function') {
|
|
628
|
+
return storage.updateToken(updates);
|
|
588
629
|
}
|
|
589
630
|
return false;
|
|
590
631
|
}
|
|
@@ -630,6 +671,58 @@ class ApiServer {
|
|
|
630
671
|
res.status(200).json({ success: true, code: 200, message: 'Success', data: payload, errors: {} });
|
|
631
672
|
});
|
|
632
673
|
}
|
|
674
|
+
loadSwaggerSpec() {
|
|
675
|
+
const candidates = [node_path_1.default.resolve(process.cwd(), 'docs/swagger/openapi.json')];
|
|
676
|
+
if (typeof __dirname === 'string') {
|
|
677
|
+
candidates.push(node_path_1.default.resolve(__dirname, '../../docs/swagger/openapi.json'));
|
|
678
|
+
}
|
|
679
|
+
try {
|
|
680
|
+
const require = (0, node_module_1.createRequire)(node_path_1.default.join(process.cwd(), 'package.json'));
|
|
681
|
+
const entry = require.resolve('@technomoron/api-server-base');
|
|
682
|
+
const packageRoot = node_path_1.default.resolve(node_path_1.default.dirname(entry), '..', '..');
|
|
683
|
+
candidates.push(node_path_1.default.join(packageRoot, 'docs/swagger/openapi.json'));
|
|
684
|
+
}
|
|
685
|
+
catch {
|
|
686
|
+
// Ignore resolution failures; fall back to any existing candidate.
|
|
687
|
+
}
|
|
688
|
+
for (const candidate of candidates) {
|
|
689
|
+
if (!node_fs_1.default.existsSync(candidate)) {
|
|
690
|
+
continue;
|
|
691
|
+
}
|
|
692
|
+
try {
|
|
693
|
+
const raw = node_fs_1.default.readFileSync(candidate, 'utf8');
|
|
694
|
+
return JSON.parse(raw);
|
|
695
|
+
}
|
|
696
|
+
catch {
|
|
697
|
+
return null;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
return null;
|
|
701
|
+
}
|
|
702
|
+
installSwaggerHandler() {
|
|
703
|
+
const rawPath = typeof this.config.swaggerPath === 'string' ? this.config.swaggerPath.trim() : '';
|
|
704
|
+
const enabled = Boolean(this.config.swaggerEnabled) || rawPath.length > 0;
|
|
705
|
+
if (!enabled) {
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
const base = this.apiBasePath === '/' ? '' : this.apiBasePath;
|
|
709
|
+
const resolved = rawPath.length > 0 ? rawPath : `${base}/swagger`;
|
|
710
|
+
const path = resolved.startsWith('/') ? resolved : `/${resolved}`;
|
|
711
|
+
const spec = this.loadSwaggerSpec();
|
|
712
|
+
this.app.get(path, (_req, res) => {
|
|
713
|
+
if (!spec) {
|
|
714
|
+
res.status(500).json({
|
|
715
|
+
success: false,
|
|
716
|
+
code: 500,
|
|
717
|
+
message: 'Swagger spec is unavailable',
|
|
718
|
+
data: null,
|
|
719
|
+
errors: {}
|
|
720
|
+
});
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
res.status(200).json(spec);
|
|
724
|
+
});
|
|
725
|
+
}
|
|
633
726
|
normalizeApiBasePath(path) {
|
|
634
727
|
if (!path || typeof path !== 'string') {
|
|
635
728
|
return '/api';
|
|
@@ -665,7 +758,7 @@ class ApiServer {
|
|
|
665
758
|
if (!this.apiNotFoundHandler) {
|
|
666
759
|
return;
|
|
667
760
|
}
|
|
668
|
-
const stack = this.app
|
|
761
|
+
const stack = this.app._router?.stack;
|
|
669
762
|
if (!stack) {
|
|
670
763
|
return;
|
|
671
764
|
}
|
|
@@ -1158,7 +1251,8 @@ class ApiServer {
|
|
|
1158
1251
|
api(module) {
|
|
1159
1252
|
const router = express_1.default.Router();
|
|
1160
1253
|
module.server = this;
|
|
1161
|
-
|
|
1254
|
+
const moduleType = module.moduleType;
|
|
1255
|
+
if (moduleType === 'auth') {
|
|
1162
1256
|
this.authModule(module);
|
|
1163
1257
|
}
|
|
1164
1258
|
module.checkConfig();
|
|
@@ -1168,7 +1262,20 @@ class ApiServer {
|
|
|
1168
1262
|
module.mountpath = mountPath;
|
|
1169
1263
|
module.defineRoutes().forEach((r) => {
|
|
1170
1264
|
const handler = this.handle_request(r.handler, r.auth);
|
|
1171
|
-
|
|
1265
|
+
switch (r.method) {
|
|
1266
|
+
case 'get':
|
|
1267
|
+
router.get(r.path, handler);
|
|
1268
|
+
break;
|
|
1269
|
+
case 'post':
|
|
1270
|
+
router.post(r.path, handler);
|
|
1271
|
+
break;
|
|
1272
|
+
case 'put':
|
|
1273
|
+
router.put(r.path, handler);
|
|
1274
|
+
break;
|
|
1275
|
+
case 'delete':
|
|
1276
|
+
router.delete(r.path, handler);
|
|
1277
|
+
break;
|
|
1278
|
+
}
|
|
1172
1279
|
if (this.config.debug) {
|
|
1173
1280
|
console.log(`Adding ${mountPath}${r.path} (${r.method.toUpperCase()})`);
|
|
1174
1281
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { Application, Request, Response, type ErrorRequestHandler, type RequestHandler } from 'express';
|
|
8
8
|
import { ApiModule } from './api-module.js';
|
|
9
|
-
import { TokenStore, type JwtDecodeResult, type JwtSignResult, type JwtVerifyResult } from './token/base.js';
|
|
9
|
+
import { TokenStore, type JwtDecodeResult, type JwtSignPayload, type JwtSignResult, type JwtVerifyResult } from './token/base.js';
|
|
10
10
|
import type { ApiAuthClass, ApiAuthType, ApiKey } from './api-module.js';
|
|
11
11
|
import type { AuthProviderModule } from './auth-api/module.js';
|
|
12
12
|
import type { AuthAdapter, AuthIdentifier } from './auth-api/types.js';
|
|
@@ -32,7 +32,7 @@ export interface ApiTokenData extends JwtPayload, Partial<Token> {
|
|
|
32
32
|
exp?: number;
|
|
33
33
|
}
|
|
34
34
|
export interface ApiRequest {
|
|
35
|
-
server:
|
|
35
|
+
server: ApiServer;
|
|
36
36
|
req: ExtendedReq;
|
|
37
37
|
res: Response;
|
|
38
38
|
tokenData?: ApiTokenData | null;
|
|
@@ -64,7 +64,7 @@ export interface ClientInfo extends ClientAgentProfile {
|
|
|
64
64
|
ipchain: string[];
|
|
65
65
|
}
|
|
66
66
|
export interface ApiServerAuthStores {
|
|
67
|
-
userStore: UserStore<
|
|
67
|
+
userStore: UserStore<unknown, unknown>;
|
|
68
68
|
tokenStore: TokenStore;
|
|
69
69
|
passkeyService?: PasskeyService;
|
|
70
70
|
oauthStore?: OAuthStore;
|
|
@@ -77,13 +77,13 @@ export { ApiModule } from './api-module.js';
|
|
|
77
77
|
export type { ApiHandler, ApiAuthType, ApiAuthClass, ApiRoute, ApiKey } from './api-module.js';
|
|
78
78
|
export interface ApiErrorParams {
|
|
79
79
|
code?: number;
|
|
80
|
-
message?:
|
|
81
|
-
data?:
|
|
80
|
+
message?: unknown;
|
|
81
|
+
data?: unknown;
|
|
82
82
|
errors?: Record<string, string>;
|
|
83
83
|
}
|
|
84
84
|
export declare class ApiError extends Error {
|
|
85
85
|
code: number;
|
|
86
|
-
data:
|
|
86
|
+
data: unknown;
|
|
87
87
|
errors: Record<string, string>;
|
|
88
88
|
constructor({ code, message, data, errors }: ApiErrorParams);
|
|
89
89
|
}
|
|
@@ -95,6 +95,8 @@ export interface ApiServerConf {
|
|
|
95
95
|
origins: string[];
|
|
96
96
|
debug: boolean;
|
|
97
97
|
apiBasePath: string;
|
|
98
|
+
swaggerEnabled?: boolean;
|
|
99
|
+
swaggerPath?: string;
|
|
98
100
|
accessSecret: string;
|
|
99
101
|
refreshSecret: string;
|
|
100
102
|
cookieDomain: string;
|
|
@@ -121,6 +123,7 @@ export declare class ApiServer {
|
|
|
121
123
|
private readonly apiBasePath;
|
|
122
124
|
private storageAdapter;
|
|
123
125
|
private moduleAdapter;
|
|
126
|
+
private serverAuthAdapter;
|
|
124
127
|
private apiNotFoundHandler;
|
|
125
128
|
private tokenStoreAdapter;
|
|
126
129
|
private userStoreAdapter;
|
|
@@ -139,8 +142,8 @@ export declare class ApiServer {
|
|
|
139
142
|
* @deprecated Use {@link ApiServer.authModule} instead.
|
|
140
143
|
*/
|
|
141
144
|
useAuthModule<UserRow>(module: AuthProviderModule<UserRow>): this;
|
|
142
|
-
getAuthStorage(): AuthAdapter<
|
|
143
|
-
getAuthModule(): AuthProviderModule<
|
|
145
|
+
getAuthStorage<UserRow = unknown, SafeUser = unknown>(): AuthAdapter<UserRow, SafeUser>;
|
|
146
|
+
getAuthModule<UserRow = unknown>(): AuthProviderModule<UserRow>;
|
|
144
147
|
setTokenStore(store: TokenStore): this;
|
|
145
148
|
getTokenStore(): TokenStore | null;
|
|
146
149
|
private ensureUserStore;
|
|
@@ -149,10 +152,11 @@ export declare class ApiServer {
|
|
|
149
152
|
listUserCredentials(userId: AuthIdentifier): Promise<StoredPasskeyCredential[]>;
|
|
150
153
|
deletePasskeyCredential(credentialId: Buffer | string): Promise<boolean>;
|
|
151
154
|
private ensureOAuthStore;
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
155
|
+
private getServerAuthAdapter;
|
|
156
|
+
getUser(identifier: AuthIdentifier): Promise<unknown | null>;
|
|
157
|
+
getUserPasswordHash(user: unknown): string;
|
|
158
|
+
getUserId(user: unknown): AuthIdentifier;
|
|
159
|
+
filterUser(user: unknown): unknown;
|
|
156
160
|
verifyPassword(password: string, hash: string): Promise<boolean>;
|
|
157
161
|
storeToken(data: Token): Promise<void>;
|
|
158
162
|
getToken(query: Partial<Token> & {
|
|
@@ -175,7 +179,7 @@ export declare class ApiServer {
|
|
|
175
179
|
realUserId: AuthIdentifier;
|
|
176
180
|
effectiveUserId: AuthIdentifier;
|
|
177
181
|
}): Promise<boolean>;
|
|
178
|
-
jwtSign(payload:
|
|
182
|
+
jwtSign(payload: JwtSignPayload, secret: string, expiresInSeconds: number, options?: SignOptions): JwtSignResult;
|
|
179
183
|
jwtVerify<T>(token: string, secret: string, options?: VerifyOptions): JwtVerifyResult<T>;
|
|
180
184
|
jwtDecode<T>(token: string, options?: import('jsonwebtoken').DecodeOptions): JwtDecodeResult<T>;
|
|
181
185
|
getApiKey<T = ApiKey>(token: string): Promise<T | null>;
|
|
@@ -186,10 +190,12 @@ export declare class ApiServer {
|
|
|
186
190
|
updateToken(updates: Partial<Token> & {
|
|
187
191
|
refreshToken: string;
|
|
188
192
|
}): Promise<boolean>;
|
|
189
|
-
guessExceptionText(error:
|
|
193
|
+
guessExceptionText(error: unknown, defMsg?: string): string;
|
|
190
194
|
protected authorize(apiReq: ApiRequest, requiredClass: ApiAuthClass): Promise<void>;
|
|
191
195
|
private middlewares;
|
|
192
196
|
private installPingHandler;
|
|
197
|
+
private loadSwaggerSpec;
|
|
198
|
+
private installSwaggerHandler;
|
|
193
199
|
private normalizeApiBasePath;
|
|
194
200
|
private installApiNotFoundHandler;
|
|
195
201
|
private ensureApiNotFoundOrdering;
|
|
@@ -216,7 +222,7 @@ export declare class ApiServer {
|
|
|
216
222
|
}): RequestHandler;
|
|
217
223
|
expressErrorHandler(): ErrorRequestHandler;
|
|
218
224
|
private handle_request;
|
|
219
|
-
api<T extends ApiModule<
|
|
225
|
+
api<T extends ApiModule<unknown>>(module: T): this;
|
|
220
226
|
dumpRequest(apiReq: ApiRequest): void;
|
|
221
227
|
private formatDebugValue;
|
|
222
228
|
dumpResponse(apiReq: ApiRequest, payload: unknown, status: number): void;
|
|
@@ -1052,26 +1052,26 @@ class AuthModule extends module_js_1.BaseAuthModule {
|
|
|
1052
1052
|
throw new api_server_base_js_1.ApiError({ code: 401, message: 'Authorization requires user authentication' });
|
|
1053
1053
|
}
|
|
1054
1054
|
hasPasskeyService() {
|
|
1055
|
-
const
|
|
1056
|
-
if (
|
|
1055
|
+
const storageHints = this.storage;
|
|
1056
|
+
if (storageHints.passkeyService || storageHints.passkeyStore) {
|
|
1057
1057
|
return true;
|
|
1058
1058
|
}
|
|
1059
|
-
if (
|
|
1059
|
+
if (storageHints.adapter?.passkeyService || storageHints.adapter?.passkeyStore) {
|
|
1060
1060
|
return true;
|
|
1061
1061
|
}
|
|
1062
|
-
const
|
|
1063
|
-
return !!
|
|
1062
|
+
const serverHints = this.server;
|
|
1063
|
+
return !!serverHints.passkeyServiceAdapter;
|
|
1064
1064
|
}
|
|
1065
1065
|
hasOAuthStore() {
|
|
1066
|
-
const
|
|
1067
|
-
if (
|
|
1066
|
+
const storageHints = this.storage;
|
|
1067
|
+
if (storageHints.oauthStore) {
|
|
1068
1068
|
return true;
|
|
1069
1069
|
}
|
|
1070
|
-
if (
|
|
1070
|
+
if (storageHints.adapter?.oauthStore) {
|
|
1071
1071
|
return true;
|
|
1072
1072
|
}
|
|
1073
|
-
const
|
|
1074
|
-
return !!
|
|
1073
|
+
const serverHints = this.server;
|
|
1074
|
+
return !!serverHints.oauthStoreAdapter;
|
|
1075
1075
|
}
|
|
1076
1076
|
storageImplements(key) {
|
|
1077
1077
|
const candidate = this.storage[key];
|
|
@@ -8,7 +8,7 @@ export interface AuthProviderModule<UserRow = unknown> {
|
|
|
8
8
|
expires?: Date;
|
|
9
9
|
}): Promise<TokenPair>;
|
|
10
10
|
}
|
|
11
|
-
export declare abstract class BaseAuthModule<UserRow = unknown> extends ApiModule
|
|
11
|
+
export declare abstract class BaseAuthModule<UserRow = unknown> extends ApiModule implements AuthProviderModule<UserRow> {
|
|
12
12
|
readonly moduleType: "auth";
|
|
13
13
|
protected constructor(opts: {
|
|
14
14
|
namespace: string;
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -3,7 +3,7 @@ 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.
|
|
6
|
+
exports.MemoryOAuthStore = exports.OAuthStore = exports.MemoryPasskeyStore = exports.PasskeyStore = exports.PasskeyService = exports.MemoryTokenStore = exports.TokenStore = exports.MemoryUserStore = exports.UserStore = exports.AuthModule = exports.MemAuthStore = exports.CompositeAuthAdapter = exports.BaseAuthModule = exports.nullAuthModule = exports.BaseAuthAdapter = exports.nullAuthAdapter = 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");
|
|
@@ -20,33 +20,23 @@ var compat_auth_storage_js_1 = require("./auth-api/compat-auth-storage.js");
|
|
|
20
20
|
Object.defineProperty(exports, "CompositeAuthAdapter", { enumerable: true, get: function () { return compat_auth_storage_js_1.CompositeAuthAdapter; } });
|
|
21
21
|
var mem_auth_store_js_1 = require("./auth-api/mem-auth-store.js");
|
|
22
22
|
Object.defineProperty(exports, "MemAuthStore", { enumerable: true, get: function () { return mem_auth_store_js_1.MemAuthStore; } });
|
|
23
|
-
var sql_auth_store_js_1 = require("./auth-api/sql-auth-store.js");
|
|
24
|
-
Object.defineProperty(exports, "SqlAuthStore", { enumerable: true, get: function () { return sql_auth_store_js_1.SqlAuthStore; } });
|
|
25
23
|
var auth_module_js_1 = require("./auth-api/auth-module.js");
|
|
26
24
|
Object.defineProperty(exports, "AuthModule", { enumerable: true, get: function () { return __importDefault(auth_module_js_1).default; } });
|
|
27
25
|
var base_js_1 = require("./user/base.js");
|
|
28
26
|
Object.defineProperty(exports, "UserStore", { enumerable: true, get: function () { return base_js_1.UserStore; } });
|
|
29
27
|
var memory_js_1 = require("./user/memory.js");
|
|
30
28
|
Object.defineProperty(exports, "MemoryUserStore", { enumerable: true, get: function () { return memory_js_1.MemoryUserStore; } });
|
|
31
|
-
var sequelize_js_1 = require("./user/sequelize.js");
|
|
32
|
-
Object.defineProperty(exports, "SequelizeUserStore", { enumerable: true, get: function () { return sequelize_js_1.SequelizeUserStore; } });
|
|
33
29
|
var base_js_2 = require("./token/base.js");
|
|
34
30
|
Object.defineProperty(exports, "TokenStore", { enumerable: true, get: function () { return base_js_2.TokenStore; } });
|
|
35
31
|
var memory_js_2 = require("./token/memory.js");
|
|
36
32
|
Object.defineProperty(exports, "MemoryTokenStore", { enumerable: true, get: function () { return memory_js_2.MemoryTokenStore; } });
|
|
37
|
-
var sequelize_js_2 = require("./token/sequelize.js");
|
|
38
|
-
Object.defineProperty(exports, "SequelizeTokenStore", { enumerable: true, get: function () { return sequelize_js_2.SequelizeTokenStore; } });
|
|
39
33
|
var service_js_1 = require("./passkey/service.js");
|
|
40
34
|
Object.defineProperty(exports, "PasskeyService", { enumerable: true, get: function () { return service_js_1.PasskeyService; } });
|
|
41
35
|
var base_js_3 = require("./passkey/base.js");
|
|
42
36
|
Object.defineProperty(exports, "PasskeyStore", { enumerable: true, get: function () { return base_js_3.PasskeyStore; } });
|
|
43
37
|
var memory_js_3 = require("./passkey/memory.js");
|
|
44
38
|
Object.defineProperty(exports, "MemoryPasskeyStore", { enumerable: true, get: function () { return memory_js_3.MemoryPasskeyStore; } });
|
|
45
|
-
var sequelize_js_3 = require("./passkey/sequelize.js");
|
|
46
|
-
Object.defineProperty(exports, "SequelizePasskeyStore", { enumerable: true, get: function () { return sequelize_js_3.SequelizePasskeyStore; } });
|
|
47
39
|
var base_js_4 = require("./oauth/base.js");
|
|
48
40
|
Object.defineProperty(exports, "OAuthStore", { enumerable: true, get: function () { return base_js_4.OAuthStore; } });
|
|
49
41
|
var memory_js_4 = require("./oauth/memory.js");
|
|
50
42
|
Object.defineProperty(exports, "MemoryOAuthStore", { enumerable: true, get: function () { return memory_js_4.MemoryOAuthStore; } });
|
|
51
|
-
var sequelize_js_4 = require("./oauth/sequelize.js");
|
|
52
|
-
Object.defineProperty(exports, "SequelizeOAuthStore", { enumerable: true, get: function () { return sequelize_js_4.SequelizeOAuthStore; } });
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -11,22 +11,17 @@ export { nullAuthAdapter, BaseAuthAdapter } from './auth-api/storage.js';
|
|
|
11
11
|
export { nullAuthModule, BaseAuthModule } from './auth-api/module.js';
|
|
12
12
|
export { CompositeAuthAdapter } from './auth-api/compat-auth-storage.js';
|
|
13
13
|
export { MemAuthStore } from './auth-api/mem-auth-store.js';
|
|
14
|
-
export { SqlAuthStore } from './auth-api/sql-auth-store.js';
|
|
15
14
|
export { default as AuthModule } from './auth-api/auth-module.js';
|
|
16
15
|
export type { OAuthStartParams, OAuthStartResult, OAuthCallbackParams, OAuthCallbackResult } from './oauth/types.js';
|
|
17
16
|
export type { BcryptHasherOptions, CreateUserInput, UpdateUserInput, PublicUserMapper } from './user/types.js';
|
|
18
17
|
export { UserStore } from './user/base.js';
|
|
19
18
|
export { MemoryUserStore } from './user/memory.js';
|
|
20
|
-
export { SequelizeUserStore } from './user/sequelize.js';
|
|
21
19
|
export type { MemoryUserAttributes, MemoryUserStoreOptions } from './user/memory.js';
|
|
22
20
|
export { TokenStore } from './token/base.js';
|
|
23
21
|
export { MemoryTokenStore } from './token/memory.js';
|
|
24
|
-
export { SequelizeTokenStore } from './token/sequelize.js';
|
|
25
22
|
export { PasskeyService } from './passkey/service.js';
|
|
26
23
|
export { PasskeyStore } from './passkey/base.js';
|
|
27
24
|
export { MemoryPasskeyStore } from './passkey/memory.js';
|
|
28
|
-
export { SequelizePasskeyStore } from './passkey/sequelize.js';
|
|
29
25
|
export type { PasskeyServiceConfig, PasskeyChallengeRecord, PasskeyUserDescriptor, StoredPasskeyCredential, PasskeyChallenge, PasskeyChallengeParams, PasskeyVerificationParams, PasskeyVerificationResult } from './passkey/types.js';
|
|
30
26
|
export { OAuthStore } from './oauth/base.js';
|
|
31
27
|
export { MemoryOAuthStore } from './oauth/memory.js';
|
|
32
|
-
export { SequelizeOAuthStore } from './oauth/sequelize.js';
|
|
@@ -96,11 +96,13 @@ function toBufferOrNull(value) {
|
|
|
96
96
|
}
|
|
97
97
|
async function spkiToCosePublicKey(spki) {
|
|
98
98
|
try {
|
|
99
|
-
const subtle = globalThis.crypto?.subtle ??
|
|
99
|
+
const subtle = (globalThis.crypto?.subtle ??
|
|
100
|
+
(await Promise.resolve().then(() => __importStar(require('crypto')))).webcrypto?.subtle);
|
|
100
101
|
if (!subtle) {
|
|
101
102
|
return null;
|
|
102
103
|
}
|
|
103
|
-
const
|
|
104
|
+
const spkiView = new Uint8Array(spki);
|
|
105
|
+
const key = await subtle.importKey('spki', spkiView, { name: 'ECDSA', namedCurve: 'P-256' }, true, ['verify']);
|
|
104
106
|
const raw = Buffer.from(await subtle.exportKey('raw', key));
|
|
105
107
|
if (raw.length !== 65 || raw[0] !== 0x04) {
|
|
106
108
|
return null;
|
|
@@ -248,15 +250,16 @@ class PasskeyService {
|
|
|
248
250
|
return { verified: false };
|
|
249
251
|
}
|
|
250
252
|
const registrationInfo = result.registrationInfo;
|
|
251
|
-
const attestationResponse = params.response
|
|
253
|
+
const attestationResponse = params.response.response;
|
|
252
254
|
const credentialIdPrimary = toBufferOrNull(registrationInfo.credentialID);
|
|
253
|
-
const credentialIdFallback = toBufferOrNull(params.response
|
|
255
|
+
const credentialIdFallback = toBufferOrNull(params.response.id);
|
|
254
256
|
const credentialId = credentialIdPrimary && credentialIdPrimary.length > 0 ? credentialIdPrimary : credentialIdFallback;
|
|
255
257
|
const publicKeyPrimary = toBufferOrNull(registrationInfo.credentialPublicKey);
|
|
256
258
|
let publicKeyFallback = toBufferOrNull(attestationResponse?.publicKey);
|
|
257
259
|
if ((!publicKeyPrimary || publicKeyPrimary.length === 0) && attestationResponse?.attestationObject) {
|
|
258
260
|
try {
|
|
259
|
-
const
|
|
261
|
+
const attestationObject = String(attestationResponse.attestationObject);
|
|
262
|
+
const attObj = (0, helpers_1.decodeAttestationObject)(helpers_1.isoBase64URL.toBuffer(attestationObject));
|
|
260
263
|
const parsedAuth = (0, helpers_1.parseAuthenticatorData)(attObj.get('authData'));
|
|
261
264
|
publicKeyFallback = toBufferOrNull(parsedAuth.credentialPublicKey) ?? publicKeyFallback;
|
|
262
265
|
}
|
|
@@ -318,13 +321,10 @@ class PasskeyService {
|
|
|
318
321
|
}
|
|
319
322
|
const user = await this.requireUser({ userId: credential.userId, login: record.login });
|
|
320
323
|
const storedAuthData = {
|
|
321
|
-
id: credential.credentialId,
|
|
322
|
-
publicKey: toBuffer(credential.publicKey),
|
|
323
|
-
credentialPublicKey: toBuffer(credential.publicKey),
|
|
324
|
+
id: toBase64Url(credential.credentialId),
|
|
325
|
+
publicKey: new Uint8Array(toBuffer(credential.publicKey)),
|
|
324
326
|
counter: credential.counter,
|
|
325
|
-
transports: credential.transports ?? undefined
|
|
326
|
-
credentialBackedUp: credential.backedUp,
|
|
327
|
-
credentialDeviceType: credential.deviceType
|
|
327
|
+
transports: credential.transports ?? undefined
|
|
328
328
|
// simplewebauthn accepts either Uint8Array or Buffer; ensure Buffer
|
|
329
329
|
// see https://github.com/MasterKale/SimpleWebAuthn/blob/master/packages/server/src/authentication/verifyAuthenticationResponse.ts
|
|
330
330
|
};
|
package/dist/cjs/token/base.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export interface JwtSignResult {
|
|
|
5
5
|
token?: string;
|
|
6
6
|
error?: string;
|
|
7
7
|
}
|
|
8
|
+
export type JwtSignPayload = string | Buffer | Record<string, unknown>;
|
|
8
9
|
export interface JwtVerifyResult<T> {
|
|
9
10
|
success: boolean;
|
|
10
11
|
data?: T;
|
|
@@ -32,7 +33,7 @@ export declare abstract class TokenStore {
|
|
|
32
33
|
}): Promise<Token[]>;
|
|
33
34
|
abstract close(): Promise<void>;
|
|
34
35
|
normalizeToken(token: Partial<Token>): Token;
|
|
35
|
-
jwtSign(payload:
|
|
36
|
+
jwtSign(payload: JwtSignPayload, secret: string, expiresInSeconds: number, options?: SignOptions): JwtSignResult;
|
|
36
37
|
jwtVerify<T>(token: string, secret: string, options?: VerifyOptions): JwtVerifyResult<T>;
|
|
37
38
|
jwtDecode<T>(token: string, options?: DecodeOptions): JwtDecodeResult<T>;
|
|
38
39
|
}
|
package/dist/esm/api-module.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ApiRequest } from './api-server-base.js';
|
|
2
|
-
export type
|
|
2
|
+
export type ApiHandlerResult<Data = unknown> = [number] | [number, Data] | [number, Data, string];
|
|
3
|
+
export type ApiHandler<Data = unknown> = (apiReq: ApiRequest) => Promise<ApiHandlerResult<Data>>;
|
|
3
4
|
export type ApiAuthType = 'none' | 'maybe' | 'yes' | 'strict' | 'apikey';
|
|
4
5
|
export type ApiAuthClass = 'any' | 'admin';
|
|
5
6
|
export interface ApiKey {
|
|
@@ -14,7 +15,7 @@ export type ApiRoute = {
|
|
|
14
15
|
req: ApiAuthClass;
|
|
15
16
|
};
|
|
16
17
|
};
|
|
17
|
-
export declare class ApiModule<T> {
|
|
18
|
+
export declare class ApiModule<T = unknown> {
|
|
18
19
|
server: T;
|
|
19
20
|
namespace: string;
|
|
20
21
|
mountpath: string;
|