@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.
@@ -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: any;
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<any, any>;
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?: any;
81
- data?: any;
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: any;
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<any, any>;
143
- getAuthModule(): AuthProviderModule<any>;
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
- getUser(identifier: AuthIdentifier): Promise<any | null>;
153
- getUserPasswordHash(user: any): string;
154
- getUserId(user: any): AuthIdentifier;
155
- filterUser(user: any): any;
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: any, secret: string, expiresInSeconds: number, options?: SignOptions): JwtSignResult;
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: any, defMsg?: string): string;
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<any>>(module: T): this;
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;
@@ -5,6 +5,9 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
7
  import { randomUUID } from 'node:crypto';
8
+ import fs from 'node:fs';
9
+ import { createRequire } from 'node:module';
10
+ import path from 'node:path';
8
11
  import cookieParser from 'cookie-parser';
9
12
  import cors from 'cors';
10
13
  import express from 'express';
@@ -39,11 +42,14 @@ function guess_exception_text(error, defMsg = 'Unknown Error') {
39
42
  msg.push(error);
40
43
  }
41
44
  else if (error && typeof error === 'object') {
42
- if (typeof error.message === 'string' && error.message.trim() !== '') {
43
- msg.push(error.message);
45
+ const errorDetails = error;
46
+ if (typeof errorDetails.message === 'string' && errorDetails.message.trim() !== '') {
47
+ msg.push(errorDetails.message);
44
48
  }
45
- if (error.parent && typeof error.parent.message === 'string' && error.parent.message.trim() !== '') {
46
- msg.push(error.parent.message);
49
+ if (errorDetails.parent &&
50
+ typeof errorDetails.parent.message === 'string' &&
51
+ errorDetails.parent.message.trim() !== '') {
52
+ msg.push(errorDetails.parent.message);
47
53
  }
48
54
  }
49
55
  return msg.length > 0 ? msg.join(' / ') : defMsg;
@@ -332,6 +338,8 @@ function fillConfig(config) {
332
338
  origins: config.origins ?? [],
333
339
  debug: config.debug ?? false,
334
340
  apiBasePath: config.apiBasePath ?? '/api',
341
+ swaggerEnabled: config.swaggerEnabled ?? false,
342
+ swaggerPath: config.swaggerPath ?? '',
335
343
  accessSecret: config.accessSecret ?? '',
336
344
  refreshSecret: config.refreshSecret ?? '',
337
345
  cookieDomain: config.cookieDomain ?? '.somewhere-over-the-rainbow.com',
@@ -354,6 +362,7 @@ function fillConfig(config) {
354
362
  export class ApiServer {
355
363
  constructor(config = {}) {
356
364
  this.currReq = null;
365
+ this.serverAuthAdapter = null;
357
366
  this.apiNotFoundHandler = null;
358
367
  this.tokenStoreAdapter = null;
359
368
  this.userStoreAdapter = null;
@@ -374,7 +383,7 @@ export class ApiServer {
374
383
  this.passkeyServiceAdapter = passkeyService ?? null;
375
384
  this.oauthStoreAdapter = oauthStore ?? null;
376
385
  this.canImpersonateAdapter = canImpersonate ?? null;
377
- this.storageAdapter = this;
386
+ this.storageAdapter = this.getServerAuthAdapter();
378
387
  }
379
388
  this.app = express();
380
389
  if (config.uploadPath) {
@@ -383,6 +392,7 @@ export class ApiServer {
383
392
  }
384
393
  this.middlewares();
385
394
  this.installPingHandler();
395
+ this.installSwaggerHandler();
386
396
  // addSwaggerUi(this.app);
387
397
  this.installApiNotFoundHandler();
388
398
  }
@@ -414,9 +424,9 @@ export class ApiServer {
414
424
  }
415
425
  setTokenStore(store) {
416
426
  this.tokenStoreAdapter = store;
417
- // If using direct stores, expose self as the auth storage.
427
+ // If using direct stores, expose the server-backed auth adapter.
418
428
  if (this.userStoreAdapter) {
419
- this.storageAdapter = this;
429
+ this.storageAdapter = this.getServerAuthAdapter();
420
430
  }
421
431
  return this;
422
432
  }
@@ -453,6 +463,33 @@ export class ApiServer {
453
463
  }
454
464
  return this.oauthStoreAdapter;
455
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
+ }
456
493
  // AuthAdapter-compatible helpers (used by AuthModule)
457
494
  async getUser(identifier) {
458
495
  return this.userStoreAdapter ? this.userStoreAdapter.findUser(identifier) : null;
@@ -473,8 +510,9 @@ export class ApiServer {
473
510
  if (this.tokenStoreAdapter) {
474
511
  return this.tokenStoreAdapter.save(data);
475
512
  }
476
- if (typeof this.storageAdapter.storeToken === 'function') {
477
- return this.storageAdapter.storeToken(data);
513
+ const storage = this.storageAdapter;
514
+ if (typeof storage.storeToken === 'function') {
515
+ return storage.storeToken(data);
478
516
  }
479
517
  throw new Error('Token store is not configured');
480
518
  }
@@ -487,8 +525,9 @@ export class ApiServer {
487
525
  if (this.tokenStoreAdapter) {
488
526
  return this.tokenStoreAdapter.get(normalized, opts);
489
527
  }
490
- if (typeof this.storageAdapter.getToken === 'function') {
491
- return this.storageAdapter.getToken(normalized, opts);
528
+ const storage = this.storageAdapter;
529
+ if (typeof storage.getToken === 'function') {
530
+ return storage.getToken(normalized, opts);
492
531
  }
493
532
  return null;
494
533
  }
@@ -501,8 +540,9 @@ export class ApiServer {
501
540
  if (this.tokenStoreAdapter) {
502
541
  return this.tokenStoreAdapter.delete(normalized);
503
542
  }
504
- if (typeof this.storageAdapter.deleteToken === 'function') {
505
- return this.storageAdapter.deleteToken(normalized);
543
+ const storage = this.storageAdapter;
544
+ if (typeof storage.deleteToken === 'function') {
545
+ return storage.deleteToken(normalized);
506
546
  }
507
547
  return 0;
508
548
  }
@@ -575,8 +615,9 @@ export class ApiServer {
575
615
  if (this.tokenStoreAdapter) {
576
616
  return this.tokenStoreAdapter.update(updates);
577
617
  }
578
- if (typeof this.storageAdapter.updateToken === 'function') {
579
- return this.storageAdapter.updateToken(updates);
618
+ const storage = this.storageAdapter;
619
+ if (typeof storage.updateToken === 'function') {
620
+ return storage.updateToken(updates);
580
621
  }
581
622
  return false;
582
623
  }
@@ -622,6 +663,58 @@ export class ApiServer {
622
663
  res.status(200).json({ success: true, code: 200, message: 'Success', data: payload, errors: {} });
623
664
  });
624
665
  }
666
+ loadSwaggerSpec() {
667
+ const candidates = [path.resolve(process.cwd(), 'docs/swagger/openapi.json')];
668
+ if (typeof __dirname === 'string') {
669
+ candidates.push(path.resolve(__dirname, '../../docs/swagger/openapi.json'));
670
+ }
671
+ try {
672
+ const require = createRequire(path.join(process.cwd(), 'package.json'));
673
+ const entry = require.resolve('@technomoron/api-server-base');
674
+ const packageRoot = path.resolve(path.dirname(entry), '..', '..');
675
+ candidates.push(path.join(packageRoot, 'docs/swagger/openapi.json'));
676
+ }
677
+ catch {
678
+ // Ignore resolution failures; fall back to any existing candidate.
679
+ }
680
+ for (const candidate of candidates) {
681
+ if (!fs.existsSync(candidate)) {
682
+ continue;
683
+ }
684
+ try {
685
+ const raw = fs.readFileSync(candidate, 'utf8');
686
+ return JSON.parse(raw);
687
+ }
688
+ catch {
689
+ return null;
690
+ }
691
+ }
692
+ return null;
693
+ }
694
+ installSwaggerHandler() {
695
+ const rawPath = typeof this.config.swaggerPath === 'string' ? this.config.swaggerPath.trim() : '';
696
+ const enabled = Boolean(this.config.swaggerEnabled) || rawPath.length > 0;
697
+ if (!enabled) {
698
+ return;
699
+ }
700
+ const base = this.apiBasePath === '/' ? '' : this.apiBasePath;
701
+ const resolved = rawPath.length > 0 ? rawPath : `${base}/swagger`;
702
+ const path = resolved.startsWith('/') ? resolved : `/${resolved}`;
703
+ const spec = this.loadSwaggerSpec();
704
+ this.app.get(path, (_req, res) => {
705
+ if (!spec) {
706
+ res.status(500).json({
707
+ success: false,
708
+ code: 500,
709
+ message: 'Swagger spec is unavailable',
710
+ data: null,
711
+ errors: {}
712
+ });
713
+ return;
714
+ }
715
+ res.status(200).json(spec);
716
+ });
717
+ }
625
718
  normalizeApiBasePath(path) {
626
719
  if (!path || typeof path !== 'string') {
627
720
  return '/api';
@@ -657,7 +750,7 @@ export class ApiServer {
657
750
  if (!this.apiNotFoundHandler) {
658
751
  return;
659
752
  }
660
- const stack = this.app?._router?.stack;
753
+ const stack = this.app._router?.stack;
661
754
  if (!stack) {
662
755
  return;
663
756
  }
@@ -1150,7 +1243,8 @@ export class ApiServer {
1150
1243
  api(module) {
1151
1244
  const router = express.Router();
1152
1245
  module.server = this;
1153
- if (module?.moduleType === 'auth') {
1246
+ const moduleType = module.moduleType;
1247
+ if (moduleType === 'auth') {
1154
1248
  this.authModule(module);
1155
1249
  }
1156
1250
  module.checkConfig();
@@ -1160,7 +1254,20 @@ export class ApiServer {
1160
1254
  module.mountpath = mountPath;
1161
1255
  module.defineRoutes().forEach((r) => {
1162
1256
  const handler = this.handle_request(r.handler, r.auth);
1163
- router[r.method](r.path, handler);
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
+ }
1164
1271
  if (this.config.debug) {
1165
1272
  console.log(`Adding ${mountPath}${r.path} (${r.method.toUpperCase()})`);
1166
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 storageAny = this.storage;
1054
- if (storageAny.passkeyService || storageAny.passkeyStore) {
1053
+ const storageHints = this.storage;
1054
+ if (storageHints.passkeyService || storageHints.passkeyStore) {
1055
1055
  return true;
1056
1056
  }
1057
- if (storageAny.adapter?.passkeyService || storageAny.adapter?.passkeyStore) {
1057
+ if (storageHints.adapter?.passkeyService || storageHints.adapter?.passkeyStore) {
1058
1058
  return true;
1059
1059
  }
1060
- const serverAny = this.server;
1061
- return !!serverAny.passkeyServiceAdapter;
1060
+ const serverHints = this.server;
1061
+ return !!serverHints.passkeyServiceAdapter;
1062
1062
  }
1063
1063
  hasOAuthStore() {
1064
- const storageAny = this.storage;
1065
- if (storageAny.oauthStore) {
1064
+ const storageHints = this.storage;
1065
+ if (storageHints.oauthStore) {
1066
1066
  return true;
1067
1067
  }
1068
- if (storageAny.adapter?.oauthStore) {
1068
+ if (storageHints.adapter?.oauthStore) {
1069
1069
  return true;
1070
1070
  }
1071
- const serverAny = this.server;
1072
- return !!serverAny.oauthStoreAdapter;
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<any> implements AuthProviderModule<UserRow> {
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;
@@ -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';
package/dist/esm/index.js CHANGED
@@ -5,18 +5,13 @@ export { nullAuthAdapter, BaseAuthAdapter } from './auth-api/storage.js';
5
5
  export { nullAuthModule, BaseAuthModule } from './auth-api/module.js';
6
6
  export { CompositeAuthAdapter } from './auth-api/compat-auth-storage.js';
7
7
  export { MemAuthStore } from './auth-api/mem-auth-store.js';
8
- export { SqlAuthStore } from './auth-api/sql-auth-store.js';
9
8
  export { default as AuthModule } from './auth-api/auth-module.js';
10
9
  export { UserStore } from './user/base.js';
11
10
  export { MemoryUserStore } from './user/memory.js';
12
- export { SequelizeUserStore } from './user/sequelize.js';
13
11
  export { TokenStore } from './token/base.js';
14
12
  export { MemoryTokenStore } from './token/memory.js';
15
- export { SequelizeTokenStore } from './token/sequelize.js';
16
13
  export { PasskeyService } from './passkey/service.js';
17
14
  export { PasskeyStore } from './passkey/base.js';
18
15
  export { MemoryPasskeyStore } from './passkey/memory.js';
19
- export { SequelizePasskeyStore } from './passkey/sequelize.js';
20
16
  export { OAuthStore } from './oauth/base.js';
21
17
  export { MemoryOAuthStore } from './oauth/memory.js';
22
- export { SequelizeOAuthStore } from './oauth/sequelize.js';
@@ -60,11 +60,13 @@ function toBufferOrNull(value) {
60
60
  }
61
61
  async function spkiToCosePublicKey(spki) {
62
62
  try {
63
- const subtle = globalThis.crypto?.subtle ?? (await import('crypto')).webcrypto?.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 key = await subtle.importKey('spki', spki, { name: 'ECDSA', namedCurve: 'P-256' }, true, ['verify']);
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?.response;
217
+ const attestationResponse = params.response.response;
216
218
  const credentialIdPrimary = toBufferOrNull(registrationInfo.credentialID);
217
- const credentialIdFallback = toBufferOrNull(params.response?.id);
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 attObj = decodeAttestationObject(isoBase64URL.toBuffer(attestationResponse.attestationObject));
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
  };
@@ -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: any, secret: string, expiresInSeconds: number, options?: SignOptions): JwtSignResult;
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
  }