@technomoron/api-server-base 2.0.0-beta.1 → 2.0.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.txt +25 -2
  2. package/dist/cjs/api-server-base.cjs +269 -39
  3. package/dist/cjs/api-server-base.d.ts +27 -7
  4. package/dist/cjs/auth-api/auth-module.d.ts +11 -2
  5. package/dist/cjs/auth-api/auth-module.js +193 -45
  6. package/dist/cjs/auth-api/compat-auth-storage.d.ts +7 -5
  7. package/dist/cjs/auth-api/compat-auth-storage.js +15 -3
  8. package/dist/cjs/auth-api/mem-auth-store.d.ts +5 -3
  9. package/dist/cjs/auth-api/mem-auth-store.js +7 -1
  10. package/dist/cjs/auth-api/sql-auth-store.d.ts +5 -3
  11. package/dist/cjs/auth-api/sql-auth-store.js +7 -1
  12. package/dist/cjs/auth-api/storage.d.ts +6 -4
  13. package/dist/cjs/auth-api/storage.js +15 -5
  14. package/dist/cjs/auth-api/types.d.ts +7 -2
  15. package/dist/cjs/index.cjs +4 -4
  16. package/dist/cjs/index.d.ts +4 -4
  17. package/dist/cjs/oauth/sequelize.js +1 -1
  18. package/dist/cjs/passkey/base.d.ts +1 -0
  19. package/dist/cjs/passkey/memory.d.ts +1 -0
  20. package/dist/cjs/passkey/memory.js +4 -0
  21. package/dist/cjs/passkey/sequelize.d.ts +1 -0
  22. package/dist/cjs/passkey/sequelize.js +11 -2
  23. package/dist/cjs/passkey/service.d.ts +5 -2
  24. package/dist/cjs/passkey/service.js +145 -10
  25. package/dist/cjs/passkey/types.d.ts +3 -0
  26. package/dist/cjs/user/base.js +2 -1
  27. package/dist/esm/api-server-base.d.ts +27 -7
  28. package/dist/esm/api-server-base.js +270 -40
  29. package/dist/esm/auth-api/auth-module.d.ts +11 -2
  30. package/dist/esm/auth-api/auth-module.js +194 -46
  31. package/dist/esm/auth-api/compat-auth-storage.d.ts +7 -5
  32. package/dist/esm/auth-api/compat-auth-storage.js +13 -1
  33. package/dist/esm/auth-api/mem-auth-store.d.ts +5 -3
  34. package/dist/esm/auth-api/mem-auth-store.js +8 -2
  35. package/dist/esm/auth-api/sql-auth-store.d.ts +5 -3
  36. package/dist/esm/auth-api/sql-auth-store.js +8 -2
  37. package/dist/esm/auth-api/storage.d.ts +6 -4
  38. package/dist/esm/auth-api/storage.js +13 -3
  39. package/dist/esm/auth-api/types.d.ts +7 -2
  40. package/dist/esm/index.d.ts +4 -4
  41. package/dist/esm/index.js +2 -2
  42. package/dist/esm/oauth/sequelize.js +1 -1
  43. package/dist/esm/passkey/base.d.ts +1 -0
  44. package/dist/esm/passkey/memory.d.ts +1 -0
  45. package/dist/esm/passkey/memory.js +4 -0
  46. package/dist/esm/passkey/sequelize.d.ts +1 -0
  47. package/dist/esm/passkey/sequelize.js +11 -2
  48. package/dist/esm/passkey/service.d.ts +5 -2
  49. package/dist/esm/passkey/service.js +113 -11
  50. package/dist/esm/passkey/types.d.ts +3 -0
  51. package/dist/esm/user/base.js +2 -1
  52. package/package.json +3 -1
@@ -1,8 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const node_crypto_1 = require("node:crypto");
4
+ const helpers_1 = require("@simplewebauthn/server/helpers");
4
5
  const api_server_base_js_1 = require("../api-server-base.js");
5
6
  const module_js_1 = require("./module.js");
7
+ const storage_js_1 = require("./storage.js");
6
8
  function isAuthIdentifier(value) {
7
9
  return typeof value === 'string' || typeof value === 'number';
8
10
  }
@@ -74,9 +76,21 @@ class AuthModule extends module_js_1.BaseAuthModule {
74
76
  }
75
77
  buildTokenMetadata(metadata = {}) {
76
78
  const scope = metadata.scope;
79
+ const domain = metadata.domain ?? this.defaultDomain ?? '';
80
+ let fingerprint = metadata.fingerprint ?? metadata.clientId ?? '';
81
+ if (typeof fingerprint === 'string') {
82
+ fingerprint = fingerprint.trim();
83
+ }
84
+ else {
85
+ fingerprint = '';
86
+ }
87
+ // Avoid every client sharing the empty-string fingerprint which collapses sessions into one bucket.
88
+ if (!fingerprint) {
89
+ fingerprint = `srv-${(0, node_crypto_1.randomUUID)()}`;
90
+ }
77
91
  return {
78
- domain: metadata.domain ?? this.defaultDomain ?? '',
79
- fingerprint: metadata.fingerprint ?? metadata.clientId ?? '',
92
+ domain,
93
+ fingerprint,
80
94
  label: metadata.label ?? (Array.isArray(scope) ? scope.join(' ') : typeof scope === 'string' ? scope : ''),
81
95
  clientId: metadata.clientId,
82
96
  ruid: metadata.ruid,
@@ -183,6 +197,44 @@ class AuthModule extends module_js_1.BaseAuthModule {
183
197
  }
184
198
  return prefs;
185
199
  }
200
+ validateCredentialId(apiReq) {
201
+ const paramId = toStringOrNull(apiReq.req.params?.credentialId);
202
+ const bodyId = toStringOrNull(apiReq.req.body?.credentialId);
203
+ const credentialId = paramId ?? bodyId;
204
+ if (!credentialId) {
205
+ throw new api_server_base_js_1.ApiError({ code: 400, message: 'credentialId is required' });
206
+ }
207
+ try {
208
+ helpers_1.isoBase64URL.toBuffer(credentialId);
209
+ }
210
+ catch {
211
+ throw new api_server_base_js_1.ApiError({ code: 400, message: 'Invalid credentialId' });
212
+ }
213
+ return credentialId;
214
+ }
215
+ normalizeCredentialId(value) {
216
+ if (Buffer.isBuffer(value)) {
217
+ return value;
218
+ }
219
+ try {
220
+ return Buffer.from(helpers_1.isoBase64URL.toBuffer(value));
221
+ }
222
+ catch {
223
+ try {
224
+ return Buffer.from(value, 'base64');
225
+ }
226
+ catch {
227
+ return Buffer.from(value);
228
+ }
229
+ }
230
+ }
231
+ toIsoDate(value) {
232
+ if (!value) {
233
+ return undefined;
234
+ }
235
+ const date = value instanceof Date ? value : new Date(value);
236
+ return Number.isNaN(date.getTime()) ? undefined : date.toISOString();
237
+ }
186
238
  cookieOptions(apiReq) {
187
239
  const conf = this.server.config;
188
240
  const forwarded = apiReq.req.headers['x-forwarded-proto'];
@@ -598,6 +650,44 @@ class AuthModule extends module_js_1.BaseAuthModule {
598
650
  const publicUser = this.storage.filterUser(user);
599
651
  return [200, { ...tokens, user: publicUser }];
600
652
  }
653
+ async getPasskeys(apiReq) {
654
+ if (typeof this.storage.listUserCredentials !== 'function') {
655
+ throw new api_server_base_js_1.ApiError({ code: 501, message: 'Passkey credential listing is not configured' });
656
+ }
657
+ const { userId } = await this.resolveActorContext(apiReq);
658
+ const credentials = await this.storage.listUserCredentials(userId);
659
+ const safeCredentials = credentials.map((credential) => {
660
+ const bufferId = this.normalizeCredentialId(credential.credentialId);
661
+ return {
662
+ id: helpers_1.isoBase64URL.fromBuffer(new Uint8Array(bufferId)),
663
+ transports: credential.transports,
664
+ backedUp: credential.backedUp,
665
+ deviceType: credential.deviceType,
666
+ createdAt: this.toIsoDate(credential.createdAt),
667
+ updatedAt: this.toIsoDate(credential.updatedAt)
668
+ };
669
+ });
670
+ return [200, { credentials: safeCredentials }];
671
+ }
672
+ async deletePasskey(apiReq) {
673
+ if (typeof this.storage.listUserCredentials !== 'function' ||
674
+ typeof this.storage.deletePasskeyCredential !== 'function') {
675
+ throw new api_server_base_js_1.ApiError({ code: 501, message: 'Passkey credential management is not configured' });
676
+ }
677
+ const { userId } = await this.resolveActorContext(apiReq);
678
+ const credentialId = this.validateCredentialId(apiReq);
679
+ const bufferId = Buffer.from(helpers_1.isoBase64URL.toBuffer(credentialId));
680
+ const credentials = await this.storage.listUserCredentials(userId);
681
+ const owns = credentials.some((credential) => {
682
+ const candidateId = this.normalizeCredentialId(credential.credentialId);
683
+ return helpers_1.isoBase64URL.fromBuffer(new Uint8Array(candidateId)) === credentialId;
684
+ });
685
+ if (!owns) {
686
+ throw new api_server_base_js_1.ApiError({ code: 404, message: 'Passkey not found' });
687
+ }
688
+ const deleted = await this.storage.deletePasskeyCredential(bufferId);
689
+ return [200, { deleted }];
690
+ }
601
691
  async postImpersonation(apiReq) {
602
692
  this.assertAuthReady();
603
693
  const { targetIdentifier, metadata } = this.parseImpersonationRequest(apiReq);
@@ -941,47 +1031,91 @@ class AuthModule extends module_js_1.BaseAuthModule {
941
1031
  }
942
1032
  throw new api_server_base_js_1.ApiError({ code: 401, message: 'Authorization requires user authentication' });
943
1033
  }
1034
+ hasPasskeyService() {
1035
+ const storageAny = this.storage;
1036
+ if (storageAny.passkeyService || storageAny.passkeyStore) {
1037
+ return true;
1038
+ }
1039
+ if (storageAny.adapter?.passkeyService || storageAny.adapter?.passkeyStore) {
1040
+ return true;
1041
+ }
1042
+ const serverAny = this.server;
1043
+ return !!serverAny.passkeyServiceAdapter;
1044
+ }
1045
+ hasOAuthStore() {
1046
+ const storageAny = this.storage;
1047
+ if (storageAny.oauthStore) {
1048
+ return true;
1049
+ }
1050
+ if (storageAny.adapter?.oauthStore) {
1051
+ return true;
1052
+ }
1053
+ const serverAny = this.server;
1054
+ return !!serverAny.oauthStoreAdapter;
1055
+ }
1056
+ storageImplements(key) {
1057
+ const candidate = this.storage[key];
1058
+ if (typeof candidate !== 'function') {
1059
+ return false;
1060
+ }
1061
+ const baseImpl = storage_js_1.BaseAuthAdapter.prototype[key];
1062
+ return candidate !== baseImpl;
1063
+ }
1064
+ storageImplementsAll(keys) {
1065
+ return keys.every((key) => this.storageImplements(key));
1066
+ }
944
1067
  defineRoutes() {
945
- const routes = [
946
- {
947
- method: 'post',
948
- path: '/v1/login',
949
- handler: (req) => this.postLogin(req),
950
- auth: { type: 'none', req: 'any' }
951
- },
952
- {
953
- method: 'post',
954
- path: '/v1/refresh',
955
- handler: (req) => this.postRefresh(req),
956
- auth: { type: 'none', req: 'any' }
957
- },
958
- {
959
- method: 'post',
960
- path: '/v1/logout',
961
- handler: (req) => this.postLogout(req),
962
- auth: { type: 'maybe', req: 'any' }
963
- },
964
- {
965
- method: 'post',
966
- path: '/v1/whoami',
967
- handler: (req) => this.postWhoAmI(req),
968
- auth: { type: 'maybe', req: 'any' }
969
- },
970
- {
971
- method: 'post',
972
- path: '/v1/impersonations',
973
- handler: (req) => this.postImpersonation(req),
974
- auth: { type: 'strict', req: 'any' }
975
- },
976
- {
977
- method: 'delete',
978
- path: '/v1/impersonations',
979
- handler: (req) => this.deleteImpersonation(req),
980
- auth: { type: 'strict', req: 'any' }
981
- }
982
- ];
983
- const storage = this.storage;
984
- const passkeysSupported = typeof storage.createPasskeyChallenge === 'function' && typeof storage.verifyPasskeyResponse === 'function';
1068
+ const routes = [];
1069
+ const coreAuthSupported = this.storageImplementsAll([
1070
+ 'getUser',
1071
+ 'getUserPasswordHash',
1072
+ 'getUserId',
1073
+ 'verifyPassword',
1074
+ 'filterUser',
1075
+ 'storeToken',
1076
+ 'getToken',
1077
+ 'deleteToken'
1078
+ ]);
1079
+ if (!coreAuthSupported) {
1080
+ return routes;
1081
+ }
1082
+ routes.push({
1083
+ method: 'post',
1084
+ path: '/v1/login',
1085
+ handler: (req) => this.postLogin(req),
1086
+ auth: { type: 'none', req: 'any' }
1087
+ }, {
1088
+ method: 'post',
1089
+ path: '/v1/refresh',
1090
+ handler: (req) => this.postRefresh(req),
1091
+ auth: { type: 'none', req: 'any' }
1092
+ }, {
1093
+ method: 'post',
1094
+ path: '/v1/logout',
1095
+ handler: (req) => this.postLogout(req),
1096
+ auth: { type: 'maybe', req: 'any' }
1097
+ }, {
1098
+ method: 'post',
1099
+ path: '/v1/whoami',
1100
+ handler: (req) => this.postWhoAmI(req),
1101
+ auth: { type: 'maybe', req: 'any' }
1102
+ }, {
1103
+ method: 'post',
1104
+ path: '/v1/impersonations',
1105
+ handler: (req) => this.postImpersonation(req),
1106
+ auth: { type: 'strict', req: 'any' }
1107
+ }, {
1108
+ method: 'delete',
1109
+ path: '/v1/impersonations',
1110
+ handler: (req) => this.deleteImpersonation(req),
1111
+ auth: { type: 'strict', req: 'any' }
1112
+ });
1113
+ const passkeysSupported = this.hasPasskeyService() &&
1114
+ this.storageImplements('createPasskeyChallenge') &&
1115
+ this.storageImplements('verifyPasskeyResponse');
1116
+ const passkeyCredentialsSupported = passkeysSupported &&
1117
+ this.storageImplements('listUserCredentials') &&
1118
+ this.storageImplements('deletePasskeyCredential');
985
1119
  if (passkeysSupported) {
986
1120
  routes.push({
987
1121
  method: 'post',
@@ -994,6 +1128,19 @@ class AuthModule extends module_js_1.BaseAuthModule {
994
1128
  handler: (req) => this.postPasskeyVerify(req),
995
1129
  auth: { type: 'none', req: 'any' }
996
1130
  });
1131
+ if (passkeyCredentialsSupported) {
1132
+ routes.push({
1133
+ method: 'get',
1134
+ path: '/v1/passkeys',
1135
+ handler: (req) => this.getPasskeys(req),
1136
+ auth: { type: 'strict', req: 'any' }
1137
+ }, {
1138
+ method: 'delete',
1139
+ path: '/v1/passkeys/:credentialId?',
1140
+ handler: (req) => this.deletePasskey(req),
1141
+ auth: { type: 'strict', req: 'any' }
1142
+ });
1143
+ }
997
1144
  }
998
1145
  const externalOAuthSupported = typeof this.server.initiateOAuth === 'function' && typeof this.server.completeOAuth === 'function';
999
1146
  if (externalOAuthSupported) {
@@ -1009,9 +1156,10 @@ class AuthModule extends module_js_1.BaseAuthModule {
1009
1156
  auth: { type: 'none', req: 'any' }
1010
1157
  });
1011
1158
  }
1012
- const oauthStorageSupported = typeof storage.getClient === 'function' &&
1013
- typeof storage.createAuthCode === 'function' &&
1014
- typeof storage.consumeAuthCode === 'function';
1159
+ const oauthStorageSupported = this.hasOAuthStore() &&
1160
+ this.storageImplements('getClient') &&
1161
+ this.storageImplements('createAuthCode') &&
1162
+ this.storageImplements('consumeAuthCode');
1015
1163
  if (oauthStorageSupported) {
1016
1164
  routes.push({
1017
1165
  method: 'post',
@@ -1,9 +1,9 @@
1
1
  import { PasskeyService } from '../passkey/service.js';
2
- import type { AuthIdentifier, AuthStorage } from './types.js';
2
+ import type { AuthAdapter, AuthIdentifier } from './types.js';
3
3
  import type { OAuthStore } from '../oauth/base.js';
4
4
  import type { AuthCodeData, AuthCodeRequest, OAuthClient } from '../oauth/types.js';
5
5
  import type { PasskeyStore } from '../passkey/base.js';
6
- import type { PasskeyChallenge, PasskeyChallengeParams, PasskeyServiceConfig, PasskeyVerificationParams, PasskeyVerificationResult } from '../passkey/types.js';
6
+ import type { PasskeyChallenge, PasskeyChallengeParams, PasskeyServiceConfig, StoredPasskeyCredential, PasskeyVerificationParams, PasskeyVerificationResult } from '../passkey/types.js';
7
7
  import type { TokenStore } from '../token/base.js';
8
8
  import type { Token } from '../token/types.js';
9
9
  import type { UserStore } from '../user/base.js';
@@ -11,7 +11,7 @@ interface PasskeyAdapterOptions {
11
11
  store: PasskeyStore;
12
12
  config: PasskeyServiceConfig;
13
13
  }
14
- export interface AuthStorageAdapterOptions<UserRow, PublicUser> {
14
+ export interface AuthAdapterOptions<UserRow, PublicUser> {
15
15
  userStore: UserStore<UserRow, PublicUser>;
16
16
  tokenStore: TokenStore;
17
17
  passkeys?: PasskeyAdapterOptions | PasskeyService;
@@ -21,13 +21,13 @@ export interface AuthStorageAdapterOptions<UserRow, PublicUser> {
21
21
  effectiveUserId: AuthIdentifier;
22
22
  }) => boolean | Promise<boolean>;
23
23
  }
24
- export declare class AuthStorageAdapter<UserRow, PublicUser> implements AuthStorage<UserRow, PublicUser> {
24
+ export declare class CompositeAuthAdapter<UserRow, PublicUser> implements AuthAdapter<UserRow, PublicUser> {
25
25
  private readonly userStore;
26
26
  private readonly tokenStore;
27
27
  private readonly oauthStore?;
28
28
  private readonly passkeyService?;
29
29
  private readonly canImpersonateFn?;
30
- constructor(options: AuthStorageAdapterOptions<UserRow, PublicUser>);
30
+ constructor(options: AuthAdapterOptions<UserRow, PublicUser>);
31
31
  getUser(identifier: AuthIdentifier): Promise<UserRow | null>;
32
32
  getUserPasswordHash(user: UserRow): string;
33
33
  getUserId(user: UserRow): AuthIdentifier;
@@ -43,6 +43,8 @@ export declare class AuthStorageAdapter<UserRow, PublicUser> implements AuthStor
43
43
  }): Promise<boolean>;
44
44
  createPasskeyChallenge(params: PasskeyChallengeParams): Promise<PasskeyChallenge>;
45
45
  verifyPasskeyResponse(params: PasskeyVerificationParams): Promise<PasskeyVerificationResult>;
46
+ listUserCredentials(userId: AuthIdentifier): Promise<StoredPasskeyCredential[]>;
47
+ deletePasskeyCredential(credentialId: Buffer | string): Promise<boolean>;
46
48
  getClient(clientId: string): Promise<OAuthClient | null>;
47
49
  verifyClientSecret(client: OAuthClient, clientSecret: string | null): Promise<boolean>;
48
50
  createAuthCode(request: AuthCodeRequest): Promise<AuthCodeData>;
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.AuthStorageAdapter = void 0;
3
+ exports.CompositeAuthAdapter = void 0;
4
4
  const node_crypto_1 = require("node:crypto");
5
5
  const service_js_1 = require("../passkey/service.js");
6
- class AuthStorageAdapter {
6
+ class CompositeAuthAdapter {
7
7
  constructor(options) {
8
8
  this.userStore = options.userStore;
9
9
  this.tokenStore = options.tokenStore;
@@ -55,6 +55,18 @@ class AuthStorageAdapter {
55
55
  }
56
56
  return this.passkeyService.verifyResponse(params);
57
57
  }
58
+ async listUserCredentials(userId) {
59
+ if (!this.passkeyService) {
60
+ throw new Error('Passkey storage is not configured');
61
+ }
62
+ return this.passkeyService.listUserCredentials(userId);
63
+ }
64
+ async deletePasskeyCredential(credentialId) {
65
+ if (!this.passkeyService) {
66
+ throw new Error('Passkey storage is not configured');
67
+ }
68
+ return this.passkeyService.deleteCredential(credentialId);
69
+ }
58
70
  async getClient(clientId) {
59
71
  if (!this.oauthStore) {
60
72
  return null;
@@ -113,4 +125,4 @@ class AuthStorageAdapter {
113
125
  return params.realUserId === params.effectiveUserId;
114
126
  }
115
127
  }
116
- exports.AuthStorageAdapter = AuthStorageAdapter;
128
+ exports.CompositeAuthAdapter = CompositeAuthAdapter;
@@ -2,11 +2,11 @@ import { MemoryOAuthStore } from '../oauth/memory.js';
2
2
  import { MemoryPasskeyStore } from '../passkey/memory.js';
3
3
  import { TokenStore } from '../token/base.js';
4
4
  import { MemoryUserStore } from '../user/memory.js';
5
- import type { AuthIdentifier, AuthStorage } from './types.js';
5
+ import type { AuthAdapter, AuthIdentifier } from './types.js';
6
6
  import type { MemoryOAuthStoreOptions } from '../oauth/memory.js';
7
7
  import type { AuthCodeData, AuthCodeRequest, OAuthClient } from '../oauth/types.js';
8
8
  import type { MemoryPasskeyStoreOptions } from '../passkey/memory.js';
9
- import type { PasskeyChallenge, PasskeyChallengeParams, PasskeyServiceConfig, PasskeyUserDescriptor, PasskeyVerificationParams, PasskeyVerificationResult } from '../passkey/types.js';
9
+ import type { PasskeyChallenge, PasskeyChallengeParams, PasskeyServiceConfig, PasskeyUserDescriptor, StoredPasskeyCredential, PasskeyVerificationParams, PasskeyVerificationResult } from '../passkey/types.js';
10
10
  import type { Token } from '../token/types.js';
11
11
  import type { MemoryUserAttributes, MemoryPublicUser, MemoryUserStoreOptions } from '../user/memory.js';
12
12
  interface PasskeyOptions extends Partial<PasskeyServiceConfig> {
@@ -24,7 +24,7 @@ export interface MemAuthStoreParams<UserAttributes extends MemoryUserAttributes
24
24
  }) => boolean | Promise<boolean>;
25
25
  tokenStore?: TokenStore;
26
26
  }
27
- export declare class MemAuthStore<UserAttributes extends MemoryUserAttributes = MemoryUserAttributes, PublicUserShape extends MemoryPublicUser<UserAttributes> = MemoryPublicUser<UserAttributes>> implements AuthStorage<UserAttributes, PublicUserShape> {
27
+ export declare class MemAuthStore<UserAttributes extends MemoryUserAttributes = MemoryUserAttributes, PublicUserShape extends MemoryPublicUser<UserAttributes> = MemoryPublicUser<UserAttributes>> implements AuthAdapter<UserAttributes, PublicUserShape> {
28
28
  readonly userStore: MemoryUserStore<UserAttributes, PublicUserShape>;
29
29
  readonly tokenStore: TokenStore;
30
30
  readonly passkeyStore?: MemoryPasskeyStore;
@@ -54,6 +54,8 @@ export declare class MemAuthStore<UserAttributes extends MemoryUserAttributes =
54
54
  }): Promise<boolean>;
55
55
  createPasskeyChallenge(params: PasskeyChallengeParams): Promise<PasskeyChallenge>;
56
56
  verifyPasskeyResponse(params: PasskeyVerificationParams): Promise<PasskeyVerificationResult>;
57
+ listUserCredentials(userId: AuthIdentifier): Promise<StoredPasskeyCredential[]>;
58
+ deletePasskeyCredential(credentialId: Buffer | string): Promise<boolean>;
57
59
  getClient(clientId: string): Promise<OAuthClient | null>;
58
60
  verifyClientSecret(client: OAuthClient, clientSecret: string | null): Promise<boolean>;
59
61
  createAuthCode(request: AuthCodeRequest): Promise<AuthCodeData>;
@@ -59,7 +59,7 @@ class MemAuthStore {
59
59
  passkeyStore = new memory_js_2.MemoryPasskeyStore({ resolveUser });
60
60
  this.passkeyStore = passkeyStore;
61
61
  }
62
- this.adapter = new compat_auth_storage_js_1.AuthStorageAdapter({
62
+ this.adapter = new compat_auth_storage_js_1.CompositeAuthAdapter({
63
63
  userStore: this.userStore,
64
64
  tokenStore: this.tokenStore,
65
65
  passkeys: passkeyStore && passkeyConfig ? { store: passkeyStore, config: passkeyConfig } : undefined,
@@ -116,6 +116,12 @@ class MemAuthStore {
116
116
  async verifyPasskeyResponse(params) {
117
117
  return this.adapter.verifyPasskeyResponse(params);
118
118
  }
119
+ async listUserCredentials(userId) {
120
+ return this.adapter.listUserCredentials(userId);
121
+ }
122
+ async deletePasskeyCredential(credentialId) {
123
+ return this.adapter.deletePasskeyCredential(credentialId);
124
+ }
119
125
  async getClient(clientId) {
120
126
  return this.adapter.getClient(clientId);
121
127
  }
@@ -3,9 +3,9 @@ import { SequelizeOAuthStore, type SequelizeOAuthStoreOptions } from '../oauth/s
3
3
  import { SequelizePasskeyStore } from '../passkey/sequelize.js';
4
4
  import { type SequelizeTokenStoreOptions } from '../token/sequelize.js';
5
5
  import { SequelizeUserStore, type AuthUserAttributes, GenericUserModel, GenericUserModelStatic } from '../user/sequelize.js';
6
- import type { AuthIdentifier, AuthStorage } from './types.js';
6
+ import type { AuthAdapter, AuthIdentifier } from './types.js';
7
7
  import type { AuthCodeData, AuthCodeRequest, OAuthClient } from '../oauth/types.js';
8
- import type { PasskeyChallenge, PasskeyChallengeParams, PasskeyServiceConfig, PasskeyUserDescriptor, PasskeyVerificationParams, PasskeyVerificationResult } from '../passkey/types.js';
8
+ import type { PasskeyChallenge, PasskeyChallengeParams, PasskeyServiceConfig, PasskeyUserDescriptor, StoredPasskeyCredential, PasskeyVerificationParams, PasskeyVerificationResult } from '../passkey/types.js';
9
9
  import type { TokenStore } from '../token/base.js';
10
10
  import type { Token } from '../token/types.js';
11
11
  interface PasskeyOptions extends Partial<PasskeyServiceConfig> {
@@ -30,7 +30,7 @@ export interface SqlAuthStoreParams<UserAttributes extends AuthUserAttributes =
30
30
  tokenStoreOptions?: Omit<SequelizeTokenStoreOptions, 'sequelize'>;
31
31
  oauthStoreOptions?: Omit<SequelizeOAuthStoreOptions, 'sequelize'>;
32
32
  }
33
- export declare class SqlAuthStore<UserAttributes extends AuthUserAttributes = AuthUserAttributes, PublicUserShape extends Omit<UserAttributes, 'password'> = Omit<UserAttributes, 'password'>> implements AuthStorage<UserAttributes, PublicUserShape> {
33
+ export declare class SqlAuthStore<UserAttributes extends AuthUserAttributes = AuthUserAttributes, PublicUserShape extends Omit<UserAttributes, 'password'> = Omit<UserAttributes, 'password'>> implements AuthAdapter<UserAttributes, PublicUserShape> {
34
34
  readonly userStore: SequelizeUserStore<UserAttributes, PublicUserShape>;
35
35
  readonly tokenStore: TokenStore;
36
36
  readonly passkeyStore?: SequelizePasskeyStore;
@@ -63,6 +63,8 @@ export declare class SqlAuthStore<UserAttributes extends AuthUserAttributes = Au
63
63
  }): Promise<boolean>;
64
64
  createPasskeyChallenge(params: PasskeyChallengeParams): Promise<PasskeyChallenge>;
65
65
  verifyPasskeyResponse(params: PasskeyVerificationParams): Promise<PasskeyVerificationResult>;
66
+ listUserCredentials(userId: AuthIdentifier): Promise<StoredPasskeyCredential[]>;
67
+ deletePasskeyCredential(credentialId: Buffer | string): Promise<boolean>;
66
68
  getClient(clientId: string): Promise<OAuthClient | null>;
67
69
  verifyClientSecret(client: OAuthClient, clientSecret: string | null): Promise<boolean>;
68
70
  createAuthCode(request: AuthCodeRequest): Promise<AuthCodeData>;
@@ -72,7 +72,7 @@ class SqlAuthStore {
72
72
  passkeyStore = new sequelize_js_2.SequelizePasskeyStore({ sequelize: this.sequelize, resolveUser });
73
73
  this.passkeyStore = passkeyStore;
74
74
  }
75
- this.adapter = new compat_auth_storage_js_1.AuthStorageAdapter({
75
+ this.adapter = new compat_auth_storage_js_1.CompositeAuthAdapter({
76
76
  userStore: this.userStore,
77
77
  tokenStore: this.tokenStore,
78
78
  passkeys: passkeyStore && passkeyConfig ? { store: passkeyStore, config: passkeyConfig } : undefined,
@@ -147,6 +147,12 @@ class SqlAuthStore {
147
147
  async verifyPasskeyResponse(params) {
148
148
  return this.adapter.verifyPasskeyResponse(params);
149
149
  }
150
+ async listUserCredentials(userId) {
151
+ return this.adapter.listUserCredentials(userId);
152
+ }
153
+ async deletePasskeyCredential(credentialId) {
154
+ return this.adapter.deletePasskeyCredential(credentialId);
155
+ }
150
156
  async getClient(clientId) {
151
157
  return this.adapter.getClient(clientId);
152
158
  }
@@ -1,8 +1,8 @@
1
- import type { AuthIdentifier, AuthStorage } from './types.js';
1
+ import type { AuthAdapter, AuthIdentifier } from './types.js';
2
2
  import type { AuthCodeData, AuthCodeRequest, OAuthClient } from '../oauth/types.js';
3
- import type { PasskeyChallenge, PasskeyChallengeParams, PasskeyVerificationParams, PasskeyVerificationResult } from '../passkey/types.js';
3
+ import type { PasskeyChallenge, PasskeyChallengeParams, PasskeyVerificationParams, PasskeyVerificationResult, StoredPasskeyCredential } from '../passkey/types.js';
4
4
  import type { Token } from '../token/types.js';
5
- export declare class BaseAuthStorage<UserRow = unknown, SafeUser = unknown> implements AuthStorage<UserRow, SafeUser> {
5
+ export declare class BaseAuthAdapter<UserRow = unknown, SafeUser = unknown> implements AuthAdapter<UserRow, SafeUser> {
6
6
  getUser(identifier: AuthIdentifier): Promise<UserRow | null>;
7
7
  getUserPasswordHash(user: UserRow): string;
8
8
  getUserId(user: UserRow): AuthIdentifier;
@@ -24,6 +24,8 @@ export declare class BaseAuthStorage<UserRow = unknown, SafeUser = unknown> impl
24
24
  }): Promise<boolean>;
25
25
  createPasskeyChallenge(params: PasskeyChallengeParams): Promise<PasskeyChallenge>;
26
26
  verifyPasskeyResponse(params: PasskeyVerificationParams): Promise<PasskeyVerificationResult>;
27
+ listUserCredentials(userId: AuthIdentifier): Promise<StoredPasskeyCredential[]>;
28
+ deletePasskeyCredential(credentialId: Buffer | string): Promise<boolean>;
27
29
  getClient(clientId: string): Promise<OAuthClient | null>;
28
30
  verifyClientSecret(client: OAuthClient, clientSecret: string | null): Promise<boolean>;
29
31
  createAuthCode(request: AuthCodeRequest): Promise<AuthCodeData>;
@@ -33,4 +35,4 @@ export declare class BaseAuthStorage<UserRow = unknown, SafeUser = unknown> impl
33
35
  effectiveUserId: AuthIdentifier;
34
36
  }): Promise<boolean>;
35
37
  }
36
- export declare const nullAuthStorage: AuthStorage<unknown, unknown>;
38
+ export declare const nullAuthAdapter: AuthAdapter<unknown, unknown>;
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.nullAuthStorage = exports.BaseAuthStorage = void 0;
4
- // Handy base you can extend when wiring a real storage adapter. Every method
3
+ exports.nullAuthAdapter = exports.BaseAuthAdapter = void 0;
4
+ // Handy base you can extend when wiring a real auth adapter. Every method
5
5
  // throws by default so unimplemented hooks fail loudly.
6
- class BaseAuthStorage {
6
+ class BaseAuthAdapter {
7
7
  // Override to load a user record by identifier
8
8
  async getUser(identifier) {
9
9
  void identifier;
@@ -60,6 +60,16 @@ class BaseAuthStorage {
60
60
  void params;
61
61
  throw new Error('Auth storage not configured');
62
62
  }
63
+ // Override to list passkey credentials for a user
64
+ async listUserCredentials(userId) {
65
+ void userId;
66
+ throw new Error('Auth storage not configured');
67
+ }
68
+ // Override to delete a passkey credential
69
+ async deletePasskeyCredential(credentialId) {
70
+ void credentialId;
71
+ throw new Error('Auth storage not configured');
72
+ }
63
73
  // Override to fetch an OAuth client by identifier
64
74
  async getClient(clientId) {
65
75
  void clientId;
@@ -88,5 +98,5 @@ class BaseAuthStorage {
88
98
  return false;
89
99
  }
90
100
  }
91
- exports.BaseAuthStorage = BaseAuthStorage;
92
- exports.nullAuthStorage = new BaseAuthStorage();
101
+ exports.BaseAuthAdapter = BaseAuthAdapter;
102
+ exports.nullAuthAdapter = new BaseAuthAdapter();
@@ -1,8 +1,9 @@
1
1
  import type { AuthCodeData, AuthCodeRequest, OAuthClient } from '../oauth/types.js';
2
- import type { PasskeyChallenge, PasskeyChallengeParams, PasskeyVerificationParams, PasskeyVerificationResult } from '../passkey/types.js';
2
+ import type { PasskeyChallenge, PasskeyChallengeParams, PasskeyVerificationParams, PasskeyVerificationResult, StoredPasskeyCredential } from '../passkey/types.js';
3
3
  import type { Token } from '../token/types.js';
4
4
  export type AuthIdentifier = string | number;
5
- export interface AuthStorage<UserRow, SafeUser> {
5
+ /** @internal */
6
+ export interface AuthAdapter<UserRow, SafeUser> {
6
7
  getUser(identifier: AuthIdentifier): Promise<UserRow | null>;
7
8
  getUserPasswordHash(user: UserRow): string;
8
9
  getUserId(user: UserRow): AuthIdentifier;
@@ -18,6 +19,8 @@ export interface AuthStorage<UserRow, SafeUser> {
18
19
  }): Promise<boolean>;
19
20
  createPasskeyChallenge?(params: PasskeyChallengeParams): Promise<PasskeyChallenge>;
20
21
  verifyPasskeyResponse?(params: PasskeyVerificationParams): Promise<PasskeyVerificationResult>;
22
+ listUserCredentials?(userId: AuthIdentifier): Promise<StoredPasskeyCredential[]>;
23
+ deletePasskeyCredential?(credentialId: Buffer | string): Promise<boolean>;
21
24
  getClient?(clientId: string): Promise<OAuthClient | null>;
22
25
  verifyClientSecret?(client: OAuthClient, clientSecret: string | null): Promise<boolean>;
23
26
  createAuthCode?(request: AuthCodeRequest): Promise<AuthCodeData>;
@@ -27,3 +30,5 @@ export interface AuthStorage<UserRow, SafeUser> {
27
30
  effectiveUserId: AuthIdentifier;
28
31
  }): Promise<boolean>;
29
32
  }
33
+ /** @internal */
34
+ export type AuthStorage<UserRow, SafeUser> = AuthAdapter<UserRow, SafeUser>;
@@ -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.SequelizeOAuthStore = exports.MemoryOAuthStore = exports.OAuthStore = exports.SequelizePasskeyStore = exports.MemoryPasskeyStore = exports.PasskeyStore = exports.PasskeyService = exports.SequelizeTokenStore = exports.MemoryTokenStore = exports.TokenStore = exports.SequelizeUserStore = exports.MemoryUserStore = exports.UserStore = exports.AuthModule = exports.SqlAuthStore = exports.MemAuthStore = exports.AuthStorageAdapter = exports.BaseAuthModule = exports.nullAuthModule = exports.BaseAuthStorage = exports.nullAuthStorage = exports.ApiModule = exports.ApiError = exports.ApiServer = void 0;
6
+ exports.SequelizeOAuthStore = exports.MemoryOAuthStore = exports.OAuthStore = exports.SequelizePasskeyStore = exports.MemoryPasskeyStore = exports.PasskeyStore = exports.PasskeyService = exports.SequelizeTokenStore = exports.MemoryTokenStore = exports.TokenStore = exports.SequelizeUserStore = exports.MemoryUserStore = exports.UserStore = exports.AuthModule = exports.SqlAuthStore = 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");
@@ -11,13 +11,13 @@ Object.defineProperty(exports, "ApiError", { enumerable: true, get: function ()
11
11
  var api_module_js_1 = require("./api-module.cjs");
12
12
  Object.defineProperty(exports, "ApiModule", { enumerable: true, get: function () { return api_module_js_1.ApiModule; } });
13
13
  var storage_js_1 = require("./auth-api/storage.js");
14
- Object.defineProperty(exports, "nullAuthStorage", { enumerable: true, get: function () { return storage_js_1.nullAuthStorage; } });
15
- Object.defineProperty(exports, "BaseAuthStorage", { enumerable: true, get: function () { return storage_js_1.BaseAuthStorage; } });
14
+ Object.defineProperty(exports, "nullAuthAdapter", { enumerable: true, get: function () { return storage_js_1.nullAuthAdapter; } });
15
+ Object.defineProperty(exports, "BaseAuthAdapter", { enumerable: true, get: function () { return storage_js_1.BaseAuthAdapter; } });
16
16
  var module_js_1 = require("./auth-api/module.js");
17
17
  Object.defineProperty(exports, "nullAuthModule", { enumerable: true, get: function () { return module_js_1.nullAuthModule; } });
18
18
  Object.defineProperty(exports, "BaseAuthModule", { enumerable: true, get: function () { return module_js_1.BaseAuthModule; } });
19
19
  var compat_auth_storage_js_1 = require("./auth-api/compat-auth-storage.js");
20
- Object.defineProperty(exports, "AuthStorageAdapter", { enumerable: true, get: function () { return compat_auth_storage_js_1.AuthStorageAdapter; } });
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
23
  var sql_auth_store_js_1 = require("./auth-api/sql-auth-store.js");
@@ -1,15 +1,15 @@
1
1
  export { default as ApiServer } from './api-server-base.js';
2
2
  export { ApiError } from './api-server-base.js';
3
3
  export { ApiModule } from './api-module.js';
4
- export type { ApiErrorParams, ApiHandler, ApiKey, ApiServerConf, ApiRequest, ApiRoute, ApiAuthType, ApiAuthClass, ApiTokenData, ExtendedReq } from './api-server-base.js';
5
- export type { AuthIdentifier, AuthStorage } from './auth-api/types.js';
4
+ export type { ApiErrorParams, ApiHandler, ApiKey, ApiServerConf, ApiRequest, ApiRoute, ApiAuthType, ApiAuthClass, ApiTokenData, ExtendedReq, ExpressApiRequest, ExpressApiLocals } from './api-server-base.js';
5
+ export type { AuthIdentifier } from './auth-api/types.js';
6
6
  export type { Token, TokenPair, TokenStatus } from './token/types.js';
7
7
  export type { JwtSignResult, JwtVerifyResult, JwtDecodeResult } from './token/base.js';
8
8
  export type { OAuthClient, AuthCodeData, AuthCodeRequest } from './oauth/types.js';
9
9
  export type { AuthProviderModule } from './auth-api/module.js';
10
- export { nullAuthStorage, BaseAuthStorage } from './auth-api/storage.js';
10
+ export { nullAuthAdapter, BaseAuthAdapter } from './auth-api/storage.js';
11
11
  export { nullAuthModule, BaseAuthModule } from './auth-api/module.js';
12
- export { AuthStorageAdapter } from './auth-api/compat-auth-storage.js';
12
+ export { CompositeAuthAdapter } from './auth-api/compat-auth-storage.js';
13
13
  export { MemAuthStore } from './auth-api/mem-auth-store.js';
14
14
  export { SqlAuthStore } from './auth-api/sql-auth-store.js';
15
15
  export { default as AuthModule } from './auth-api/auth-module.js';
@@ -126,7 +126,7 @@ class SequelizeOAuthStore extends base_js_1.OAuthStore {
126
126
  const existing = await this.clients.findByPk(input.clientId);
127
127
  const hashedSecret = input.clientSecret !== undefined && input.clientSecret !== null
128
128
  ? await bcryptjs_1.default.hash(input.clientSecret, this.bcryptRounds)
129
- : existing?.client_secret ?? '';
129
+ : (existing?.client_secret ?? '');
130
130
  const redirectUris = input.redirectUris ?? (existing ? decodeStringArray(existing.redirect_uris) : undefined);
131
131
  const scope = input.scope ?? (existing ? decodeStringArray(existing.scope) : undefined);
132
132
  const metadata = input.metadata ?? (existing ? parseMetadata(existing.metadata) : undefined);
@@ -6,6 +6,7 @@ export declare abstract class PasskeyStore implements PasskeyStorageAdapter {
6
6
  login?: string;
7
7
  }): Promise<PasskeyUserDescriptor | null>;
8
8
  abstract listUserCredentials(userId: AuthIdentifier): Promise<StoredPasskeyCredential[]>;
9
+ abstract deleteCredential(credentialId: Buffer | string): Promise<boolean>;
9
10
  abstract findCredentialById(credentialId: Buffer): Promise<StoredPasskeyCredential | null>;
10
11
  abstract saveCredential(record: StoredPasskeyCredential): Promise<void>;
11
12
  abstract updateCredentialCounter(credentialId: Buffer, counter: number): Promise<void>;