@technomoron/apicore-server 1.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cjs/api-module.cjs +34 -0
  3. package/dist/cjs/api-module.d.ts +45 -0
  4. package/dist/cjs/apicore-server.cjs +1561 -0
  5. package/dist/cjs/apicore-server.d.ts +288 -0
  6. package/dist/cjs/auth-api/auth-module.cjs +1248 -0
  7. package/dist/cjs/auth-api/auth-module.d.ts +116 -0
  8. package/dist/cjs/auth-api/compat-auth-storage.cjs +128 -0
  9. package/dist/cjs/auth-api/compat-auth-storage.d.ts +57 -0
  10. package/dist/cjs/auth-api/mem-auth-store.cjs +121 -0
  11. package/dist/cjs/auth-api/mem-auth-store.d.ts +68 -0
  12. package/dist/cjs/auth-api/module.cjs +25 -0
  13. package/dist/cjs/auth-api/module.d.ts +20 -0
  14. package/dist/cjs/auth-api/schemas.cjs +171 -0
  15. package/dist/cjs/auth-api/schemas.d.ts +21 -0
  16. package/dist/cjs/auth-api/sql-auth-store.cjs +179 -0
  17. package/dist/cjs/auth-api/sql-auth-store.d.ts +87 -0
  18. package/dist/cjs/auth-api/storage.cjs +102 -0
  19. package/dist/cjs/auth-api/storage.d.ts +38 -0
  20. package/dist/cjs/auth-api/types.cjs +2 -0
  21. package/dist/cjs/auth-api/types.d.ts +34 -0
  22. package/dist/cjs/auth-api/user-id.cjs +47 -0
  23. package/dist/cjs/auth-api/user-id.d.ts +5 -0
  24. package/dist/cjs/auth-cookie-options.cjs +66 -0
  25. package/dist/cjs/auth-cookie-options.d.ts +13 -0
  26. package/dist/cjs/base/client-info.cjs +285 -0
  27. package/dist/cjs/base/client-info.d.ts +27 -0
  28. package/dist/cjs/base/error-utils.cjs +50 -0
  29. package/dist/cjs/base/error-utils.d.ts +16 -0
  30. package/dist/cjs/base/request-utils.cjs +27 -0
  31. package/dist/cjs/base/request-utils.d.ts +8 -0
  32. package/dist/cjs/index.cjs +51 -0
  33. package/dist/cjs/index.d.ts +34 -0
  34. package/dist/cjs/limiter/auth-rate-limiter.cjs +35 -0
  35. package/dist/cjs/limiter/auth-rate-limiter.d.ts +12 -0
  36. package/dist/cjs/limiter/fixed-window.cjs +41 -0
  37. package/dist/cjs/limiter/fixed-window.d.ts +11 -0
  38. package/dist/cjs/oauth/base.cjs +7 -0
  39. package/dist/cjs/oauth/base.d.ts +17 -0
  40. package/dist/cjs/oauth/memory.cjs +135 -0
  41. package/dist/cjs/oauth/memory.d.ts +22 -0
  42. package/dist/cjs/oauth/models.cjs +47 -0
  43. package/dist/cjs/oauth/models.d.ts +50 -0
  44. package/dist/cjs/oauth/sequelize.cjs +159 -0
  45. package/dist/cjs/oauth/sequelize.d.ts +30 -0
  46. package/dist/cjs/oauth/types.cjs +3 -0
  47. package/dist/cjs/oauth/types.d.ts +51 -0
  48. package/dist/cjs/passkey/base.cjs +7 -0
  49. package/dist/cjs/passkey/base.d.ts +28 -0
  50. package/dist/cjs/passkey/config.cjs +26 -0
  51. package/dist/cjs/passkey/config.d.ts +2 -0
  52. package/dist/cjs/passkey/memory.cjs +123 -0
  53. package/dist/cjs/passkey/memory.d.ts +34 -0
  54. package/dist/cjs/passkey/models.cjs +142 -0
  55. package/dist/cjs/passkey/models.d.ts +34 -0
  56. package/dist/cjs/passkey/sequelize.cjs +126 -0
  57. package/dist/cjs/passkey/sequelize.d.ts +42 -0
  58. package/dist/cjs/passkey/service.cjs +413 -0
  59. package/dist/cjs/passkey/service.d.ts +21 -0
  60. package/dist/cjs/passkey/types.cjs +2 -0
  61. package/dist/cjs/passkey/types.d.ts +84 -0
  62. package/dist/cjs/sequelize-utils.cjs +56 -0
  63. package/dist/cjs/sequelize-utils.d.ts +8 -0
  64. package/dist/cjs/token/base.cjs +120 -0
  65. package/dist/cjs/token/base.d.ts +46 -0
  66. package/dist/cjs/token/memory.cjs +234 -0
  67. package/dist/cjs/token/memory.d.ts +29 -0
  68. package/dist/cjs/token/sequelize.cjs +400 -0
  69. package/dist/cjs/token/sequelize.d.ts +58 -0
  70. package/dist/cjs/token/types.cjs +2 -0
  71. package/dist/cjs/token/types.d.ts +34 -0
  72. package/dist/cjs/upload/memory.cjs +92 -0
  73. package/dist/cjs/upload/memory.d.ts +17 -0
  74. package/dist/cjs/upload/tus-module.cjs +270 -0
  75. package/dist/cjs/upload/tus-module.d.ts +38 -0
  76. package/dist/cjs/upload/types.cjs +2 -0
  77. package/dist/cjs/upload/types.d.ts +28 -0
  78. package/dist/cjs/user/base.cjs +53 -0
  79. package/dist/cjs/user/base.d.ts +36 -0
  80. package/dist/cjs/user/memory.cjs +194 -0
  81. package/dist/cjs/user/memory.d.ts +37 -0
  82. package/dist/cjs/user/sequelize.cjs +194 -0
  83. package/dist/cjs/user/sequelize.d.ts +46 -0
  84. package/dist/cjs/user/types.cjs +2 -0
  85. package/dist/cjs/user/types.d.ts +11 -0
  86. package/dist/esm/api-module.d.ts +45 -0
  87. package/dist/esm/api-module.js +30 -0
  88. package/dist/esm/apicore-server.d.ts +288 -0
  89. package/dist/esm/apicore-server.js +1552 -0
  90. package/dist/esm/auth-api/auth-module.d.ts +116 -0
  91. package/dist/esm/auth-api/auth-module.js +1246 -0
  92. package/dist/esm/auth-api/compat-auth-storage.d.ts +57 -0
  93. package/dist/esm/auth-api/compat-auth-storage.js +124 -0
  94. package/dist/esm/auth-api/mem-auth-store.d.ts +68 -0
  95. package/dist/esm/auth-api/mem-auth-store.js +117 -0
  96. package/dist/esm/auth-api/module.d.ts +20 -0
  97. package/dist/esm/auth-api/module.js +21 -0
  98. package/dist/esm/auth-api/schemas.d.ts +21 -0
  99. package/dist/esm/auth-api/schemas.js +168 -0
  100. package/dist/esm/auth-api/sql-auth-store.d.ts +87 -0
  101. package/dist/esm/auth-api/sql-auth-store.js +175 -0
  102. package/dist/esm/auth-api/storage.d.ts +38 -0
  103. package/dist/esm/auth-api/storage.js +98 -0
  104. package/dist/esm/auth-api/types.d.ts +34 -0
  105. package/dist/esm/auth-api/types.js +1 -0
  106. package/dist/esm/auth-api/user-id.d.ts +5 -0
  107. package/dist/esm/auth-api/user-id.js +41 -0
  108. package/dist/esm/auth-cookie-options.d.ts +13 -0
  109. package/dist/esm/auth-cookie-options.js +63 -0
  110. package/dist/esm/base/client-info.d.ts +27 -0
  111. package/dist/esm/base/client-info.js +282 -0
  112. package/dist/esm/base/error-utils.d.ts +16 -0
  113. package/dist/esm/base/error-utils.js +44 -0
  114. package/dist/esm/base/request-utils.d.ts +8 -0
  115. package/dist/esm/base/request-utils.js +23 -0
  116. package/dist/esm/index.d.ts +34 -0
  117. package/dist/esm/index.js +21 -0
  118. package/dist/esm/limiter/auth-rate-limiter.d.ts +12 -0
  119. package/dist/esm/limiter/auth-rate-limiter.js +32 -0
  120. package/dist/esm/limiter/fixed-window.d.ts +11 -0
  121. package/dist/esm/limiter/fixed-window.js +37 -0
  122. package/dist/esm/oauth/base.d.ts +17 -0
  123. package/dist/esm/oauth/base.js +3 -0
  124. package/dist/esm/oauth/memory.d.ts +22 -0
  125. package/dist/esm/oauth/memory.js +128 -0
  126. package/dist/esm/oauth/models.d.ts +50 -0
  127. package/dist/esm/oauth/models.js +38 -0
  128. package/dist/esm/oauth/sequelize.d.ts +30 -0
  129. package/dist/esm/oauth/sequelize.js +148 -0
  130. package/dist/esm/oauth/types.d.ts +51 -0
  131. package/dist/esm/oauth/types.js +2 -0
  132. package/dist/esm/passkey/base.d.ts +28 -0
  133. package/dist/esm/passkey/base.js +3 -0
  134. package/dist/esm/passkey/config.d.ts +2 -0
  135. package/dist/esm/passkey/config.js +23 -0
  136. package/dist/esm/passkey/memory.d.ts +34 -0
  137. package/dist/esm/passkey/memory.js +119 -0
  138. package/dist/esm/passkey/models.d.ts +34 -0
  139. package/dist/esm/passkey/models.js +135 -0
  140. package/dist/esm/passkey/sequelize.d.ts +42 -0
  141. package/dist/esm/passkey/sequelize.js +122 -0
  142. package/dist/esm/passkey/service.d.ts +21 -0
  143. package/dist/esm/passkey/service.js +376 -0
  144. package/dist/esm/passkey/types.d.ts +84 -0
  145. package/dist/esm/passkey/types.js +1 -0
  146. package/dist/esm/sequelize-utils.d.ts +8 -0
  147. package/dist/esm/sequelize-utils.js +47 -0
  148. package/dist/esm/token/base.d.ts +46 -0
  149. package/dist/esm/token/base.js +113 -0
  150. package/dist/esm/token/memory.d.ts +29 -0
  151. package/dist/esm/token/memory.js +230 -0
  152. package/dist/esm/token/sequelize.d.ts +58 -0
  153. package/dist/esm/token/sequelize.js +396 -0
  154. package/dist/esm/token/types.d.ts +34 -0
  155. package/dist/esm/token/types.js +1 -0
  156. package/dist/esm/upload/memory.d.ts +17 -0
  157. package/dist/esm/upload/memory.js +86 -0
  158. package/dist/esm/upload/tus-module.d.ts +38 -0
  159. package/dist/esm/upload/tus-module.js +266 -0
  160. package/dist/esm/upload/types.d.ts +28 -0
  161. package/dist/esm/upload/types.js +1 -0
  162. package/dist/esm/user/base.d.ts +36 -0
  163. package/dist/esm/user/base.js +46 -0
  164. package/dist/esm/user/memory.d.ts +37 -0
  165. package/dist/esm/user/memory.js +190 -0
  166. package/dist/esm/user/sequelize.d.ts +46 -0
  167. package/dist/esm/user/sequelize.js +188 -0
  168. package/dist/esm/user/types.d.ts +11 -0
  169. package/dist/esm/user/types.js +1 -0
  170. package/docs/swagger/openapi.json +2162 -0
  171. package/package.json +131 -0
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizePasskeyConfig = normalizePasskeyConfig;
4
+ const DEFAULT_PASSKEY_CONFIG = {
5
+ rpId: 'localhost',
6
+ rpName: 'API Server',
7
+ origins: ['http://localhost:5173'],
8
+ timeoutMs: 5 * 60 * 1000,
9
+ userVerification: 'preferred'
10
+ };
11
+ function isOriginString(origin) {
12
+ return typeof origin === 'string' && origin.trim().length > 0;
13
+ }
14
+ function normalizePasskeyConfig(config = {}) {
15
+ const candidateOrigins = Array.isArray(config.origins) && config.origins.length > 0 ? config.origins.filter(isOriginString) : null;
16
+ return {
17
+ rpId: config.rpId?.trim() || DEFAULT_PASSKEY_CONFIG.rpId,
18
+ rpName: config.rpName?.trim() || DEFAULT_PASSKEY_CONFIG.rpName,
19
+ origins: candidateOrigins ? candidateOrigins.map((origin) => origin.trim()) : DEFAULT_PASSKEY_CONFIG.origins,
20
+ timeoutMs: typeof config.timeoutMs === 'number' && config.timeoutMs > 0
21
+ ? config.timeoutMs
22
+ : DEFAULT_PASSKEY_CONFIG.timeoutMs,
23
+ userVerification: config.userVerification ?? DEFAULT_PASSKEY_CONFIG.userVerification,
24
+ debug: Boolean(config.debug)
25
+ };
26
+ }
@@ -0,0 +1,2 @@
1
+ import type { PasskeyServiceConfig } from './types.js';
2
+ export declare function normalizePasskeyConfig(config?: Partial<PasskeyServiceConfig>): PasskeyServiceConfig;
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MemoryPasskeyStore = void 0;
4
+ const user_id_js_1 = require("../auth-api/user-id.cjs");
5
+ const base_js_1 = require("./base.cjs");
6
+ function encodeCredentialId(value) {
7
+ return Buffer.isBuffer(value) ? value.toString('base64') : value;
8
+ }
9
+ function cloneCredential(record) {
10
+ return {
11
+ ...record,
12
+ credentialId: Buffer.isBuffer(record.credentialId) ? Buffer.from(record.credentialId) : record.credentialId,
13
+ publicKey: Buffer.from(record.publicKey),
14
+ transports: record.transports ? [...record.transports] : undefined
15
+ };
16
+ }
17
+ class MemoryPasskeyStore extends base_js_1.PasskeyStore {
18
+ constructor(options) {
19
+ super();
20
+ this.credentials = new Map();
21
+ this.challenges = new Map();
22
+ this.resolveUserFn = options.resolveUser;
23
+ this.maxCredentials =
24
+ typeof options.maxCredentials === 'number' &&
25
+ Number.isFinite(options.maxCredentials) &&
26
+ options.maxCredentials > 0
27
+ ? Math.floor(options.maxCredentials)
28
+ : undefined;
29
+ this.maxChallenges =
30
+ typeof options.maxChallenges === 'number' &&
31
+ Number.isFinite(options.maxChallenges) &&
32
+ options.maxChallenges > 0
33
+ ? Math.floor(options.maxChallenges)
34
+ : undefined;
35
+ }
36
+ async resolveUser(params) {
37
+ return this.resolveUserFn(params);
38
+ }
39
+ async listUserCredentials(userId) {
40
+ const normalizedUserId = (0, user_id_js_1.normalizeComparableUserId)(userId);
41
+ return [...this.credentials.values()]
42
+ .filter((record) => (0, user_id_js_1.normalizeComparableUserId)(record.userId) === normalizedUserId)
43
+ .map((record) => cloneCredential(record));
44
+ }
45
+ async deleteCredential(credentialId) {
46
+ const key = encodeCredentialId(credentialId);
47
+ return this.credentials.delete(key);
48
+ }
49
+ async findCredentialById(credentialId) {
50
+ const record = this.credentials.get(encodeCredentialId(credentialId));
51
+ return record ? cloneCredential(record) : null;
52
+ }
53
+ async saveCredential(record) {
54
+ this.credentials.set(encodeCredentialId(record.credentialId), {
55
+ ...record,
56
+ userId: (0, user_id_js_1.normalizeComparableUserId)(record.userId),
57
+ credentialId: Buffer.isBuffer(record.credentialId) ? Buffer.from(record.credentialId) : record.credentialId,
58
+ transports: record.transports ? [...record.transports] : undefined
59
+ });
60
+ this.enforceCredentialCapacity();
61
+ }
62
+ async updateCredentialCounter(credentialId, counter) {
63
+ const key = encodeCredentialId(credentialId);
64
+ const existing = this.credentials.get(key);
65
+ if (existing) {
66
+ existing.counter = counter;
67
+ }
68
+ }
69
+ async saveChallenge(record) {
70
+ this.challenges.set(record.challenge, {
71
+ challenge: record.challenge,
72
+ action: record.action,
73
+ userId: record.userId !== undefined ? (0, user_id_js_1.normalizeComparableUserId)(record.userId) : undefined,
74
+ login: record.login ?? undefined,
75
+ expiresAt: record.expiresAt
76
+ });
77
+ this.enforceChallengeCapacity();
78
+ }
79
+ async getChallenge(challenge) {
80
+ const record = this.challenges.get(challenge);
81
+ return record ? { ...record } : null;
82
+ }
83
+ async consumeChallenge(challenge) {
84
+ const record = this.challenges.get(challenge);
85
+ if (!record) {
86
+ return null;
87
+ }
88
+ this.challenges.delete(challenge);
89
+ return { ...record };
90
+ }
91
+ async cleanupChallenges(now) {
92
+ for (const [challenge, record] of this.challenges.entries()) {
93
+ if (record.expiresAt && new Date(record.expiresAt) <= now) {
94
+ this.challenges.delete(challenge);
95
+ }
96
+ }
97
+ }
98
+ enforceCredentialCapacity() {
99
+ if (!this.maxCredentials) {
100
+ return;
101
+ }
102
+ while (this.credentials.size > this.maxCredentials) {
103
+ const oldest = this.credentials.keys().next().value;
104
+ if (!oldest) {
105
+ return;
106
+ }
107
+ this.credentials.delete(oldest);
108
+ }
109
+ }
110
+ enforceChallengeCapacity() {
111
+ if (!this.maxChallenges) {
112
+ return;
113
+ }
114
+ while (this.challenges.size > this.maxChallenges) {
115
+ const oldest = this.challenges.keys().next().value;
116
+ if (!oldest) {
117
+ return;
118
+ }
119
+ this.challenges.delete(oldest);
120
+ }
121
+ }
122
+ }
123
+ exports.MemoryPasskeyStore = MemoryPasskeyStore;
@@ -0,0 +1,34 @@
1
+ import { PasskeyStore } from './base.js';
2
+ import type { PasskeyChallengeRecord, PasskeyUserDescriptor, StoredPasskeyCredential } from './types.js';
3
+ import type { AuthIdentifier } from '../auth-api/types.js';
4
+ export interface MemoryPasskeyStoreOptions {
5
+ resolveUser: (params: {
6
+ userId?: AuthIdentifier;
7
+ login?: string;
8
+ }) => Promise<PasskeyUserDescriptor | null>;
9
+ maxCredentials?: number;
10
+ maxChallenges?: number;
11
+ }
12
+ export declare class MemoryPasskeyStore extends PasskeyStore {
13
+ private readonly resolveUserFn;
14
+ private readonly credentials;
15
+ private readonly challenges;
16
+ private readonly maxCredentials?;
17
+ private readonly maxChallenges?;
18
+ constructor(options: MemoryPasskeyStoreOptions);
19
+ resolveUser(params: {
20
+ userId?: AuthIdentifier;
21
+ login?: string;
22
+ }): Promise<PasskeyUserDescriptor | null>;
23
+ listUserCredentials(userId: AuthIdentifier): Promise<StoredPasskeyCredential[]>;
24
+ deleteCredential(credentialId: Buffer | string): Promise<boolean>;
25
+ findCredentialById(credentialId: Buffer): Promise<StoredPasskeyCredential | null>;
26
+ saveCredential(record: StoredPasskeyCredential): Promise<void>;
27
+ updateCredentialCounter(credentialId: Buffer, counter: number): Promise<void>;
28
+ saveChallenge(record: PasskeyChallengeRecord): Promise<void>;
29
+ getChallenge(challenge: string): Promise<PasskeyChallengeRecord | null>;
30
+ consumeChallenge(challenge: string): Promise<PasskeyChallengeRecord | null>;
31
+ cleanupChallenges(now: Date): Promise<void>;
32
+ private enforceCredentialCapacity;
33
+ private enforceChallengeCapacity;
34
+ }
@@ -0,0 +1,142 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PasskeyChallengeModel = exports.PasskeyCredentialModel = void 0;
4
+ exports.initPasskeyCredentialModel = initPasskeyCredentialModel;
5
+ exports.initPasskeyChallengeModel = initPasskeyChallengeModel;
6
+ const sequelize_1 = require("sequelize");
7
+ const sequelize_utils_js_1 = require("../sequelize-utils.cjs");
8
+ class PasskeyCredentialModel extends sequelize_1.Model {
9
+ }
10
+ exports.PasskeyCredentialModel = PasskeyCredentialModel;
11
+ class PasskeyChallengeModel extends sequelize_1.Model {
12
+ }
13
+ exports.PasskeyChallengeModel = PasskeyChallengeModel;
14
+ function initPasskeyCredentialModel(sequelize, options = {}) {
15
+ const idType = (0, sequelize_utils_js_1.integerIdType)(sequelize);
16
+ return PasskeyCredentialModel.init({
17
+ credentialId: {
18
+ field: 'credential_id',
19
+ type: sequelize_1.DataTypes.STRING(768),
20
+ primaryKey: true,
21
+ allowNull: false,
22
+ get() {
23
+ const raw = this.getDataValue('credentialId');
24
+ if (!raw) {
25
+ return raw;
26
+ }
27
+ if (Buffer.isBuffer(raw)) {
28
+ return raw;
29
+ }
30
+ return Buffer.from(raw, 'base64');
31
+ },
32
+ set(value) {
33
+ const encoded = typeof value === 'string' ? value : value.toString('base64');
34
+ this.setDataValue('credentialId', encoded);
35
+ }
36
+ },
37
+ userId: {
38
+ field: 'user_id',
39
+ type: idType,
40
+ allowNull: false
41
+ },
42
+ publicKey: {
43
+ field: 'public_key',
44
+ type: sequelize_1.DataTypes.BLOB,
45
+ allowNull: false
46
+ },
47
+ counter: {
48
+ type: sequelize_1.DataTypes.INTEGER,
49
+ allowNull: false,
50
+ defaultValue: 0
51
+ },
52
+ transports: {
53
+ type: sequelize_1.DataTypes.JSON,
54
+ allowNull: true
55
+ },
56
+ backedUp: {
57
+ field: 'backed_up',
58
+ type: sequelize_1.DataTypes.BOOLEAN,
59
+ allowNull: false,
60
+ defaultValue: false
61
+ },
62
+ deviceType: {
63
+ field: 'device_type',
64
+ type: sequelize_1.DataTypes.STRING(32),
65
+ allowNull: false,
66
+ defaultValue: 'multiDevice'
67
+ },
68
+ label: {
69
+ type: sequelize_1.DataTypes.STRING(120),
70
+ allowNull: true
71
+ },
72
+ createdDomain: {
73
+ field: 'created_domain',
74
+ type: sequelize_1.DataTypes.STRING(255),
75
+ allowNull: true
76
+ },
77
+ createdUserAgent: {
78
+ field: 'created_user_agent',
79
+ type: sequelize_1.DataTypes.TEXT,
80
+ allowNull: true
81
+ },
82
+ createdBrowser: {
83
+ field: 'created_browser',
84
+ type: sequelize_1.DataTypes.STRING(120),
85
+ allowNull: true
86
+ },
87
+ createdOs: {
88
+ field: 'created_os',
89
+ type: sequelize_1.DataTypes.STRING(120),
90
+ allowNull: true
91
+ },
92
+ createdDevice: {
93
+ field: 'created_device',
94
+ type: sequelize_1.DataTypes.STRING(120),
95
+ allowNull: true
96
+ },
97
+ createdIp: {
98
+ field: 'created_ip',
99
+ type: sequelize_1.DataTypes.STRING(45),
100
+ allowNull: true
101
+ }
102
+ }, {
103
+ sequelize,
104
+ tableName: (0, sequelize_utils_js_1.applyTablePrefix)(options.tablePrefix, 'passkey_credentials'),
105
+ timestamps: true,
106
+ underscored: true
107
+ });
108
+ }
109
+ function initPasskeyChallengeModel(sequelize, options = {}) {
110
+ const idType = (0, sequelize_utils_js_1.integerIdType)(sequelize);
111
+ return PasskeyChallengeModel.init({
112
+ challenge: {
113
+ type: sequelize_1.DataTypes.STRING(255),
114
+ primaryKey: true,
115
+ allowNull: false
116
+ },
117
+ action: {
118
+ type: sequelize_1.DataTypes.STRING(16),
119
+ allowNull: false
120
+ },
121
+ userId: {
122
+ field: 'user_id',
123
+ type: idType,
124
+ allowNull: true
125
+ },
126
+ login: {
127
+ type: sequelize_1.DataTypes.STRING(128),
128
+ allowNull: true
129
+ },
130
+ expiresAt: {
131
+ field: 'expires_at',
132
+ type: sequelize_1.DataTypes.DATE,
133
+ allowNull: false
134
+ }
135
+ }, {
136
+ sequelize,
137
+ tableName: (0, sequelize_utils_js_1.applyTablePrefix)(options.tablePrefix, 'passkey_challenges'),
138
+ timestamps: true,
139
+ underscored: true,
140
+ indexes: [{ fields: ['expires_at'] }, { fields: ['user_id'] }]
141
+ });
142
+ }
@@ -0,0 +1,34 @@
1
+ import { Model, type InferAttributes, type InferCreationAttributes, type ModelStatic, type Sequelize } from 'sequelize';
2
+ export declare class PasskeyCredentialModel extends Model<InferAttributes<PasskeyCredentialModel>, InferCreationAttributes<PasskeyCredentialModel>> {
3
+ credentialId: Buffer;
4
+ userId: number;
5
+ publicKey: Buffer;
6
+ counter: number;
7
+ transports: string[] | null;
8
+ backedUp: boolean;
9
+ deviceType: string;
10
+ label: string | null;
11
+ createdDomain: string | null;
12
+ createdUserAgent: string | null;
13
+ createdBrowser: string | null;
14
+ createdOs: string | null;
15
+ createdDevice: string | null;
16
+ createdIp: string | null;
17
+ createdAt?: Date;
18
+ updatedAt?: Date;
19
+ }
20
+ export declare class PasskeyChallengeModel extends Model<InferAttributes<PasskeyChallengeModel>, InferCreationAttributes<PasskeyChallengeModel>> {
21
+ challenge: string;
22
+ action: 'register' | 'authenticate';
23
+ userId: number | null;
24
+ login: string | null;
25
+ expiresAt: Date;
26
+ createdAt?: Date;
27
+ updatedAt?: Date;
28
+ }
29
+ export declare function initPasskeyCredentialModel(sequelize: Sequelize, options?: {
30
+ tablePrefix?: string;
31
+ }): ModelStatic<PasskeyCredentialModel>;
32
+ export declare function initPasskeyChallengeModel(sequelize: Sequelize, options?: {
33
+ tablePrefix?: string;
34
+ }): ModelStatic<PasskeyChallengeModel>;
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SequelizePasskeyStore = void 0;
4
+ const sequelize_1 = require("sequelize");
5
+ const user_id_js_1 = require("../auth-api/user-id.cjs");
6
+ const base_js_1 = require("./base.cjs");
7
+ const models_js_1 = require("./models.cjs");
8
+ function encodeCredentialId(value) {
9
+ return Buffer.isBuffer(value) ? value.toString('base64') : value;
10
+ }
11
+ class SequelizePasskeyStore extends base_js_1.PasskeyStore {
12
+ constructor(options) {
13
+ super();
14
+ if (!options?.sequelize) {
15
+ throw new Error('SequelizePasskeyStore requires an initialised Sequelize instance');
16
+ }
17
+ this.resolveUserFn = options.resolveUser;
18
+ this.credentials =
19
+ options.credentialModel ??
20
+ (options.credentialModelFactory ?? models_js_1.initPasskeyCredentialModel)(options.sequelize, {
21
+ tablePrefix: options.tablePrefix
22
+ });
23
+ this.challenges =
24
+ options.challengeModel ??
25
+ (options.challengeModelFactory ?? models_js_1.initPasskeyChallengeModel)(options.sequelize, {
26
+ tablePrefix: options.tablePrefix
27
+ });
28
+ }
29
+ async resolveUser(params) {
30
+ return this.resolveUserFn(params);
31
+ }
32
+ async listUserCredentials(userId) {
33
+ const models = await this.credentials.findAll({ where: { userId: (0, user_id_js_1.normalizeNumericUserId)(userId) } });
34
+ return models.map((model) => this.toStoredCredential(model));
35
+ }
36
+ async deleteCredential(credentialId) {
37
+ const encoded = Buffer.isBuffer(credentialId) ? credentialId.toString('base64') : credentialId;
38
+ const deleted = await this.credentials.destroy({ where: { credentialId: encoded } });
39
+ return deleted > 0;
40
+ }
41
+ async findCredentialById(credentialId) {
42
+ const model = await this.credentials.findByPk(encodeCredentialId(credentialId));
43
+ return model ? this.toStoredCredential(model) : null;
44
+ }
45
+ async saveCredential(record) {
46
+ await this.credentials.upsert({
47
+ credentialId: record.credentialId,
48
+ userId: (0, user_id_js_1.normalizeNumericUserId)(record.userId),
49
+ publicKey: record.publicKey,
50
+ counter: record.counter,
51
+ transports: record.transports ?? null,
52
+ backedUp: record.backedUp,
53
+ deviceType: record.deviceType,
54
+ label: record.label ?? null,
55
+ createdDomain: record.createdDomain ?? null,
56
+ createdUserAgent: record.createdUserAgent ?? null,
57
+ createdBrowser: record.createdBrowser ?? null,
58
+ createdOs: record.createdOs ?? null,
59
+ createdDevice: record.createdDevice ?? null,
60
+ createdIp: record.createdIp ?? null
61
+ });
62
+ }
63
+ async updateCredentialCounter(credentialId, counter) {
64
+ await this.credentials.update({ counter }, { where: { credentialId: encodeCredentialId(credentialId) } });
65
+ }
66
+ async saveChallenge(record) {
67
+ await this.challenges.upsert({
68
+ challenge: record.challenge,
69
+ action: record.action,
70
+ userId: record.userId !== undefined ? (0, user_id_js_1.normalizeNumericUserId)(record.userId) : null,
71
+ login: record.login ?? null,
72
+ expiresAt: record.expiresAt
73
+ });
74
+ }
75
+ async getChallenge(challenge) {
76
+ const model = await this.challenges.findByPk(challenge);
77
+ return model ? this.toChallengeRecord(model) : null;
78
+ }
79
+ async consumeChallenge(challenge) {
80
+ const sequelize = this.challenges.sequelize;
81
+ if (!sequelize) {
82
+ throw new Error('Challenge model is not bound to a Sequelize instance');
83
+ }
84
+ return sequelize.transaction({ isolationLevel: sequelize_1.Transaction.ISOLATION_LEVELS.READ_COMMITTED }, async (transaction) => {
85
+ const model = await this.challenges.findByPk(challenge, { transaction, lock: true });
86
+ if (!model) {
87
+ return null;
88
+ }
89
+ await model.destroy({ transaction });
90
+ return this.toChallengeRecord(model);
91
+ });
92
+ }
93
+ async cleanupChallenges(now) {
94
+ await this.challenges.destroy({ where: { expiresAt: { [sequelize_1.Op.lte]: now } } });
95
+ }
96
+ toChallengeRecord(model) {
97
+ return {
98
+ challenge: model.challenge,
99
+ action: model.action,
100
+ userId: model.userId !== null ? String(model.userId) : undefined,
101
+ login: model.login ?? undefined,
102
+ expiresAt: model.expiresAt
103
+ };
104
+ }
105
+ toStoredCredential(model) {
106
+ return {
107
+ userId: String(model.userId),
108
+ credentialId: model.credentialId,
109
+ publicKey: model.publicKey,
110
+ counter: model.counter,
111
+ transports: (model.transports ?? undefined),
112
+ backedUp: model.backedUp,
113
+ deviceType: model.deviceType,
114
+ label: model.label ?? undefined,
115
+ createdDomain: model.createdDomain ?? undefined,
116
+ createdUserAgent: model.createdUserAgent ?? undefined,
117
+ createdBrowser: model.createdBrowser ?? undefined,
118
+ createdOs: model.createdOs ?? undefined,
119
+ createdDevice: model.createdDevice ?? undefined,
120
+ createdIp: model.createdIp ?? undefined,
121
+ createdAt: model.createdAt ?? undefined,
122
+ updatedAt: model.updatedAt ?? undefined
123
+ };
124
+ }
125
+ }
126
+ exports.SequelizePasskeyStore = SequelizePasskeyStore;
@@ -0,0 +1,42 @@
1
+ import { type ModelStatic, type Sequelize } from 'sequelize';
2
+ import { PasskeyStore } from './base.js';
3
+ import { PasskeyChallengeModel, PasskeyCredentialModel } from './models.js';
4
+ import type { PasskeyChallengeRecord, PasskeyUserDescriptor, StoredPasskeyCredential } from './types.js';
5
+ import type { AuthIdentifier } from '../auth-api/types.js';
6
+ export interface SequelizePasskeyStoreOptions {
7
+ sequelize: Sequelize;
8
+ tablePrefix?: string;
9
+ credentialModel?: ModelStatic<PasskeyCredentialModel>;
10
+ challengeModel?: ModelStatic<PasskeyChallengeModel>;
11
+ credentialModelFactory?: (sequelize: Sequelize, options?: {
12
+ tablePrefix?: string;
13
+ }) => ModelStatic<PasskeyCredentialModel>;
14
+ challengeModelFactory?: (sequelize: Sequelize, options?: {
15
+ tablePrefix?: string;
16
+ }) => ModelStatic<PasskeyChallengeModel>;
17
+ resolveUser: (params: {
18
+ userId?: AuthIdentifier;
19
+ login?: string;
20
+ }) => Promise<PasskeyUserDescriptor | null>;
21
+ }
22
+ export declare class SequelizePasskeyStore extends PasskeyStore {
23
+ private readonly resolveUserFn;
24
+ private readonly credentials;
25
+ private readonly challenges;
26
+ constructor(options: SequelizePasskeyStoreOptions);
27
+ resolveUser(params: {
28
+ userId?: AuthIdentifier;
29
+ login?: string;
30
+ }): Promise<PasskeyUserDescriptor | null>;
31
+ listUserCredentials(userId: AuthIdentifier): Promise<StoredPasskeyCredential[]>;
32
+ deleteCredential(credentialId: Buffer | string): Promise<boolean>;
33
+ findCredentialById(credentialId: Buffer): Promise<StoredPasskeyCredential | null>;
34
+ saveCredential(record: StoredPasskeyCredential): Promise<void>;
35
+ updateCredentialCounter(credentialId: Buffer, counter: number): Promise<void>;
36
+ saveChallenge(record: PasskeyChallengeRecord): Promise<void>;
37
+ getChallenge(challenge: string): Promise<PasskeyChallengeRecord | null>;
38
+ consumeChallenge(challenge: string): Promise<PasskeyChallengeRecord | null>;
39
+ cleanupChallenges(now: Date): Promise<void>;
40
+ private toChallengeRecord;
41
+ private toStoredCredential;
42
+ }