@brightchain/brightchain-api-lib 0.27.1 → 0.29.0

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 (105) hide show
  1. package/package.json +5 -5
  2. package/src/index.d.ts +0 -1
  3. package/src/index.d.ts.map +1 -1
  4. package/src/index.js +0 -1
  5. package/src/index.js.map +1 -1
  6. package/src/lib/application.d.ts +0 -5
  7. package/src/lib/application.d.ts.map +1 -1
  8. package/src/lib/application.js +0 -6
  9. package/src/lib/application.js.map +1 -1
  10. package/src/lib/controllers/api/brightpass.d.ts +2 -0
  11. package/src/lib/controllers/api/brightpass.d.ts.map +1 -1
  12. package/src/lib/controllers/api/brightpass.js +51 -1
  13. package/src/lib/controllers/api/brightpass.js.map +1 -1
  14. package/src/lib/controllers/api/quorum.d.ts.map +1 -1
  15. package/src/lib/controllers/api/quorum.js +7 -3
  16. package/src/lib/controllers/api/quorum.js.map +1 -1
  17. package/src/lib/controllers/api/user.d.ts +17 -31
  18. package/src/lib/controllers/api/user.d.ts.map +1 -1
  19. package/src/lib/controllers/api/user.js +58 -697
  20. package/src/lib/controllers/api/user.js.map +1 -1
  21. package/src/lib/enumerations/schema-collection.d.ts +1 -29
  22. package/src/lib/enumerations/schema-collection.d.ts.map +1 -1
  23. package/src/lib/enumerations/schema-collection.js +2 -30
  24. package/src/lib/enumerations/schema-collection.js.map +1 -1
  25. package/src/lib/interfaces/auth-credentials.d.ts +1 -5
  26. package/src/lib/interfaces/auth-credentials.d.ts.map +1 -1
  27. package/src/lib/interfaces/auth-token.d.ts +1 -5
  28. package/src/lib/interfaces/auth-token.d.ts.map +1 -1
  29. package/src/lib/interfaces/brightchain-init-result.d.ts +6 -11
  30. package/src/lib/interfaces/brightchain-init-result.d.ts.map +1 -1
  31. package/src/lib/interfaces/brightchain-init-result.js +0 -6
  32. package/src/lib/interfaces/brightchain-init-result.js.map +1 -1
  33. package/src/lib/interfaces/responses/api-backup-codes-response.d.ts +1 -3
  34. package/src/lib/interfaces/responses/api-backup-codes-response.d.ts.map +1 -1
  35. package/src/lib/interfaces/responses/api-code-count-response.d.ts +1 -3
  36. package/src/lib/interfaces/responses/api-code-count-response.d.ts.map +1 -1
  37. package/src/lib/interfaces/responses/api-login-response.d.ts +1 -3
  38. package/src/lib/interfaces/responses/api-login-response.d.ts.map +1 -1
  39. package/src/lib/interfaces/responses/api-password-change-response.d.ts +1 -3
  40. package/src/lib/interfaces/responses/api-password-change-response.d.ts.map +1 -1
  41. package/src/lib/interfaces/responses/api-recovery-response.d.ts +1 -3
  42. package/src/lib/interfaces/responses/api-recovery-response.d.ts.map +1 -1
  43. package/src/lib/interfaces/responses/api-request-user-response.d.ts +1 -3
  44. package/src/lib/interfaces/responses/api-request-user-response.d.ts.map +1 -1
  45. package/src/lib/interfaces/token-payload.d.ts +1 -8
  46. package/src/lib/interfaces/token-payload.d.ts.map +1 -1
  47. package/src/lib/interfaces/userApiResponse.d.ts +1 -16
  48. package/src/lib/interfaces/userApiResponse.d.ts.map +1 -1
  49. package/src/lib/middlewares.d.ts.map +1 -1
  50. package/src/lib/middlewares.js +1 -0
  51. package/src/lib/middlewares.js.map +1 -1
  52. package/src/lib/routers/api.d.ts +18 -6
  53. package/src/lib/routers/api.d.ts.map +1 -1
  54. package/src/lib/routers/api.js +20 -12
  55. package/src/lib/routers/api.js.map +1 -1
  56. package/src/lib/routers/app.d.ts +16 -32
  57. package/src/lib/routers/app.d.ts.map +1 -1
  58. package/src/lib/routers/app.js +23 -120
  59. package/src/lib/routers/app.js.map +1 -1
  60. package/src/lib/secureEnclaveKeyring.js +2 -2
  61. package/src/lib/secureEnclaveKeyring.js.map +1 -1
  62. package/src/lib/services/auth.d.ts +15 -20
  63. package/src/lib/services/auth.d.ts.map +1 -1
  64. package/src/lib/services/auth.js +45 -211
  65. package/src/lib/services/auth.js.map +1 -1
  66. package/src/lib/services/brighthub/feedService.d.ts.map +1 -1
  67. package/src/lib/services/brighthub/feedService.js +1 -0
  68. package/src/lib/services/brighthub/feedService.js.map +1 -1
  69. package/src/lib/services/brighthub/messagingService.test-helpers.js +3 -36
  70. package/src/lib/services/brighthub/messagingService.test-helpers.js.map +1 -1
  71. package/src/lib/services/brighthub/notificationService.test-helpers.js +3 -36
  72. package/src/lib/services/brighthub/notificationService.test-helpers.js.map +1 -1
  73. package/src/lib/services/brighthub/postService.d.ts.map +1 -1
  74. package/src/lib/services/brighthub/postService.js +4 -0
  75. package/src/lib/services/brighthub/postService.js.map +1 -1
  76. package/src/lib/services/brighthub/threadService.d.ts.map +1 -1
  77. package/src/lib/services/brighthub/threadService.js +2 -0
  78. package/src/lib/services/brighthub/threadService.js.map +1 -1
  79. package/src/lib/services/brightpass.d.ts +18 -2
  80. package/src/lib/services/brightpass.d.ts.map +1 -1
  81. package/src/lib/services/brightpass.js +31 -5
  82. package/src/lib/services/brightpass.js.map +1 -1
  83. package/src/lib/services/emailGateway/emailGatewayService.d.ts.map +1 -1
  84. package/src/lib/services/emailGateway/emailGatewayService.js +2 -1
  85. package/src/lib/services/emailGateway/emailGatewayService.js.map +1 -1
  86. package/src/lib/services/index.d.ts +1 -0
  87. package/src/lib/services/index.d.ts.map +1 -1
  88. package/src/lib/services/index.js +1 -0
  89. package/src/lib/services/index.js.map +1 -1
  90. package/src/lib/services/mnemonic-hmac.service.d.ts +35 -0
  91. package/src/lib/services/mnemonic-hmac.service.d.ts.map +1 -0
  92. package/src/lib/services/mnemonic-hmac.service.js +47 -0
  93. package/src/lib/services/mnemonic-hmac.service.js.map +1 -0
  94. package/src/lib/services/role.d.ts +11 -11
  95. package/src/lib/services/role.d.ts.map +1 -1
  96. package/src/lib/services/role.js +17 -5
  97. package/src/lib/services/role.js.map +1 -1
  98. package/src/lib/stores/diskBlockStore.js +1 -34
  99. package/src/lib/stores/diskBlockStore.js.map +1 -1
  100. package/src/lib/systemKeyring.js +2 -35
  101. package/src/lib/systemKeyring.js.map +1 -1
  102. package/src/lib/utils/type-converters.d.ts +0 -7
  103. package/src/lib/utils/type-converters.d.ts.map +0 -1
  104. package/src/lib/utils/type-converters.js +0 -42
  105. package/src/lib/utils/type-converters.js.map +0 -1
@@ -3,12 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.UserController = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const brightchain_lib_1 = require("@brightchain/brightchain-lib");
6
- const ecies_lib_1 = require("@digitaldefiance/ecies-lib");
7
6
  const i18n_lib_1 = require("@digitaldefiance/i18n-lib");
8
7
  const node_express_suite_1 = require("@digitaldefiance/node-express-suite");
8
+ const node_express_suite_2 = require("@brightchain/node-express-suite");
9
9
  const suite_core_lib_1 = require("@digitaldefiance/suite-core-lib");
10
- const crypto_1 = require("crypto");
11
- const userValidation_1 = require("../../validation/userValidation");
12
10
  /** Extract the member ID from req.user, preferring memberId over id. */
13
11
  function getUserId(user) {
14
12
  const id = user.memberId ?? user.id;
@@ -16,315 +14,87 @@ function getUserId(user) {
16
14
  throw new Error('No member ID on request user');
17
15
  return id;
18
16
  }
19
- let UserController = class UserController extends node_express_suite_1.DecoratorBaseController {
17
+ /**
18
+ * BrightChain domain-specific UserController.
19
+ *
20
+ * Extends BrightDbUserController with:
21
+ * - BrightHub social profile creation on registration
22
+ * - Backup code routes (generate, regenerate, get count, recover-with-backup)
23
+ * - Direct challenge verification route (ECIES signature-based login)
24
+ */
25
+ let UserController = class UserController extends node_express_suite_2.BrightDbUserController {
20
26
  constructor(application) {
27
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
21
28
  super(application);
22
29
  }
23
- async register(req, _res, _next) {
24
- const validation = (0, userValidation_1.validateRegistration)(req.body);
25
- if (!validation.valid) {
26
- return {
27
- statusCode: 400,
28
- response: {
29
- message: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Validation_MissingValidatedData),
30
- errors: validation.errors,
31
- },
32
- };
33
- }
34
- try {
35
- const { username, email, password } = req.body;
36
- const authService = this.application.services.get('auth');
37
- const result = await authService.register(username, email, new ecies_lib_1.SecureString(password));
38
- // Create BrightHub social profile for the new user
39
- try {
40
- const userProfileService = this.application.services.get('userProfileService');
41
- if (userProfileService) {
42
- await userProfileService.createProfileForUser(result.memberId, username);
43
- }
44
- }
45
- catch {
46
- // Non-fatal: profile can be created lazily if this fails
47
- }
48
- const authResponse = {
49
- token: result.token,
50
- memberId: result.memberId,
51
- energyBalance: result.energyBalance,
52
- };
53
- return {
54
- statusCode: 201,
55
- response: {
56
- message: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Registration_Success),
57
- data: authResponse,
58
- },
59
- };
60
- }
61
- catch (error) {
62
- const errorMessage = error instanceof Error
63
- ? error.message
64
- : (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Common_UnexpectedError);
65
- return {
66
- statusCode: 400,
67
- response: {
68
- message: errorMessage,
69
- error: errorMessage,
70
- },
71
- };
72
- }
73
- }
74
- async login(req, _res, _next) {
75
- const validation = (0, userValidation_1.validateLogin)(req.body);
76
- if (!validation.valid) {
77
- return {
78
- statusCode: 400,
79
- response: {
80
- message: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Validation_MissingValidatedData),
81
- errors: validation.errors,
82
- },
83
- };
84
- }
85
- try {
86
- const { username, password } = req.body;
87
- const authService = this.application.services.get('auth');
88
- const result = await authService.login({
89
- username,
90
- password: new ecies_lib_1.SecureString(password),
91
- });
92
- const authResponse = {
93
- token: result.token,
94
- memberId: result.memberId,
95
- energyBalance: result.energyBalance,
96
- };
97
- return {
98
- statusCode: 200,
99
- response: {
100
- message: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.LoggedIn_Success),
101
- data: authResponse,
102
- },
103
- };
104
- }
105
- catch {
106
- return {
107
- statusCode: 401,
108
- response: {
109
- message: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Validation_InvalidCredentials),
110
- error: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Validation_InvalidCredentials),
111
- },
112
- };
113
- }
114
- }
115
- async getProfile(req, _res, _next) {
116
- const user = req.user;
117
- if (!user) {
118
- throw new i18n_lib_1.HandleableError(new Error((0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Common_NoUserOnRequest)), { statusCode: 401 });
119
- }
120
- try {
121
- const memberId = getUserId(user);
122
- const sp = brightchain_lib_1.ServiceProvider.getInstance();
123
- const typedId = sp.idProvider.idFromString(memberId);
124
- const idRawBytes = sp.idProvider.toBytes(typedId);
125
- const memberChecksum = sp.checksumService.calculateChecksum(idRawBytes);
126
- const energyStore = this.application.services.get('energyStore');
127
- const energyAccount = await energyStore.getOrCreate(memberChecksum);
128
- let email = '';
129
- const memberStore = this.application.services.get('memberStore');
130
- try {
131
- if (memberStore) {
132
- const member = await memberStore.getMember(typedId);
133
- email = member.email.toString();
134
- }
135
- }
136
- catch {
137
- // Member lookup failed, continue with empty email
138
- }
139
- let memberProfile;
140
- try {
141
- if (memberStore) {
142
- const profile = await memberStore.getMemberProfile(typedId);
143
- if (profile.publicProfile) {
144
- memberProfile = {
145
- status: profile.publicProfile.status,
146
- storageQuota: profile.publicProfile.storageQuota?.toString(),
147
- storageUsed: profile.publicProfile.storageUsed?.toString(),
148
- lastActive: profile.publicProfile.lastActive?.toISOString(),
149
- dateCreated: profile.publicProfile.dateCreated?.toISOString(),
150
- };
151
- }
152
- }
153
- }
154
- catch {
155
- // MemberStore profile not available
156
- }
157
- const userProfile = {
158
- memberId,
159
- username: user.username,
160
- email,
161
- energyBalance: energyAccount.balance,
162
- availableBalance: energyAccount.availableBalance,
163
- earned: energyAccount.earned,
164
- spent: energyAccount.spent,
165
- reserved: energyAccount.reserved,
166
- reputation: energyAccount.reputation,
167
- createdAt: energyAccount.createdAt.toISOString(),
168
- lastUpdated: energyAccount.lastUpdated.toISOString(),
169
- ...(memberProfile && { profile: memberProfile }),
170
- };
171
- return {
172
- statusCode: 200,
173
- response: {
174
- message: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Settings_RetrievedSuccess),
175
- data: userProfile,
176
- },
177
- };
178
- }
179
- catch {
180
- return {
181
- statusCode: 500,
182
- response: {
183
- message: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Common_UnexpectedError),
184
- error: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Common_UnexpectedError),
185
- },
186
- };
187
- }
188
- }
189
- async updateProfile(req, _res, _next) {
190
- const user = req.user;
191
- if (!user) {
192
- throw new i18n_lib_1.HandleableError(new Error((0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Common_NoUserOnRequest)), { statusCode: 401 });
193
- }
30
+ /**
31
+ * Hook called after successful registration.
32
+ * Creates a BrightHub social profile for the new user.
33
+ */
34
+ async onPostRegister(memberId, username) {
194
35
  try {
195
- const memberId = getUserId(user);
196
- const updateData = req.body;
197
- const sp = brightchain_lib_1.ServiceProvider.getInstance();
198
- const typedId = sp.idProvider.idFromString(memberId);
199
- const idRawBytes = sp.idProvider.toBytes(typedId);
200
- const memberChecksum = sp.checksumService.calculateChecksum(idRawBytes);
201
- const memberStore = this.application.services.get('memberStore');
202
- if (memberStore && updateData.settings) {
203
- const completeSettings = {
204
- autoReplication: updateData.settings.autoReplication ?? true,
205
- minRedundancy: updateData.settings.minRedundancy ?? 3,
206
- preferredRegions: updateData.settings.preferredRegions ?? [],
207
- };
208
- await memberStore.updateMember(typedId, {
209
- id: typedId,
210
- privateChanges: {
211
- settings: completeSettings,
212
- },
213
- });
214
- }
215
- const energyStore = this.application.services.get('energyStore');
216
- const energyAccount = await energyStore.getOrCreate(memberChecksum);
217
- let email = '';
218
- try {
219
- if (memberStore) {
220
- const member = await memberStore.getMember(typedId);
221
- email = member.email.toString();
222
- }
223
- }
224
- catch {
225
- // Member lookup failed
36
+ const userProfileService = this.application.services.get('userProfileService');
37
+ if (userProfileService) {
38
+ await userProfileService.createProfileForUser(memberId, username);
226
39
  }
227
- let memberProfile;
228
- try {
229
- if (memberStore) {
230
- const profile = await memberStore.getMemberProfile(typedId);
231
- if (profile.publicProfile) {
232
- memberProfile = {
233
- status: profile.publicProfile.status,
234
- storageQuota: profile.publicProfile.storageQuota?.toString(),
235
- storageUsed: profile.publicProfile.storageUsed?.toString(),
236
- lastActive: profile.publicProfile.lastActive?.toISOString(),
237
- dateCreated: profile.publicProfile.dateCreated?.toISOString(),
238
- };
239
- }
240
- }
241
- }
242
- catch {
243
- // MemberStore profile not available
244
- }
245
- const userProfile = {
246
- memberId,
247
- username: user.username,
248
- email,
249
- energyBalance: energyAccount.balance,
250
- availableBalance: energyAccount.availableBalance,
251
- earned: energyAccount.earned,
252
- spent: energyAccount.spent,
253
- reserved: energyAccount.reserved,
254
- reputation: energyAccount.reputation,
255
- createdAt: energyAccount.createdAt.toISOString(),
256
- lastUpdated: energyAccount.lastUpdated.toISOString(),
257
- ...(memberProfile && { profile: memberProfile }),
258
- };
259
- return {
260
- statusCode: 200,
261
- response: {
262
- message: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Settings_SaveSuccess),
263
- data: userProfile,
264
- },
265
- };
266
40
  }
267
41
  catch {
268
- return {
269
- statusCode: 500,
270
- response: {
271
- message: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Common_UnexpectedError),
272
- error: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Common_UnexpectedError),
273
- },
274
- };
42
+ // Non-fatal: profile can be created lazily if this fails
275
43
  }
276
44
  }
277
- async changePassword(req, _res, _next) {
278
- const validation = (0, userValidation_1.validatePasswordChange)(req.body);
279
- if (!validation.valid) {
280
- return {
281
- statusCode: 400,
282
- response: {
283
- message: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Validation_MissingValidatedData),
284
- errors: validation.errors,
285
- },
286
- };
287
- }
288
- const user = req.user;
289
- if (!user) {
290
- throw new i18n_lib_1.HandleableError(new Error((0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Common_NoUserOnRequest)), { statusCode: 401 });
291
- }
45
+ async directChallenge(req, _res, _next) {
46
+ const { challenge, signature, username, email } = req.body;
292
47
  try {
293
- const memberId = getUserId(user);
294
- const { currentPassword, newPassword } = req.body;
295
- const sp = brightchain_lib_1.ServiceProvider.getInstance();
296
- const typedId = sp.idProvider.idFromString(memberId);
297
48
  const authService = this.application.services.get('auth');
298
- await authService.changePassword(typedId, currentPassword, newPassword);
299
- return {
300
- statusCode: 200,
301
- response: {
302
- message: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.PasswordChange_Success),
303
- data: {
304
- memberId,
305
- success: true,
49
+ const { member, memberId, userDTO } = await authService.verifyDirectLoginChallenge(String(challenge), String(signature), username ? String(username) : undefined, email ? String(email) : undefined);
50
+ const token = authService.signToken(memberId, member.name, member.type);
51
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
52
+ const env = this.application.environment;
53
+ const serverPublicKey = env.systemPublicKeyHex ?? '';
54
+ const response = {
55
+ message: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.LoggedIn_Success),
56
+ user: userDTO ?? {
57
+ id: memberId,
58
+ username: member.name,
59
+ email: member.email.toString(),
60
+ roles: [],
61
+ rolePrivileges: {
62
+ admin: false,
63
+ member: true,
64
+ child: false,
65
+ system: false,
306
66
  },
67
+ emailVerified: true,
68
+ timezone: 'UTC',
69
+ siteLanguage: 'en',
70
+ darkMode: false,
71
+ currency: 'USD',
72
+ directChallenge: false,
307
73
  },
74
+ token,
75
+ serverPublicKey,
308
76
  };
77
+ return { statusCode: 200, response };
309
78
  }
310
79
  catch (error) {
311
- const errorMessage = error instanceof Error
312
- ? error.message
313
- : (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Error_PasswordChange);
314
- if (errorMessage === 'Invalid credentials') {
80
+ const msg = error instanceof Error ? error.message : 'Unexpected error';
81
+ if (msg === 'Challenge expired' ||
82
+ msg === 'Invalid challenge' ||
83
+ msg === 'Invalid credentials' ||
84
+ msg === 'Challenge already used') {
315
85
  return {
316
86
  statusCode: 401,
317
87
  response: {
318
88
  message: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Validation_InvalidCredentials),
319
- error: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Validation_InvalidCredentials),
89
+ error: msg,
320
90
  },
321
91
  };
322
92
  }
323
93
  return {
324
94
  statusCode: 500,
325
95
  response: {
326
- message: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Error_PasswordChange),
327
- error: errorMessage,
96
+ message: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Common_UnexpectedError),
97
+ error: msg,
328
98
  },
329
99
  };
330
100
  }
@@ -471,375 +241,14 @@ let UserController = class UserController extends node_express_suite_1.Decorator
471
241
  };
472
242
  }
473
243
  }
474
- async recover(req, _res, _next) {
475
- const validation = (0, userValidation_1.validateRecovery)(req.body);
476
- if (!validation.valid) {
477
- return {
478
- statusCode: 400,
479
- response: {
480
- message: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Validation_MissingValidatedData),
481
- errors: validation.errors,
482
- },
483
- };
484
- }
485
- try {
486
- const { email, mnemonic, newPassword } = req.body;
487
- const authService = this.application.services.get('auth');
488
- const result = await authService.recoverWithMnemonic(email, new ecies_lib_1.SecureString(mnemonic), newPassword);
489
- return {
490
- statusCode: 200,
491
- response: {
492
- message: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.MnemonicRecovery_Success),
493
- data: result,
494
- },
495
- };
496
- }
497
- catch (error) {
498
- const errorMessage = error instanceof Error
499
- ? error.message
500
- : (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Common_UnexpectedError);
501
- if (errorMessage === 'Invalid credentials' ||
502
- errorMessage === 'Invalid mnemonic') {
503
- return {
504
- statusCode: 401,
505
- response: {
506
- message: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Validation_InvalidCredentials),
507
- error: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Validation_InvalidCredentials),
508
- },
509
- };
510
- }
511
- return {
512
- statusCode: 500,
513
- response: {
514
- message: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Common_UnexpectedError),
515
- error: errorMessage,
516
- },
517
- };
518
- }
519
- }
520
- async logout(req, _res, _next) {
521
- const user = req.user;
522
- if (!user) {
523
- throw new i18n_lib_1.HandleableError(new Error((0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Common_NoUserOnRequest)), { statusCode: 401 });
524
- }
525
- try {
526
- const authHeader = String(req.headers
527
- ?.authorization ?? '');
528
- if (!authHeader.startsWith('Bearer ')) {
529
- return {
530
- statusCode: 401,
531
- response: {
532
- message: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Validation_TokenMissing),
533
- error: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Validation_TokenMissing),
534
- },
535
- };
536
- }
537
- const token = authHeader.slice('Bearer '.length);
538
- const sessionAdapter = this.application.services.get('sessionAdapter');
539
- const session = await sessionAdapter.validateToken(token);
540
- if (session) {
541
- await sessionAdapter.deleteSession(session.sessionId);
542
- }
543
- return {
544
- statusCode: 200,
545
- response: {
546
- message: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Common_Success),
547
- },
548
- };
549
- }
550
- catch {
551
- return {
552
- statusCode: 500,
553
- response: {
554
- message: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Common_UnexpectedError),
555
- error: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Common_UnexpectedError),
556
- },
557
- };
558
- }
559
- }
560
- async requestDirectLogin(_req, _res, _next) {
561
- const systemUser = node_express_suite_1.SystemUserService.getSystemUser(
562
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
563
- this.application.environment, this.application.constants);
564
- const time = Buffer.alloc(8);
565
- time.writeBigUInt64BE(BigInt(new Date().getTime()));
566
- const nonce = (0, crypto_1.randomBytes)(32);
567
- const signature = systemUser.sign(Buffer.concat([time, nonce]));
568
- const challenge = Buffer.concat([time, nonce, signature]).toString('hex');
569
- return {
570
- statusCode: 200,
571
- response: {
572
- challenge,
573
- message: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Login_ChallengeGenerated),
574
- serverPublicKey: this.application.environment.systemPublicKeyHex ?? '',
575
- },
576
- };
577
- }
578
- async directChallenge(req, _res, _next) {
579
- const { challenge, signature, username, email } = req.body;
580
- try {
581
- const authService = this.application.services.get('auth');
582
- const { member, memberId, userDTO } = await authService.verifyDirectLoginChallenge(String(challenge), String(signature), username ? String(username) : undefined, email ? String(email) : undefined);
583
- const token = authService.signToken(memberId, member.name, member.type);
584
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
585
- const env = this.application.environment;
586
- const serverPublicKey = env.systemPublicKeyHex ?? '';
587
- const response = {
588
- message: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.LoggedIn_Success),
589
- user: userDTO ?? {
590
- id: memberId,
591
- username: member.name,
592
- email: member.email.toString(),
593
- roles: [],
594
- rolePrivileges: {
595
- admin: false,
596
- member: true,
597
- child: false,
598
- system: false,
599
- },
600
- emailVerified: true,
601
- timezone: 'UTC',
602
- siteLanguage: 'en',
603
- darkMode: false,
604
- currency: 'USD',
605
- directChallenge: false,
606
- },
607
- token,
608
- serverPublicKey,
609
- };
610
- return { statusCode: 200, response };
611
- }
612
- catch (error) {
613
- const msg = error instanceof Error ? error.message : 'Unexpected error';
614
- if (msg === 'Challenge expired' ||
615
- msg === 'Invalid challenge' ||
616
- msg === 'Invalid credentials' ||
617
- msg === 'Challenge already used') {
618
- return {
619
- statusCode: 401,
620
- response: {
621
- message: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Validation_InvalidCredentials),
622
- error: msg,
623
- },
624
- };
625
- }
626
- return {
627
- statusCode: 500,
628
- response: {
629
- message: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Common_UnexpectedError),
630
- error: msg,
631
- },
632
- };
633
- }
634
- }
635
- /**
636
- * GET /verify — returns the authenticated user's DTO.
637
- * The auth middleware already populates req.user with a full IRequestUserDTO
638
- * via buildRequestUserDTO, so we just return it.
639
- */
640
- async verify(req, _res, _next) {
641
- if (!req.user) {
642
- throw new i18n_lib_1.HandleableError(new Error((0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Common_NoUserOnRequest)), { statusCode: 401 });
643
- }
644
- const user = req.user;
645
- return {
646
- statusCode: 200,
647
- response: {
648
- message: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Validation_TokenValid),
649
- user: {
650
- id: user.id,
651
- email: user.email,
652
- username: user.username,
653
- roles: user.roles || [],
654
- rolePrivileges: user.rolePrivileges,
655
- timezone: user.timezone,
656
- currency: user.currency,
657
- emailVerified: user.emailVerified,
658
- darkMode: user.darkMode,
659
- siteLanguage: user.siteLanguage,
660
- directChallenge: user.directChallenge,
661
- ...(user.lastLogin && { lastLogin: user.lastLogin }),
662
- },
663
- },
664
- };
665
- }
666
- /**
667
- * GET /settings — returns the authenticated user's settings.
668
- */
669
- async getSettings(req, _res, _next) {
670
- if (!req.user) {
671
- throw new i18n_lib_1.HandleableError(new Error((0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Common_NoUserOnRequest)), { statusCode: 401 });
672
- }
673
- const user = req.user;
674
- return {
675
- statusCode: 200,
676
- response: {
677
- message: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Settings_RetrievedSuccess),
678
- settings: {
679
- email: user.email || '',
680
- timezone: user.timezone || '',
681
- currency: user.currency || '',
682
- siteLanguage: user.siteLanguage || '',
683
- darkMode: user.darkMode || false,
684
- directChallenge: user.directChallenge || false,
685
- },
686
- },
687
- };
688
- }
689
- /**
690
- * POST /settings — updates the authenticated user's settings.
691
- */
692
- async updateSettings(req, _res, _next) {
693
- if (!req.user) {
694
- throw new i18n_lib_1.HandleableError(new Error((0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Common_NoUserOnRequest)), { statusCode: 401 });
695
- }
696
- try {
697
- const user = req.user;
698
- const memberId = user.id;
699
- const { email, timezone, siteLanguage, currency, darkMode, directChallenge, } = req.body;
700
- const sp = brightchain_lib_1.ServiceProvider.getInstance();
701
- const typedId = sp.idProvider.idFromString(memberId);
702
- const idHex = sp.idProvider.toString(typedId, 'hex');
703
- // Update user-facing settings (timezone, darkMode, etc.) in the DB
704
- // users collection. These are NOT part of the member store's
705
- // IPrivateMemberData.settings (which tracks replication/storage config).
706
- try {
707
- const db = this.application.services.get('db');
708
- if (db) {
709
- const usersCol = db.collection('user_settings');
710
- const updateFields = {};
711
- if (email !== undefined)
712
- updateFields['email'] = email;
713
- if (timezone !== undefined)
714
- updateFields['timezone'] = timezone;
715
- if (siteLanguage !== undefined)
716
- updateFields['siteLanguage'] = siteLanguage;
717
- if (currency !== undefined)
718
- updateFields['currency'] = currency;
719
- if (darkMode !== undefined)
720
- updateFields['darkMode'] = darkMode;
721
- if (directChallenge !== undefined)
722
- updateFields['directChallenge'] = directChallenge;
723
- if (Object.keys(updateFields).length > 0) {
724
- await usersCol.updateOne({ _id: idHex }, { $set: updateFields }, { upsert: true });
725
- }
726
- }
727
- }
728
- catch {
729
- // DB update is best-effort
730
- }
731
- // Build updated user DTO
732
- const updatedUser = {
733
- ...user,
734
- ...(email !== undefined && { email }),
735
- ...(timezone !== undefined && { timezone }),
736
- ...(siteLanguage !== undefined && { siteLanguage }),
737
- ...(currency !== undefined && { currency }),
738
- ...(darkMode !== undefined && { darkMode }),
739
- ...(directChallenge !== undefined && { directChallenge }),
740
- };
741
- return {
742
- statusCode: 200,
743
- response: {
744
- message: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Settings_SaveSuccess),
745
- user: updatedUser,
746
- },
747
- };
748
- }
749
- catch {
750
- return {
751
- statusCode: 500,
752
- response: {
753
- message: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Common_UnexpectedError),
754
- error: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Common_UnexpectedError),
755
- },
756
- };
757
- }
758
- }
759
- /**
760
- * GET /refresh-token — re-signs the JWT and returns a new token + user DTO.
761
- */
762
- async refreshToken(req, _res, _next) {
763
- if (!req.user) {
764
- throw new i18n_lib_1.HandleableError(new Error((0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Common_NoUserOnRequest)), { statusCode: 401 });
765
- }
766
- try {
767
- const user = req.user;
768
- const authService = this.application.services.get('auth');
769
- // Verify the current token is still valid
770
- const authHeader = String(req.headers
771
- ?.authorization ?? '');
772
- if (!authHeader.startsWith('Bearer ')) {
773
- return {
774
- statusCode: 401,
775
- response: {
776
- message: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Validation_TokenMissing),
777
- error: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Validation_TokenMissing),
778
- },
779
- };
780
- }
781
- const oldToken = authHeader.slice('Bearer '.length);
782
- const decoded = await authService.verifyToken(oldToken);
783
- // Re-sign with fresh expiry
784
- const newToken = authService.signToken(decoded.memberId, decoded.username, decoded.type);
785
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
786
- const env = this.application.environment;
787
- const serverPublicKey = env.systemPublicKeyHex ?? '';
788
- return {
789
- statusCode: 200,
790
- response: {
791
- message: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Common_Success),
792
- user,
793
- token: newToken,
794
- serverPublicKey,
795
- },
796
- headers: {
797
- Authorization: `Bearer ${newToken}`,
798
- },
799
- };
800
- }
801
- catch {
802
- return {
803
- statusCode: 500,
804
- response: {
805
- message: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Common_UnexpectedError),
806
- error: (0, suite_core_lib_1.getSuiteCoreTranslation)(suite_core_lib_1.SuiteCoreStringKey.Common_UnexpectedError),
807
- },
808
- };
809
- }
810
- }
811
244
  };
812
245
  exports.UserController = UserController;
813
246
  tslib_1.__decorate([
814
- (0, node_express_suite_1.Post)('/register'),
815
- tslib_1.__metadata("design:type", Function),
816
- tslib_1.__metadata("design:paramtypes", [Object, Object, Function]),
817
- tslib_1.__metadata("design:returntype", Promise)
818
- ], UserController.prototype, "register", null);
819
- tslib_1.__decorate([
820
- (0, node_express_suite_1.Post)('/login'),
821
- tslib_1.__metadata("design:type", Function),
822
- tslib_1.__metadata("design:paramtypes", [Object, Object, Function]),
823
- tslib_1.__metadata("design:returntype", Promise)
824
- ], UserController.prototype, "login", null);
825
- tslib_1.__decorate([
826
- (0, node_express_suite_1.Get)('/profile', { auth: true }),
827
- tslib_1.__metadata("design:type", Function),
828
- tslib_1.__metadata("design:paramtypes", [Object, Object, Function]),
829
- tslib_1.__metadata("design:returntype", Promise)
830
- ], UserController.prototype, "getProfile", null);
831
- tslib_1.__decorate([
832
- (0, node_express_suite_1.Put)('/profile', { auth: true }),
833
- tslib_1.__metadata("design:type", Function),
834
- tslib_1.__metadata("design:paramtypes", [Object, Object, Function]),
835
- tslib_1.__metadata("design:returntype", Promise)
836
- ], UserController.prototype, "updateProfile", null);
837
- tslib_1.__decorate([
838
- (0, node_express_suite_1.Post)('/change-password', { auth: true }),
247
+ (0, node_express_suite_1.Post)('/direct-challenge'),
839
248
  tslib_1.__metadata("design:type", Function),
840
249
  tslib_1.__metadata("design:paramtypes", [Object, Object, Function]),
841
250
  tslib_1.__metadata("design:returntype", Promise)
842
- ], UserController.prototype, "changePassword", null);
251
+ ], UserController.prototype, "directChallenge", null);
843
252
  tslib_1.__decorate([
844
253
  (0, node_express_suite_1.Post)('/backup-codes', { auth: true }),
845
254
  tslib_1.__metadata("design:type", Function),
@@ -864,54 +273,6 @@ tslib_1.__decorate([
864
273
  tslib_1.__metadata("design:paramtypes", [Object, Object, Function]),
865
274
  tslib_1.__metadata("design:returntype", Promise)
866
275
  ], UserController.prototype, "recoverWithBackupCode", null);
867
- tslib_1.__decorate([
868
- (0, node_express_suite_1.Post)('/recover'),
869
- tslib_1.__metadata("design:type", Function),
870
- tslib_1.__metadata("design:paramtypes", [Object, Object, Function]),
871
- tslib_1.__metadata("design:returntype", Promise)
872
- ], UserController.prototype, "recover", null);
873
- tslib_1.__decorate([
874
- (0, node_express_suite_1.Post)('/logout', { auth: true }),
875
- tslib_1.__metadata("design:type", Function),
876
- tslib_1.__metadata("design:paramtypes", [Object, Object, Function]),
877
- tslib_1.__metadata("design:returntype", Promise)
878
- ], UserController.prototype, "logout", null);
879
- tslib_1.__decorate([
880
- (0, node_express_suite_1.Post)('/request-direct-login'),
881
- tslib_1.__metadata("design:type", Function),
882
- tslib_1.__metadata("design:paramtypes", [Object, Object, Function]),
883
- tslib_1.__metadata("design:returntype", Promise)
884
- ], UserController.prototype, "requestDirectLogin", null);
885
- tslib_1.__decorate([
886
- (0, node_express_suite_1.Post)('/direct-challenge'),
887
- tslib_1.__metadata("design:type", Function),
888
- tslib_1.__metadata("design:paramtypes", [Object, Object, Function]),
889
- tslib_1.__metadata("design:returntype", Promise)
890
- ], UserController.prototype, "directChallenge", null);
891
- tslib_1.__decorate([
892
- (0, node_express_suite_1.Get)('/verify', { auth: true }),
893
- tslib_1.__metadata("design:type", Function),
894
- tslib_1.__metadata("design:paramtypes", [Object, Object, Function]),
895
- tslib_1.__metadata("design:returntype", Promise)
896
- ], UserController.prototype, "verify", null);
897
- tslib_1.__decorate([
898
- (0, node_express_suite_1.Get)('/settings', { auth: true }),
899
- tslib_1.__metadata("design:type", Function),
900
- tslib_1.__metadata("design:paramtypes", [Object, Object, Function]),
901
- tslib_1.__metadata("design:returntype", Promise)
902
- ], UserController.prototype, "getSettings", null);
903
- tslib_1.__decorate([
904
- (0, node_express_suite_1.Post)('/settings', { auth: true }),
905
- tslib_1.__metadata("design:type", Function),
906
- tslib_1.__metadata("design:paramtypes", [Object, Object, Function]),
907
- tslib_1.__metadata("design:returntype", Promise)
908
- ], UserController.prototype, "updateSettings", null);
909
- tslib_1.__decorate([
910
- (0, node_express_suite_1.Get)('/refresh-token', { auth: true }),
911
- tslib_1.__metadata("design:type", Function),
912
- tslib_1.__metadata("design:paramtypes", [Object, Object, Function]),
913
- tslib_1.__metadata("design:returntype", Promise)
914
- ], UserController.prototype, "refreshToken", null);
915
276
  exports.UserController = UserController = tslib_1.__decorate([
916
277
  (0, node_express_suite_1.Controller)(),
917
278
  tslib_1.__metadata("design:paramtypes", [Object])