@unchainedshop/core-users 3.0.0-rc1 → 3.0.0-rc10
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/lib/db/UsersCollection.d.ts +54 -2
- package/lib/db/UsersCollection.d.ts.map +1 -1
- package/lib/db/UsersCollection.js.map +1 -1
- package/lib/db/WebAuthnCredentialsCreationRequestsCollection.d.ts +6 -1
- package/lib/db/WebAuthnCredentialsCreationRequestsCollection.d.ts.map +1 -1
- package/lib/db/WebAuthnCredentialsCreationRequestsCollection.js.map +1 -1
- package/lib/module/addMigrations.d.ts.map +1 -1
- package/lib/module/addMigrations.js +32 -41
- package/lib/module/addMigrations.js.map +1 -1
- package/lib/module/configureUsersModule.d.ts +85 -48
- package/lib/module/configureUsersModule.d.ts.map +1 -1
- package/lib/module/configureUsersModule.js +49 -20
- package/lib/module/configureUsersModule.js.map +1 -1
- package/lib/module/configureUsersWebAuthnModule.d.ts +21 -8
- package/lib/module/configureUsersWebAuthnModule.d.ts.map +1 -1
- package/lib/module/configureUsersWebAuthnModule.js +32 -11
- package/lib/module/configureUsersWebAuthnModule.js.map +1 -1
- package/lib/module/pbkdf2.js +1 -1
- package/lib/module/pbkdf2.js.map +1 -1
- package/lib/users-index.d.ts +3 -1
- package/lib/users-index.d.ts.map +1 -1
- package/lib/users-index.js +3 -1
- package/lib/users-index.js.map +1 -1
- package/lib/users-settings.d.ts +1 -1
- package/lib/users-settings.d.ts.map +1 -1
- package/lib/users-settings.js.map +1 -1
- package/package.json +20 -9
- package/src/db/UsersCollection.ts +59 -2
- package/src/db/WebAuthnCredentialsCreationRequestsCollection.ts +7 -1
- package/src/module/addMigrations.ts +40 -53
- package/src/module/configureUsersModule.ts +83 -105
- package/src/module/configureUsersWebAuthnModule.ts +47 -43
- package/src/module/pbkdf2.ts +1 -1
- package/src/module/removeConfidentialServiceHashes.test.ts +1 -1
- package/src/users-index.ts +3 -1
- package/src/users-settings.ts +1 -2
- package/lib/module/configureUsersModule_BACKUP_57123.d.ts +0 -73
- package/lib/module/configureUsersModule_BACKUP_57123.d.ts.map +0 -1
- package/lib/module/configureUsersModule_BACKUP_57123.js +0 -567
- package/lib/module/configureUsersModule_BACKUP_57123.js.map +0 -1
- package/lib/module/configureUsersModule_BASE_57123.d.ts +0 -131
- package/lib/module/configureUsersModule_BASE_57123.d.ts.map +0 -1
- package/lib/module/configureUsersModule_BASE_57123.js +0 -563
- package/lib/module/configureUsersModule_BASE_57123.js.map +0 -1
- package/lib/module/configureUsersModule_LOCAL_57123.d.ts +0 -131
- package/lib/module/configureUsersModule_LOCAL_57123.d.ts.map +0 -1
- package/lib/module/configureUsersModule_LOCAL_57123.js +0 -564
- package/lib/module/configureUsersModule_LOCAL_57123.js.map +0 -1
- package/lib/module/configureUsersModule_REMOTE_57123.d.ts +0 -74
- package/lib/module/configureUsersModule_REMOTE_57123.d.ts.map +0 -1
- package/lib/module/configureUsersModule_REMOTE_57123.js +0 -567
- package/lib/module/configureUsersModule_REMOTE_57123.js.map +0 -1
- package/lib/module/sha256.d.ts +0 -2
- package/lib/module/sha256.d.ts.map +0 -1
- package/lib/module/sha256.js +0 -6
- package/lib/module/sha256.js.map +0 -1
- package/lib/types.d.ts +0 -91
- package/lib/types.d.ts.map +0 -1
- package/lib/types.js +0 -2
- package/lib/types.js.map +0 -1
- package/src/types.ts +0 -104
|
@@ -1,97 +1,39 @@
|
|
|
1
|
-
import bcrypt from '@node-rs/bcrypt';
|
|
2
|
-
import { ModuleInput, Address, Contact } from '@unchainedshop/mongodb';
|
|
3
|
-
import { User, UserQuery, Email, UserLastLogin, UserProfile, UserData } from '../types.js';
|
|
4
|
-
import { emit, registerEvents } from '@unchainedshop/events';
|
|
1
|
+
import * as bcrypt from '@node-rs/bcrypt';
|
|
5
2
|
import {
|
|
3
|
+
ModuleInput,
|
|
4
|
+
Address,
|
|
5
|
+
Contact,
|
|
6
6
|
generateDbFilterById,
|
|
7
7
|
buildSortOptions,
|
|
8
8
|
mongodb,
|
|
9
9
|
generateDbObjectId,
|
|
10
10
|
} from '@unchainedshop/mongodb';
|
|
11
|
+
import {
|
|
12
|
+
User,
|
|
13
|
+
UserQuery,
|
|
14
|
+
Email,
|
|
15
|
+
UserLastLogin,
|
|
16
|
+
UserProfile,
|
|
17
|
+
UsersCollection,
|
|
18
|
+
} from '../db/UsersCollection.js';
|
|
19
|
+
import { emit, registerEvents } from '@unchainedshop/events';
|
|
11
20
|
import { systemLocale, SortDirection, SortOption, sha256 } from '@unchainedshop/utils';
|
|
12
|
-
import { UsersCollection } from '../db/UsersCollection.js';
|
|
13
21
|
import addMigrations from './addMigrations.js';
|
|
14
22
|
import { userSettings, UserSettingsOptions } from '../users-settings.js';
|
|
15
|
-
import { configureUsersWebAuthnModule
|
|
23
|
+
import { configureUsersWebAuthnModule } from './configureUsersWebAuthnModule.js';
|
|
16
24
|
import * as pbkdf2 from './pbkdf2.js';
|
|
17
25
|
|
|
18
|
-
export
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
address: string;
|
|
30
|
-
when: Date;
|
|
31
|
-
}>;
|
|
32
|
-
findUserByEmail(email: string): Promise<User>;
|
|
33
|
-
findUserByUsername(username: string): Promise<User>;
|
|
34
|
-
findUser: (
|
|
35
|
-
selector: UserQuery & { sort?: Array<SortOption> },
|
|
36
|
-
options?: mongodb.FindOptions,
|
|
37
|
-
) => Promise<User>;
|
|
38
|
-
findUsers: (
|
|
39
|
-
query: UserQuery & {
|
|
40
|
-
sort?: Array<SortOption>;
|
|
41
|
-
limit?: number;
|
|
42
|
-
offset?: number;
|
|
43
|
-
},
|
|
44
|
-
) => Promise<Array<User>>;
|
|
45
|
-
userExists: (query: { userId: string }) => Promise<boolean>;
|
|
46
|
-
// Transformations
|
|
47
|
-
primaryEmail: (user: User) => Email;
|
|
48
|
-
userLocale: (user: User) => Intl.Locale;
|
|
49
|
-
|
|
50
|
-
// Mutations
|
|
51
|
-
createUser: (
|
|
52
|
-
userData: UserData,
|
|
53
|
-
options: { skipMessaging?: boolean; skipPasswordEnrollment?: boolean },
|
|
54
|
-
) => Promise<string>;
|
|
55
|
-
addEmail: (userId: string, email: string) => Promise<void>;
|
|
56
|
-
removeEmail: (userId: string, email: string) => Promise<void>;
|
|
57
|
-
sendVerificationEmail: (userId: string, email: string) => Promise<void>;
|
|
58
|
-
sendResetPasswordEmail: (userId: string, email: string, isEnrollment?: boolean) => Promise<void>;
|
|
59
|
-
verifyEmail: (userId: string, email: string) => Promise<void>;
|
|
60
|
-
setUsername: (userId: string, newUsername: string) => Promise<void>;
|
|
61
|
-
setPassword: (userId: string, newPassword?: string) => Promise<void>;
|
|
62
|
-
verifyPassword: (hashObject: Record<string, string>, password: string) => Promise<boolean>;
|
|
63
|
-
addRoles: (userId: string, roles: Array<string>) => Promise<number>;
|
|
64
|
-
updateAvatar: (_id: string, fileId: string) => Promise<User>;
|
|
65
|
-
updateGuest: (user: User, guest: boolean) => Promise<void>;
|
|
66
|
-
updateHeartbeat: (userId: string, doc: UserLastLogin) => Promise<User>;
|
|
67
|
-
updateLastBillingAddress: (_id: string, lastAddress: Address) => Promise<User>;
|
|
68
|
-
updateLastContact: (_id: string, lastContact: Contact) => Promise<User>;
|
|
69
|
-
updateProfile: (
|
|
70
|
-
_id: string,
|
|
71
|
-
{ profile, meta }: { profile?: UserProfile; meta?: any },
|
|
72
|
-
) => Promise<User>;
|
|
73
|
-
markDeleted: (userId: string) => Promise<User>;
|
|
74
|
-
deletePermanently: (params: { userId: string }) => Promise<User>;
|
|
75
|
-
updateRoles: (_id: string, roles: Array<string>) => Promise<User>;
|
|
76
|
-
updateTags: (_id: string, tags: Array<string>) => Promise<User>;
|
|
77
|
-
updateUser: (
|
|
78
|
-
selector: mongodb.Filter<User>,
|
|
79
|
-
modifier: mongodb.UpdateFilter<User>,
|
|
80
|
-
options: mongodb.FindOneAndUpdateOptions,
|
|
81
|
-
) => Promise<User>;
|
|
82
|
-
addPushSubscription: (
|
|
83
|
-
userId: string,
|
|
84
|
-
subscription: any,
|
|
85
|
-
options?: {
|
|
86
|
-
userAgent: string;
|
|
87
|
-
unsubscribeFromOtherUsers: boolean;
|
|
88
|
-
},
|
|
89
|
-
) => Promise<void>;
|
|
90
|
-
removePushSubscription: (userId: string, p256dh: string) => Promise<void>;
|
|
91
|
-
hashPassword(password: string): Promise<{
|
|
92
|
-
pbkdf2: string;
|
|
93
|
-
}>;
|
|
94
|
-
};
|
|
26
|
+
export interface UserData {
|
|
27
|
+
email?: string;
|
|
28
|
+
guest?: boolean;
|
|
29
|
+
initialPassword?: boolean;
|
|
30
|
+
lastBillingAddress?: User['lastBillingAddress'];
|
|
31
|
+
password: string | null;
|
|
32
|
+
webAuthnPublicKeyCredentials?: any;
|
|
33
|
+
profile?: UserProfile;
|
|
34
|
+
roles?: Array<string>;
|
|
35
|
+
username?: string;
|
|
36
|
+
}
|
|
95
37
|
|
|
96
38
|
const USER_EVENTS = [
|
|
97
39
|
'USER_ACCOUNT_ACTION',
|
|
@@ -152,7 +94,7 @@ export const configureUsersModule = async ({
|
|
|
152
94
|
db,
|
|
153
95
|
options,
|
|
154
96
|
migrationRepository,
|
|
155
|
-
}: ModuleInput<UserSettingsOptions>)
|
|
97
|
+
}: ModuleInput<UserSettingsOptions>) => {
|
|
156
98
|
userSettings.configureSettings(options || {}, db);
|
|
157
99
|
registerEvents(USER_EVENTS);
|
|
158
100
|
const Users = await UsersCollection(db);
|
|
@@ -212,31 +154,37 @@ export const configureUsersModule = async ({
|
|
|
212
154
|
},
|
|
213
155
|
|
|
214
156
|
async verifyEmail(userId: string, address: string): Promise<void> {
|
|
215
|
-
await Users.updateOne(
|
|
216
|
-
{
|
|
157
|
+
const updated = await Users.updateOne(
|
|
158
|
+
{
|
|
159
|
+
_id: userId,
|
|
160
|
+
emails: { $elemMatch: { address: { $regex: address, $options: 'i' }, verified: false } },
|
|
161
|
+
},
|
|
217
162
|
{
|
|
218
163
|
$set: {
|
|
219
|
-
'emails
|
|
164
|
+
'emails.$.verified': true,
|
|
220
165
|
},
|
|
221
166
|
$pull: {
|
|
222
167
|
'services.email.verificationTokens': {
|
|
223
|
-
address,
|
|
168
|
+
address: { $regex: address, $options: 'i' },
|
|
224
169
|
},
|
|
225
170
|
},
|
|
226
171
|
},
|
|
227
|
-
{
|
|
228
|
-
arrayFilters: [{ 'email.address': address }],
|
|
229
|
-
},
|
|
230
172
|
);
|
|
231
173
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
174
|
+
if (updated.modifiedCount > 0) {
|
|
175
|
+
await emit('USER_ACCOUNT_ACTION', {
|
|
176
|
+
action: 'email-verified',
|
|
177
|
+
address,
|
|
178
|
+
userId,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
237
181
|
},
|
|
238
182
|
|
|
239
|
-
async
|
|
183
|
+
async findResetToken(plainToken: string): Promise<{
|
|
184
|
+
userId: string;
|
|
185
|
+
address: string;
|
|
186
|
+
when: Date;
|
|
187
|
+
}> {
|
|
240
188
|
const token = await sha256(plainToken);
|
|
241
189
|
const user = await Users.findOne(
|
|
242
190
|
{
|
|
@@ -249,10 +197,15 @@ export const configureUsersModule = async ({
|
|
|
249
197
|
},
|
|
250
198
|
{},
|
|
251
199
|
);
|
|
252
|
-
return
|
|
200
|
+
if (!user) return null;
|
|
201
|
+
const resetToken = user.services.password.reset.find((v) => v.token === token);
|
|
202
|
+
return {
|
|
203
|
+
userId: user._id,
|
|
204
|
+
...resetToken,
|
|
205
|
+
};
|
|
253
206
|
},
|
|
254
207
|
|
|
255
|
-
async findUserByToken(plainToken
|
|
208
|
+
async findUserByToken(plainToken: string): Promise<User> {
|
|
256
209
|
const token = await sha256(plainToken);
|
|
257
210
|
|
|
258
211
|
if (token) {
|
|
@@ -307,14 +260,18 @@ export const configureUsersModule = async ({
|
|
|
307
260
|
|
|
308
261
|
// Transformations
|
|
309
262
|
primaryEmail(user: User): Email {
|
|
310
|
-
return (user.emails || []).
|
|
263
|
+
return (user.emails || []).toSorted(
|
|
311
264
|
(left, right) => Number(right.verified) - Number(left.verified),
|
|
312
265
|
)?.[0];
|
|
313
266
|
},
|
|
314
267
|
|
|
315
268
|
userLocale(user: User): Intl.Locale {
|
|
316
269
|
if (!user?.lastLogin?.locale) return systemLocale;
|
|
317
|
-
|
|
270
|
+
try {
|
|
271
|
+
return new Intl.Locale(user.lastLogin.locale);
|
|
272
|
+
} catch {
|
|
273
|
+
return systemLocale;
|
|
274
|
+
}
|
|
318
275
|
},
|
|
319
276
|
|
|
320
277
|
// Mutations
|
|
@@ -403,7 +360,9 @@ export const configureUsersModule = async ({
|
|
|
403
360
|
return userId;
|
|
404
361
|
},
|
|
405
362
|
|
|
406
|
-
async hashPassword(password) {
|
|
363
|
+
async hashPassword(password: string): Promise<{
|
|
364
|
+
pbkdf2: string;
|
|
365
|
+
}> {
|
|
407
366
|
const salt = pbkdf2.generateSalt();
|
|
408
367
|
const hashedPassword = await pbkdf2.getDerivedKey(salt, password);
|
|
409
368
|
return { pbkdf2: `${salt}:${hashedPassword}` };
|
|
@@ -534,12 +493,12 @@ export const configureUsersModule = async ({
|
|
|
534
493
|
});
|
|
535
494
|
},
|
|
536
495
|
|
|
537
|
-
async setPassword(userId: string, plainPassword: string) {
|
|
496
|
+
async setPassword(userId: string, plainPassword: string): Promise<User> {
|
|
538
497
|
if (!(await userSettings.validatePassword(plainPassword))) {
|
|
539
498
|
throw new Error(`Password ***** is invalid`, { cause: 'PASSWORD_INVALID' });
|
|
540
499
|
}
|
|
541
500
|
const password = plainPassword || crypto.randomUUID().split('-').pop();
|
|
542
|
-
await Users.
|
|
501
|
+
const user = await Users.findOneAndUpdate(
|
|
543
502
|
{ _id: userId },
|
|
544
503
|
{
|
|
545
504
|
$set: {
|
|
@@ -549,11 +508,28 @@ export const configureUsersModule = async ({
|
|
|
549
508
|
),
|
|
550
509
|
},
|
|
551
510
|
},
|
|
511
|
+
{ returnDocument: 'after' },
|
|
552
512
|
);
|
|
553
|
-
const user = await Users.findOne({ _id: userId }, {});
|
|
554
513
|
await emit('USER_UPDATE_PASSWORD', {
|
|
555
514
|
user: removeConfidentialServiceHashes(user),
|
|
556
515
|
});
|
|
516
|
+
return user;
|
|
517
|
+
},
|
|
518
|
+
|
|
519
|
+
async resetPassword(token: string, newPassword: string): Promise<User> {
|
|
520
|
+
const resetToken = await this.findResetToken(token);
|
|
521
|
+
if (!resetToken) return null;
|
|
522
|
+
const updatedUser = await this.setPassword(resetToken.userId, newPassword);
|
|
523
|
+
if (updatedUser) {
|
|
524
|
+
// Now invalidate the reset token
|
|
525
|
+
await emit('USER_ACCOUNT_ACTION', {
|
|
526
|
+
action: 'password-resetted',
|
|
527
|
+
userId: updatedUser._id,
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
await this.verifyEmail(updatedUser._id, resetToken.address);
|
|
531
|
+
}
|
|
532
|
+
return updatedUser;
|
|
557
533
|
},
|
|
558
534
|
|
|
559
535
|
updateAvatar: async (_id: string, fileId: string): Promise<User> => {
|
|
@@ -835,3 +811,5 @@ export const configureUsersModule = async ({
|
|
|
835
811
|
},
|
|
836
812
|
};
|
|
837
813
|
};
|
|
814
|
+
|
|
815
|
+
export type UsersModule = Awaited<ReturnType<typeof configureUsersModule>>;
|
|
@@ -1,29 +1,8 @@
|
|
|
1
1
|
import { ModuleInput } from '@unchainedshop/mongodb';
|
|
2
|
-
import {
|
|
3
|
-
Fido2Lib,
|
|
4
|
-
PublicKeyCredentialCreationOptions,
|
|
5
|
-
PublicKeyCredentialRequestOptions,
|
|
6
|
-
} from 'fido2-lib';
|
|
7
2
|
import { createLogger } from '@unchainedshop/logger';
|
|
8
3
|
import { WebAuthnCredentialsCreationRequestsCollection } from '../db/WebAuthnCredentialsCreationRequestsCollection.js';
|
|
9
4
|
|
|
10
|
-
|
|
11
|
-
findMDSMetadataForAAGUID: (aaguid: string) => Promise<any>;
|
|
12
|
-
|
|
13
|
-
createCredentialCreationOptions: (
|
|
14
|
-
origin: string,
|
|
15
|
-
username: string,
|
|
16
|
-
extensionOptions?: any,
|
|
17
|
-
) => Promise<any>;
|
|
18
|
-
verifyCredentialCreation: (username: string, credentials: any) => Promise<any>;
|
|
19
|
-
|
|
20
|
-
createCredentialRequestOptions: (
|
|
21
|
-
origin: string,
|
|
22
|
-
username?: string,
|
|
23
|
-
extensionOptions?: any,
|
|
24
|
-
) => Promise<any>;
|
|
25
|
-
verifyCredentialRequest: (userPublicKeys: any[], username: string, credentials: any) => Promise<any>;
|
|
26
|
-
}
|
|
5
|
+
import type { PublicKeyCredentialCreationOptions, PublicKeyCredentialRequestOptions } from 'fido2-lib';
|
|
27
6
|
|
|
28
7
|
const logger = createLogger('unchained:core-users');
|
|
29
8
|
|
|
@@ -39,6 +18,9 @@ const setupMDSCollection = async () => {
|
|
|
39
18
|
try {
|
|
40
19
|
const tocResult = await fetch('https://mds.fidoalliance.org');
|
|
41
20
|
const tocBase64 = await tocResult.text();
|
|
21
|
+
// eslint-disable-next-line
|
|
22
|
+
// @ts-ignore
|
|
23
|
+
const { Fido2Lib } = await import('fido2-lib');
|
|
42
24
|
const mc = (Fido2Lib as any).createMdsCollection('FIDO MDS v3'); // createMdsCollection exists but not typed in official package!
|
|
43
25
|
const tocObj = await mc.addToc(tocBase64);
|
|
44
26
|
return tocObj.entries;
|
|
@@ -65,24 +47,30 @@ export function buf2hex(buffer) {
|
|
|
65
47
|
.join('');
|
|
66
48
|
}
|
|
67
49
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
50
|
+
const initF2L = async () => {
|
|
51
|
+
try {
|
|
52
|
+
const thisDomain = new URL(ROOT_URL).hostname;
|
|
53
|
+
const { Fido2Lib } = await import('fido2-lib');
|
|
54
|
+
const f2l = new Fido2Lib({
|
|
55
|
+
rpId: thisDomain,
|
|
56
|
+
rpName: EMAIL_WEBSITE_NAME,
|
|
57
|
+
rpIcon:
|
|
58
|
+
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAARCSURBVHgB7Ve9UxNbFL8fm7yA4ktegHnlMk98dMbOMnR2D6pnGTqtCH+BWFoBpZXQaaelFbGzEztHHF26DKizzijDx+Zef+fu3vXuJgsxztjonQnZOZw993d+5zOM/epHslFO1a/KS/UbuvTnETsKQ/YDh3+XNi72yuVlzVgbL1YhCaOTk2ssDAI24hHDKsrpfxZkufwCj6t0OUAEBMkrlR6wHzjDMVCd872yemeete7gczeKoh2wQTICsyi0DrTWVc55eHqwu8OGPEMCMNSby5hS89H7Nx0Se9NXVvF1J69O7MDwJsKzgfCcmSP9SQhvxcV6S5cnummCHYVH4kJ9DE9NfHx1+HGLxKo0sSOkvIXHSnyz7mjOySufdLmUN3Vp4slZiSoyXk7NrhHVQui1fGzhzTq+QsZ5U07NtowQ3oH2FavDhXjS2389w7W+lrDgI2+2yfaZAEpT/y5LopjztnEkf1F6GXsc38TvWKO9g91NfO3EBGgjpxzooTosCISvXQjAm7zc1Fyvm7IChRHQM+uVcxGBAbstI84bVcqyUE3lAMyVWkrky0UsuGUYRqeni4zQk1cAk15ELxMYy07OqElKqo5ieVWWymsDAbhKGa9Qatag8P5omcQiUEot9nmb1c9cFkm5FJPJWqWp2UYegKkCMf7XHjRaeGwgs+9T1iPTA8ibkM/h5ev4XwVJthEd7D7kF+ozANRI9B9RlpO+lUO/IcZqz0jGPr8PUUFU7k185mwFZUIwBAuGUqWUob8nxCqLQ0H6qbeOnNmQGftOBVHO9QEw+ozZRKLE8y0wxDxFLISIE6n7KsDfjUS8kBoludZbCYBvl8XNaCMPLAOASsde5vYAlFPbeqWV+q/Pq5xR6HRS6zkWzPzIlXdmGKWXFaM/V06zwAGQaVppSJ3yzk7DAqpcb7UQD/q8cuRoxb75dtlxm5Zb3mzALEj7O7I/zWRUBRurHcO7G9Sw5MXJT+rLh+dmRlSqe6iOm1aOC/5nBELr2yTDOw3YO4Z+x3icq7iB01BMXm4j4dbIO+rtVi6nr7xLBg0tIjN20mGGbBPdVs+8J07mvcjzkbnbGf14tNNeYcb4wJVMH358jpo2zSf1lgCM1V4m6CsFXiUG9Iref9txe4kQXgVT9jqXejNZaEKAvFu4D5ikStBjus3bJcPxdjALNE8OduetHep+yIsXGQcZf9wTxyusGwSFS2mCvmaMcn6Lj9d9OV77BGN7iOuCYQFeqcMPT43+peln2IraFH/EdsvuALDTTTthvE0tIRnvsc/x/8/diIq2HnsSFoKEhXUAWM6zYA5VwoDtaLiV7O85XypFy+hVnbRlhCXoo9zZHWkpGWY3/L613D25PZFzeVVxvZqs6ywSYKZ7/ro+2g8TOu6eSBXAGfWIio2z2n871GY8OgN06BcSfitQuZr1CxuQ3Zh/6qkk0/P3GeV8BeBCYUHc/WHIAAAAAElFTkSuQmCC',
|
|
59
|
+
challengeSize: 128,
|
|
60
|
+
attestation: 'none',
|
|
61
|
+
});
|
|
62
|
+
return f2l;
|
|
63
|
+
} catch {
|
|
64
|
+
logger.warn(`optional peer npm package 'fido2-lib' not installed, WebAuthn will not work`);
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
83
68
|
|
|
69
|
+
export const configureUsersWebAuthnModule = async ({ db }: ModuleInput<any>) => {
|
|
70
|
+
const WebAuthnCredentialsCreationRequests = await WebAuthnCredentialsCreationRequestsCollection(db);
|
|
71
|
+
const f2l = await initF2L();
|
|
84
72
|
return {
|
|
85
|
-
findMDSMetadataForAAGUID: async (aaguid) => {
|
|
73
|
+
findMDSMetadataForAAGUID: async (aaguid: string) => {
|
|
86
74
|
const mdsCollection = await fetchMDS();
|
|
87
75
|
const foundEntry = mdsCollection.find((entry) => {
|
|
88
76
|
return entry.aaguid === aaguid;
|
|
@@ -90,7 +78,13 @@ export const configureUsersWebAuthnModule = async ({
|
|
|
90
78
|
return foundEntry?.metadataStatement;
|
|
91
79
|
},
|
|
92
80
|
|
|
93
|
-
createCredentialCreationOptions: async (
|
|
81
|
+
createCredentialCreationOptions: async (
|
|
82
|
+
origin: string,
|
|
83
|
+
username: string,
|
|
84
|
+
extensionOptions?: any,
|
|
85
|
+
) => {
|
|
86
|
+
if (!f2l) return null;
|
|
87
|
+
|
|
94
88
|
const registrationOptions = await f2l.attestationOptions(extensionOptions);
|
|
95
89
|
const challenge = Buffer.from(registrationOptions.challenge).toString('base64');
|
|
96
90
|
const { insertedId } = await WebAuthnCredentialsCreationRequests.insertOne({
|
|
@@ -108,7 +102,13 @@ export const configureUsersWebAuthnModule = async ({
|
|
|
108
102
|
} as SerializedOptions<PublicKeyCredentialCreationOptions>;
|
|
109
103
|
},
|
|
110
104
|
|
|
111
|
-
createCredentialRequestOptions: async (
|
|
105
|
+
createCredentialRequestOptions: async (
|
|
106
|
+
origin: string,
|
|
107
|
+
username?: string,
|
|
108
|
+
extensionOptions?: any,
|
|
109
|
+
) => {
|
|
110
|
+
if (!f2l) return null;
|
|
111
|
+
|
|
112
112
|
const loginOptions = await f2l.assertionOptions(extensionOptions);
|
|
113
113
|
const challenge = Buffer.from(loginOptions.challenge).toString('base64');
|
|
114
114
|
const { insertedId } = await WebAuthnCredentialsCreationRequests.insertOne({
|
|
@@ -126,7 +126,9 @@ export const configureUsersWebAuthnModule = async ({
|
|
|
126
126
|
} as SerializedOptions<PublicKeyCredentialRequestOptions>;
|
|
127
127
|
},
|
|
128
128
|
|
|
129
|
-
verifyCredentialCreation: async (username, credentials) => {
|
|
129
|
+
verifyCredentialCreation: async (username: string, credentials: any) => {
|
|
130
|
+
if (!f2l) return null;
|
|
131
|
+
|
|
130
132
|
const request = await WebAuthnCredentialsCreationRequests.findOne(
|
|
131
133
|
{
|
|
132
134
|
username,
|
|
@@ -173,7 +175,9 @@ export const configureUsersWebAuthnModule = async ({
|
|
|
173
175
|
return { publicKey, counter, id: credentials.id, aaguid, created: new Date() };
|
|
174
176
|
},
|
|
175
177
|
|
|
176
|
-
verifyCredentialRequest: async (userPublicKeys, username, credentials) => {
|
|
178
|
+
verifyCredentialRequest: async (userPublicKeys: any[], username: string, credentials: any) => {
|
|
179
|
+
if (!f2l) return null;
|
|
180
|
+
|
|
177
181
|
const request = await WebAuthnCredentialsCreationRequests.findOne(
|
|
178
182
|
{
|
|
179
183
|
_id: credentials.requestId,
|
|
@@ -192,7 +196,7 @@ export const configureUsersWebAuthnModule = async ({
|
|
|
192
196
|
return credentials.id === publicCredentials.id;
|
|
193
197
|
}) || {};
|
|
194
198
|
|
|
195
|
-
if (!publicKey)
|
|
199
|
+
if (!publicKey) return null;
|
|
196
200
|
|
|
197
201
|
const assertionExpectations = {
|
|
198
202
|
challenge: request.challenge,
|
package/src/module/pbkdf2.ts
CHANGED
|
@@ -4,7 +4,7 @@ const PBKDF2_KEY_LENGTH = 256; // Bytes
|
|
|
4
4
|
const PBKDF2_SALT_LENGTH = 16; // Bytes
|
|
5
5
|
|
|
6
6
|
export function generateSalt(saltLength = PBKDF2_SALT_LENGTH) {
|
|
7
|
-
const array = new
|
|
7
|
+
const array = new Uint8Array(saltLength);
|
|
8
8
|
return Buffer.from(crypto.getRandomValues(array)).toString('hex');
|
|
9
9
|
}
|
|
10
10
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { User } from '../types.js';
|
|
2
1
|
import { removeConfidentialServiceHashes } from './configureUsersModule.js';
|
|
3
2
|
import user from '../../tests/mock/user-mock.js';
|
|
3
|
+
import { User } from '../db/UsersCollection.js';
|
|
4
4
|
|
|
5
5
|
describe('removeConfidentialServiceHashes', () => {
|
|
6
6
|
it('Should remove sensitive user credentials ', () => {
|
package/src/users-index.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
export * from './
|
|
1
|
+
export * from './db/UsersCollection.js';
|
|
2
|
+
export * from './db/WebAuthnCredentialsCreationRequestsCollection.js';
|
|
2
3
|
export * from './module/configureUsersModule.js';
|
|
4
|
+
export * from './module/configureUsersWebAuthnModule.js';
|
|
3
5
|
export * from './users-settings.js';
|
package/src/users-settings.ts
CHANGED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { ModuleInput, Address, Contact } from '@unchainedshop/mongodb';
|
|
2
|
-
import { User, UserQuery, Email, UserLastLogin, UserProfile, UserData } from '../types.js';
|
|
3
|
-
import { mongodb } from '@unchainedshop/mongodb';
|
|
4
|
-
import { SortOption } from '@unchainedshop/utils';
|
|
5
|
-
import { UserSettingsOptions } from '../users-settings.js';
|
|
6
|
-
import { UsersWebAuthnModule } from './configureUsersWebAuthnModule.js';
|
|
7
|
-
export type UsersModule = {
|
|
8
|
-
webAuthn: UsersWebAuthnModule;
|
|
9
|
-
count: (query: UserQuery) => Promise<number>;
|
|
10
|
-
findUserById: (userId: string) => Promise<User>;
|
|
11
|
-
findUserByToken: (hashedToken?: string) => Promise<User>;
|
|
12
|
-
findUserByResetToken: (token: string) => Promise<User>;
|
|
13
|
-
findUnverifiedEmailToken: (token: string) => Promise<{
|
|
14
|
-
userId: string;
|
|
15
|
-
address: string;
|
|
16
|
-
when: Date;
|
|
17
|
-
}>;
|
|
18
|
-
findUserByEmail(email: string): Promise<User>;
|
|
19
|
-
findUserByUsername(username: string): Promise<User>;
|
|
20
|
-
findUser: (selector: UserQuery & {
|
|
21
|
-
sort?: Array<SortOption>;
|
|
22
|
-
}, options?: mongodb.FindOptions) => Promise<User>;
|
|
23
|
-
findUsers: (query: UserQuery & {
|
|
24
|
-
sort?: Array<SortOption>;
|
|
25
|
-
limit?: number;
|
|
26
|
-
offset?: number;
|
|
27
|
-
}) => Promise<Array<User>>;
|
|
28
|
-
userExists: (query: {
|
|
29
|
-
userId: string;
|
|
30
|
-
}) => Promise<boolean>;
|
|
31
|
-
primaryEmail: (user: User) => Email;
|
|
32
|
-
userLocale: (user: User) => Intl.Locale;
|
|
33
|
-
createUser: (userData: UserData, options: {
|
|
34
|
-
skipMessaging?: boolean;
|
|
35
|
-
skipPasswordEnrollment?: boolean;
|
|
36
|
-
}) => Promise<string>;
|
|
37
|
-
addEmail: (userId: string, email: string) => Promise<void>;
|
|
38
|
-
removeEmail: (userId: string, email: string) => Promise<void>;
|
|
39
|
-
sendVerificationEmail: (userId: string, email: string) => Promise<void>;
|
|
40
|
-
sendResetPasswordEmail: (userId: string, email: string, isEnrollment?: boolean) => Promise<void>;
|
|
41
|
-
verifyEmail: (userId: string, email: string) => Promise<void>;
|
|
42
|
-
setUsername: (userId: string, newUsername: string) => Promise<void>;
|
|
43
|
-
setPassword: (userId: string, newPassword?: string) => Promise<void>;
|
|
44
|
-
verifyPassword: (hashObject: Record<string, string>, password: string) => Promise<boolean>;
|
|
45
|
-
addRoles: (userId: string, roles: Array<string>) => Promise<number>;
|
|
46
|
-
updateAvatar: (_id: string, fileId: string) => Promise<User>;
|
|
47
|
-
updateGuest: (user: User, guest: boolean) => Promise<void>;
|
|
48
|
-
updateHeartbeat: (userId: string, doc: UserLastLogin) => Promise<User>;
|
|
49
|
-
updateLastBillingAddress: (_id: string, lastAddress: Address) => Promise<User>;
|
|
50
|
-
updateLastContact: (_id: string, lastContact: Contact) => Promise<User>;
|
|
51
|
-
updateProfile: (_id: string, { profile, meta }: {
|
|
52
|
-
profile?: UserProfile;
|
|
53
|
-
meta?: any;
|
|
54
|
-
}) => Promise<User>;
|
|
55
|
-
delete: (params: {
|
|
56
|
-
userId: string;
|
|
57
|
-
}, context: UnchainedCore) => Promise<User>;
|
|
58
|
-
updateRoles: (_id: string, roles: Array<string>) => Promise<User>;
|
|
59
|
-
updateTags: (_id: string, tags: Array<string>) => Promise<User>;
|
|
60
|
-
updateUser: (selector: mongodb.Filter<User>, modifier: mongodb.UpdateFilter<User>, options: mongodb.FindOneAndUpdateOptions) => Promise<User>;
|
|
61
|
-
addPushSubscription: (userId: string, subscription: any, options?: {
|
|
62
|
-
userAgent: string;
|
|
63
|
-
unsubscribeFromOtherUsers: boolean;
|
|
64
|
-
}) => Promise<void>;
|
|
65
|
-
removePushSubscription: (userId: string, p256dh: string) => Promise<void>;
|
|
66
|
-
hashPassword(password: string): Promise<{
|
|
67
|
-
pbkdf2: string;
|
|
68
|
-
}>;
|
|
69
|
-
};
|
|
70
|
-
export declare const removeConfidentialServiceHashes: (rawUser: User) => User;
|
|
71
|
-
export declare const buildFindSelector: ({ includeGuests, queryString, ...rest }: UserQuery) => mongodb.Filter<User>;
|
|
72
|
-
export declare const configureUsersModule: ({ db, options, migrationRepository, }: ModuleInput<UserSettingsOptions>) => Promise<UsersModule>;
|
|
73
|
-
//# sourceMappingURL=configureUsersModule_BACKUP_57123.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"configureUsersModule_BACKUP_57123.d.ts","sourceRoot":"","sources":["../../src/module/configureUsersModule_BACKUP_57123.ts"],"names":[],"mappings":"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"}
|