@technomoron/api-server-base 2.0.0-beta.19 → 2.0.0-beta.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/api-server-base.cjs +48 -21
- package/dist/cjs/auth-api/auth-module.js +16 -30
- package/dist/cjs/auth-api/mem-auth-store.js +5 -4
- package/dist/cjs/auth-api/sql-auth-store.js +6 -4
- package/dist/cjs/auth-api/user-id.d.ts +1 -0
- package/dist/cjs/auth-api/user-id.js +7 -0
- package/dist/cjs/auth-cookie-options.js +10 -1
- package/dist/cjs/oauth/memory.d.ts +6 -0
- package/dist/cjs/oauth/memory.js +43 -4
- package/dist/cjs/oauth/models.d.ts +1 -0
- package/dist/cjs/oauth/models.js +7 -18
- package/dist/cjs/oauth/sequelize.d.ts +5 -52
- package/dist/cjs/oauth/sequelize.js +34 -93
- package/dist/cjs/oauth/types.d.ts +1 -0
- package/dist/cjs/passkey/base.d.ts +1 -0
- package/dist/cjs/passkey/memory.d.ts +7 -0
- package/dist/cjs/passkey/memory.js +47 -5
- package/dist/cjs/passkey/models.js +2 -5
- package/dist/cjs/passkey/sequelize.d.ts +5 -29
- package/dist/cjs/passkey/sequelize.js +48 -191
- package/dist/cjs/passkey/service.d.ts +1 -0
- package/dist/cjs/passkey/service.js +52 -15
- package/dist/cjs/passkey/types.d.ts +1 -0
- package/dist/cjs/sequelize-utils.d.ts +5 -0
- package/dist/cjs/sequelize-utils.js +40 -0
- package/dist/cjs/token/base.js +3 -1
- package/dist/cjs/token/memory.d.ts +6 -0
- package/dist/cjs/token/memory.js +32 -7
- package/dist/cjs/token/sequelize.d.ts +0 -3
- package/dist/cjs/token/sequelize.js +50 -81
- package/dist/cjs/token/types.d.ts +1 -1
- package/dist/cjs/user/base.d.ts +1 -0
- package/dist/cjs/user/base.js +11 -4
- package/dist/cjs/user/memory.d.ts +2 -0
- package/dist/cjs/user/memory.js +8 -2
- package/dist/cjs/user/sequelize.js +12 -22
- package/dist/esm/api-server-base.js +48 -21
- package/dist/esm/auth-api/auth-module.js +16 -30
- package/dist/esm/auth-api/mem-auth-store.js +5 -4
- package/dist/esm/auth-api/sql-auth-store.js +6 -4
- package/dist/esm/auth-api/user-id.d.ts +1 -0
- package/dist/esm/auth-api/user-id.js +6 -0
- package/dist/esm/auth-cookie-options.js +10 -1
- package/dist/esm/oauth/memory.d.ts +6 -0
- package/dist/esm/oauth/memory.js +44 -5
- package/dist/esm/oauth/models.d.ts +1 -0
- package/dist/esm/oauth/models.js +2 -15
- package/dist/esm/oauth/sequelize.d.ts +5 -52
- package/dist/esm/oauth/sequelize.js +21 -80
- package/dist/esm/oauth/types.d.ts +1 -0
- package/dist/esm/passkey/base.d.ts +1 -0
- package/dist/esm/passkey/memory.d.ts +7 -0
- package/dist/esm/passkey/memory.js +47 -5
- package/dist/esm/passkey/models.js +1 -4
- package/dist/esm/passkey/sequelize.d.ts +5 -29
- package/dist/esm/passkey/sequelize.js +47 -190
- package/dist/esm/passkey/service.d.ts +1 -0
- package/dist/esm/passkey/service.js +52 -15
- package/dist/esm/passkey/types.d.ts +1 -0
- package/dist/esm/sequelize-utils.d.ts +5 -0
- package/dist/esm/sequelize-utils.js +36 -0
- package/dist/esm/token/base.js +3 -1
- package/dist/esm/token/memory.d.ts +6 -0
- package/dist/esm/token/memory.js +32 -7
- package/dist/esm/token/sequelize.d.ts +0 -3
- package/dist/esm/token/sequelize.js +51 -82
- package/dist/esm/token/types.d.ts +1 -1
- package/dist/esm/user/base.d.ts +1 -0
- package/dist/esm/user/base.js +11 -4
- package/dist/esm/user/memory.d.ts +2 -0
- package/dist/esm/user/memory.js +8 -2
- package/dist/esm/user/sequelize.js +13 -23
- package/package.json +5 -5
|
@@ -3,82 +3,18 @@ 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.OAuthCodeModel = exports.OAuthClientModel = void 0;
|
|
7
|
-
exports.initOAuthClientModel = initOAuthClientModel;
|
|
8
|
-
exports.initOAuthCodeModel = initOAuthCodeModel;
|
|
6
|
+
exports.SequelizeOAuthStore = exports.initOAuthCodeModel = exports.initOAuthClientModel = exports.OAuthCodeModel = exports.OAuthClientModel = void 0;
|
|
9
7
|
const bcryptjs_1 = __importDefault(require("bcryptjs"));
|
|
10
8
|
const sequelize_1 = require("sequelize");
|
|
11
9
|
const user_id_js_1 = require("../auth-api/user-id.js");
|
|
12
10
|
const sequelize_utils_js_1 = require("../sequelize-utils.js");
|
|
13
11
|
const base_js_1 = require("./base.js");
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
Object.assign(opts, extra);
|
|
21
|
-
}
|
|
22
|
-
if (sequelize_utils_js_1.DIALECTS_SUPPORTING_UNSIGNED.has(sequelize.getDialect())) {
|
|
23
|
-
opts.charset = 'utf8mb4';
|
|
24
|
-
opts.collate = 'utf8mb4_unicode_ci';
|
|
25
|
-
}
|
|
26
|
-
return opts;
|
|
27
|
-
}
|
|
28
|
-
class OAuthClientModel extends sequelize_1.Model {
|
|
29
|
-
}
|
|
30
|
-
exports.OAuthClientModel = OAuthClientModel;
|
|
31
|
-
function initOAuthClientModel(sequelize, options = {}) {
|
|
32
|
-
OAuthClientModel.init({
|
|
33
|
-
client_id: { type: sequelize_1.DataTypes.STRING(128), allowNull: false, primaryKey: true },
|
|
34
|
-
client_secret: { type: sequelize_1.DataTypes.STRING(255), allowNull: false, defaultValue: '' },
|
|
35
|
-
name: { type: sequelize_1.DataTypes.STRING(128), allowNull: true, defaultValue: null },
|
|
36
|
-
redirect_uris: { type: sequelize_1.DataTypes.TEXT, allowNull: false, defaultValue: '[]' },
|
|
37
|
-
scope: { type: sequelize_1.DataTypes.TEXT, allowNull: false, defaultValue: '[]' },
|
|
38
|
-
metadata: { type: sequelize_1.DataTypes.TEXT, allowNull: true, defaultValue: null },
|
|
39
|
-
first_party: { type: sequelize_1.DataTypes.BOOLEAN, allowNull: false, defaultValue: false }
|
|
40
|
-
}, tableOptions(sequelize, 'oauth_clients', options.tablePrefix, { timestamps: false }));
|
|
41
|
-
return OAuthClientModel;
|
|
42
|
-
}
|
|
43
|
-
class OAuthCodeModel extends sequelize_1.Model {
|
|
44
|
-
}
|
|
45
|
-
exports.OAuthCodeModel = OAuthCodeModel;
|
|
46
|
-
function initOAuthCodeModel(sequelize, options = {}) {
|
|
47
|
-
const idType = integerIdType(sequelize);
|
|
48
|
-
OAuthCodeModel.init({
|
|
49
|
-
code: { type: sequelize_1.DataTypes.STRING(128), allowNull: false, primaryKey: true },
|
|
50
|
-
client_id: { type: sequelize_1.DataTypes.STRING(128), allowNull: false },
|
|
51
|
-
user_id: { type: idType, allowNull: false },
|
|
52
|
-
redirect_uri: { type: sequelize_1.DataTypes.TEXT, allowNull: false },
|
|
53
|
-
scope: { type: sequelize_1.DataTypes.TEXT, allowNull: false, defaultValue: '[]' },
|
|
54
|
-
code_challenge: { type: sequelize_1.DataTypes.STRING(255), allowNull: true, defaultValue: null },
|
|
55
|
-
code_challenge_method: { type: sequelize_1.DataTypes.STRING(10), allowNull: true, defaultValue: null },
|
|
56
|
-
expires: { type: sequelize_1.DataTypes.DATE, allowNull: false },
|
|
57
|
-
metadata: { type: sequelize_1.DataTypes.TEXT, allowNull: true, defaultValue: null }
|
|
58
|
-
}, tableOptions(sequelize, 'oauth_codes', options.tablePrefix, { timestamps: false }));
|
|
59
|
-
return OAuthCodeModel;
|
|
60
|
-
}
|
|
61
|
-
function encodeStringArray(values) {
|
|
62
|
-
return JSON.stringify(values ?? []);
|
|
63
|
-
}
|
|
64
|
-
function decodeStringArray(raw) {
|
|
65
|
-
if (!raw) {
|
|
66
|
-
return [];
|
|
67
|
-
}
|
|
68
|
-
try {
|
|
69
|
-
const parsed = JSON.parse(raw);
|
|
70
|
-
if (Array.isArray(parsed)) {
|
|
71
|
-
return parsed.filter((entry) => typeof entry === 'string' && entry.length > 0);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
catch {
|
|
75
|
-
// ignore malformed values
|
|
76
|
-
}
|
|
77
|
-
return raw
|
|
78
|
-
.split(/\s+/)
|
|
79
|
-
.map((entry) => entry.trim())
|
|
80
|
-
.filter((entry) => entry.length > 0);
|
|
81
|
-
}
|
|
12
|
+
const models_js_1 = require("./models.js");
|
|
13
|
+
var models_js_2 = require("./models.js");
|
|
14
|
+
Object.defineProperty(exports, "OAuthClientModel", { enumerable: true, get: function () { return models_js_2.OAuthClientModel; } });
|
|
15
|
+
Object.defineProperty(exports, "OAuthCodeModel", { enumerable: true, get: function () { return models_js_2.OAuthCodeModel; } });
|
|
16
|
+
Object.defineProperty(exports, "initOAuthClientModel", { enumerable: true, get: function () { return models_js_2.initOAuthClientModel; } });
|
|
17
|
+
Object.defineProperty(exports, "initOAuthCodeModel", { enumerable: true, get: function () { return models_js_2.initOAuthCodeModel; } });
|
|
82
18
|
function serializeMetadata(metadata) {
|
|
83
19
|
if (!metadata) {
|
|
84
20
|
return null;
|
|
@@ -100,9 +36,6 @@ function parseMetadata(raw) {
|
|
|
100
36
|
}
|
|
101
37
|
return undefined;
|
|
102
38
|
}
|
|
103
|
-
function normalizeUserId(identifier) {
|
|
104
|
-
return (0, user_id_js_1.normalizeNumericUserId)(identifier);
|
|
105
|
-
}
|
|
106
39
|
class SequelizeOAuthStore extends base_js_1.OAuthStore {
|
|
107
40
|
constructor(options) {
|
|
108
41
|
super();
|
|
@@ -111,12 +44,12 @@ class SequelizeOAuthStore extends base_js_1.OAuthStore {
|
|
|
111
44
|
}
|
|
112
45
|
this.clients =
|
|
113
46
|
options.clientModel ??
|
|
114
|
-
(options.clientModelFactory ?? initOAuthClientModel)(options.sequelize, {
|
|
47
|
+
(options.clientModelFactory ?? models_js_1.initOAuthClientModel)(options.sequelize, {
|
|
115
48
|
tablePrefix: options.tablePrefix
|
|
116
49
|
});
|
|
117
50
|
this.codes =
|
|
118
51
|
options.codeModel ??
|
|
119
|
-
(options.codeModelFactory ?? initOAuthCodeModel)(options.sequelize, {
|
|
52
|
+
(options.codeModelFactory ?? models_js_1.initOAuthCodeModel)(options.sequelize, {
|
|
120
53
|
tablePrefix: options.tablePrefix
|
|
121
54
|
});
|
|
122
55
|
this.bcryptRounds = options.bcryptRounds ?? 12;
|
|
@@ -130,15 +63,15 @@ class SequelizeOAuthStore extends base_js_1.OAuthStore {
|
|
|
130
63
|
const hashedSecret = input.clientSecret !== undefined && input.clientSecret !== null
|
|
131
64
|
? await bcryptjs_1.default.hash(input.clientSecret, this.bcryptRounds)
|
|
132
65
|
: (existing?.client_secret ?? '');
|
|
133
|
-
const redirectUris = input.redirectUris ?? (existing ? decodeStringArray(existing.redirect_uris) : undefined);
|
|
134
|
-
const scope = input.scope ?? (existing ? decodeStringArray(existing.scope) : undefined);
|
|
66
|
+
const redirectUris = input.redirectUris ?? (existing ? (0, sequelize_utils_js_1.decodeStringArray)(existing.redirect_uris) : undefined);
|
|
67
|
+
const scope = input.scope ?? (existing ? (0, sequelize_utils_js_1.decodeStringArray)(existing.scope) : undefined);
|
|
135
68
|
const metadata = input.metadata ?? (existing ? parseMetadata(existing.metadata) : undefined);
|
|
136
69
|
await this.clients.upsert({
|
|
137
70
|
client_id: input.clientId,
|
|
138
71
|
client_secret: hashedSecret,
|
|
139
72
|
name: input.name ?? existing?.name ?? null,
|
|
140
|
-
redirect_uris: encodeStringArray(redirectUris),
|
|
141
|
-
scope: encodeStringArray(scope),
|
|
73
|
+
redirect_uris: (0, sequelize_utils_js_1.encodeStringArray)(redirectUris),
|
|
74
|
+
scope: (0, sequelize_utils_js_1.encodeStringArray)(scope),
|
|
142
75
|
metadata: serializeMetadata(metadata),
|
|
143
76
|
first_party: input.firstParty ?? existing?.first_party ?? false
|
|
144
77
|
});
|
|
@@ -165,9 +98,9 @@ class SequelizeOAuthStore extends base_js_1.OAuthStore {
|
|
|
165
98
|
await this.codes.create({
|
|
166
99
|
code: code.code,
|
|
167
100
|
client_id: code.clientId,
|
|
168
|
-
user_id:
|
|
101
|
+
user_id: (0, user_id_js_1.normalizeNumericUserId)(code.userId),
|
|
169
102
|
redirect_uri: code.redirectUri ?? '',
|
|
170
|
-
scope: encodeStringArray(code.scope),
|
|
103
|
+
scope: (0, sequelize_utils_js_1.encodeStringArray)(code.scope),
|
|
171
104
|
code_challenge: code.codeChallenge ?? null,
|
|
172
105
|
code_challenge_method: code.codeChallengeMethod ?? null,
|
|
173
106
|
expires: code.expiresAt,
|
|
@@ -175,12 +108,21 @@ class SequelizeOAuthStore extends base_js_1.OAuthStore {
|
|
|
175
108
|
});
|
|
176
109
|
}
|
|
177
110
|
async consumeAuthCode(code) {
|
|
178
|
-
const
|
|
179
|
-
if (!
|
|
180
|
-
|
|
111
|
+
const sequelize = this.codes.sequelize;
|
|
112
|
+
if (!sequelize) {
|
|
113
|
+
throw new Error('Code model is not bound to a Sequelize instance');
|
|
181
114
|
}
|
|
182
|
-
|
|
183
|
-
|
|
115
|
+
return sequelize.transaction({ isolationLevel: sequelize_1.Transaction.ISOLATION_LEVELS.READ_COMMITTED }, async (transaction) => {
|
|
116
|
+
const model = await this.codes.findByPk(code, { transaction, lock: true });
|
|
117
|
+
if (!model) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
await model.destroy({ transaction });
|
|
121
|
+
if (model.expires.getTime() <= Date.now()) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
return this.toAuthCode(model);
|
|
125
|
+
});
|
|
184
126
|
}
|
|
185
127
|
async close() {
|
|
186
128
|
return;
|
|
@@ -188,11 +130,10 @@ class SequelizeOAuthStore extends base_js_1.OAuthStore {
|
|
|
188
130
|
toOAuthClient(model) {
|
|
189
131
|
return {
|
|
190
132
|
clientId: model.client_id,
|
|
191
|
-
|
|
192
|
-
clientSecret: model.client_secret ? '__stored__' : undefined,
|
|
133
|
+
hasSecret: Boolean(model.client_secret),
|
|
193
134
|
name: model.name ?? undefined,
|
|
194
|
-
redirectUris: decodeStringArray(model.redirect_uris),
|
|
195
|
-
scope: decodeStringArray(model.scope),
|
|
135
|
+
redirectUris: (0, sequelize_utils_js_1.decodeStringArray)(model.redirect_uris),
|
|
136
|
+
scope: (0, sequelize_utils_js_1.decodeStringArray)(model.scope),
|
|
196
137
|
metadata: parseMetadata(model.metadata),
|
|
197
138
|
firstParty: model.first_party ?? false
|
|
198
139
|
};
|
|
@@ -201,9 +142,9 @@ class SequelizeOAuthStore extends base_js_1.OAuthStore {
|
|
|
201
142
|
return {
|
|
202
143
|
code: model.code,
|
|
203
144
|
clientId: model.client_id,
|
|
204
|
-
userId: model.user_id,
|
|
145
|
+
userId: String(model.user_id),
|
|
205
146
|
redirectUri: model.redirect_uri,
|
|
206
|
-
scope: decodeStringArray(model.scope),
|
|
147
|
+
scope: (0, sequelize_utils_js_1.decodeStringArray)(model.scope),
|
|
207
148
|
codeChallenge: model.code_challenge ?? undefined,
|
|
208
149
|
codeChallengeMethod: model.code_challenge_method ?? undefined,
|
|
209
150
|
expiresAt: model.expires,
|
|
@@ -11,6 +11,7 @@ export declare abstract class PasskeyStore implements PasskeyStorageAdapter {
|
|
|
11
11
|
abstract saveCredential(record: StoredPasskeyCredential): Promise<void>;
|
|
12
12
|
abstract updateCredentialCounter(credentialId: Buffer, counter: number): Promise<void>;
|
|
13
13
|
abstract saveChallenge(record: PasskeyChallengeRecord): Promise<void>;
|
|
14
|
+
abstract getChallenge(challenge: string): Promise<PasskeyChallengeRecord | null>;
|
|
14
15
|
abstract consumeChallenge(challenge: string): Promise<PasskeyChallengeRecord | null>;
|
|
15
16
|
abstract cleanupChallenges(now: Date): Promise<void>;
|
|
16
17
|
}
|
|
@@ -6,11 +6,15 @@ export interface MemoryPasskeyStoreOptions {
|
|
|
6
6
|
userId?: AuthIdentifier;
|
|
7
7
|
login?: string;
|
|
8
8
|
}) => Promise<PasskeyUserDescriptor | null>;
|
|
9
|
+
maxCredentials?: number;
|
|
10
|
+
maxChallenges?: number;
|
|
9
11
|
}
|
|
10
12
|
export declare class MemoryPasskeyStore extends PasskeyStore {
|
|
11
13
|
private readonly resolveUserFn;
|
|
12
14
|
private readonly credentials;
|
|
13
15
|
private readonly challenges;
|
|
16
|
+
private readonly maxCredentials?;
|
|
17
|
+
private readonly maxChallenges?;
|
|
14
18
|
constructor(options: MemoryPasskeyStoreOptions);
|
|
15
19
|
resolveUser(params: {
|
|
16
20
|
userId?: AuthIdentifier;
|
|
@@ -22,6 +26,9 @@ export declare class MemoryPasskeyStore extends PasskeyStore {
|
|
|
22
26
|
saveCredential(record: StoredPasskeyCredential): Promise<void>;
|
|
23
27
|
updateCredentialCounter(credentialId: Buffer, counter: number): Promise<void>;
|
|
24
28
|
saveChallenge(record: PasskeyChallengeRecord): Promise<void>;
|
|
29
|
+
getChallenge(challenge: string): Promise<PasskeyChallengeRecord | null>;
|
|
25
30
|
consumeChallenge(challenge: string): Promise<PasskeyChallengeRecord | null>;
|
|
26
31
|
cleanupChallenges(now: Date): Promise<void>;
|
|
32
|
+
private enforceCredentialCapacity;
|
|
33
|
+
private enforceChallengeCapacity;
|
|
27
34
|
}
|
|
@@ -6,11 +6,11 @@ const base_js_1 = require("./base.js");
|
|
|
6
6
|
function encodeCredentialId(value) {
|
|
7
7
|
return Buffer.isBuffer(value) ? value.toString('base64') : value;
|
|
8
8
|
}
|
|
9
|
-
const normalizeUserId = user_id_js_1.normalizeComparableUserId;
|
|
10
9
|
function cloneCredential(record) {
|
|
11
10
|
return {
|
|
12
11
|
...record,
|
|
13
12
|
credentialId: Buffer.isBuffer(record.credentialId) ? Buffer.from(record.credentialId) : record.credentialId,
|
|
13
|
+
publicKey: Buffer.from(record.publicKey),
|
|
14
14
|
transports: record.transports ? [...record.transports] : undefined
|
|
15
15
|
};
|
|
16
16
|
}
|
|
@@ -20,14 +20,26 @@ class MemoryPasskeyStore extends base_js_1.PasskeyStore {
|
|
|
20
20
|
this.credentials = new Map();
|
|
21
21
|
this.challenges = new Map();
|
|
22
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;
|
|
23
35
|
}
|
|
24
36
|
async resolveUser(params) {
|
|
25
37
|
return this.resolveUserFn(params);
|
|
26
38
|
}
|
|
27
39
|
async listUserCredentials(userId) {
|
|
28
|
-
const normalizedUserId =
|
|
40
|
+
const normalizedUserId = (0, user_id_js_1.normalizeComparableUserId)(userId);
|
|
29
41
|
return [...this.credentials.values()]
|
|
30
|
-
.filter((record) =>
|
|
42
|
+
.filter((record) => (0, user_id_js_1.normalizeComparableUserId)(record.userId) === normalizedUserId)
|
|
31
43
|
.map((record) => cloneCredential(record));
|
|
32
44
|
}
|
|
33
45
|
async deleteCredential(credentialId) {
|
|
@@ -41,10 +53,11 @@ class MemoryPasskeyStore extends base_js_1.PasskeyStore {
|
|
|
41
53
|
async saveCredential(record) {
|
|
42
54
|
this.credentials.set(encodeCredentialId(record.credentialId), {
|
|
43
55
|
...record,
|
|
44
|
-
userId:
|
|
56
|
+
userId: (0, user_id_js_1.normalizeComparableUserId)(record.userId),
|
|
45
57
|
credentialId: Buffer.isBuffer(record.credentialId) ? Buffer.from(record.credentialId) : record.credentialId,
|
|
46
58
|
transports: record.transports ? [...record.transports] : undefined
|
|
47
59
|
});
|
|
60
|
+
this.enforceCredentialCapacity();
|
|
48
61
|
}
|
|
49
62
|
async updateCredentialCounter(credentialId, counter) {
|
|
50
63
|
const key = encodeCredentialId(credentialId);
|
|
@@ -57,10 +70,15 @@ class MemoryPasskeyStore extends base_js_1.PasskeyStore {
|
|
|
57
70
|
this.challenges.set(record.challenge, {
|
|
58
71
|
challenge: record.challenge,
|
|
59
72
|
action: record.action,
|
|
60
|
-
userId: record.userId !== undefined ?
|
|
73
|
+
userId: record.userId !== undefined ? (0, user_id_js_1.normalizeComparableUserId)(record.userId) : undefined,
|
|
61
74
|
login: record.login ?? undefined,
|
|
62
75
|
expiresAt: record.expiresAt
|
|
63
76
|
});
|
|
77
|
+
this.enforceChallengeCapacity();
|
|
78
|
+
}
|
|
79
|
+
async getChallenge(challenge) {
|
|
80
|
+
const record = this.challenges.get(challenge);
|
|
81
|
+
return record ? { ...record } : null;
|
|
64
82
|
}
|
|
65
83
|
async consumeChallenge(challenge) {
|
|
66
84
|
const record = this.challenges.get(challenge);
|
|
@@ -77,5 +95,29 @@ class MemoryPasskeyStore extends base_js_1.PasskeyStore {
|
|
|
77
95
|
}
|
|
78
96
|
}
|
|
79
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
|
+
}
|
|
80
122
|
}
|
|
81
123
|
exports.MemoryPasskeyStore = MemoryPasskeyStore;
|
|
@@ -5,9 +5,6 @@ exports.initPasskeyCredentialModel = initPasskeyCredentialModel;
|
|
|
5
5
|
exports.initPasskeyChallengeModel = initPasskeyChallengeModel;
|
|
6
6
|
const sequelize_1 = require("sequelize");
|
|
7
7
|
const sequelize_utils_js_1 = require("../sequelize-utils.js");
|
|
8
|
-
function integerIdType(sequelize) {
|
|
9
|
-
return sequelize_utils_js_1.DIALECTS_SUPPORTING_UNSIGNED.has(sequelize.getDialect()) ? sequelize_1.DataTypes.INTEGER.UNSIGNED : sequelize_1.DataTypes.INTEGER;
|
|
10
|
-
}
|
|
11
8
|
class PasskeyCredentialModel extends sequelize_1.Model {
|
|
12
9
|
}
|
|
13
10
|
exports.PasskeyCredentialModel = PasskeyCredentialModel;
|
|
@@ -15,7 +12,7 @@ class PasskeyChallengeModel extends sequelize_1.Model {
|
|
|
15
12
|
}
|
|
16
13
|
exports.PasskeyChallengeModel = PasskeyChallengeModel;
|
|
17
14
|
function initPasskeyCredentialModel(sequelize, options = {}) {
|
|
18
|
-
const idType = integerIdType(sequelize);
|
|
15
|
+
const idType = (0, sequelize_utils_js_1.integerIdType)(sequelize);
|
|
19
16
|
return PasskeyCredentialModel.init({
|
|
20
17
|
credentialId: {
|
|
21
18
|
field: 'credential_id',
|
|
@@ -110,7 +107,7 @@ function initPasskeyCredentialModel(sequelize, options = {}) {
|
|
|
110
107
|
});
|
|
111
108
|
}
|
|
112
109
|
function initPasskeyChallengeModel(sequelize, options = {}) {
|
|
113
|
-
const idType = integerIdType(sequelize);
|
|
110
|
+
const idType = (0, sequelize_utils_js_1.integerIdType)(sequelize);
|
|
114
111
|
return PasskeyChallengeModel.init({
|
|
115
112
|
challenge: {
|
|
116
113
|
type: sequelize_1.DataTypes.STRING(255),
|
|
@@ -1,34 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type ModelStatic, type Sequelize } from 'sequelize';
|
|
2
2
|
import { PasskeyStore } from './base.js';
|
|
3
|
+
import { PasskeyChallengeModel, PasskeyCredentialModel } from './models.js';
|
|
3
4
|
import type { PasskeyChallengeRecord, PasskeyUserDescriptor, StoredPasskeyCredential } from './types.js';
|
|
4
5
|
import type { AuthIdentifier } from '../auth-api/types.js';
|
|
5
|
-
declare class PasskeyCredentialModel extends Model<InferAttributes<PasskeyCredentialModel>, InferCreationAttributes<PasskeyCredentialModel>> {
|
|
6
|
-
credentialId: Buffer;
|
|
7
|
-
userId: number;
|
|
8
|
-
publicKey: Buffer;
|
|
9
|
-
counter: number;
|
|
10
|
-
transports: string[] | null;
|
|
11
|
-
backedUp: boolean;
|
|
12
|
-
deviceType: string;
|
|
13
|
-
label: string | null;
|
|
14
|
-
createdDomain: string | null;
|
|
15
|
-
createdUserAgent: string | null;
|
|
16
|
-
createdBrowser: string | null;
|
|
17
|
-
createdOs: string | null;
|
|
18
|
-
createdDevice: string | null;
|
|
19
|
-
createdIp: string | null;
|
|
20
|
-
createdAt?: Date;
|
|
21
|
-
updatedAt?: Date;
|
|
22
|
-
}
|
|
23
|
-
declare class PasskeyChallengeModel extends Model<InferAttributes<PasskeyChallengeModel>, InferCreationAttributes<PasskeyChallengeModel>> {
|
|
24
|
-
challenge: string;
|
|
25
|
-
action: 'register' | 'authenticate';
|
|
26
|
-
userId: number | null;
|
|
27
|
-
login: string | null;
|
|
28
|
-
expiresAt: Date;
|
|
29
|
-
createdAt?: Date;
|
|
30
|
-
updatedAt?: Date;
|
|
31
|
-
}
|
|
32
6
|
export interface SequelizePasskeyStoreOptions {
|
|
33
7
|
sequelize: Sequelize;
|
|
34
8
|
tablePrefix?: string;
|
|
@@ -60,7 +34,9 @@ export declare class SequelizePasskeyStore extends PasskeyStore {
|
|
|
60
34
|
saveCredential(record: StoredPasskeyCredential): Promise<void>;
|
|
61
35
|
updateCredentialCounter(credentialId: Buffer, counter: number): Promise<void>;
|
|
62
36
|
saveChallenge(record: PasskeyChallengeRecord): Promise<void>;
|
|
37
|
+
getChallenge(challenge: string): Promise<PasskeyChallengeRecord | null>;
|
|
63
38
|
consumeChallenge(challenge: string): Promise<PasskeyChallengeRecord | null>;
|
|
64
39
|
cleanupChallenges(now: Date): Promise<void>;
|
|
40
|
+
private toChallengeRecord;
|
|
41
|
+
private toStoredCredential;
|
|
65
42
|
}
|
|
66
|
-
export {};
|