@technomoron/api-server-base 2.0.0-beta.14 → 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 +67 -18
- package/dist/cjs/api-server-base.d.ts +17 -15
- package/dist/cjs/auth-api/auth-module.js +10 -10
- package/dist/cjs/auth-api/module.d.ts +1 -1
- 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 +17 -15
- package/dist/esm/api-server-base.js +67 -18
- package/dist/esm/auth-api/auth-module.js +10 -10
- package/dist/esm/auth-api/module.d.ts +1 -1
- package/dist/esm/passkey/service.js +11 -11
- package/dist/esm/token/base.d.ts +2 -1
- package/package.json +11 -10
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;
|
|
@@ -49,11 +49,14 @@ function guess_exception_text(error, defMsg = 'Unknown Error') {
|
|
|
49
49
|
msg.push(error);
|
|
50
50
|
}
|
|
51
51
|
else if (error && typeof error === 'object') {
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
const errorDetails = error;
|
|
53
|
+
if (typeof errorDetails.message === 'string' && errorDetails.message.trim() !== '') {
|
|
54
|
+
msg.push(errorDetails.message);
|
|
54
55
|
}
|
|
55
|
-
if (
|
|
56
|
-
|
|
56
|
+
if (errorDetails.parent &&
|
|
57
|
+
typeof errorDetails.parent.message === 'string' &&
|
|
58
|
+
errorDetails.parent.message.trim() !== '') {
|
|
59
|
+
msg.push(errorDetails.parent.message);
|
|
57
60
|
}
|
|
58
61
|
}
|
|
59
62
|
return msg.length > 0 ? msg.join(' / ') : defMsg;
|
|
@@ -367,6 +370,7 @@ function fillConfig(config) {
|
|
|
367
370
|
class ApiServer {
|
|
368
371
|
constructor(config = {}) {
|
|
369
372
|
this.currReq = null;
|
|
373
|
+
this.serverAuthAdapter = null;
|
|
370
374
|
this.apiNotFoundHandler = null;
|
|
371
375
|
this.tokenStoreAdapter = null;
|
|
372
376
|
this.userStoreAdapter = null;
|
|
@@ -387,7 +391,7 @@ class ApiServer {
|
|
|
387
391
|
this.passkeyServiceAdapter = passkeyService ?? null;
|
|
388
392
|
this.oauthStoreAdapter = oauthStore ?? null;
|
|
389
393
|
this.canImpersonateAdapter = canImpersonate ?? null;
|
|
390
|
-
this.storageAdapter = this;
|
|
394
|
+
this.storageAdapter = this.getServerAuthAdapter();
|
|
391
395
|
}
|
|
392
396
|
this.app = (0, express_1.default)();
|
|
393
397
|
if (config.uploadPath) {
|
|
@@ -428,9 +432,9 @@ class ApiServer {
|
|
|
428
432
|
}
|
|
429
433
|
setTokenStore(store) {
|
|
430
434
|
this.tokenStoreAdapter = store;
|
|
431
|
-
// If using direct stores, expose
|
|
435
|
+
// If using direct stores, expose the server-backed auth adapter.
|
|
432
436
|
if (this.userStoreAdapter) {
|
|
433
|
-
this.storageAdapter = this;
|
|
437
|
+
this.storageAdapter = this.getServerAuthAdapter();
|
|
434
438
|
}
|
|
435
439
|
return this;
|
|
436
440
|
}
|
|
@@ -467,6 +471,33 @@ class ApiServer {
|
|
|
467
471
|
}
|
|
468
472
|
return this.oauthStoreAdapter;
|
|
469
473
|
}
|
|
474
|
+
getServerAuthAdapter() {
|
|
475
|
+
if (this.serverAuthAdapter) {
|
|
476
|
+
return this.serverAuthAdapter;
|
|
477
|
+
}
|
|
478
|
+
const server = this;
|
|
479
|
+
this.serverAuthAdapter = {
|
|
480
|
+
getUser: (identifier) => server.getUser(identifier),
|
|
481
|
+
getUserPasswordHash: (user) => server.getUserPasswordHash(user),
|
|
482
|
+
getUserId: (user) => server.getUserId(user),
|
|
483
|
+
filterUser: (user) => server.filterUser(user),
|
|
484
|
+
verifyPassword: (password, hash) => server.verifyPassword(password, hash),
|
|
485
|
+
storeToken: (data) => server.storeToken(data),
|
|
486
|
+
getToken: (query, opts) => server.getToken(query, opts),
|
|
487
|
+
deleteToken: (query) => server.deleteToken(query),
|
|
488
|
+
updateToken: (updates) => server.updateToken(updates),
|
|
489
|
+
createPasskeyChallenge: (params) => server.createPasskeyChallenge(params),
|
|
490
|
+
verifyPasskeyResponse: (params) => server.verifyPasskeyResponse(params),
|
|
491
|
+
listUserCredentials: (userId) => server.listUserCredentials(userId),
|
|
492
|
+
deletePasskeyCredential: (credentialId) => server.deletePasskeyCredential(credentialId),
|
|
493
|
+
getClient: (clientId) => server.getClient(clientId),
|
|
494
|
+
verifyClientSecret: (client, clientSecret) => server.verifyClientSecret(client, clientSecret),
|
|
495
|
+
createAuthCode: (request) => server.createAuthCode(request),
|
|
496
|
+
consumeAuthCode: (code, clientId) => server.consumeAuthCode(code, clientId),
|
|
497
|
+
canImpersonate: (params) => server.canImpersonate(params)
|
|
498
|
+
};
|
|
499
|
+
return this.serverAuthAdapter;
|
|
500
|
+
}
|
|
470
501
|
// AuthAdapter-compatible helpers (used by AuthModule)
|
|
471
502
|
async getUser(identifier) {
|
|
472
503
|
return this.userStoreAdapter ? this.userStoreAdapter.findUser(identifier) : null;
|
|
@@ -487,8 +518,9 @@ class ApiServer {
|
|
|
487
518
|
if (this.tokenStoreAdapter) {
|
|
488
519
|
return this.tokenStoreAdapter.save(data);
|
|
489
520
|
}
|
|
490
|
-
|
|
491
|
-
|
|
521
|
+
const storage = this.storageAdapter;
|
|
522
|
+
if (typeof storage.storeToken === 'function') {
|
|
523
|
+
return storage.storeToken(data);
|
|
492
524
|
}
|
|
493
525
|
throw new Error('Token store is not configured');
|
|
494
526
|
}
|
|
@@ -501,8 +533,9 @@ class ApiServer {
|
|
|
501
533
|
if (this.tokenStoreAdapter) {
|
|
502
534
|
return this.tokenStoreAdapter.get(normalized, opts);
|
|
503
535
|
}
|
|
504
|
-
|
|
505
|
-
|
|
536
|
+
const storage = this.storageAdapter;
|
|
537
|
+
if (typeof storage.getToken === 'function') {
|
|
538
|
+
return storage.getToken(normalized, opts);
|
|
506
539
|
}
|
|
507
540
|
return null;
|
|
508
541
|
}
|
|
@@ -515,8 +548,9 @@ class ApiServer {
|
|
|
515
548
|
if (this.tokenStoreAdapter) {
|
|
516
549
|
return this.tokenStoreAdapter.delete(normalized);
|
|
517
550
|
}
|
|
518
|
-
|
|
519
|
-
|
|
551
|
+
const storage = this.storageAdapter;
|
|
552
|
+
if (typeof storage.deleteToken === 'function') {
|
|
553
|
+
return storage.deleteToken(normalized);
|
|
520
554
|
}
|
|
521
555
|
return 0;
|
|
522
556
|
}
|
|
@@ -589,8 +623,9 @@ class ApiServer {
|
|
|
589
623
|
if (this.tokenStoreAdapter) {
|
|
590
624
|
return this.tokenStoreAdapter.update(updates);
|
|
591
625
|
}
|
|
592
|
-
|
|
593
|
-
|
|
626
|
+
const storage = this.storageAdapter;
|
|
627
|
+
if (typeof storage.updateToken === 'function') {
|
|
628
|
+
return storage.updateToken(updates);
|
|
594
629
|
}
|
|
595
630
|
return false;
|
|
596
631
|
}
|
|
@@ -723,7 +758,7 @@ class ApiServer {
|
|
|
723
758
|
if (!this.apiNotFoundHandler) {
|
|
724
759
|
return;
|
|
725
760
|
}
|
|
726
|
-
const stack = this.app
|
|
761
|
+
const stack = this.app._router?.stack;
|
|
727
762
|
if (!stack) {
|
|
728
763
|
return;
|
|
729
764
|
}
|
|
@@ -1216,7 +1251,8 @@ class ApiServer {
|
|
|
1216
1251
|
api(module) {
|
|
1217
1252
|
const router = express_1.default.Router();
|
|
1218
1253
|
module.server = this;
|
|
1219
|
-
|
|
1254
|
+
const moduleType = module.moduleType;
|
|
1255
|
+
if (moduleType === 'auth') {
|
|
1220
1256
|
this.authModule(module);
|
|
1221
1257
|
}
|
|
1222
1258
|
module.checkConfig();
|
|
@@ -1226,7 +1262,20 @@ class ApiServer {
|
|
|
1226
1262
|
module.mountpath = mountPath;
|
|
1227
1263
|
module.defineRoutes().forEach((r) => {
|
|
1228
1264
|
const handler = this.handle_request(r.handler, r.auth);
|
|
1229
|
-
|
|
1265
|
+
switch (r.method) {
|
|
1266
|
+
case 'get':
|
|
1267
|
+
router.get(r.path, handler);
|
|
1268
|
+
break;
|
|
1269
|
+
case 'post':
|
|
1270
|
+
router.post(r.path, handler);
|
|
1271
|
+
break;
|
|
1272
|
+
case 'put':
|
|
1273
|
+
router.put(r.path, handler);
|
|
1274
|
+
break;
|
|
1275
|
+
case 'delete':
|
|
1276
|
+
router.delete(r.path, handler);
|
|
1277
|
+
break;
|
|
1278
|
+
}
|
|
1230
1279
|
if (this.config.debug) {
|
|
1231
1280
|
console.log(`Adding ${mountPath}${r.path} (${r.method.toUpperCase()})`);
|
|
1232
1281
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { Application, Request, Response, type ErrorRequestHandler, type RequestHandler } from 'express';
|
|
8
8
|
import { ApiModule } from './api-module.js';
|
|
9
|
-
import { TokenStore, type JwtDecodeResult, type JwtSignResult, type JwtVerifyResult } from './token/base.js';
|
|
9
|
+
import { TokenStore, type JwtDecodeResult, type JwtSignPayload, type JwtSignResult, type JwtVerifyResult } from './token/base.js';
|
|
10
10
|
import type { ApiAuthClass, ApiAuthType, ApiKey } from './api-module.js';
|
|
11
11
|
import type { AuthProviderModule } from './auth-api/module.js';
|
|
12
12
|
import type { AuthAdapter, AuthIdentifier } from './auth-api/types.js';
|
|
@@ -32,7 +32,7 @@ export interface ApiTokenData extends JwtPayload, Partial<Token> {
|
|
|
32
32
|
exp?: number;
|
|
33
33
|
}
|
|
34
34
|
export interface ApiRequest {
|
|
35
|
-
server:
|
|
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
|
}
|
|
@@ -123,6 +123,7 @@ export declare class ApiServer {
|
|
|
123
123
|
private readonly apiBasePath;
|
|
124
124
|
private storageAdapter;
|
|
125
125
|
private moduleAdapter;
|
|
126
|
+
private serverAuthAdapter;
|
|
126
127
|
private apiNotFoundHandler;
|
|
127
128
|
private tokenStoreAdapter;
|
|
128
129
|
private userStoreAdapter;
|
|
@@ -141,8 +142,8 @@ export declare class ApiServer {
|
|
|
141
142
|
* @deprecated Use {@link ApiServer.authModule} instead.
|
|
142
143
|
*/
|
|
143
144
|
useAuthModule<UserRow>(module: AuthProviderModule<UserRow>): this;
|
|
144
|
-
getAuthStorage(): AuthAdapter<
|
|
145
|
-
getAuthModule(): AuthProviderModule<
|
|
145
|
+
getAuthStorage<UserRow = unknown, SafeUser = unknown>(): AuthAdapter<UserRow, SafeUser>;
|
|
146
|
+
getAuthModule<UserRow = unknown>(): AuthProviderModule<UserRow>;
|
|
146
147
|
setTokenStore(store: TokenStore): this;
|
|
147
148
|
getTokenStore(): TokenStore | null;
|
|
148
149
|
private ensureUserStore;
|
|
@@ -151,10 +152,11 @@ export declare class ApiServer {
|
|
|
151
152
|
listUserCredentials(userId: AuthIdentifier): Promise<StoredPasskeyCredential[]>;
|
|
152
153
|
deletePasskeyCredential(credentialId: Buffer | string): Promise<boolean>;
|
|
153
154
|
private ensureOAuthStore;
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
155
|
+
private getServerAuthAdapter;
|
|
156
|
+
getUser(identifier: AuthIdentifier): Promise<unknown | null>;
|
|
157
|
+
getUserPasswordHash(user: unknown): string;
|
|
158
|
+
getUserId(user: unknown): AuthIdentifier;
|
|
159
|
+
filterUser(user: unknown): unknown;
|
|
158
160
|
verifyPassword(password: string, hash: string): Promise<boolean>;
|
|
159
161
|
storeToken(data: Token): Promise<void>;
|
|
160
162
|
getToken(query: Partial<Token> & {
|
|
@@ -177,7 +179,7 @@ export declare class ApiServer {
|
|
|
177
179
|
realUserId: AuthIdentifier;
|
|
178
180
|
effectiveUserId: AuthIdentifier;
|
|
179
181
|
}): Promise<boolean>;
|
|
180
|
-
jwtSign(payload:
|
|
182
|
+
jwtSign(payload: JwtSignPayload, secret: string, expiresInSeconds: number, options?: SignOptions): JwtSignResult;
|
|
181
183
|
jwtVerify<T>(token: string, secret: string, options?: VerifyOptions): JwtVerifyResult<T>;
|
|
182
184
|
jwtDecode<T>(token: string, options?: import('jsonwebtoken').DecodeOptions): JwtDecodeResult<T>;
|
|
183
185
|
getApiKey<T = ApiKey>(token: string): Promise<T | null>;
|
|
@@ -188,7 +190,7 @@ export declare class ApiServer {
|
|
|
188
190
|
updateToken(updates: Partial<Token> & {
|
|
189
191
|
refreshToken: string;
|
|
190
192
|
}): Promise<boolean>;
|
|
191
|
-
guessExceptionText(error:
|
|
193
|
+
guessExceptionText(error: unknown, defMsg?: string): string;
|
|
192
194
|
protected authorize(apiReq: ApiRequest, requiredClass: ApiAuthClass): Promise<void>;
|
|
193
195
|
private middlewares;
|
|
194
196
|
private installPingHandler;
|
|
@@ -220,7 +222,7 @@ export declare class ApiServer {
|
|
|
220
222
|
}): RequestHandler;
|
|
221
223
|
expressErrorHandler(): ErrorRequestHandler;
|
|
222
224
|
private handle_request;
|
|
223
|
-
api<T extends ApiModule<
|
|
225
|
+
api<T extends ApiModule<unknown>>(module: T): this;
|
|
224
226
|
dumpRequest(apiReq: ApiRequest): void;
|
|
225
227
|
private formatDebugValue;
|
|
226
228
|
dumpResponse(apiReq: ApiRequest, payload: unknown, status: number): void;
|
|
@@ -1052,26 +1052,26 @@ class AuthModule extends module_js_1.BaseAuthModule {
|
|
|
1052
1052
|
throw new api_server_base_js_1.ApiError({ code: 401, message: 'Authorization requires user authentication' });
|
|
1053
1053
|
}
|
|
1054
1054
|
hasPasskeyService() {
|
|
1055
|
-
const
|
|
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;
|
|
@@ -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;
|
|
@@ -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
|
}
|
|
@@ -123,6 +123,7 @@ export declare class ApiServer {
|
|
|
123
123
|
private readonly apiBasePath;
|
|
124
124
|
private storageAdapter;
|
|
125
125
|
private moduleAdapter;
|
|
126
|
+
private serverAuthAdapter;
|
|
126
127
|
private apiNotFoundHandler;
|
|
127
128
|
private tokenStoreAdapter;
|
|
128
129
|
private userStoreAdapter;
|
|
@@ -141,8 +142,8 @@ export declare class ApiServer {
|
|
|
141
142
|
* @deprecated Use {@link ApiServer.authModule} instead.
|
|
142
143
|
*/
|
|
143
144
|
useAuthModule<UserRow>(module: AuthProviderModule<UserRow>): this;
|
|
144
|
-
getAuthStorage(): AuthAdapter<
|
|
145
|
-
getAuthModule(): AuthProviderModule<
|
|
145
|
+
getAuthStorage<UserRow = unknown, SafeUser = unknown>(): AuthAdapter<UserRow, SafeUser>;
|
|
146
|
+
getAuthModule<UserRow = unknown>(): AuthProviderModule<UserRow>;
|
|
146
147
|
setTokenStore(store: TokenStore): this;
|
|
147
148
|
getTokenStore(): TokenStore | null;
|
|
148
149
|
private ensureUserStore;
|
|
@@ -151,10 +152,11 @@ export declare class ApiServer {
|
|
|
151
152
|
listUserCredentials(userId: AuthIdentifier): Promise<StoredPasskeyCredential[]>;
|
|
152
153
|
deletePasskeyCredential(credentialId: Buffer | string): Promise<boolean>;
|
|
153
154
|
private ensureOAuthStore;
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
155
|
+
private getServerAuthAdapter;
|
|
156
|
+
getUser(identifier: AuthIdentifier): Promise<unknown | null>;
|
|
157
|
+
getUserPasswordHash(user: unknown): string;
|
|
158
|
+
getUserId(user: unknown): AuthIdentifier;
|
|
159
|
+
filterUser(user: unknown): unknown;
|
|
158
160
|
verifyPassword(password: string, hash: string): Promise<boolean>;
|
|
159
161
|
storeToken(data: Token): Promise<void>;
|
|
160
162
|
getToken(query: Partial<Token> & {
|
|
@@ -177,7 +179,7 @@ export declare class ApiServer {
|
|
|
177
179
|
realUserId: AuthIdentifier;
|
|
178
180
|
effectiveUserId: AuthIdentifier;
|
|
179
181
|
}): Promise<boolean>;
|
|
180
|
-
jwtSign(payload:
|
|
182
|
+
jwtSign(payload: JwtSignPayload, secret: string, expiresInSeconds: number, options?: SignOptions): JwtSignResult;
|
|
181
183
|
jwtVerify<T>(token: string, secret: string, options?: VerifyOptions): JwtVerifyResult<T>;
|
|
182
184
|
jwtDecode<T>(token: string, options?: import('jsonwebtoken').DecodeOptions): JwtDecodeResult<T>;
|
|
183
185
|
getApiKey<T = ApiKey>(token: string): Promise<T | null>;
|
|
@@ -188,7 +190,7 @@ export declare class ApiServer {
|
|
|
188
190
|
updateToken(updates: Partial<Token> & {
|
|
189
191
|
refreshToken: string;
|
|
190
192
|
}): Promise<boolean>;
|
|
191
|
-
guessExceptionText(error:
|
|
193
|
+
guessExceptionText(error: unknown, defMsg?: string): string;
|
|
192
194
|
protected authorize(apiReq: ApiRequest, requiredClass: ApiAuthClass): Promise<void>;
|
|
193
195
|
private middlewares;
|
|
194
196
|
private installPingHandler;
|
|
@@ -220,7 +222,7 @@ export declare class ApiServer {
|
|
|
220
222
|
}): RequestHandler;
|
|
221
223
|
expressErrorHandler(): ErrorRequestHandler;
|
|
222
224
|
private handle_request;
|
|
223
|
-
api<T extends ApiModule<
|
|
225
|
+
api<T extends ApiModule<unknown>>(module: T): this;
|
|
224
226
|
dumpRequest(apiReq: ApiRequest): void;
|
|
225
227
|
private formatDebugValue;
|
|
226
228
|
dumpResponse(apiReq: ApiRequest, payload: unknown, status: number): void;
|
|
@@ -42,11 +42,14 @@ function guess_exception_text(error, defMsg = 'Unknown Error') {
|
|
|
42
42
|
msg.push(error);
|
|
43
43
|
}
|
|
44
44
|
else if (error && typeof error === 'object') {
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
const errorDetails = error;
|
|
46
|
+
if (typeof errorDetails.message === 'string' && errorDetails.message.trim() !== '') {
|
|
47
|
+
msg.push(errorDetails.message);
|
|
47
48
|
}
|
|
48
|
-
if (
|
|
49
|
-
|
|
49
|
+
if (errorDetails.parent &&
|
|
50
|
+
typeof errorDetails.parent.message === 'string' &&
|
|
51
|
+
errorDetails.parent.message.trim() !== '') {
|
|
52
|
+
msg.push(errorDetails.parent.message);
|
|
50
53
|
}
|
|
51
54
|
}
|
|
52
55
|
return msg.length > 0 ? msg.join(' / ') : defMsg;
|
|
@@ -359,6 +362,7 @@ function fillConfig(config) {
|
|
|
359
362
|
export class ApiServer {
|
|
360
363
|
constructor(config = {}) {
|
|
361
364
|
this.currReq = null;
|
|
365
|
+
this.serverAuthAdapter = null;
|
|
362
366
|
this.apiNotFoundHandler = null;
|
|
363
367
|
this.tokenStoreAdapter = null;
|
|
364
368
|
this.userStoreAdapter = null;
|
|
@@ -379,7 +383,7 @@ export class ApiServer {
|
|
|
379
383
|
this.passkeyServiceAdapter = passkeyService ?? null;
|
|
380
384
|
this.oauthStoreAdapter = oauthStore ?? null;
|
|
381
385
|
this.canImpersonateAdapter = canImpersonate ?? null;
|
|
382
|
-
this.storageAdapter = this;
|
|
386
|
+
this.storageAdapter = this.getServerAuthAdapter();
|
|
383
387
|
}
|
|
384
388
|
this.app = express();
|
|
385
389
|
if (config.uploadPath) {
|
|
@@ -420,9 +424,9 @@ export class ApiServer {
|
|
|
420
424
|
}
|
|
421
425
|
setTokenStore(store) {
|
|
422
426
|
this.tokenStoreAdapter = store;
|
|
423
|
-
// If using direct stores, expose
|
|
427
|
+
// If using direct stores, expose the server-backed auth adapter.
|
|
424
428
|
if (this.userStoreAdapter) {
|
|
425
|
-
this.storageAdapter = this;
|
|
429
|
+
this.storageAdapter = this.getServerAuthAdapter();
|
|
426
430
|
}
|
|
427
431
|
return this;
|
|
428
432
|
}
|
|
@@ -459,6 +463,33 @@ export class ApiServer {
|
|
|
459
463
|
}
|
|
460
464
|
return this.oauthStoreAdapter;
|
|
461
465
|
}
|
|
466
|
+
getServerAuthAdapter() {
|
|
467
|
+
if (this.serverAuthAdapter) {
|
|
468
|
+
return this.serverAuthAdapter;
|
|
469
|
+
}
|
|
470
|
+
const server = this;
|
|
471
|
+
this.serverAuthAdapter = {
|
|
472
|
+
getUser: (identifier) => server.getUser(identifier),
|
|
473
|
+
getUserPasswordHash: (user) => server.getUserPasswordHash(user),
|
|
474
|
+
getUserId: (user) => server.getUserId(user),
|
|
475
|
+
filterUser: (user) => server.filterUser(user),
|
|
476
|
+
verifyPassword: (password, hash) => server.verifyPassword(password, hash),
|
|
477
|
+
storeToken: (data) => server.storeToken(data),
|
|
478
|
+
getToken: (query, opts) => server.getToken(query, opts),
|
|
479
|
+
deleteToken: (query) => server.deleteToken(query),
|
|
480
|
+
updateToken: (updates) => server.updateToken(updates),
|
|
481
|
+
createPasskeyChallenge: (params) => server.createPasskeyChallenge(params),
|
|
482
|
+
verifyPasskeyResponse: (params) => server.verifyPasskeyResponse(params),
|
|
483
|
+
listUserCredentials: (userId) => server.listUserCredentials(userId),
|
|
484
|
+
deletePasskeyCredential: (credentialId) => server.deletePasskeyCredential(credentialId),
|
|
485
|
+
getClient: (clientId) => server.getClient(clientId),
|
|
486
|
+
verifyClientSecret: (client, clientSecret) => server.verifyClientSecret(client, clientSecret),
|
|
487
|
+
createAuthCode: (request) => server.createAuthCode(request),
|
|
488
|
+
consumeAuthCode: (code, clientId) => server.consumeAuthCode(code, clientId),
|
|
489
|
+
canImpersonate: (params) => server.canImpersonate(params)
|
|
490
|
+
};
|
|
491
|
+
return this.serverAuthAdapter;
|
|
492
|
+
}
|
|
462
493
|
// AuthAdapter-compatible helpers (used by AuthModule)
|
|
463
494
|
async getUser(identifier) {
|
|
464
495
|
return this.userStoreAdapter ? this.userStoreAdapter.findUser(identifier) : null;
|
|
@@ -479,8 +510,9 @@ export class ApiServer {
|
|
|
479
510
|
if (this.tokenStoreAdapter) {
|
|
480
511
|
return this.tokenStoreAdapter.save(data);
|
|
481
512
|
}
|
|
482
|
-
|
|
483
|
-
|
|
513
|
+
const storage = this.storageAdapter;
|
|
514
|
+
if (typeof storage.storeToken === 'function') {
|
|
515
|
+
return storage.storeToken(data);
|
|
484
516
|
}
|
|
485
517
|
throw new Error('Token store is not configured');
|
|
486
518
|
}
|
|
@@ -493,8 +525,9 @@ export class ApiServer {
|
|
|
493
525
|
if (this.tokenStoreAdapter) {
|
|
494
526
|
return this.tokenStoreAdapter.get(normalized, opts);
|
|
495
527
|
}
|
|
496
|
-
|
|
497
|
-
|
|
528
|
+
const storage = this.storageAdapter;
|
|
529
|
+
if (typeof storage.getToken === 'function') {
|
|
530
|
+
return storage.getToken(normalized, opts);
|
|
498
531
|
}
|
|
499
532
|
return null;
|
|
500
533
|
}
|
|
@@ -507,8 +540,9 @@ export class ApiServer {
|
|
|
507
540
|
if (this.tokenStoreAdapter) {
|
|
508
541
|
return this.tokenStoreAdapter.delete(normalized);
|
|
509
542
|
}
|
|
510
|
-
|
|
511
|
-
|
|
543
|
+
const storage = this.storageAdapter;
|
|
544
|
+
if (typeof storage.deleteToken === 'function') {
|
|
545
|
+
return storage.deleteToken(normalized);
|
|
512
546
|
}
|
|
513
547
|
return 0;
|
|
514
548
|
}
|
|
@@ -581,8 +615,9 @@ export class ApiServer {
|
|
|
581
615
|
if (this.tokenStoreAdapter) {
|
|
582
616
|
return this.tokenStoreAdapter.update(updates);
|
|
583
617
|
}
|
|
584
|
-
|
|
585
|
-
|
|
618
|
+
const storage = this.storageAdapter;
|
|
619
|
+
if (typeof storage.updateToken === 'function') {
|
|
620
|
+
return storage.updateToken(updates);
|
|
586
621
|
}
|
|
587
622
|
return false;
|
|
588
623
|
}
|
|
@@ -715,7 +750,7 @@ export class ApiServer {
|
|
|
715
750
|
if (!this.apiNotFoundHandler) {
|
|
716
751
|
return;
|
|
717
752
|
}
|
|
718
|
-
const stack = this.app
|
|
753
|
+
const stack = this.app._router?.stack;
|
|
719
754
|
if (!stack) {
|
|
720
755
|
return;
|
|
721
756
|
}
|
|
@@ -1208,7 +1243,8 @@ export class ApiServer {
|
|
|
1208
1243
|
api(module) {
|
|
1209
1244
|
const router = express.Router();
|
|
1210
1245
|
module.server = this;
|
|
1211
|
-
|
|
1246
|
+
const moduleType = module.moduleType;
|
|
1247
|
+
if (moduleType === 'auth') {
|
|
1212
1248
|
this.authModule(module);
|
|
1213
1249
|
}
|
|
1214
1250
|
module.checkConfig();
|
|
@@ -1218,7 +1254,20 @@ export class ApiServer {
|
|
|
1218
1254
|
module.mountpath = mountPath;
|
|
1219
1255
|
module.defineRoutes().forEach((r) => {
|
|
1220
1256
|
const handler = this.handle_request(r.handler, r.auth);
|
|
1221
|
-
|
|
1257
|
+
switch (r.method) {
|
|
1258
|
+
case 'get':
|
|
1259
|
+
router.get(r.path, handler);
|
|
1260
|
+
break;
|
|
1261
|
+
case 'post':
|
|
1262
|
+
router.post(r.path, handler);
|
|
1263
|
+
break;
|
|
1264
|
+
case 'put':
|
|
1265
|
+
router.put(r.path, handler);
|
|
1266
|
+
break;
|
|
1267
|
+
case 'delete':
|
|
1268
|
+
router.delete(r.path, handler);
|
|
1269
|
+
break;
|
|
1270
|
+
}
|
|
1222
1271
|
if (this.config.debug) {
|
|
1223
1272
|
console.log(`Adding ${mountPath}${r.path} (${r.method.toUpperCase()})`);
|
|
1224
1273
|
}
|
|
@@ -1050,26 +1050,26 @@ class AuthModule extends BaseAuthModule {
|
|
|
1050
1050
|
throw new ApiError({ code: 401, message: 'Authorization requires user authentication' });
|
|
1051
1051
|
}
|
|
1052
1052
|
hasPasskeyService() {
|
|
1053
|
-
const
|
|
1054
|
-
if (
|
|
1053
|
+
const storageHints = this.storage;
|
|
1054
|
+
if (storageHints.passkeyService || storageHints.passkeyStore) {
|
|
1055
1055
|
return true;
|
|
1056
1056
|
}
|
|
1057
|
-
if (
|
|
1057
|
+
if (storageHints.adapter?.passkeyService || storageHints.adapter?.passkeyStore) {
|
|
1058
1058
|
return true;
|
|
1059
1059
|
}
|
|
1060
|
-
const
|
|
1061
|
-
return !!
|
|
1060
|
+
const serverHints = this.server;
|
|
1061
|
+
return !!serverHints.passkeyServiceAdapter;
|
|
1062
1062
|
}
|
|
1063
1063
|
hasOAuthStore() {
|
|
1064
|
-
const
|
|
1065
|
-
if (
|
|
1064
|
+
const storageHints = this.storage;
|
|
1065
|
+
if (storageHints.oauthStore) {
|
|
1066
1066
|
return true;
|
|
1067
1067
|
}
|
|
1068
|
-
if (
|
|
1068
|
+
if (storageHints.adapter?.oauthStore) {
|
|
1069
1069
|
return true;
|
|
1070
1070
|
}
|
|
1071
|
-
const
|
|
1072
|
-
return !!
|
|
1071
|
+
const serverHints = this.server;
|
|
1072
|
+
return !!serverHints.oauthStoreAdapter;
|
|
1073
1073
|
}
|
|
1074
1074
|
storageImplements(key) {
|
|
1075
1075
|
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;
|
|
@@ -60,11 +60,13 @@ function toBufferOrNull(value) {
|
|
|
60
60
|
}
|
|
61
61
|
async function spkiToCosePublicKey(spki) {
|
|
62
62
|
try {
|
|
63
|
-
const subtle = globalThis.crypto?.subtle ??
|
|
63
|
+
const subtle = (globalThis.crypto?.subtle ??
|
|
64
|
+
(await import('crypto')).webcrypto?.subtle);
|
|
64
65
|
if (!subtle) {
|
|
65
66
|
return null;
|
|
66
67
|
}
|
|
67
|
-
const
|
|
68
|
+
const spkiView = new Uint8Array(spki);
|
|
69
|
+
const key = await subtle.importKey('spki', spkiView, { name: 'ECDSA', namedCurve: 'P-256' }, true, ['verify']);
|
|
68
70
|
const raw = Buffer.from(await subtle.exportKey('raw', key));
|
|
69
71
|
if (raw.length !== 65 || raw[0] !== 0x04) {
|
|
70
72
|
return null;
|
|
@@ -212,15 +214,16 @@ export class PasskeyService {
|
|
|
212
214
|
return { verified: false };
|
|
213
215
|
}
|
|
214
216
|
const registrationInfo = result.registrationInfo;
|
|
215
|
-
const attestationResponse = params.response
|
|
217
|
+
const attestationResponse = params.response.response;
|
|
216
218
|
const credentialIdPrimary = toBufferOrNull(registrationInfo.credentialID);
|
|
217
|
-
const credentialIdFallback = toBufferOrNull(params.response
|
|
219
|
+
const credentialIdFallback = toBufferOrNull(params.response.id);
|
|
218
220
|
const credentialId = credentialIdPrimary && credentialIdPrimary.length > 0 ? credentialIdPrimary : credentialIdFallback;
|
|
219
221
|
const publicKeyPrimary = toBufferOrNull(registrationInfo.credentialPublicKey);
|
|
220
222
|
let publicKeyFallback = toBufferOrNull(attestationResponse?.publicKey);
|
|
221
223
|
if ((!publicKeyPrimary || publicKeyPrimary.length === 0) && attestationResponse?.attestationObject) {
|
|
222
224
|
try {
|
|
223
|
-
const
|
|
225
|
+
const attestationObject = String(attestationResponse.attestationObject);
|
|
226
|
+
const attObj = decodeAttestationObject(isoBase64URL.toBuffer(attestationObject));
|
|
224
227
|
const parsedAuth = parseAuthenticatorData(attObj.get('authData'));
|
|
225
228
|
publicKeyFallback = toBufferOrNull(parsedAuth.credentialPublicKey) ?? publicKeyFallback;
|
|
226
229
|
}
|
|
@@ -282,13 +285,10 @@ export class PasskeyService {
|
|
|
282
285
|
}
|
|
283
286
|
const user = await this.requireUser({ userId: credential.userId, login: record.login });
|
|
284
287
|
const storedAuthData = {
|
|
285
|
-
id: credential.credentialId,
|
|
286
|
-
publicKey: toBuffer(credential.publicKey),
|
|
287
|
-
credentialPublicKey: toBuffer(credential.publicKey),
|
|
288
|
+
id: toBase64Url(credential.credentialId),
|
|
289
|
+
publicKey: new Uint8Array(toBuffer(credential.publicKey)),
|
|
288
290
|
counter: credential.counter,
|
|
289
|
-
transports: credential.transports ?? undefined
|
|
290
|
-
credentialBackedUp: credential.backedUp,
|
|
291
|
-
credentialDeviceType: credential.deviceType
|
|
291
|
+
transports: credential.transports ?? undefined
|
|
292
292
|
// simplewebauthn accepts either Uint8Array or Buffer; ensure Buffer
|
|
293
293
|
// see https://github.com/MasterKale/SimpleWebAuthn/blob/master/packages/server/src/authentication/verifyAuthenticationResponse.ts
|
|
294
294
|
};
|
package/dist/esm/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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@technomoron/api-server-base",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.15",
|
|
4
4
|
"description": "Api Server Skeleton / Base Class",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/cjs/index.cjs",
|
|
@@ -59,11 +59,12 @@
|
|
|
59
59
|
"test:functional": "vitest run tests/functional",
|
|
60
60
|
"test:watch": "vitest --watch",
|
|
61
61
|
"prepublishOnly": "node scripts/run-builds.cjs",
|
|
62
|
-
"lint": "eslint --ext .js,.ts,.vue ./",
|
|
63
|
-
"lintfix": "eslint --fix --ext .js,.ts,.vue ./",
|
|
64
|
-
"format": "
|
|
65
|
-
"cleanbuild": "rm -rf ./dist/ &&
|
|
66
|
-
"pretty": "prettier --write \"**/*.{js,jsx,ts,tsx,vue,json,
|
|
62
|
+
"lint": "eslint --no-error-on-unmatched-pattern --ext .js,.cjs,.mjs,.ts,.mts,.tsx,.vue,.json ./",
|
|
63
|
+
"lintfix": "eslint --fix --no-error-on-unmatched-pattern --ext .js,.cjs,.mjs,.ts,.mts,.tsx,.vue,.json ./",
|
|
64
|
+
"format": "npm run lintfix && npm run pretty",
|
|
65
|
+
"cleanbuild": "rm -rf ./dist/ && npm run format && npm run build",
|
|
66
|
+
"pretty": "prettier --write \"**/*.{js,jsx,cjs,mjs,ts,tsx,mts,vue,json,md}\"",
|
|
67
|
+
"lintconfig": "node lintconfig.cjs"
|
|
67
68
|
},
|
|
68
69
|
"dependencies": {
|
|
69
70
|
"@simplewebauthn/server": "^13.2.2",
|
|
@@ -85,11 +86,10 @@
|
|
|
85
86
|
"@types/supertest": "^6.0.3",
|
|
86
87
|
"@typescript-eslint/eslint-plugin": "^8.50.1",
|
|
87
88
|
"@typescript-eslint/parser": "^8.50.1",
|
|
88
|
-
"@vue/eslint-config-prettier": "10.2.0",
|
|
89
|
-
"@vue/eslint-config-typescript": "^14.6.0",
|
|
90
89
|
"eslint": "^9.39.2",
|
|
90
|
+
"eslint-config-prettier": "^10.1.8",
|
|
91
91
|
"eslint-plugin-import": "^2.32.0",
|
|
92
|
-
"eslint-
|
|
92
|
+
"jsonc-eslint-parser": "^2.4.1",
|
|
93
93
|
"mysql2": "^3.16.0",
|
|
94
94
|
"pg": "^8.16.3",
|
|
95
95
|
"prettier": "^3.7.4",
|
|
@@ -129,5 +129,6 @@
|
|
|
129
129
|
"dist/",
|
|
130
130
|
"docs/swagger/openapi.json",
|
|
131
131
|
"package.json"
|
|
132
|
-
]
|
|
132
|
+
],
|
|
133
|
+
"packageManager": "pnpm@10.27.0+sha512.72d699da16b1179c14ba9e64dc71c9a40988cbdc65c264cb0e489db7de917f20dcf4d64d8723625f2969ba52d4b7e2a1170682d9ac2a5dcaeaab732b7e16f04a"
|
|
133
134
|
}
|