@digitaldefiance/express-suite-react-components 5.0.0 → 5.0.2

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 (312) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +4 -0
  3. package/package.json +3 -4
  4. package/src/auth/AdminRoute.tsx +54 -0
  5. package/src/auth/Private.tsx +17 -0
  6. package/src/auth/PrivateRoute.tsx +28 -0
  7. package/src/auth/UnAuth.tsx +16 -0
  8. package/src/auth/UnAuthRoute.tsx +30 -0
  9. package/src/auth/{index.d.ts → index.ts} +2 -3
  10. package/src/components/ApiAccess.tsx +175 -0
  11. package/src/components/BackupCodeLoginForm.tsx +489 -0
  12. package/src/components/BackupCodesForm.tsx +287 -0
  13. package/src/components/CategoryMenu.tsx +155 -0
  14. package/src/components/ChangePasswordForm.tsx +273 -0
  15. package/src/components/ConfirmationDialog.tsx +48 -0
  16. package/src/components/CurrencyCodeSelector.tsx +60 -0
  17. package/src/components/CurrencyInput.tsx +80 -0
  18. package/src/components/DashboardPage.tsx +24 -0
  19. package/src/components/DropdownMenu.tsx +105 -0
  20. package/src/components/ExpirationSecondsSelector.tsx +60 -0
  21. package/src/components/FakeEmailAdminPanel.tsx +374 -0
  22. package/src/components/Flag.tsx +52 -0
  23. package/src/components/ForgotPasswordForm.tsx +174 -0
  24. package/src/components/LoginForm.tsx +468 -0
  25. package/src/components/LogoutPage.tsx +21 -0
  26. package/src/components/RegisterForm.tsx +1169 -0
  27. package/src/components/ResetPasswordForm.tsx +247 -0
  28. package/src/components/SideMenu.tsx +46 -0
  29. package/src/components/SideMenuListItem.tsx +75 -0
  30. package/src/components/T.tsx +24 -0
  31. package/src/components/TDiv.tsx +26 -0
  32. package/src/components/TImgAlt.tsx +28 -0
  33. package/src/components/TopMenu.tsx +252 -0
  34. package/src/components/TotpSetupForm.tsx +188 -0
  35. package/src/components/TotpVerificationForm.tsx +148 -0
  36. package/src/components/TranslatedTitle.tsx +31 -0
  37. package/src/components/UserLanguageSelector.tsx +45 -0
  38. package/src/components/UserMenu.tsx +15 -0
  39. package/src/components/UserSettingsForm.tsx +904 -0
  40. package/src/components/VerifyEmailPage.tsx +222 -0
  41. package/src/components/__tests__/RegisterForm.totp.spec.tsx +377 -0
  42. package/src/components/__tests__/TotpSetupForm.spec.tsx +258 -0
  43. package/src/components/__tests__/TotpVerificationForm.spec.tsx +259 -0
  44. package/src/components/__tests__/UserSettingsForm.totp.spec.tsx +396 -0
  45. package/src/components/{index.d.ts → index.ts} +1 -1
  46. package/src/contexts/AuthProvider.spec.tsx +1284 -0
  47. package/src/contexts/AuthProvider.tsx +977 -0
  48. package/src/contexts/I18nProvider.tsx +164 -0
  49. package/src/contexts/MenuContext.tsx +426 -0
  50. package/src/contexts/SuiteConfigProvider.tsx +93 -0
  51. package/src/contexts/ThemeProvider.tsx +67 -0
  52. package/src/contexts/{index.d.ts → index.ts} +0 -1
  53. package/src/hooks/{index.d.ts → index.ts} +0 -1
  54. package/src/hooks/useAuthenticatedApi.tsx +67 -0
  55. package/src/hooks/useBackupCodes.ts +105 -0
  56. package/src/hooks/useEmailVerification.ts +54 -0
  57. package/src/hooks/useExpiringValue.ts +78 -0
  58. package/src/hooks/useLocalStorage.ts +18 -0
  59. package/src/hooks/useUserSettings.ts +271 -0
  60. package/src/{index.d.ts → index.ts} +1 -1
  61. package/src/interfaces/IAppConfig.ts +7 -0
  62. package/src/interfaces/ICategoryConfig.ts +41 -0
  63. package/src/interfaces/IMenuConfig.ts +28 -0
  64. package/src/interfaces/IMenuOption.ts +79 -0
  65. package/src/interfaces/{index.d.ts → index.ts} +0 -1
  66. package/src/services/__mocks__/authService.ts +14 -0
  67. package/src/services/__tests__/authService.totp.property.spec.ts +360 -0
  68. package/src/services/__tests__/authService.totp.spec.ts +439 -0
  69. package/src/services/api.ts +13 -0
  70. package/src/services/authService.ts +692 -0
  71. package/src/services/authenticatedApi.ts +17 -0
  72. package/src/services/index.ts +3 -0
  73. package/src/types/MenuType.ts +15 -0
  74. package/src/types/expirationSeconds.ts +18 -0
  75. package/src/types/index.ts +1 -0
  76. package/src/types/translation.ts +20 -0
  77. package/src/wrappers/BackupCodeLoginWrapper.tsx +34 -0
  78. package/src/wrappers/BackupCodesWrapper.tsx +28 -0
  79. package/src/wrappers/ChangePasswordFormWrapper.tsx +34 -0
  80. package/src/wrappers/ForgotPasswordFormWrapper.tsx +45 -0
  81. package/src/wrappers/LoginFormWrapper.tsx +108 -0
  82. package/src/wrappers/LogoutPageWrapper.tsx +30 -0
  83. package/src/wrappers/RegisterFormWrapper.tsx +109 -0
  84. package/src/wrappers/ResetPasswordFormWrapper.tsx +57 -0
  85. package/src/wrappers/UserSettingsFormWrapper.tsx +140 -0
  86. package/src/wrappers/VerifyEmailPageWrapper.tsx +27 -0
  87. package/src/wrappers/__tests__/LoginFormWrapper.totp.spec.tsx +339 -0
  88. package/src/wrappers/{index.d.ts → index.tsx} +10 -1
  89. package/src/auth/AdminRoute.d.ts +0 -8
  90. package/src/auth/AdminRoute.d.ts.map +0 -1
  91. package/src/auth/AdminRoute.js +0 -28
  92. package/src/auth/Private.d.ts +0 -6
  93. package/src/auth/Private.d.ts.map +0 -1
  94. package/src/auth/Private.js +0 -14
  95. package/src/auth/PrivateRoute.d.ts +0 -8
  96. package/src/auth/PrivateRoute.d.ts.map +0 -1
  97. package/src/auth/PrivateRoute.js +0 -23
  98. package/src/auth/UnAuth.d.ts +0 -6
  99. package/src/auth/UnAuth.d.ts.map +0 -1
  100. package/src/auth/UnAuth.js +0 -14
  101. package/src/auth/UnAuthRoute.d.ts +0 -8
  102. package/src/auth/UnAuthRoute.d.ts.map +0 -1
  103. package/src/auth/UnAuthRoute.js +0 -22
  104. package/src/auth/index.d.ts.map +0 -1
  105. package/src/auth/index.js +0 -12
  106. package/src/components/ApiAccess.d.ts +0 -16
  107. package/src/components/ApiAccess.d.ts.map +0 -1
  108. package/src/components/ApiAccess.js +0 -77
  109. package/src/components/BackupCodeLoginForm.d.ts +0 -43
  110. package/src/components/BackupCodeLoginForm.d.ts.map +0 -1
  111. package/src/components/BackupCodeLoginForm.js +0 -139
  112. package/src/components/BackupCodesForm.d.ts +0 -26
  113. package/src/components/BackupCodesForm.d.ts.map +0 -1
  114. package/src/components/BackupCodesForm.js +0 -120
  115. package/src/components/CategoryMenu.d.ts +0 -17
  116. package/src/components/CategoryMenu.d.ts.map +0 -1
  117. package/src/components/CategoryMenu.js +0 -65
  118. package/src/components/ChangePasswordForm.d.ts +0 -26
  119. package/src/components/ChangePasswordForm.d.ts.map +0 -1
  120. package/src/components/ChangePasswordForm.js +0 -78
  121. package/src/components/ConfirmationDialog.d.ts +0 -13
  122. package/src/components/ConfirmationDialog.d.ts.map +0 -1
  123. package/src/components/ConfirmationDialog.js +0 -10
  124. package/src/components/CurrencyCodeSelector.d.ts +0 -9
  125. package/src/components/CurrencyCodeSelector.d.ts.map +0 -1
  126. package/src/components/CurrencyCodeSelector.js +0 -31
  127. package/src/components/CurrencyInput.d.ts +0 -13
  128. package/src/components/CurrencyInput.d.ts.map +0 -1
  129. package/src/components/CurrencyInput.js +0 -22
  130. package/src/components/DashboardPage.d.ts +0 -8
  131. package/src/components/DashboardPage.d.ts.map +0 -1
  132. package/src/components/DashboardPage.js +0 -10
  133. package/src/components/DropdownMenu.d.ts +0 -13
  134. package/src/components/DropdownMenu.d.ts.map +0 -1
  135. package/src/components/DropdownMenu.js +0 -63
  136. package/src/components/ExpirationSecondsSelector.d.ts +0 -13
  137. package/src/components/ExpirationSecondsSelector.d.ts.map +0 -1
  138. package/src/components/ExpirationSecondsSelector.js +0 -32
  139. package/src/components/FakeEmailAdminPanel.d.ts +0 -23
  140. package/src/components/FakeEmailAdminPanel.d.ts.map +0 -1
  141. package/src/components/FakeEmailAdminPanel.js +0 -147
  142. package/src/components/Flag.d.ts +0 -20
  143. package/src/components/Flag.d.ts.map +0 -1
  144. package/src/components/Flag.js +0 -43
  145. package/src/components/ForgotPasswordForm.d.ts +0 -18
  146. package/src/components/ForgotPasswordForm.d.ts.map +0 -1
  147. package/src/components/ForgotPasswordForm.js +0 -61
  148. package/src/components/LoginForm.d.ts +0 -44
  149. package/src/components/LoginForm.d.ts.map +0 -1
  150. package/src/components/LoginForm.js +0 -123
  151. package/src/components/LogoutPage.d.ts +0 -8
  152. package/src/components/LogoutPage.d.ts.map +0 -1
  153. package/src/components/LogoutPage.js +0 -16
  154. package/src/components/RegisterForm.d.ts +0 -100
  155. package/src/components/RegisterForm.d.ts.map +0 -1
  156. package/src/components/RegisterForm.js +0 -306
  157. package/src/components/ResetPasswordForm.d.ts +0 -23
  158. package/src/components/ResetPasswordForm.d.ts.map +0 -1
  159. package/src/components/ResetPasswordForm.js +0 -78
  160. package/src/components/SideMenu.d.ts +0 -8
  161. package/src/components/SideMenu.d.ts.map +0 -1
  162. package/src/components/SideMenu.js +0 -25
  163. package/src/components/SideMenuListItem.d.ts +0 -13
  164. package/src/components/SideMenuListItem.d.ts.map +0 -1
  165. package/src/components/SideMenuListItem.js +0 -44
  166. package/src/components/T.d.ts +0 -7
  167. package/src/components/T.d.ts.map +0 -1
  168. package/src/components/T.js +0 -13
  169. package/src/components/TDiv.d.ts +0 -9
  170. package/src/components/TDiv.d.ts.map +0 -1
  171. package/src/components/TDiv.js +0 -14
  172. package/src/components/TImgAlt.d.ts +0 -10
  173. package/src/components/TImgAlt.d.ts.map +0 -1
  174. package/src/components/TImgAlt.js +0 -14
  175. package/src/components/TopMenu.d.ts +0 -50
  176. package/src/components/TopMenu.d.ts.map +0 -1
  177. package/src/components/TopMenu.js +0 -79
  178. package/src/components/TotpSetupForm.d.ts +0 -24
  179. package/src/components/TotpSetupForm.d.ts.map +0 -1
  180. package/src/components/TotpSetupForm.js +0 -82
  181. package/src/components/TotpVerificationForm.d.ts +0 -23
  182. package/src/components/TotpVerificationForm.d.ts.map +0 -1
  183. package/src/components/TotpVerificationForm.js +0 -66
  184. package/src/components/TranslatedTitle.d.ts +0 -8
  185. package/src/components/TranslatedTitle.d.ts.map +0 -1
  186. package/src/components/TranslatedTitle.js +0 -15
  187. package/src/components/UserLanguageSelector.d.ts +0 -4
  188. package/src/components/UserLanguageSelector.d.ts.map +0 -1
  189. package/src/components/UserLanguageSelector.js +0 -31
  190. package/src/components/UserMenu.d.ts +0 -4
  191. package/src/components/UserMenu.d.ts.map +0 -1
  192. package/src/components/UserMenu.js +0 -12
  193. package/src/components/UserSettingsForm.d.ts +0 -113
  194. package/src/components/UserSettingsForm.d.ts.map +0 -1
  195. package/src/components/UserSettingsForm.js +0 -269
  196. package/src/components/VerifyEmailPage.d.ts +0 -24
  197. package/src/components/VerifyEmailPage.d.ts.map +0 -1
  198. package/src/components/VerifyEmailPage.js +0 -81
  199. package/src/components/index.d.ts.map +0 -1
  200. package/src/components/index.js +0 -35
  201. package/src/contexts/AuthProvider.d.ts +0 -186
  202. package/src/contexts/AuthProvider.d.ts.map +0 -1
  203. package/src/contexts/AuthProvider.js +0 -515
  204. package/src/contexts/I18nProvider.d.ts +0 -27
  205. package/src/contexts/I18nProvider.d.ts.map +0 -1
  206. package/src/contexts/I18nProvider.js +0 -60
  207. package/src/contexts/MenuContext.d.ts +0 -20
  208. package/src/contexts/MenuContext.d.ts.map +0 -1
  209. package/src/contexts/MenuContext.js +0 -275
  210. package/src/contexts/SuiteConfigProvider.d.ts +0 -44
  211. package/src/contexts/SuiteConfigProvider.d.ts.map +0 -1
  212. package/src/contexts/SuiteConfigProvider.js +0 -43
  213. package/src/contexts/ThemeProvider.d.ts +0 -15
  214. package/src/contexts/ThemeProvider.d.ts.map +0 -1
  215. package/src/contexts/ThemeProvider.js +0 -36
  216. package/src/contexts/index.d.ts.map +0 -1
  217. package/src/contexts/index.js +0 -8
  218. package/src/hooks/index.d.ts.map +0 -1
  219. package/src/hooks/index.js +0 -9
  220. package/src/hooks/useAuthenticatedApi.d.ts +0 -34
  221. package/src/hooks/useAuthenticatedApi.d.ts.map +0 -1
  222. package/src/hooks/useAuthenticatedApi.js +0 -46
  223. package/src/hooks/useBackupCodes.d.ts +0 -15
  224. package/src/hooks/useBackupCodes.d.ts.map +0 -1
  225. package/src/hooks/useBackupCodes.js +0 -74
  226. package/src/hooks/useEmailVerification.d.ts +0 -10
  227. package/src/hooks/useEmailVerification.d.ts.map +0 -1
  228. package/src/hooks/useEmailVerification.js +0 -40
  229. package/src/hooks/useExpiringValue.d.ts +0 -14
  230. package/src/hooks/useExpiringValue.d.ts.map +0 -1
  231. package/src/hooks/useExpiringValue.js +0 -53
  232. package/src/hooks/useLocalStorage.d.ts +0 -2
  233. package/src/hooks/useLocalStorage.d.ts.map +0 -1
  234. package/src/hooks/useLocalStorage.js +0 -15
  235. package/src/hooks/useUserSettings.d.ts +0 -48
  236. package/src/hooks/useUserSettings.d.ts.map +0 -1
  237. package/src/hooks/useUserSettings.js +0 -171
  238. package/src/index.d.ts.map +0 -1
  239. package/src/index.js +0 -12
  240. package/src/interfaces/IAppConfig.d.ts +0 -8
  241. package/src/interfaces/IAppConfig.d.ts.map +0 -1
  242. package/src/interfaces/IAppConfig.js +0 -2
  243. package/src/interfaces/ICategoryConfig.d.ts +0 -41
  244. package/src/interfaces/ICategoryConfig.d.ts.map +0 -1
  245. package/src/interfaces/ICategoryConfig.js +0 -2
  246. package/src/interfaces/IMenuConfig.d.ts +0 -28
  247. package/src/interfaces/IMenuConfig.d.ts.map +0 -1
  248. package/src/interfaces/IMenuConfig.js +0 -2
  249. package/src/interfaces/IMenuOption.d.ts +0 -82
  250. package/src/interfaces/IMenuOption.d.ts.map +0 -1
  251. package/src/interfaces/IMenuOption.js +0 -2
  252. package/src/interfaces/index.d.ts.map +0 -1
  253. package/src/interfaces/index.js +0 -7
  254. package/src/services/__mocks__/authService.d.ts +0 -21
  255. package/src/services/__mocks__/authService.d.ts.map +0 -1
  256. package/src/services/__mocks__/authService.js +0 -15
  257. package/src/services/api.d.ts +0 -3
  258. package/src/services/api.d.ts.map +0 -1
  259. package/src/services/api.js +0 -14
  260. package/src/services/authService.d.ts +0 -126
  261. package/src/services/authService.d.ts.map +0 -1
  262. package/src/services/authService.js +0 -471
  263. package/src/services/authenticatedApi.d.ts +0 -3
  264. package/src/services/authenticatedApi.d.ts.map +0 -1
  265. package/src/services/authenticatedApi.js +0 -18
  266. package/src/services/index.d.ts +0 -4
  267. package/src/services/index.d.ts.map +0 -1
  268. package/src/services/index.js +0 -6
  269. package/src/types/MenuType.d.ts +0 -11
  270. package/src/types/MenuType.d.ts.map +0 -1
  271. package/src/types/MenuType.js +0 -12
  272. package/src/types/expirationSeconds.d.ts +0 -3
  273. package/src/types/expirationSeconds.d.ts.map +0 -1
  274. package/src/types/expirationSeconds.js +0 -17
  275. package/src/types/index.d.ts +0 -2
  276. package/src/types/index.d.ts.map +0 -1
  277. package/src/types/index.js +0 -4
  278. package/src/types/translation.d.ts +0 -10
  279. package/src/types/translation.d.ts.map +0 -1
  280. package/src/types/translation.js +0 -9
  281. package/src/wrappers/BackupCodeLoginWrapper.d.ts +0 -8
  282. package/src/wrappers/BackupCodeLoginWrapper.d.ts.map +0 -1
  283. package/src/wrappers/BackupCodeLoginWrapper.js +0 -20
  284. package/src/wrappers/BackupCodesWrapper.d.ts +0 -7
  285. package/src/wrappers/BackupCodesWrapper.d.ts.map +0 -1
  286. package/src/wrappers/BackupCodesWrapper.js +0 -17
  287. package/src/wrappers/ChangePasswordFormWrapper.d.ts +0 -8
  288. package/src/wrappers/ChangePasswordFormWrapper.d.ts.map +0 -1
  289. package/src/wrappers/ChangePasswordFormWrapper.js +0 -21
  290. package/src/wrappers/ForgotPasswordFormWrapper.d.ts +0 -8
  291. package/src/wrappers/ForgotPasswordFormWrapper.d.ts.map +0 -1
  292. package/src/wrappers/ForgotPasswordFormWrapper.js +0 -29
  293. package/src/wrappers/LoginFormWrapper.d.ts +0 -21
  294. package/src/wrappers/LoginFormWrapper.d.ts.map +0 -1
  295. package/src/wrappers/LoginFormWrapper.js +0 -72
  296. package/src/wrappers/LogoutPageWrapper.d.ts +0 -9
  297. package/src/wrappers/LogoutPageWrapper.d.ts.map +0 -1
  298. package/src/wrappers/LogoutPageWrapper.js +0 -21
  299. package/src/wrappers/RegisterFormWrapper.d.ts +0 -20
  300. package/src/wrappers/RegisterFormWrapper.d.ts.map +0 -1
  301. package/src/wrappers/RegisterFormWrapper.js +0 -51
  302. package/src/wrappers/ResetPasswordFormWrapper.d.ts +0 -9
  303. package/src/wrappers/ResetPasswordFormWrapper.d.ts.map +0 -1
  304. package/src/wrappers/ResetPasswordFormWrapper.js +0 -34
  305. package/src/wrappers/UserSettingsFormWrapper.d.ts +0 -29
  306. package/src/wrappers/UserSettingsFormWrapper.d.ts.map +0 -1
  307. package/src/wrappers/UserSettingsFormWrapper.js +0 -95
  308. package/src/wrappers/VerifyEmailPageWrapper.d.ts +0 -8
  309. package/src/wrappers/VerifyEmailPageWrapper.d.ts.map +0 -1
  310. package/src/wrappers/VerifyEmailPageWrapper.js +0 -21
  311. package/src/wrappers/index.d.ts.map +0 -1
  312. package/src/wrappers/index.js +0 -24
@@ -0,0 +1,692 @@
1
+ // services/authService.js
2
+ import {
3
+ EciesCryptoCore,
4
+ ECIESService,
5
+ EmailString,
6
+ hexToUint8Array,
7
+ IECIESConfig,
8
+ SecureString,
9
+ uint8ArrayToHex,
10
+ } from '@digitaldefiance/ecies-lib';
11
+ import {
12
+ getSuiteCoreTranslation,
13
+ IConstants,
14
+ IRequestUserDTO,
15
+ SuiteCoreStringKey,
16
+ TranslatableSuiteError,
17
+ } from '@digitaldefiance/suite-core-lib';
18
+ import { Wallet } from '@ethereumjs/wallet';
19
+ import axios, { isAxiosError } from 'axios';
20
+ import { createApiClient } from './api';
21
+ import { createAuthenticatedApiClient } from './authenticatedApi';
22
+
23
+ // API Response Types
24
+ interface ApiErrorResponse {
25
+ message?: string;
26
+ error?: string | { message?: string; statusCode?: number; stack?: string };
27
+ errorType?: string;
28
+ field?: string;
29
+ errors?: Array<{ path: string; msg: string }>;
30
+ }
31
+
32
+ /**
33
+ * Extract a human-readable error string from an API error response.
34
+ * The backend may send `error` as either a plain string or an object
35
+ * with `{ message, statusCode, stack }`.
36
+ */
37
+ export function extractErrorMessage(
38
+ errorData: ApiErrorResponse
39
+ ): string | undefined {
40
+ if (typeof errorData.error === 'string') {
41
+ return errorData.error;
42
+ }
43
+ if (
44
+ typeof errorData.error === 'object' &&
45
+ errorData.error !== null &&
46
+ typeof errorData.error.message === 'string'
47
+ ) {
48
+ return errorData.error.message;
49
+ }
50
+ return errorData.message;
51
+ }
52
+
53
+ interface RegisterResponse {
54
+ message: string;
55
+ mnemonic?: string;
56
+ data?: {
57
+ token: string;
58
+ memberId: string;
59
+ energyBalance: number;
60
+ mnemonic?: string;
61
+ };
62
+ }
63
+
64
+ interface LoginResponse {
65
+ token?: string;
66
+ user?: IRequestUserDTO;
67
+ message?: string;
68
+ pendingTotpToken?: string;
69
+ }
70
+
71
+ interface ChallengeResponse {
72
+ challenge: string;
73
+ }
74
+
75
+ interface BackupCodeLoginResponse {
76
+ token: string;
77
+ user: IRequestUserDTO;
78
+ codeCount: number;
79
+ mnemonic?: string;
80
+ message?: string;
81
+ }
82
+
83
+ export class AuthService {
84
+ private eciesService: ECIESService;
85
+ private cryptoCore: EciesCryptoCore;
86
+ private apiClient: axios.AxiosInstance;
87
+ private authenticatedApiClient: axios.AxiosInstance;
88
+
89
+ constructor(
90
+ private constants: IConstants,
91
+ private baseUrl: string,
92
+ eciesConfig: IECIESConfig,
93
+ private readonly siteDomain: string
94
+ ) {
95
+ this.eciesService = new ECIESService(eciesConfig);
96
+ this.cryptoCore = new EciesCryptoCore(eciesConfig);
97
+ this.apiClient = createApiClient(this.baseUrl);
98
+ this.authenticatedApiClient = createAuthenticatedApiClient(this.baseUrl);
99
+ }
100
+
101
+ getSiteDomain(): string {
102
+ return this.siteDomain;
103
+ }
104
+
105
+ async register(
106
+ username: string,
107
+ email: string,
108
+ timezone: string,
109
+ password?: string,
110
+ mnemonic?: string,
111
+ displayName?: string,
112
+ directChallenge?: boolean
113
+ ): Promise<
114
+ | { success: boolean; message: string; mnemonic: string }
115
+ | {
116
+ error: string;
117
+ errorType?: string;
118
+ field?: string;
119
+ errors?: Array<Record<string, string>>;
120
+ }
121
+ > {
122
+ try {
123
+ const response = await this.apiClient.post<RegisterResponse>(
124
+ '/user/register',
125
+ {
126
+ username,
127
+ email,
128
+ timezone,
129
+ ...(password ? { password } : {}),
130
+ ...(mnemonic ? { mnemonic } : {}),
131
+ ...(displayName ? { displayName } : {}),
132
+ ...(directChallenge ? { directChallenge } : {}),
133
+ }
134
+ );
135
+ if (response.status !== 201) {
136
+ const errorData = response.data as unknown as ApiErrorResponse;
137
+ return {
138
+ error: errorData.message
139
+ ? errorData.message
140
+ : getSuiteCoreTranslation(SuiteCoreStringKey.Registration_Error),
141
+ ...(errorData.errorType ? { errorType: errorData.errorType } : {}),
142
+ ...(errorData.errors ? { errors: errorData.errors } : {}),
143
+ };
144
+ }
145
+ return {
146
+ success: true,
147
+ message:
148
+ response.data.message ??
149
+ getSuiteCoreTranslation(SuiteCoreStringKey.Registration_Success, {
150
+ MNEMONIC: response.data.data?.mnemonic ?? response.data.mnemonic,
151
+ }),
152
+ mnemonic: response.data.data?.mnemonic ?? response.data.mnemonic ?? '',
153
+ };
154
+ } catch (error) {
155
+ if (isAxiosError<ApiErrorResponse>(error) && error.response) {
156
+ const errorData = error.response.data;
157
+ return {
158
+ error:
159
+ extractErrorMessage(errorData) ??
160
+ (error as Error).message ??
161
+ getSuiteCoreTranslation(SuiteCoreStringKey.Common_UnexpectedError),
162
+ ...(errorData.errorType ? { errorType: errorData.errorType } : {}),
163
+ ...(errorData.field
164
+ ? { field: errorData.field }
165
+ : errorData.error
166
+ ? { field: 'general' }
167
+ : {}),
168
+ ...(errorData.errors ? { errors: errorData.errors } : {}),
169
+ };
170
+ } else {
171
+ return {
172
+ error: getSuiteCoreTranslation(
173
+ SuiteCoreStringKey.Common_UnexpectedError
174
+ ),
175
+ };
176
+ }
177
+ }
178
+ }
179
+
180
+ async directLogin(
181
+ mnemonic: SecureString,
182
+ username?: string,
183
+ email?: EmailString
184
+ ): Promise<
185
+ | { token: string; user: IRequestUserDTO; wallet: Wallet; message: string }
186
+ | { pendingTotpToken: string }
187
+ | { error: string; errorType?: string }
188
+ > {
189
+ if (!username && !email) {
190
+ return {
191
+ error: getSuiteCoreTranslation(
192
+ SuiteCoreStringKey.Validation_UsernameOrEmailRequired
193
+ ),
194
+ };
195
+ }
196
+ try {
197
+ const loginRequest = await this.apiClient.post<ChallengeResponse>(
198
+ '/user/request-direct-login'
199
+ );
200
+ if (loginRequest.data.challenge) {
201
+ const { wallet } = this.cryptoCore.walletAndSeedFromMnemonic(mnemonic);
202
+ const challengeBuffer = hexToUint8Array(loginRequest.data.challenge);
203
+ const privateKeyBuffer = wallet.getPrivateKey();
204
+ const signature = this.eciesService.signMessage(
205
+ privateKeyBuffer,
206
+ challengeBuffer
207
+ );
208
+ const signatureHex = uint8ArrayToHex(signature);
209
+ const loginResponse = await this.apiClient.post<LoginResponse>(
210
+ '/user/direct-challenge',
211
+ {
212
+ username: username,
213
+ email: email ? email.email : undefined,
214
+ challenge: loginRequest.data.challenge,
215
+ signature: signatureHex,
216
+ }
217
+ );
218
+ if (loginResponse.data.pendingTotpToken) {
219
+ return {
220
+ pendingTotpToken: loginResponse.data.pendingTotpToken,
221
+ };
222
+ }
223
+ if (loginResponse.data.token && loginResponse.data.user) {
224
+ return {
225
+ message: loginResponse.data.message ?? '',
226
+ token: loginResponse.data.token,
227
+ user: loginResponse.data.user,
228
+ wallet,
229
+ };
230
+ }
231
+ }
232
+ } catch (error) {
233
+ if (isAxiosError<ApiErrorResponse>(error) && error.response) {
234
+ // Check for DirectChallengeNotEnabled error
235
+ if (
236
+ error.response.status === 403 &&
237
+ error.response.data.errorType === 'DirectChallengeNotEnabledError'
238
+ ) {
239
+ return {
240
+ error: getSuiteCoreTranslation(
241
+ SuiteCoreStringKey.Error_Login_DirectChallengeNotEnabled
242
+ ),
243
+ errorType: 'DirectChallengeNotEnabled',
244
+ };
245
+ }
246
+ // Check for PasswordLoginNotEnabled error
247
+ if (
248
+ error.response.status === 403 &&
249
+ error.response.data.errorType === 'PasswordLoginNotEnabledError'
250
+ ) {
251
+ return {
252
+ error: getSuiteCoreTranslation(
253
+ SuiteCoreStringKey.Error_Login_PasswordLoginNotEnabled
254
+ ),
255
+ errorType: 'PasswordLoginNotEnabled',
256
+ };
257
+ }
258
+
259
+ const errorData = error.response.data;
260
+ return {
261
+ error:
262
+ extractErrorMessage(errorData) ??
263
+ (error as Error).message ??
264
+ getSuiteCoreTranslation(SuiteCoreStringKey.Common_UnexpectedError),
265
+ ...(errorData.errorType ? { errorType: errorData.errorType } : {}),
266
+ };
267
+ }
268
+ console.error('directLogin: non-axios error:', error);
269
+ }
270
+ return {
271
+ error: getSuiteCoreTranslation(SuiteCoreStringKey.Common_UnexpectedError),
272
+ };
273
+ }
274
+
275
+ async requestEmailLogin(
276
+ username?: string,
277
+ email?: EmailString
278
+ ): Promise<string | { error: string; errorType?: string }> {
279
+ if (!username && !email) {
280
+ return {
281
+ error: getSuiteCoreTranslation(
282
+ SuiteCoreStringKey.Validation_UsernameOrEmailRequired
283
+ ),
284
+ };
285
+ }
286
+ try {
287
+ const result = await this.apiClient.post<{ message: string }>(
288
+ '/user/request-email-login',
289
+ {
290
+ email: email ? email.email : undefined,
291
+ username,
292
+ }
293
+ );
294
+ if (result.data.message) {
295
+ return result.data.message;
296
+ }
297
+ } catch (error) {
298
+ if (isAxiosError<ApiErrorResponse>(error) && error.response) {
299
+ const errorData = error.response.data;
300
+ return {
301
+ error:
302
+ extractErrorMessage(errorData) ??
303
+ (error as Error).message ??
304
+ getSuiteCoreTranslation(SuiteCoreStringKey.Common_UnexpectedError),
305
+ ...(errorData.errorType ? { errorType: errorData.errorType } : {}),
306
+ };
307
+ }
308
+ }
309
+ return {
310
+ error: getSuiteCoreTranslation(SuiteCoreStringKey.Common_UnexpectedError),
311
+ };
312
+ }
313
+
314
+ async emailChallengeLogin(
315
+ mnemonic: SecureString,
316
+ token: string,
317
+ username?: string,
318
+ email?: EmailString
319
+ ): Promise<
320
+ | { token: string; user: IRequestUserDTO; wallet: Wallet; message: string }
321
+ | { pendingTotpToken: string }
322
+ | { error: string; errorType?: string }
323
+ > {
324
+ if (!username && !email) {
325
+ return {
326
+ error: getSuiteCoreTranslation(
327
+ SuiteCoreStringKey.Validation_UsernameOrEmailRequired
328
+ ),
329
+ };
330
+ }
331
+ try {
332
+ const { wallet } = this.cryptoCore.walletAndSeedFromMnemonic(mnemonic);
333
+ const challengeBuffer = hexToUint8Array(token);
334
+ const privateKeyBuffer = wallet.getPrivateKey();
335
+ const signature = this.eciesService.signMessage(
336
+ privateKeyBuffer,
337
+ challengeBuffer
338
+ );
339
+ const signatureHex = uint8ArrayToHex(signature);
340
+ const response = await this.apiClient.post<LoginResponse>(
341
+ '/user/email-login',
342
+ {
343
+ token,
344
+ signature: signatureHex,
345
+ username: username ?? null,
346
+ email: email ?? null,
347
+ }
348
+ );
349
+ if (response.data.pendingTotpToken) {
350
+ return {
351
+ pendingTotpToken: response.data.pendingTotpToken,
352
+ };
353
+ }
354
+ if (response.data.token && response.data.user) {
355
+ return {
356
+ message: response.data.message ?? '',
357
+ token: response.data.token,
358
+ user: response.data.user,
359
+ wallet,
360
+ };
361
+ }
362
+ } catch (error) {
363
+ if (isAxiosError<ApiErrorResponse>(error) && error.response) {
364
+ const errorData = error.response.data;
365
+ return {
366
+ error:
367
+ extractErrorMessage(errorData) ??
368
+ (error as Error).message ??
369
+ getSuiteCoreTranslation(SuiteCoreStringKey.Common_UnexpectedError),
370
+ ...(errorData.errorType ? { errorType: errorData.errorType } : {}),
371
+ };
372
+ }
373
+ }
374
+ return {
375
+ error: getSuiteCoreTranslation(SuiteCoreStringKey.Common_UnexpectedError),
376
+ };
377
+ }
378
+
379
+ async verifyToken(
380
+ token: string
381
+ ): Promise<IRequestUserDTO | { error: string; errorType?: string }> {
382
+ try {
383
+ const response = await this.apiClient.get<{ user: IRequestUserDTO }>(
384
+ '/user/verify',
385
+ {
386
+ headers: { Authorization: `Bearer ${token}` },
387
+ }
388
+ );
389
+ return response.data.user;
390
+ } catch (error) {
391
+ if (isAxiosError<ApiErrorResponse>(error) && error.response) {
392
+ const errorData = error.response.data;
393
+ return {
394
+ error: errorData.message
395
+ ? errorData.message
396
+ : (error as Error).message
397
+ ? (error as Error).message
398
+ : getSuiteCoreTranslation(
399
+ SuiteCoreStringKey.Common_UnexpectedError
400
+ ),
401
+ ...(errorData.errorType ? { errorType: errorData.errorType } : {}),
402
+ };
403
+ } else {
404
+ return {
405
+ error: getSuiteCoreTranslation(
406
+ SuiteCoreStringKey.Common_UnexpectedError
407
+ ),
408
+ };
409
+ }
410
+ }
411
+ }
412
+
413
+ async refreshToken(): Promise<{
414
+ token: string;
415
+ user: IRequestUserDTO;
416
+ }> {
417
+ try {
418
+ const refreshResponse = await this.authenticatedApiClient.get<{
419
+ user: IRequestUserDTO;
420
+ }>('/user/refresh-token');
421
+ if (refreshResponse.status === 200) {
422
+ const newToken = refreshResponse.headers['authorization'] as
423
+ | string
424
+ | undefined;
425
+ let token: string | undefined = undefined;
426
+ let user: IRequestUserDTO | undefined = undefined;
427
+ if (
428
+ newToken &&
429
+ typeof newToken === 'string' &&
430
+ newToken.startsWith('Bearer ')
431
+ ) {
432
+ token = newToken.slice(7);
433
+ }
434
+ if (refreshResponse.data.user) {
435
+ user = refreshResponse.data.user;
436
+ }
437
+ if (token && user) {
438
+ return { token, user };
439
+ }
440
+ }
441
+ } catch (error) {
442
+ console.error('Token refresh error:', error);
443
+ }
444
+ throw new TranslatableSuiteError(SuiteCoreStringKey.Common_UnexpectedError);
445
+ }
446
+
447
+ async changePassword(
448
+ currentPassword: string,
449
+ newPassword: string
450
+ ): Promise<{ success: boolean } | { error: string; errorType?: string }> {
451
+ try {
452
+ await this.authenticatedApiClient.post('/user/change-password', {
453
+ currentPassword,
454
+ newPassword,
455
+ });
456
+ return { success: true };
457
+ } catch (error) {
458
+ if (isAxiosError<ApiErrorResponse>(error) && error.response) {
459
+ const errorData = error.response.data;
460
+ return {
461
+ error:
462
+ extractErrorMessage(errorData) ??
463
+ (error as Error).message ??
464
+ getSuiteCoreTranslation(SuiteCoreStringKey.Common_UnexpectedError),
465
+ ...(errorData.errorType ? { errorType: errorData.errorType } : {}),
466
+ };
467
+ }
468
+ return {
469
+ error: getSuiteCoreTranslation(
470
+ SuiteCoreStringKey.Common_UnexpectedError
471
+ ),
472
+ };
473
+ }
474
+ }
475
+
476
+ async backupCodeLogin(
477
+ identifier: string,
478
+ code: string,
479
+ isEmail: boolean,
480
+ recoverMnemonic: boolean,
481
+ newPassword?: string
482
+ ): Promise<
483
+ | {
484
+ token: string;
485
+ codeCount: number;
486
+ user?: IRequestUserDTO;
487
+ mnemonic?: string;
488
+ message?: string;
489
+ }
490
+ | { error: string; status?: number; errorType?: string }
491
+ > {
492
+ try {
493
+ const response = await this.apiClient.post<BackupCodeLoginResponse>(
494
+ '/user/backup-code',
495
+ {
496
+ [isEmail ? 'email' : 'username']: identifier,
497
+ code,
498
+ recoverMnemonic,
499
+ ...(newPassword ? { newPassword } : {}),
500
+ }
501
+ );
502
+ if (response.data.token && response.data.user) {
503
+ return {
504
+ token: response.data.token,
505
+ user: response.data.user,
506
+ codeCount: response.data.codeCount ?? 0,
507
+ ...(response.data.mnemonic
508
+ ? { mnemonic: response.data.mnemonic }
509
+ : {}),
510
+ ...(response.data.message ? { message: response.data.message } : {}),
511
+ };
512
+ }
513
+ const errorData = response.data as unknown as ApiErrorResponse;
514
+ return {
515
+ error: errorData.message
516
+ ? errorData.message
517
+ : getSuiteCoreTranslation(SuiteCoreStringKey.Validation_InvalidToken),
518
+ ...(errorData.errorType ? { errorType: errorData.errorType } : {}),
519
+ };
520
+ } catch (error) {
521
+ console.error('Backup code login error:', error);
522
+ if (isAxiosError<ApiErrorResponse>(error) && error.response) {
523
+ const errorData = error.response.data;
524
+ return {
525
+ error:
526
+ extractErrorMessage(errorData) ??
527
+ (error as Error).message ??
528
+ getSuiteCoreTranslation(SuiteCoreStringKey.Common_UnexpectedError),
529
+ ...(errorData.errorType ? { errorType: errorData.errorType } : {}),
530
+ status: error.response.status,
531
+ };
532
+ }
533
+ return {
534
+ error: getSuiteCoreTranslation(
535
+ SuiteCoreStringKey.Common_UnexpectedError
536
+ ),
537
+ };
538
+ }
539
+ }
540
+
541
+ async setupTotp(): Promise<
542
+ { provisioningUri: string; secret: string } | { error: string }
543
+ > {
544
+ try {
545
+ const response = await this.authenticatedApiClient.post<{
546
+ provisioningUri: string;
547
+ secret: string;
548
+ }>('/user/totp/setup');
549
+ return {
550
+ provisioningUri: response.data.provisioningUri,
551
+ secret: response.data.secret,
552
+ };
553
+ } catch (error) {
554
+ if (isAxiosError<ApiErrorResponse>(error) && error.response) {
555
+ const errorData = error.response.data;
556
+ return {
557
+ error:
558
+ extractErrorMessage(errorData) ??
559
+ (error as Error).message ??
560
+ getSuiteCoreTranslation(SuiteCoreStringKey.Common_UnexpectedError),
561
+ };
562
+ }
563
+ return {
564
+ error: getSuiteCoreTranslation(
565
+ SuiteCoreStringKey.Common_UnexpectedError
566
+ ),
567
+ };
568
+ }
569
+ }
570
+
571
+ async confirmTotp(
572
+ code: string
573
+ ): Promise<{ success: boolean } | { error: string }> {
574
+ try {
575
+ await this.authenticatedApiClient.post('/user/totp/confirm', { code });
576
+ return { success: true };
577
+ } catch (error) {
578
+ if (isAxiosError<ApiErrorResponse>(error) && error.response) {
579
+ const errorData = error.response.data;
580
+ return {
581
+ error:
582
+ extractErrorMessage(errorData) ??
583
+ (error as Error).message ??
584
+ getSuiteCoreTranslation(SuiteCoreStringKey.Common_UnexpectedError),
585
+ };
586
+ }
587
+ return {
588
+ error: getSuiteCoreTranslation(
589
+ SuiteCoreStringKey.Common_UnexpectedError
590
+ ),
591
+ };
592
+ }
593
+ }
594
+
595
+ async disableTotp(
596
+ code: string
597
+ ): Promise<{ success: boolean } | { error: string }> {
598
+ try {
599
+ await this.authenticatedApiClient.post('/user/totp/disable', { code });
600
+ return { success: true };
601
+ } catch (error) {
602
+ if (isAxiosError<ApiErrorResponse>(error) && error.response) {
603
+ const errorData = error.response.data;
604
+ return {
605
+ error:
606
+ extractErrorMessage(errorData) ??
607
+ (error as Error).message ??
608
+ getSuiteCoreTranslation(SuiteCoreStringKey.Common_UnexpectedError),
609
+ };
610
+ }
611
+ return {
612
+ error: getSuiteCoreTranslation(
613
+ SuiteCoreStringKey.Common_UnexpectedError
614
+ ),
615
+ };
616
+ }
617
+ }
618
+
619
+ async resetTotp(
620
+ code: string
621
+ ): Promise<{ provisioningUri: string; secret: string } | { error: string }> {
622
+ try {
623
+ const response = await this.authenticatedApiClient.post<{
624
+ provisioningUri: string;
625
+ secret: string;
626
+ }>('/user/totp/reset', { code });
627
+ return {
628
+ provisioningUri: response.data.provisioningUri,
629
+ secret: response.data.secret,
630
+ };
631
+ } catch (error) {
632
+ if (isAxiosError<ApiErrorResponse>(error) && error.response) {
633
+ const errorData = error.response.data;
634
+ return {
635
+ error:
636
+ extractErrorMessage(errorData) ??
637
+ (error as Error).message ??
638
+ getSuiteCoreTranslation(SuiteCoreStringKey.Common_UnexpectedError),
639
+ };
640
+ }
641
+ return {
642
+ error: getSuiteCoreTranslation(
643
+ SuiteCoreStringKey.Common_UnexpectedError
644
+ ),
645
+ };
646
+ }
647
+ }
648
+
649
+ async verifyTotpLogin(
650
+ pendingTotpToken: string,
651
+ code: string
652
+ ): Promise<{ token: string; user: IRequestUserDTO } | { error: string }> {
653
+ try {
654
+ const response = await this.apiClient.post<{
655
+ token: string;
656
+ user: IRequestUserDTO;
657
+ }>(
658
+ '/user/totp/verify',
659
+ { code },
660
+ {
661
+ headers: { Authorization: `Bearer ${pendingTotpToken}` },
662
+ }
663
+ );
664
+ return {
665
+ token: response.data.token,
666
+ user: response.data.user,
667
+ };
668
+ } catch (error) {
669
+ if (isAxiosError<ApiErrorResponse>(error) && error.response) {
670
+ const errorData = error.response.data;
671
+ return {
672
+ error:
673
+ extractErrorMessage(errorData) ??
674
+ (error as Error).message ??
675
+ getSuiteCoreTranslation(SuiteCoreStringKey.Common_UnexpectedError),
676
+ };
677
+ }
678
+ return {
679
+ error: getSuiteCoreTranslation(
680
+ SuiteCoreStringKey.Common_UnexpectedError
681
+ ),
682
+ };
683
+ }
684
+ }
685
+ }
686
+
687
+ export const createAuthService = (
688
+ constants: IConstants,
689
+ baseUrl: string,
690
+ eciesConfig: IECIESConfig,
691
+ siteDomain: string
692
+ ) => new AuthService(constants, baseUrl, eciesConfig, siteDomain);
@@ -0,0 +1,17 @@
1
+ import axios from 'axios';
2
+
3
+ export function createAuthenticatedApiClient(baseURL: string, tokenKey: string = 'authToken') {
4
+ const api = axios.create({ baseURL });
5
+
6
+ api.interceptors.request.use((config) => {
7
+ const token = localStorage.getItem(tokenKey);
8
+ if (token) {
9
+ config.headers['Authorization'] = `Bearer ${token}`;
10
+ }
11
+ const languageCode = localStorage.getItem('languageCode') ?? 'en-US';
12
+ config.headers['Accept-Language'] = languageCode;
13
+ return config;
14
+ });
15
+
16
+ return api;
17
+ }
@@ -0,0 +1,3 @@
1
+ export * from './api';
2
+ export * from './authenticatedApi';
3
+ export * from './authService';