@unchainedshop/core-users 5.0.0-alpha.1 → 5.0.0-alpha.11

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 (44) hide show
  1. package/README.md +9 -130
  2. package/lib/db/UsersCollection.js +51 -29
  3. package/lib/db/WebAuthnCredentialsCreationRequestsCollection.d.ts +1 -0
  4. package/lib/db/WebAuthnCredentialsCreationRequestsCollection.js +2 -5
  5. package/lib/migrations/20260625120200-normalize-profile-phone.d.ts +2 -0
  6. package/lib/migrations/20260625120200-normalize-profile-phone.js +52 -0
  7. package/lib/module/configureUsersModule.d.ts +17 -0
  8. package/lib/module/configureUsersModule.js +119 -45
  9. package/lib/module/configureUsersWebAuthnModule.js +3 -1
  10. package/lib/users-settings.d.ts +1 -0
  11. package/lib/users-settings.js +4 -1
  12. package/package.json +10 -8
  13. package/lib/domain/model/Email.d.ts +0 -17
  14. package/lib/domain/model/Email.js +0 -21
  15. package/lib/domain/model/PasswordHash.d.ts +0 -16
  16. package/lib/domain/model/PasswordHash.js +0 -31
  17. package/lib/domain/model/PushSubscription.d.ts +0 -23
  18. package/lib/domain/model/PushSubscription.js +0 -25
  19. package/lib/domain/model/User.d.ts +0 -116
  20. package/lib/domain/model/User.js +0 -386
  21. package/lib/domain/model/UserProfile.d.ts +0 -14
  22. package/lib/domain/model/UserProfile.js +0 -29
  23. package/lib/domain/model/Web3Address.d.ts +0 -16
  24. package/lib/domain/model/Web3Address.js +0 -32
  25. package/lib/domain/model/WebAuthnCredential.d.ts +0 -22
  26. package/lib/domain/model/WebAuthnCredential.js +0 -28
  27. package/lib/domain/model/index.d.ts +0 -14
  28. package/lib/domain/model/index.js +0 -7
  29. package/lib/domain/repository/UserRepository.d.ts +0 -48
  30. package/lib/domain/repository/UserRepository.js +0 -1
  31. package/lib/domain/services/PasswordService.d.ts +0 -5
  32. package/lib/domain/services/PasswordService.js +0 -1
  33. package/lib/domain/services/TokenService.d.ts +0 -8
  34. package/lib/domain/services/TokenService.js +0 -1
  35. package/lib/domain/types.d.ts +0 -28
  36. package/lib/domain/types.js +0 -1
  37. package/lib/infrastructure/persistence/MongoUserRepository.d.ts +0 -4
  38. package/lib/infrastructure/persistence/MongoUserRepository.js +0 -159
  39. package/lib/infrastructure/persistence/buildUserSelector.d.ts +0 -4
  40. package/lib/infrastructure/persistence/buildUserSelector.js +0 -54
  41. package/lib/infrastructure/services/CryptoTokenService.d.ts +0 -2
  42. package/lib/infrastructure/services/CryptoTokenService.js +0 -16
  43. package/lib/infrastructure/services/Pbkdf2PasswordService.d.ts +0 -2
  44. package/lib/infrastructure/services/Pbkdf2PasswordService.js +0 -23
@@ -8,6 +8,7 @@ export const UserAccountAction = {
8
8
  };
9
9
  const defaultAutoMessagingAfterUserCreation = true;
10
10
  const defaultMergeUserCartsOnLogin = true;
11
+ const defaultGuestUserMaxAgeInDays = Number(process.env.UNCHAINED_GUEST_USER_EXPIRY_DAYS) || 30;
11
12
  const defaultEarliestValidTokenDate = () => {
12
13
  return new Date(new Date().getTime() - 1000 * 60 * 60);
13
14
  };
@@ -25,12 +26,13 @@ const defaultValidatePassword = async (password) => {
25
26
  export const userSettings = {
26
27
  autoMessagingAfterUserCreation: defaultAutoMessagingAfterUserCreation,
27
28
  mergeUserCartsOnLogin: defaultMergeUserCartsOnLogin,
29
+ guestUserMaxAgeInDays: defaultGuestUserMaxAgeInDays,
28
30
  earliestValidTokenDate: defaultEarliestValidTokenDate,
29
31
  validateNewUser: defaultValidateNewUser,
30
32
  validateEmail: () => Promise.resolve(true),
31
33
  validateUsername: () => Promise.resolve(true),
32
34
  validatePassword: () => Promise.resolve(true),
33
- configureSettings: ({ mergeUserCartsOnLogin, autoMessagingAfterUserCreation, earliestValidTokenDate, validateEmail, validateUsername, validateNewUser, validatePassword, }, db) => {
35
+ configureSettings: ({ mergeUserCartsOnLogin, autoMessagingAfterUserCreation, guestUserMaxAgeInDays, earliestValidTokenDate, validateEmail, validateUsername, validateNewUser, validatePassword, }, db) => {
34
36
  const defaultValidateEmail = async (rawEmail) => {
35
37
  if (!rawEmail?.includes?.('@'))
36
38
  return false;
@@ -54,6 +56,7 @@ export const userSettings = {
54
56
  userSettings.mergeUserCartsOnLogin = mergeUserCartsOnLogin ?? defaultMergeUserCartsOnLogin;
55
57
  userSettings.autoMessagingAfterUserCreation =
56
58
  autoMessagingAfterUserCreation ?? defaultAutoMessagingAfterUserCreation;
59
+ userSettings.guestUserMaxAgeInDays = guestUserMaxAgeInDays ?? defaultGuestUserMaxAgeInDays;
57
60
  userSettings.earliestValidTokenDate = earliestValidTokenDate || defaultEarliestValidTokenDate;
58
61
  userSettings.validateEmail = validateEmail || defaultValidateEmail;
59
62
  userSettings.validateUsername = validateUsername || defaultValidateUsername;
package/package.json CHANGED
@@ -1,7 +1,10 @@
1
1
  {
2
2
  "name": "@unchainedshop/core-users",
3
3
  "description": "User management module for the Unchained Engine with authentication support",
4
- "version": "5.0.0-alpha.1",
4
+ "version": "5.0.0-alpha.11",
5
+ "files": [
6
+ "lib"
7
+ ],
5
8
  "main": "lib/users-index.js",
6
9
  "types": "lib/users-index.d.ts",
7
10
  "type": "module",
@@ -36,11 +39,10 @@
36
39
  "homepage": "https://github.com/unchainedshop/unchained#readme",
37
40
  "dependencies": {
38
41
  "@passwordless-id/webauthn": "^2.3.1",
39
- "@unchainedshop/events": "^5.0.0-alpha.1",
40
- "@unchainedshop/logger": "^5.0.0-alpha.1",
41
- "@unchainedshop/mongodb": "^5.0.0-alpha.1",
42
- "@unchainedshop/roles": "^5.0.0-alpha.1",
43
- "@unchainedshop/utils": "^5.0.0-alpha.1",
42
+ "@unchainedshop/events": "^5.0.0-alpha.11",
43
+ "@unchainedshop/logger": "^5.0.0-alpha.11",
44
+ "@unchainedshop/mongodb": "^5.0.0-alpha.11",
45
+ "@unchainedshop/utils": "^5.0.0-alpha.11",
44
46
  "bcryptjs": "^3.0.2"
45
47
  },
46
48
  "peerDependencies": {
@@ -58,7 +60,7 @@
58
60
  "devDependencies": {
59
61
  "@noble/curves": "^2.0.0",
60
62
  "@noble/hashes": "^2.0.0",
61
- "@types/node": "^25.0.0",
62
- "typescript": "^5.8.3"
63
+ "@types/node": "^26.2.0",
64
+ "typescript": "^6.0.3"
63
65
  }
64
66
  }
@@ -1,17 +0,0 @@
1
- export interface Email {
2
- readonly address: string;
3
- verified: boolean;
4
- }
5
- export declare const Email: {
6
- create(address: string, verified?: boolean): Email;
7
- fromData(data: {
8
- address: string;
9
- verified: boolean;
10
- }): Email;
11
- isValid(address: string): boolean;
12
- equals(a: Email, b: Email): boolean;
13
- toData(email: Email): {
14
- address: string;
15
- verified: boolean;
16
- };
17
- };
@@ -1,21 +0,0 @@
1
- export const Email = {
2
- create(address, verified = false) {
3
- const normalized = address.trim().toLowerCase();
4
- if (!Email.isValid(normalized)) {
5
- throw new Error(`Invalid email address: ${address}`);
6
- }
7
- return { address: normalized, verified };
8
- },
9
- fromData(data) {
10
- return { address: data.address, verified: data.verified };
11
- },
12
- isValid(address) {
13
- return typeof address === 'string' && address.includes('@') && address.length >= 3;
14
- },
15
- equals(a, b) {
16
- return a.address.toLowerCase() === b.address.toLowerCase();
17
- },
18
- toData(email) {
19
- return { address: email.address, verified: email.verified };
20
- },
21
- };
@@ -1,16 +0,0 @@
1
- export interface PasswordHash {
2
- pbkdf2?: string;
3
- bcrypt?: string;
4
- }
5
- export declare const PasswordHash: {
6
- create(pbkdf2Hash: string): PasswordHash;
7
- fromLegacyBcrypt(bcryptHash: string): PasswordHash;
8
- fromData(data: {
9
- pbkdf2?: string;
10
- bcrypt?: string;
11
- }): PasswordHash;
12
- isEmpty(hash: PasswordHash | undefined): boolean;
13
- isLegacyBcrypt(hash: PasswordHash): boolean;
14
- getSalt(hash: PasswordHash): string | undefined;
15
- getHash(hash: PasswordHash): string | undefined;
16
- };
@@ -1,31 +0,0 @@
1
- export const PasswordHash = {
2
- create(pbkdf2Hash) {
3
- return { pbkdf2: pbkdf2Hash };
4
- },
5
- fromLegacyBcrypt(bcryptHash) {
6
- return { bcrypt: bcryptHash };
7
- },
8
- fromData(data) {
9
- return { ...data };
10
- },
11
- isEmpty(hash) {
12
- if (!hash)
13
- return true;
14
- return !hash.pbkdf2 && !hash.bcrypt;
15
- },
16
- isLegacyBcrypt(hash) {
17
- return !!hash.bcrypt && !hash.pbkdf2;
18
- },
19
- getSalt(hash) {
20
- if (!hash.pbkdf2)
21
- return undefined;
22
- const [salt] = hash.pbkdf2.split(':');
23
- return salt;
24
- },
25
- getHash(hash) {
26
- if (!hash.pbkdf2)
27
- return undefined;
28
- const [, hashValue] = hash.pbkdf2.split(':');
29
- return hashValue;
30
- },
31
- };
@@ -1,23 +0,0 @@
1
- export interface PushSubscription {
2
- userAgent: string;
3
- endpoint: string;
4
- expirationTime: number;
5
- keys: {
6
- auth: string;
7
- p256dh: string;
8
- };
9
- }
10
- export declare const PushSubscription: {
11
- create(data: {
12
- userAgent: string;
13
- endpoint: string;
14
- expirationTime?: number;
15
- keys: {
16
- auth: string;
17
- p256dh: string;
18
- };
19
- }): PushSubscription;
20
- fromData(data: PushSubscription): PushSubscription;
21
- equals(a: PushSubscription, b: PushSubscription): boolean;
22
- matchesKey(subscription: PushSubscription, p256dh: string): boolean;
23
- };
@@ -1,25 +0,0 @@
1
- export const PushSubscription = {
2
- create(data) {
3
- if (!data.endpoint || !data.keys?.p256dh || !data.keys?.auth) {
4
- throw new Error('Push subscription requires endpoint and keys');
5
- }
6
- return {
7
- userAgent: data.userAgent,
8
- endpoint: data.endpoint,
9
- expirationTime: data.expirationTime ?? 0,
10
- keys: {
11
- auth: data.keys.auth,
12
- p256dh: data.keys.p256dh,
13
- },
14
- };
15
- },
16
- fromData(data) {
17
- return { ...data };
18
- },
19
- equals(a, b) {
20
- return a.keys.p256dh === b.keys.p256dh;
21
- },
22
- matchesKey(subscription, p256dh) {
23
- return subscription.keys.p256dh === p256dh;
24
- },
25
- };
@@ -1,116 +0,0 @@
1
- import { type Email } from './Email.ts';
2
- import { type UserProfile } from './UserProfile.ts';
3
- import { type Web3Address } from './Web3Address.ts';
4
- import { type WebAuthnCredential } from './WebAuthnCredential.ts';
5
- import { type PasswordHash } from './PasswordHash.ts';
6
- import { type PushSubscription } from './PushSubscription.ts';
7
- import type { Address, Contact, UserLastLogin, TokenRecord } from '../types.ts';
8
- export interface UserData {
9
- _id: string;
10
- emails: Email[];
11
- username?: string;
12
- profile?: UserProfile;
13
- roles: string[];
14
- tags: string[];
15
- guest: boolean;
16
- initialPassword: boolean;
17
- avatarId?: string;
18
- lastBillingAddress?: Address;
19
- lastContact?: Contact;
20
- lastLogin?: UserLastLogin;
21
- pushSubscriptions: PushSubscription[];
22
- meta?: Record<string, unknown>;
23
- created: Date;
24
- updated?: Date;
25
- deleted?: Date | null;
26
- services: {
27
- password?: PasswordHash;
28
- web3?: Web3Address[];
29
- webAuthn?: WebAuthnCredential[];
30
- token?: {
31
- secret: string;
32
- };
33
- email?: {
34
- verificationTokens: TokenRecord[];
35
- };
36
- };
37
- }
38
- export interface CreateUserParams {
39
- _id?: string;
40
- email?: string;
41
- username?: string;
42
- password?: PasswordHash;
43
- webAuthnCredential?: WebAuthnCredential;
44
- roles?: string[];
45
- tags?: string[];
46
- guest?: boolean;
47
- initialPassword?: boolean;
48
- profile?: Partial<UserProfile>;
49
- meta?: Record<string, unknown>;
50
- pushSubscriptions?: PushSubscription[];
51
- }
52
- export interface User {
53
- readonly id: string;
54
- readonly _id: string;
55
- readonly emails: Email[];
56
- readonly username: string | undefined;
57
- readonly profile: UserProfile | undefined;
58
- readonly roles: string[];
59
- readonly tags: string[];
60
- readonly guest: boolean;
61
- readonly initialPassword: boolean;
62
- readonly avatarId: string | undefined;
63
- readonly lastBillingAddress: Address | undefined;
64
- readonly lastContact: Contact | undefined;
65
- readonly lastLogin: UserLastLogin | undefined;
66
- readonly pushSubscriptions: PushSubscription[];
67
- readonly meta: Record<string, unknown> | undefined;
68
- readonly created: Date;
69
- readonly updated: Date | undefined;
70
- readonly deleted: Date | null | undefined;
71
- readonly services: UserData['services'];
72
- readonly isDeleted: boolean;
73
- primaryEmail(): Email | undefined;
74
- hasVerifiedEmail(): boolean;
75
- hasPassword(): boolean;
76
- getPasswordHash(): PasswordHash | undefined;
77
- findWeb3Address(address: string): Web3Address | undefined;
78
- getWebAuthnCredentials(): WebAuthnCredential[];
79
- findEmailByAddress(address: string): Email | undefined;
80
- addEmail(email: Email): void;
81
- removeEmail(address: string): void;
82
- markEmailVerified(address: string): boolean;
83
- setUsername(username: string): void;
84
- setProfile(profile: Partial<UserProfile>): void;
85
- setMeta(meta: Record<string, unknown>): void;
86
- setAvatar(fileId: string): void;
87
- setPassword(passwordHash: PasswordHash): void;
88
- setRoles(roles: string[]): void;
89
- addRoles(roles: string[]): void;
90
- setTags(tags: string[]): void;
91
- setGuest(isGuest: boolean): void;
92
- updateLastLogin(loginInfo: Omit<UserLastLogin, 'timestamp'>): void;
93
- updateLastBillingAddress(address: Address, updateDisplayName?: boolean): void;
94
- updateLastContact(contact: Contact, updatePhoneMobile?: boolean): void;
95
- addWeb3Address(address: string): string;
96
- removeWeb3Address(address: string): boolean;
97
- verifyWeb3Address(address: string): boolean;
98
- addWebAuthnCredential(credential: WebAuthnCredential): void;
99
- removeWebAuthnCredential(credentialId: string): boolean;
100
- updateWebAuthnCounter(credentialId: string, counter: number): boolean;
101
- addPushSubscription(subscription: PushSubscription): boolean;
102
- removePushSubscription(p256dh: string): boolean;
103
- setAccessToken(hashedToken: string): void;
104
- addVerificationToken(token: TokenRecord): void;
105
- addResetToken(token: TokenRecord): void;
106
- removeResetToken(hashedToken: string): void;
107
- clearVerificationTokens(address: string): void;
108
- markAsDeleted(): void;
109
- readonly isDirty: boolean;
110
- readonly changes: readonly string[];
111
- clearChanges(): void;
112
- toData(): UserData;
113
- toPublicData(): Omit<UserData, 'services'>;
114
- }
115
- export declare const createUser: (params: CreateUserParams) => User;
116
- export declare const reconstituteUser: (data: UserData) => User;