@entropy-softworks/ui 2026.7.31

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 (590) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/LICENSE +21 -0
  3. package/README.md +115 -0
  4. package/lib/components/auth/LockOverlay.d.ts +22 -0
  5. package/lib/components/auth/LockOverlay.d.ts.map +1 -0
  6. package/lib/components/auth/LockOverlay.jsx +82 -0
  7. package/lib/components/auth/LockOverlay.jsx.map +1 -0
  8. package/lib/components/auth/PasswordStrengthMeter.d.ts +12 -0
  9. package/lib/components/auth/PasswordStrengthMeter.d.ts.map +1 -0
  10. package/lib/components/auth/PasswordStrengthMeter.jsx +81 -0
  11. package/lib/components/auth/PasswordStrengthMeter.jsx.map +1 -0
  12. package/lib/components/auth/ProtectedRoute.d.ts +24 -0
  13. package/lib/components/auth/ProtectedRoute.d.ts.map +1 -0
  14. package/lib/components/auth/ProtectedRoute.jsx +56 -0
  15. package/lib/components/auth/ProtectedRoute.jsx.map +1 -0
  16. package/lib/components/auth/PwnedPasswordWarning.d.ts +13 -0
  17. package/lib/components/auth/PwnedPasswordWarning.d.ts.map +1 -0
  18. package/lib/components/auth/PwnedPasswordWarning.jsx +29 -0
  19. package/lib/components/auth/PwnedPasswordWarning.jsx.map +1 -0
  20. package/lib/components/auth/index.d.ts +5 -0
  21. package/lib/components/auth/index.d.ts.map +1 -0
  22. package/lib/components/auth/index.js +5 -0
  23. package/lib/components/auth/index.js.map +1 -0
  24. package/lib/components/bottom-nav.d.ts +25 -0
  25. package/lib/components/bottom-nav.d.ts.map +1 -0
  26. package/lib/components/bottom-nav.jsx +51 -0
  27. package/lib/components/bottom-nav.jsx.map +1 -0
  28. package/lib/components/button.d.ts +16 -0
  29. package/lib/components/button.d.ts.map +1 -0
  30. package/lib/components/button.jsx +73 -0
  31. package/lib/components/button.jsx.map +1 -0
  32. package/lib/components/card.d.ts +22 -0
  33. package/lib/components/card.d.ts.map +1 -0
  34. package/lib/components/card.jsx +36 -0
  35. package/lib/components/card.jsx.map +1 -0
  36. package/lib/components/checkbox.d.ts +12 -0
  37. package/lib/components/checkbox.d.ts.map +1 -0
  38. package/lib/components/checkbox.jsx +16 -0
  39. package/lib/components/checkbox.jsx.map +1 -0
  40. package/lib/components/connection-settings.d.ts +41 -0
  41. package/lib/components/connection-settings.d.ts.map +1 -0
  42. package/lib/components/connection-settings.jsx +354 -0
  43. package/lib/components/connection-settings.jsx.map +1 -0
  44. package/lib/components/context-menu.d.ts +52 -0
  45. package/lib/components/context-menu.d.ts.map +1 -0
  46. package/lib/components/context-menu.jsx +24 -0
  47. package/lib/components/context-menu.jsx.map +1 -0
  48. package/lib/components/data-table.d.ts +59 -0
  49. package/lib/components/data-table.d.ts.map +1 -0
  50. package/lib/components/data-table.jsx +101 -0
  51. package/lib/components/data-table.jsx.map +1 -0
  52. package/lib/components/dialog.d.ts +58 -0
  53. package/lib/components/dialog.d.ts.map +1 -0
  54. package/lib/components/dialog.jsx +197 -0
  55. package/lib/components/dialog.jsx.map +1 -0
  56. package/lib/components/dropdown-menu.d.ts +55 -0
  57. package/lib/components/dropdown-menu.d.ts.map +1 -0
  58. package/lib/components/dropdown-menu.jsx +38 -0
  59. package/lib/components/dropdown-menu.jsx.map +1 -0
  60. package/lib/components/entropy-boot-splash.d.ts +24 -0
  61. package/lib/components/entropy-boot-splash.d.ts.map +1 -0
  62. package/lib/components/entropy-boot-splash.jsx +83 -0
  63. package/lib/components/entropy-boot-splash.jsx.map +1 -0
  64. package/lib/components/entropy-mark-animated.d.ts +20 -0
  65. package/lib/components/entropy-mark-animated.d.ts.map +1 -0
  66. package/lib/components/entropy-mark-animated.jsx +148 -0
  67. package/lib/components/entropy-mark-animated.jsx.map +1 -0
  68. package/lib/components/entropy-mark-geometry.d.ts +8 -0
  69. package/lib/components/entropy-mark-geometry.d.ts.map +1 -0
  70. package/lib/components/entropy-mark-geometry.js +10 -0
  71. package/lib/components/entropy-mark-geometry.js.map +1 -0
  72. package/lib/components/entropy-mark-sequential.d.ts +12 -0
  73. package/lib/components/entropy-mark-sequential.d.ts.map +1 -0
  74. package/lib/components/entropy-mark-sequential.jsx +53 -0
  75. package/lib/components/entropy-mark-sequential.jsx.map +1 -0
  76. package/lib/components/entropy-mark.d.ts +29 -0
  77. package/lib/components/entropy-mark.d.ts.map +1 -0
  78. package/lib/components/entropy-mark.jsx +66 -0
  79. package/lib/components/entropy-mark.jsx.map +1 -0
  80. package/lib/components/error-boundary.d.ts +31 -0
  81. package/lib/components/error-boundary.d.ts.map +1 -0
  82. package/lib/components/error-boundary.jsx +40 -0
  83. package/lib/components/error-boundary.jsx.map +1 -0
  84. package/lib/components/index.d.ts +41 -0
  85. package/lib/components/index.d.ts.map +1 -0
  86. package/lib/components/index.js +43 -0
  87. package/lib/components/index.js.map +1 -0
  88. package/lib/components/input.d.ts +26 -0
  89. package/lib/components/input.d.ts.map +1 -0
  90. package/lib/components/input.jsx +95 -0
  91. package/lib/components/input.jsx.map +1 -0
  92. package/lib/components/item-icon.d.ts +32 -0
  93. package/lib/components/item-icon.d.ts.map +1 -0
  94. package/lib/components/item-icon.jsx +54 -0
  95. package/lib/components/item-icon.jsx.map +1 -0
  96. package/lib/components/label.d.ts +13 -0
  97. package/lib/components/label.d.ts.map +1 -0
  98. package/lib/components/label.jsx +9 -0
  99. package/lib/components/label.jsx.map +1 -0
  100. package/lib/components/legal-dialog.d.ts +18 -0
  101. package/lib/components/legal-dialog.d.ts.map +1 -0
  102. package/lib/components/legal-dialog.jsx +31 -0
  103. package/lib/components/legal-dialog.jsx.map +1 -0
  104. package/lib/components/license-gate.d.ts +17 -0
  105. package/lib/components/license-gate.d.ts.map +1 -0
  106. package/lib/components/license-gate.jsx +21 -0
  107. package/lib/components/license-gate.jsx.map +1 -0
  108. package/lib/components/logo.d.ts +15 -0
  109. package/lib/components/logo.d.ts.map +1 -0
  110. package/lib/components/logo.jsx +20 -0
  111. package/lib/components/logo.jsx.map +1 -0
  112. package/lib/components/onboarding-wizard.d.ts +89 -0
  113. package/lib/components/onboarding-wizard.d.ts.map +1 -0
  114. package/lib/components/onboarding-wizard.jsx +185 -0
  115. package/lib/components/onboarding-wizard.jsx.map +1 -0
  116. package/lib/components/pagination.d.ts +43 -0
  117. package/lib/components/pagination.d.ts.map +1 -0
  118. package/lib/components/pagination.jsx +72 -0
  119. package/lib/components/pagination.jsx.map +1 -0
  120. package/lib/components/password-generator.d.ts +15 -0
  121. package/lib/components/password-generator.d.ts.map +1 -0
  122. package/lib/components/password-generator.jsx +273 -0
  123. package/lib/components/password-generator.jsx.map +1 -0
  124. package/lib/components/pin-pad.d.ts +20 -0
  125. package/lib/components/pin-pad.d.ts.map +1 -0
  126. package/lib/components/pin-pad.jsx +93 -0
  127. package/lib/components/pin-pad.jsx.map +1 -0
  128. package/lib/components/popover.d.ts +27 -0
  129. package/lib/components/popover.d.ts.map +1 -0
  130. package/lib/components/popover.jsx +18 -0
  131. package/lib/components/popover.jsx.map +1 -0
  132. package/lib/components/profile/ProfileDialog.d.ts +25 -0
  133. package/lib/components/profile/ProfileDialog.d.ts.map +1 -0
  134. package/lib/components/profile/ProfileDialog.jsx +71 -0
  135. package/lib/components/profile/ProfileDialog.jsx.map +1 -0
  136. package/lib/components/profile/ProfileTab.d.ts +12 -0
  137. package/lib/components/profile/ProfileTab.d.ts.map +1 -0
  138. package/lib/components/profile/ProfileTab.jsx +112 -0
  139. package/lib/components/profile/ProfileTab.jsx.map +1 -0
  140. package/lib/components/profile/SecurityTab.d.ts +32 -0
  141. package/lib/components/profile/SecurityTab.d.ts.map +1 -0
  142. package/lib/components/profile/SecurityTab.jsx +145 -0
  143. package/lib/components/profile/SecurityTab.jsx.map +1 -0
  144. package/lib/components/profile/index.d.ts +4 -0
  145. package/lib/components/profile/index.d.ts.map +1 -0
  146. package/lib/components/profile/index.js +4 -0
  147. package/lib/components/profile/index.js.map +1 -0
  148. package/lib/components/segmented-toggle.d.ts +28 -0
  149. package/lib/components/segmented-toggle.d.ts.map +1 -0
  150. package/lib/components/segmented-toggle.jsx +64 -0
  151. package/lib/components/segmented-toggle.jsx.map +1 -0
  152. package/lib/components/separator.d.ts +13 -0
  153. package/lib/components/separator.d.ts.map +1 -0
  154. package/lib/components/separator.jsx +16 -0
  155. package/lib/components/separator.jsx.map +1 -0
  156. package/lib/components/server-settings.d.ts +27 -0
  157. package/lib/components/server-settings.d.ts.map +1 -0
  158. package/lib/components/server-settings.jsx +307 -0
  159. package/lib/components/server-settings.jsx.map +1 -0
  160. package/lib/components/settings/AccountSection.d.ts +26 -0
  161. package/lib/components/settings/AccountSection.d.ts.map +1 -0
  162. package/lib/components/settings/AccountSection.jsx +32 -0
  163. package/lib/components/settings/AccountSection.jsx.map +1 -0
  164. package/lib/components/settings/DangerZoneSection.d.ts +13 -0
  165. package/lib/components/settings/DangerZoneSection.d.ts.map +1 -0
  166. package/lib/components/settings/DangerZoneSection.jsx +16 -0
  167. package/lib/components/settings/DangerZoneSection.jsx.map +1 -0
  168. package/lib/components/settings/GeneralSection.d.ts +11 -0
  169. package/lib/components/settings/GeneralSection.d.ts.map +1 -0
  170. package/lib/components/settings/GeneralSection.jsx +12 -0
  171. package/lib/components/settings/GeneralSection.jsx.map +1 -0
  172. package/lib/components/settings/SupportSection.d.ts +11 -0
  173. package/lib/components/settings/SupportSection.d.ts.map +1 -0
  174. package/lib/components/settings/SupportSection.jsx +22 -0
  175. package/lib/components/settings/SupportSection.jsx.map +1 -0
  176. package/lib/components/settings/index.d.ts +5 -0
  177. package/lib/components/settings/index.d.ts.map +1 -0
  178. package/lib/components/settings/index.js +5 -0
  179. package/lib/components/settings/index.js.map +1 -0
  180. package/lib/components/settings-row.d.ts +28 -0
  181. package/lib/components/settings-row.d.ts.map +1 -0
  182. package/lib/components/settings-row.jsx +68 -0
  183. package/lib/components/settings-row.jsx.map +1 -0
  184. package/lib/components/skeleton.d.ts +8 -0
  185. package/lib/components/skeleton.d.ts.map +1 -0
  186. package/lib/components/skeleton.jsx +24 -0
  187. package/lib/components/skeleton.jsx.map +1 -0
  188. package/lib/components/social-connections.d.ts +15 -0
  189. package/lib/components/social-connections.d.ts.map +1 -0
  190. package/lib/components/social-connections.jsx +31 -0
  191. package/lib/components/social-connections.jsx.map +1 -0
  192. package/lib/components/stat-card.d.ts +29 -0
  193. package/lib/components/stat-card.d.ts.map +1 -0
  194. package/lib/components/stat-card.jsx +36 -0
  195. package/lib/components/stat-card.jsx.map +1 -0
  196. package/lib/components/tag-chip.d.ts +18 -0
  197. package/lib/components/tag-chip.d.ts.map +1 -0
  198. package/lib/components/tag-chip.jsx +54 -0
  199. package/lib/components/tag-chip.jsx.map +1 -0
  200. package/lib/components/tag-multi-select.d.ts +27 -0
  201. package/lib/components/tag-multi-select.d.ts.map +1 -0
  202. package/lib/components/tag-multi-select.jsx +65 -0
  203. package/lib/components/tag-multi-select.jsx.map +1 -0
  204. package/lib/components/text.d.ts +22 -0
  205. package/lib/components/text.d.ts.map +1 -0
  206. package/lib/components/text.jsx +43 -0
  207. package/lib/components/text.jsx.map +1 -0
  208. package/lib/components/toast/ToastPortal.d.ts +11 -0
  209. package/lib/components/toast/ToastPortal.d.ts.map +1 -0
  210. package/lib/components/toast/ToastPortal.jsx +15 -0
  211. package/lib/components/toast/ToastPortal.jsx.map +1 -0
  212. package/lib/components/toast/ToastPortal.web.d.ts +14 -0
  213. package/lib/components/toast/ToastPortal.web.d.ts.map +1 -0
  214. package/lib/components/toast/ToastPortal.web.jsx +39 -0
  215. package/lib/components/toast/ToastPortal.web.jsx.map +1 -0
  216. package/lib/components/toast/index.d.ts +3 -0
  217. package/lib/components/toast/index.d.ts.map +1 -0
  218. package/lib/components/toast/index.js +3 -0
  219. package/lib/components/toast/index.js.map +1 -0
  220. package/lib/components/toast/toast-config.d.ts +14 -0
  221. package/lib/components/toast/toast-config.d.ts.map +1 -0
  222. package/lib/components/toast/toast-config.jsx +24 -0
  223. package/lib/components/toast/toast-config.jsx.map +1 -0
  224. package/lib/components/tooltip.d.ts +30 -0
  225. package/lib/components/tooltip.d.ts.map +1 -0
  226. package/lib/components/tooltip.jsx +14 -0
  227. package/lib/components/tooltip.jsx.map +1 -0
  228. package/lib/components/totp-display.d.ts +23 -0
  229. package/lib/components/totp-display.d.ts.map +1 -0
  230. package/lib/components/totp-display.jsx +132 -0
  231. package/lib/components/totp-display.jsx.map +1 -0
  232. package/lib/components/user-avatar.d.ts +18 -0
  233. package/lib/components/user-avatar.d.ts.map +1 -0
  234. package/lib/components/user-avatar.jsx +25 -0
  235. package/lib/components/user-avatar.jsx.map +1 -0
  236. package/lib/context/AppConfigContext.d.ts +56 -0
  237. package/lib/context/AppConfigContext.d.ts.map +1 -0
  238. package/lib/context/AppConfigContext.jsx +14 -0
  239. package/lib/context/AppConfigContext.jsx.map +1 -0
  240. package/lib/context/NavigationContext.d.ts +42 -0
  241. package/lib/context/NavigationContext.d.ts.map +1 -0
  242. package/lib/context/NavigationContext.jsx +53 -0
  243. package/lib/context/NavigationContext.jsx.map +1 -0
  244. package/lib/context/NotificationContext.d.ts +32 -0
  245. package/lib/context/NotificationContext.d.ts.map +1 -0
  246. package/lib/context/NotificationContext.jsx +49 -0
  247. package/lib/context/NotificationContext.jsx.map +1 -0
  248. package/lib/context/ThemeContext.d.ts +12 -0
  249. package/lib/context/ThemeContext.d.ts.map +1 -0
  250. package/lib/context/ThemeContext.jsx +66 -0
  251. package/lib/context/ThemeContext.jsx.map +1 -0
  252. package/lib/context/index.d.ts +5 -0
  253. package/lib/context/index.d.ts.map +1 -0
  254. package/lib/context/index.js +5 -0
  255. package/lib/context/index.js.map +1 -0
  256. package/lib/flags.d.ts +9 -0
  257. package/lib/flags.d.ts.map +1 -0
  258. package/lib/flags.js +37 -0
  259. package/lib/flags.js.map +1 -0
  260. package/lib/forms/form-error.d.ts +12 -0
  261. package/lib/forms/form-error.d.ts.map +1 -0
  262. package/lib/forms/form-error.jsx +14 -0
  263. package/lib/forms/form-error.jsx.map +1 -0
  264. package/lib/forms/index.d.ts +3 -0
  265. package/lib/forms/index.d.ts.map +1 -0
  266. package/lib/forms/index.js +3 -0
  267. package/lib/forms/index.js.map +1 -0
  268. package/lib/forms/shake-field.d.ts +18 -0
  269. package/lib/forms/shake-field.d.ts.map +1 -0
  270. package/lib/forms/shake-field.jsx +30 -0
  271. package/lib/forms/shake-field.jsx.map +1 -0
  272. package/lib/hooks/index.d.ts +6 -0
  273. package/lib/hooks/index.d.ts.map +1 -0
  274. package/lib/hooks/index.js +6 -0
  275. package/lib/hooks/index.js.map +1 -0
  276. package/lib/hooks/useAuth.d.ts +109 -0
  277. package/lib/hooks/useAuth.d.ts.map +1 -0
  278. package/lib/hooks/useAuth.jsx +404 -0
  279. package/lib/hooks/useAuth.jsx.map +1 -0
  280. package/lib/hooks/useAutoLock.d.ts +22 -0
  281. package/lib/hooks/useAutoLock.d.ts.map +1 -0
  282. package/lib/hooks/useAutoLock.js +85 -0
  283. package/lib/hooks/useAutoLock.js.map +1 -0
  284. package/lib/hooks/useDebouncedValue.d.ts +19 -0
  285. package/lib/hooks/useDebouncedValue.d.ts.map +1 -0
  286. package/lib/hooks/useDebouncedValue.js +29 -0
  287. package/lib/hooks/useDebouncedValue.js.map +1 -0
  288. package/lib/hooks/useShakeAnimation.d.ts +17 -0
  289. package/lib/hooks/useShakeAnimation.d.ts.map +1 -0
  290. package/lib/hooks/useShakeAnimation.js +22 -0
  291. package/lib/hooks/useShakeAnimation.js.map +1 -0
  292. package/lib/hooks/useUploadBlobUrl.d.ts +13 -0
  293. package/lib/hooks/useUploadBlobUrl.d.ts.map +1 -0
  294. package/lib/hooks/useUploadBlobUrl.js +60 -0
  295. package/lib/hooks/useUploadBlobUrl.js.map +1 -0
  296. package/lib/index.d.ts +10 -0
  297. package/lib/index.d.ts.map +1 -0
  298. package/lib/index.js +47 -0
  299. package/lib/index.js.map +1 -0
  300. package/lib/screens/auth/AccountPickerScreen.d.ts +24 -0
  301. package/lib/screens/auth/AccountPickerScreen.d.ts.map +1 -0
  302. package/lib/screens/auth/AccountPickerScreen.jsx +47 -0
  303. package/lib/screens/auth/AccountPickerScreen.jsx.map +1 -0
  304. package/lib/screens/auth/AuthScreen.d.ts +59 -0
  305. package/lib/screens/auth/AuthScreen.d.ts.map +1 -0
  306. package/lib/screens/auth/AuthScreen.jsx +594 -0
  307. package/lib/screens/auth/AuthScreen.jsx.map +1 -0
  308. package/lib/screens/auth/ConsentScreen.d.ts +27 -0
  309. package/lib/screens/auth/ConsentScreen.d.ts.map +1 -0
  310. package/lib/screens/auth/ConsentScreen.jsx +55 -0
  311. package/lib/screens/auth/ConsentScreen.jsx.map +1 -0
  312. package/lib/screens/auth/ErrorScreen.d.ts +20 -0
  313. package/lib/screens/auth/ErrorScreen.d.ts.map +1 -0
  314. package/lib/screens/auth/ErrorScreen.jsx +43 -0
  315. package/lib/screens/auth/ErrorScreen.jsx.map +1 -0
  316. package/lib/screens/auth/ExpiredLinkScreen.d.ts +19 -0
  317. package/lib/screens/auth/ExpiredLinkScreen.d.ts.map +1 -0
  318. package/lib/screens/auth/ExpiredLinkScreen.jsx +38 -0
  319. package/lib/screens/auth/ExpiredLinkScreen.jsx.map +1 -0
  320. package/lib/screens/auth/ForgotPasswordScreen.d.ts +4 -0
  321. package/lib/screens/auth/ForgotPasswordScreen.d.ts.map +1 -0
  322. package/lib/screens/auth/ForgotPasswordScreen.jsx +123 -0
  323. package/lib/screens/auth/ForgotPasswordScreen.jsx.map +1 -0
  324. package/lib/screens/auth/ImportDataScreen.d.ts +17 -0
  325. package/lib/screens/auth/ImportDataScreen.d.ts.map +1 -0
  326. package/lib/screens/auth/ImportDataScreen.jsx +73 -0
  327. package/lib/screens/auth/ImportDataScreen.jsx.map +1 -0
  328. package/lib/screens/auth/LoginScreen.d.ts +38 -0
  329. package/lib/screens/auth/LoginScreen.d.ts.map +1 -0
  330. package/lib/screens/auth/LoginScreen.jsx +326 -0
  331. package/lib/screens/auth/LoginScreen.jsx.map +1 -0
  332. package/lib/screens/auth/MfaChallengeScreen.d.ts +4 -0
  333. package/lib/screens/auth/MfaChallengeScreen.d.ts.map +1 -0
  334. package/lib/screens/auth/MfaChallengeScreen.jsx +139 -0
  335. package/lib/screens/auth/MfaChallengeScreen.jsx.map +1 -0
  336. package/lib/screens/auth/MfaEnrollScreen.d.ts +12 -0
  337. package/lib/screens/auth/MfaEnrollScreen.d.ts.map +1 -0
  338. package/lib/screens/auth/MfaEnrollScreen.jsx +267 -0
  339. package/lib/screens/auth/MfaEnrollScreen.jsx.map +1 -0
  340. package/lib/screens/auth/PasskeyEnrollScreen.d.ts +18 -0
  341. package/lib/screens/auth/PasskeyEnrollScreen.d.ts.map +1 -0
  342. package/lib/screens/auth/PasskeyEnrollScreen.jsx +51 -0
  343. package/lib/screens/auth/PasskeyEnrollScreen.jsx.map +1 -0
  344. package/lib/screens/auth/PrivacyPolicyScreen.d.ts +19 -0
  345. package/lib/screens/auth/PrivacyPolicyScreen.d.ts.map +1 -0
  346. package/lib/screens/auth/PrivacyPolicyScreen.jsx +143 -0
  347. package/lib/screens/auth/PrivacyPolicyScreen.jsx.map +1 -0
  348. package/lib/screens/auth/RegisterScreen.d.ts +14 -0
  349. package/lib/screens/auth/RegisterScreen.d.ts.map +1 -0
  350. package/lib/screens/auth/RegisterScreen.jsx +296 -0
  351. package/lib/screens/auth/RegisterScreen.jsx.map +1 -0
  352. package/lib/screens/auth/ResetPasswordScreen.d.ts +12 -0
  353. package/lib/screens/auth/ResetPasswordScreen.d.ts.map +1 -0
  354. package/lib/screens/auth/ResetPasswordScreen.jsx +276 -0
  355. package/lib/screens/auth/ResetPasswordScreen.jsx.map +1 -0
  356. package/lib/screens/auth/ServerConfigScreen.d.ts +18 -0
  357. package/lib/screens/auth/ServerConfigScreen.d.ts.map +1 -0
  358. package/lib/screens/auth/ServerConfigScreen.jsx +259 -0
  359. package/lib/screens/auth/ServerConfigScreen.jsx.map +1 -0
  360. package/lib/screens/auth/TermsOfServiceScreen.d.ts +30 -0
  361. package/lib/screens/auth/TermsOfServiceScreen.d.ts.map +1 -0
  362. package/lib/screens/auth/TermsOfServiceScreen.jsx +130 -0
  363. package/lib/screens/auth/TermsOfServiceScreen.jsx.map +1 -0
  364. package/lib/screens/auth/VerifyEmailScreen.d.ts +4 -0
  365. package/lib/screens/auth/VerifyEmailScreen.d.ts.map +1 -0
  366. package/lib/screens/auth/VerifyEmailScreen.jsx +249 -0
  367. package/lib/screens/auth/VerifyEmailScreen.jsx.map +1 -0
  368. package/lib/screens/auth/WelcomeScreen.d.ts +17 -0
  369. package/lib/screens/auth/WelcomeScreen.d.ts.map +1 -0
  370. package/lib/screens/auth/WelcomeScreen.jsx +58 -0
  371. package/lib/screens/auth/WelcomeScreen.jsx.map +1 -0
  372. package/lib/screens/auth/index.d.ts +19 -0
  373. package/lib/screens/auth/index.d.ts.map +1 -0
  374. package/lib/screens/auth/index.js +19 -0
  375. package/lib/screens/auth/index.js.map +1 -0
  376. package/lib/screens/index.d.ts +2 -0
  377. package/lib/screens/index.d.ts.map +1 -0
  378. package/lib/screens/index.js +2 -0
  379. package/lib/screens/index.js.map +1 -0
  380. package/lib/services/api-config.d.ts +87 -0
  381. package/lib/services/api-config.d.ts.map +1 -0
  382. package/lib/services/api-config.js +202 -0
  383. package/lib/services/api-config.js.map +1 -0
  384. package/lib/services/auth.service.d.ts +140 -0
  385. package/lib/services/auth.service.d.ts.map +1 -0
  386. package/lib/services/auth.service.js +376 -0
  387. package/lib/services/auth.service.js.map +1 -0
  388. package/lib/services/favicon.d.ts +46 -0
  389. package/lib/services/favicon.d.ts.map +1 -0
  390. package/lib/services/favicon.js +84 -0
  391. package/lib/services/favicon.js.map +1 -0
  392. package/lib/services/hibp-cache.d.ts +23 -0
  393. package/lib/services/hibp-cache.d.ts.map +1 -0
  394. package/lib/services/hibp-cache.js +56 -0
  395. package/lib/services/hibp-cache.js.map +1 -0
  396. package/lib/services/hibp.d.ts +28 -0
  397. package/lib/services/hibp.d.ts.map +1 -0
  398. package/lib/services/hibp.js +92 -0
  399. package/lib/services/hibp.js.map +1 -0
  400. package/lib/services/index.d.ts +8 -0
  401. package/lib/services/index.d.ts.map +1 -0
  402. package/lib/services/index.js +7 -0
  403. package/lib/services/index.js.map +1 -0
  404. package/lib/services/passkey.d.ts +10 -0
  405. package/lib/services/passkey.d.ts.map +1 -0
  406. package/lib/services/passkey.js +192 -0
  407. package/lib/services/passkey.js.map +1 -0
  408. package/lib/utils/api-fetch.d.ts +11 -0
  409. package/lib/utils/api-fetch.d.ts.map +1 -0
  410. package/lib/utils/api-fetch.js +52 -0
  411. package/lib/utils/api-fetch.js.map +1 -0
  412. package/lib/utils/clipboard.d.ts +31 -0
  413. package/lib/utils/clipboard.d.ts.map +1 -0
  414. package/lib/utils/clipboard.js +66 -0
  415. package/lib/utils/clipboard.js.map +1 -0
  416. package/lib/utils/cn.d.ts +9 -0
  417. package/lib/utils/cn.d.ts.map +1 -0
  418. package/lib/utils/cn.js +12 -0
  419. package/lib/utils/cn.js.map +1 -0
  420. package/lib/utils/debounce.d.ts +13 -0
  421. package/lib/utils/debounce.d.ts.map +1 -0
  422. package/lib/utils/debounce.js +28 -0
  423. package/lib/utils/debounce.js.map +1 -0
  424. package/lib/utils/error.d.ts +9 -0
  425. package/lib/utils/error.d.ts.map +1 -0
  426. package/lib/utils/error.js +11 -0
  427. package/lib/utils/error.js.map +1 -0
  428. package/lib/utils/fetch.d.ts +17 -0
  429. package/lib/utils/fetch.d.ts.map +1 -0
  430. package/lib/utils/fetch.js +29 -0
  431. package/lib/utils/fetch.js.map +1 -0
  432. package/lib/utils/http-error.d.ts +41 -0
  433. package/lib/utils/http-error.d.ts.map +1 -0
  434. package/lib/utils/http-error.js +63 -0
  435. package/lib/utils/http-error.js.map +1 -0
  436. package/lib/utils/index.d.ts +11 -0
  437. package/lib/utils/index.d.ts.map +1 -0
  438. package/lib/utils/index.js +11 -0
  439. package/lib/utils/index.js.map +1 -0
  440. package/lib/utils/palette.d.ts +46 -0
  441. package/lib/utils/palette.d.ts.map +1 -0
  442. package/lib/utils/palette.js +75 -0
  443. package/lib/utils/palette.js.map +1 -0
  444. package/lib/utils/password.d.ts +20 -0
  445. package/lib/utils/password.d.ts.map +1 -0
  446. package/lib/utils/password.js +51 -0
  447. package/lib/utils/password.js.map +1 -0
  448. package/lib/utils/settings-storage.d.ts +3 -0
  449. package/lib/utils/settings-storage.d.ts.map +1 -0
  450. package/lib/utils/settings-storage.js +62 -0
  451. package/lib/utils/settings-storage.js.map +1 -0
  452. package/lib/utils/validation.d.ts +29 -0
  453. package/lib/utils/validation.d.ts.map +1 -0
  454. package/lib/utils/validation.js +58 -0
  455. package/lib/utils/validation.js.map +1 -0
  456. package/package.json +204 -0
  457. package/src/components/__tests__/data-table.test.ts +94 -0
  458. package/src/components/__tests__/pagination.test.ts +73 -0
  459. package/src/components/__tests__/stat-card.test.ts +21 -0
  460. package/src/components/auth/LockOverlay.tsx +122 -0
  461. package/src/components/auth/PasswordStrengthMeter.tsx +96 -0
  462. package/src/components/auth/ProtectedRoute.tsx +91 -0
  463. package/src/components/auth/PwnedPasswordWarning.tsx +54 -0
  464. package/src/components/auth/index.ts +10 -0
  465. package/src/components/bottom-nav.tsx +99 -0
  466. package/src/components/button.tsx +144 -0
  467. package/src/components/card.tsx +82 -0
  468. package/src/components/checkbox.tsx +48 -0
  469. package/src/components/connection-settings.tsx +583 -0
  470. package/src/components/context-menu.tsx +95 -0
  471. package/src/components/data-table.tsx +223 -0
  472. package/src/components/dialog.tsx +376 -0
  473. package/src/components/dropdown-menu.tsx +120 -0
  474. package/src/components/entropy-boot-splash.tsx +114 -0
  475. package/src/components/entropy-mark-animated.tsx +260 -0
  476. package/src/components/entropy-mark-geometry.ts +11 -0
  477. package/src/components/entropy-mark-sequential.tsx +132 -0
  478. package/src/components/entropy-mark.tsx +135 -0
  479. package/src/components/error-boundary.tsx +70 -0
  480. package/src/components/index.ts +156 -0
  481. package/src/components/input.tsx +246 -0
  482. package/src/components/item-icon.tsx +111 -0
  483. package/src/components/label.tsx +25 -0
  484. package/src/components/legal-dialog.tsx +47 -0
  485. package/src/components/license-gate.tsx +28 -0
  486. package/src/components/logo.tsx +34 -0
  487. package/src/components/onboarding-wizard.tsx +353 -0
  488. package/src/components/pagination.tsx +154 -0
  489. package/src/components/password-generator.tsx +393 -0
  490. package/src/components/pin-pad.tsx +156 -0
  491. package/src/components/popover.tsx +38 -0
  492. package/src/components/profile/ProfileDialog.tsx +129 -0
  493. package/src/components/profile/ProfileTab.tsx +151 -0
  494. package/src/components/profile/SecurityTab.tsx +229 -0
  495. package/src/components/profile/index.ts +3 -0
  496. package/src/components/segmented-toggle.tsx +119 -0
  497. package/src/components/separator.tsx +41 -0
  498. package/src/components/server-settings.tsx +442 -0
  499. package/src/components/settings/AccountSection.tsx +76 -0
  500. package/src/components/settings/DangerZoneSection.tsx +37 -0
  501. package/src/components/settings/GeneralSection.tsx +39 -0
  502. package/src/components/settings/SupportSection.tsx +53 -0
  503. package/src/components/settings/index.ts +4 -0
  504. package/src/components/settings-row.tsx +126 -0
  505. package/src/components/skeleton.tsx +48 -0
  506. package/src/components/social-connections.tsx +55 -0
  507. package/src/components/stat-card.tsx +64 -0
  508. package/src/components/tag-chip.tsx +78 -0
  509. package/src/components/tag-multi-select.tsx +136 -0
  510. package/src/components/text.tsx +77 -0
  511. package/src/components/toast/ToastPortal.tsx +16 -0
  512. package/src/components/toast/ToastPortal.web.tsx +47 -0
  513. package/src/components/toast/index.ts +2 -0
  514. package/src/components/toast/toast-config.tsx +31 -0
  515. package/src/components/tooltip.tsx +30 -0
  516. package/src/components/totp-display.tsx +207 -0
  517. package/src/components/user-avatar.tsx +46 -0
  518. package/src/context/AppConfigContext.tsx +76 -0
  519. package/src/context/NavigationContext.tsx +75 -0
  520. package/src/context/NotificationContext.tsx +69 -0
  521. package/src/context/ThemeContext.tsx +83 -0
  522. package/src/context/__tests__/NavigationContext.test.tsx +53 -0
  523. package/src/context/index.ts +24 -0
  524. package/src/flags.ts +44 -0
  525. package/src/forms/form-error.tsx +20 -0
  526. package/src/forms/index.ts +2 -0
  527. package/src/forms/shake-field.tsx +52 -0
  528. package/src/hooks/__tests__/useUploadBlobUrl.test.tsx +77 -0
  529. package/src/hooks/index.ts +18 -0
  530. package/src/hooks/useAuth.tsx +567 -0
  531. package/src/hooks/useAutoLock.ts +110 -0
  532. package/src/hooks/useDebouncedValue.ts +32 -0
  533. package/src/hooks/useShakeAnimation.ts +42 -0
  534. package/src/hooks/useUploadBlobUrl.ts +75 -0
  535. package/src/index.ts +48 -0
  536. package/src/screens/auth/AccountPickerScreen.tsx +103 -0
  537. package/src/screens/auth/AuthScreen.tsx +941 -0
  538. package/src/screens/auth/ConsentScreen.tsx +118 -0
  539. package/src/screens/auth/ErrorScreen.tsx +96 -0
  540. package/src/screens/auth/ExpiredLinkScreen.tsx +87 -0
  541. package/src/screens/auth/ForgotPasswordScreen.tsx +182 -0
  542. package/src/screens/auth/ImportDataScreen.tsx +112 -0
  543. package/src/screens/auth/LoginScreen.tsx +484 -0
  544. package/src/screens/auth/MfaChallengeScreen.tsx +216 -0
  545. package/src/screens/auth/MfaEnrollScreen.tsx +388 -0
  546. package/src/screens/auth/PasskeyEnrollScreen.tsx +96 -0
  547. package/src/screens/auth/PrivacyPolicyScreen.tsx +249 -0
  548. package/src/screens/auth/RegisterScreen.tsx +430 -0
  549. package/src/screens/auth/ResetPasswordScreen.tsx +377 -0
  550. package/src/screens/auth/ServerConfigScreen.tsx +388 -0
  551. package/src/screens/auth/TermsOfServiceScreen.tsx +236 -0
  552. package/src/screens/auth/VerifyEmailScreen.tsx +310 -0
  553. package/src/screens/auth/WelcomeScreen.tsx +78 -0
  554. package/src/screens/auth/index.ts +36 -0
  555. package/src/screens/index.ts +1 -0
  556. package/src/services/__tests__/api-config.test.ts +67 -0
  557. package/src/services/__tests__/favicon.test.ts +88 -0
  558. package/src/services/__tests__/hibp-cache.test.ts +37 -0
  559. package/src/services/__tests__/hibp.test.ts +39 -0
  560. package/src/services/api-config.ts +260 -0
  561. package/src/services/auth.service.ts +498 -0
  562. package/src/services/favicon.ts +102 -0
  563. package/src/services/hibp-cache.ts +71 -0
  564. package/src/services/hibp.ts +120 -0
  565. package/src/services/index.ts +52 -0
  566. package/src/services/passkey.ts +213 -0
  567. package/src/types/external.d.ts +24 -0
  568. package/src/utils/__tests__/clipboard.test.ts +59 -0
  569. package/src/utils/__tests__/cn.test.ts +24 -0
  570. package/src/utils/__tests__/debounce.test.ts +42 -0
  571. package/src/utils/__tests__/error.test.ts +18 -0
  572. package/src/utils/__tests__/http-error.test.ts +33 -0
  573. package/src/utils/__tests__/palette.test.ts +22 -0
  574. package/src/utils/__tests__/password.test.ts +55 -0
  575. package/src/utils/__tests__/settings-storage.test.ts +15 -0
  576. package/src/utils/__tests__/validation.test.ts +52 -0
  577. package/src/utils/api-fetch.ts +61 -0
  578. package/src/utils/clipboard.ts +69 -0
  579. package/src/utils/cn.ts +12 -0
  580. package/src/utils/debounce.ts +33 -0
  581. package/src/utils/error.ts +10 -0
  582. package/src/utils/fetch.ts +33 -0
  583. package/src/utils/http-error.ts +81 -0
  584. package/src/utils/index.ts +23 -0
  585. package/src/utils/palette.ts +105 -0
  586. package/src/utils/password.ts +64 -0
  587. package/src/utils/settings-storage.ts +68 -0
  588. package/src/utils/validation.ts +71 -0
  589. package/styles/globals.css +262 -0
  590. package/tailwind-preset.js +163 -0
@@ -0,0 +1,498 @@
1
+ import { Platform } from "react-native";
2
+ import { apiFetch } from "../utils/api-fetch";
3
+ import { extractApiError, extractErrorMessage } from "../utils/http-error";
4
+ import {
5
+ validateEmail as validateEmailUtil,
6
+ validatePassword as validatePasswordUtil,
7
+ validateUsername as validateUsernameUtil,
8
+ } from "../utils/validation";
9
+ import * as ApiConfig from "./api-config";
10
+ import { clearCachedPolicy as clearHibpPolicyCache } from "./hibp-cache";
11
+
12
+ // Native persists the user record in SecureStore so the next app launch
13
+ // has it before /auth/me lands. Web keeps it in process memory only —
14
+ // sessionStorage is XSS-readable and the cookie-bound session lets us
15
+ // re-fetch the record from the server on the next mount without a
16
+ // noticeable UX regression. See SECURITY.md.
17
+ const USER_DATA_KEY_SUFFIX = "user_data";
18
+ let inMemoryUser: User | null = null;
19
+
20
+ function userDataKey(): string {
21
+ return `${ApiConfig.getStoragePrefix()}_${USER_DATA_KEY_SUFFIX}`;
22
+ }
23
+
24
+ async function persistUser(user: User): Promise<void> {
25
+ if (Platform.OS === "web") {
26
+ inMemoryUser = user;
27
+ return;
28
+ }
29
+ await ApiConfig.kvPut(userDataKey(), JSON.stringify(user));
30
+ }
31
+
32
+ async function clearPersistedUser(): Promise<void> {
33
+ if (Platform.OS === "web") {
34
+ inMemoryUser = null;
35
+ return;
36
+ }
37
+ await ApiConfig.kvPut(userDataKey(), null);
38
+ }
39
+
40
+ /**
41
+ * Normalize a login/register response body into an {@link AuthResponse}.
42
+ * Single-tenant servers wrap the user as `{ user, session_token }`;
43
+ * the multi-tenant IdP (access) returns the user record directly and
44
+ * establishes the session via an HttpOnly cookie (no bearer token).
45
+ */
46
+ async function parseAuthResponse(response: Response): Promise<AuthResponse> {
47
+ const json: unknown = await response.json();
48
+ if (json && typeof json === "object" && "user" in json) {
49
+ return json as AuthResponse;
50
+ }
51
+ return { user: json as User, session_token: "" };
52
+ }
53
+
54
+ async function loadPersistedUser(): Promise<User | null> {
55
+ if (Platform.OS === "web") {
56
+ return inMemoryUser;
57
+ }
58
+ try {
59
+ const userJson = await ApiConfig.kvGet(userDataKey());
60
+ if (!userJson) {
61
+ return null;
62
+ }
63
+ return JSON.parse(userJson) as User;
64
+ } catch {
65
+ return null;
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Shape of the user record returned by Entropy Softworks servers. Apps
71
+ * may carry additional fields — extend the interface in the consumer if
72
+ * needed and pass the extended type through `useAuth<TUser>()`.
73
+ */
74
+ /**
75
+ * Conventional onboarding-step values produced by Entropy Softworks
76
+ * servers. Apps may carry additional vendor-specific steps — keep the
77
+ * field typed as `string` to stay compatible with the openapi-generated
78
+ * `UserPublic` shape that vault and bootstrap import from their shared
79
+ * packages.
80
+ */
81
+ export type OnboardingStep = "email_verification" | "mfa_enrollment" | "import" | "complete";
82
+
83
+ export interface User {
84
+ id: string;
85
+ email: string;
86
+ /** Optional: email-only servers (e.g. Simple Finance) don't issue usernames. */
87
+ username?: string;
88
+ display_name?: string;
89
+ role: "Admin" | "User";
90
+ /**
91
+ * Pending step in the post-signup onboarding flow. Typed as `string`
92
+ * (rather than the `OnboardingStep` union) to stay assignment-compatible
93
+ * with server-generated openapi types; the auth screens compare against
94
+ * the known constants and treat unknown values as "complete".
95
+ */
96
+ onboarding_step?: string | null;
97
+ email_verified?: boolean;
98
+ mfa_enabled?: boolean;
99
+ mfa_verified?: boolean;
100
+ created_at?: string;
101
+ }
102
+
103
+ export interface AuthResponse {
104
+ user: User;
105
+ /**
106
+ * Bearer token for native clients. Web clients receive an HttpOnly
107
+ * cookie and ignore this field.
108
+ */
109
+ session_token: string;
110
+ }
111
+
112
+ /** Public server feature flags from `GET /config`. */
113
+ export interface ServerConfig {
114
+ /** New accounts must enroll a passkey/TOTP during onboarding. */
115
+ require_mfa_enrollment: boolean;
116
+ registration_enabled: boolean;
117
+ }
118
+
119
+ export interface LoginRequest {
120
+ email: string;
121
+ password: string;
122
+ }
123
+
124
+ export interface RegisterRequest {
125
+ email: string;
126
+ /** Optional: omitted for email-only servers (e.g. Simple Finance). */
127
+ username?: string;
128
+ password: string;
129
+ display_name?: string;
130
+ }
131
+
132
+ export interface ErrorResponse {
133
+ error: {
134
+ message: string;
135
+ details?: string[];
136
+ code?: string;
137
+ };
138
+ }
139
+
140
+ export interface MfaSetupResponse {
141
+ uri: string;
142
+ secret: string;
143
+ recovery_codes: string[];
144
+ }
145
+
146
+ /**
147
+ * Auth client for Entropy Softworks servers. Apps call `configureApi`
148
+ * (from `@entropy-softworks/ui/services`) at startup so storage keys and
149
+ * the base URL are wired up before any of these methods run.
150
+ */
151
+ class AuthService {
152
+ static loadServerUrlOverride = ApiConfig.loadServerUrlOverride;
153
+ static getBaseUrl = ApiConfig.getBaseUrl;
154
+ static setBaseUrl = ApiConfig.setBaseUrl;
155
+ static resetBaseUrl = ApiConfig.resetBaseUrl;
156
+ static getSessionToken = ApiConfig.getSessionToken;
157
+
158
+ /**
159
+ * Persist the session credential + user record produced by an
160
+ * alternative auth path (passkey-finish, magic-link, etc.) so the
161
+ * rest of `useAuth` / `getCurrentUser` sees the same state the
162
+ * password `login` flow leaves behind.
163
+ *
164
+ * Web: session credential is a server-set HttpOnly cookie; only
165
+ * the user record persists here, and it lives in process memory
166
+ * (NOT sessionStorage) so an XSS exploit cannot read it.
167
+ * Native: token goes through SecureStore, user record goes
168
+ * through SecureStore alongside it.
169
+ */
170
+ static async persistAuthSession(data: AuthResponse): Promise<void> {
171
+ // Both writes target independent storage keys; parallelising them
172
+ // saves one round-trip on the login / register hot path.
173
+ await Promise.all([ApiConfig.storeSessionToken(data.session_token), persistUser(data.user)]);
174
+ }
175
+
176
+ static async login(email: string, password: string): Promise<AuthResponse> {
177
+ // NFC-normalize every password that crosses the wire so the
178
+ // server-side auth hash agrees with whatever key the client
179
+ // derived from the same string. Without this a master password
180
+ // typed on a keyboard / IME that emits combining-character
181
+ // sequences on one device and precomposed forms on another
182
+ // produces two different hashes — login looks like wrong
183
+ // password while everything visually matches. See
184
+ // `shared/crypto/webcrypto.ts deriveMasterKey` for the
185
+ // client-side twin.
186
+ const pw = password.normalize("NFC");
187
+ // Multi-tenant IdP (access): { tenant, identifier, password } where the
188
+ // identifier is an email OR username and the tenant is contextual (set
189
+ // via ApiConfig.setTenant from the OIDC flow). Single-tenant servers use
190
+ // the { email, password } contract. `email` is the identifier either way.
191
+ const body =
192
+ ApiConfig.getAuthMode() === "multi-tenant"
193
+ ? { tenant: ApiConfig.getTenant(), identifier: email, password: pw }
194
+ : ({ email, password: pw } satisfies LoginRequest);
195
+ const response = await apiFetch("/auth/login", {
196
+ method: "POST",
197
+ headers: { "Content-Type": "application/json" },
198
+ body: JSON.stringify(body),
199
+ });
200
+
201
+ if (!response.ok) {
202
+ throw await extractApiError(response, "Login failed");
203
+ }
204
+
205
+ const data = await parseAuthResponse(response);
206
+ await AuthService.persistAuthSession(data);
207
+ return data;
208
+ }
209
+
210
+ static async register(
211
+ email: string,
212
+ username: string | undefined,
213
+ password: string,
214
+ displayName?: string
215
+ ): Promise<AuthResponse> {
216
+ const body: RegisterRequest = { email, password: password.normalize("NFC") };
217
+ if (username) {
218
+ body.username = username;
219
+ }
220
+ if (displayName !== undefined) {
221
+ body.display_name = displayName;
222
+ }
223
+ const response = await apiFetch("/auth/register", {
224
+ method: "POST",
225
+ headers: { "Content-Type": "application/json" },
226
+ body: JSON.stringify(body),
227
+ });
228
+
229
+ if (!response.ok) {
230
+ throw await extractApiError(response, "Registration failed");
231
+ }
232
+
233
+ const data = await parseAuthResponse(response);
234
+ await AuthService.persistAuthSession(data);
235
+ return data;
236
+ }
237
+
238
+ static async logout(): Promise<void> {
239
+ try {
240
+ await apiFetch("/auth/logout", { method: "POST" });
241
+ } catch (e) {
242
+ // Logout network call failed — still clear local state below.
243
+ // Surface the underlying reason in dev so a half-broken logout
244
+ // (server session still alive after the user thinks they're
245
+ // gone) doesn't slip past unnoticed. In production we always
246
+ // clear local state so the UX is "logged out" even if the
247
+ // server didn't get the request.
248
+ if (__DEV__) {
249
+ console.warn("[auth] /auth/logout failed; clearing local state anyway:", e);
250
+ }
251
+ } finally {
252
+ await ApiConfig.clearSessionToken();
253
+ await clearPersistedUser();
254
+ clearHibpPolicyCache();
255
+ }
256
+ }
257
+
258
+ static async getCurrentUser(): Promise<User | null> {
259
+ return loadPersistedUser();
260
+ }
261
+
262
+ static async refreshUserData(): Promise<User | null> {
263
+ try {
264
+ if (!(await this.isAuthenticated())) {
265
+ return null;
266
+ }
267
+ const response = await apiFetch("/auth/me", { method: "GET" });
268
+ if (!response.ok) {
269
+ if (response.status === 401) {
270
+ await this.logout();
271
+ }
272
+ return null;
273
+ }
274
+ const user: User = await response.json();
275
+ await persistUser(user);
276
+ return user;
277
+ } catch {
278
+ return null;
279
+ }
280
+ }
281
+
282
+ /**
283
+ * Web: HttpOnly cookies aren't readable from JS. We use the
284
+ * presence of the (non-HttpOnly) `csrf_token` cookie — set by the
285
+ * server alongside the session cookie at login — as the
286
+ * "authenticated" probe. Falls back to the in-memory user record if
287
+ * cookies are unreadable (SSR / non-browser test envs).
288
+ * Native: a token in SecureStore.
289
+ */
290
+ static async isAuthenticated(): Promise<boolean> {
291
+ if (Platform.OS === "web") {
292
+ if (typeof document !== "undefined" && document.cookie) {
293
+ if (/(?:^|;\s*)csrf_token=/.test(document.cookie)) {
294
+ return true;
295
+ }
296
+ }
297
+ return (await this.getCurrentUser()) !== null;
298
+ }
299
+ const token = await ApiConfig.getSessionToken();
300
+ return token !== null;
301
+ }
302
+
303
+ static async hasRole(role: "Admin" | "User"): Promise<boolean> {
304
+ const user = await this.getCurrentUser();
305
+ return user?.role === role;
306
+ }
307
+
308
+ static async deleteAccount(password: string): Promise<void> {
309
+ const response = await apiFetch("/auth/delete", {
310
+ method: "DELETE",
311
+ headers: { "Content-Type": "application/json" },
312
+ body: JSON.stringify({ password: password.normalize("NFC") }),
313
+ });
314
+ if (!response.ok) {
315
+ throw new Error(await extractErrorMessage(response, "Failed to delete account"));
316
+ }
317
+ await ApiConfig.clearSessionToken();
318
+ await clearPersistedUser();
319
+ }
320
+
321
+ static async changePassword(
322
+ currentPassword: string,
323
+ newPassword: string,
324
+ reWrappedKey?: { symmetric_key_enc: string; pbkdf2_salt: string }
325
+ ): Promise<void> {
326
+ const body: Record<string, unknown> = {
327
+ current_password: currentPassword.normalize("NFC"),
328
+ new_password: newPassword.normalize("NFC"),
329
+ };
330
+ if (reWrappedKey) {
331
+ body.symmetric_key_enc = reWrappedKey.symmetric_key_enc;
332
+ body.pbkdf2_salt = reWrappedKey.pbkdf2_salt;
333
+ }
334
+ const response = await apiFetch("/auth/password", {
335
+ method: "PUT",
336
+ headers: { "Content-Type": "application/json" },
337
+ body: JSON.stringify(body),
338
+ });
339
+ if (!response.ok) {
340
+ throw new Error(await extractErrorMessage(response, "Failed to change password"));
341
+ }
342
+ }
343
+
344
+ static async changeEmail(newEmail: string, password: string): Promise<void> {
345
+ const response = await apiFetch("/auth/email", {
346
+ method: "PUT",
347
+ headers: { "Content-Type": "application/json" },
348
+ body: JSON.stringify({ new_email: newEmail, password: password.normalize("NFC") }),
349
+ });
350
+ if (!response.ok) {
351
+ throw new Error(await extractErrorMessage(response, "Failed to change email"));
352
+ }
353
+ }
354
+
355
+ static async updateDisplayName(displayName: string): Promise<void> {
356
+ const response = await apiFetch("/auth/display-name", {
357
+ method: "PUT",
358
+ headers: { "Content-Type": "application/json" },
359
+ body: JSON.stringify({ display_name: displayName }),
360
+ });
361
+ if (!response.ok) {
362
+ throw new Error(await extractErrorMessage(response, "Failed to update display name"));
363
+ }
364
+ }
365
+
366
+ static async setupMfa(): Promise<MfaSetupResponse> {
367
+ const response = await apiFetch("/auth/mfa/setup", { method: "POST" });
368
+ if (!response.ok) {
369
+ throw new Error(await extractErrorMessage(response, "Failed to set up MFA"));
370
+ }
371
+ return await response.json();
372
+ }
373
+
374
+ static async verifyMfaSetup(code: string): Promise<void> {
375
+ const response = await apiFetch("/auth/mfa/verify-setup", {
376
+ method: "POST",
377
+ headers: { "Content-Type": "application/json" },
378
+ body: JSON.stringify({ code }),
379
+ });
380
+ if (!response.ok) {
381
+ throw new Error(await extractErrorMessage(response, "Invalid verification code"));
382
+ }
383
+ }
384
+
385
+ static async verifyMfa(code: string): Promise<void> {
386
+ const response = await apiFetch("/auth/mfa/verify", {
387
+ method: "POST",
388
+ headers: { "Content-Type": "application/json" },
389
+ body: JSON.stringify({ code }),
390
+ });
391
+ if (!response.ok) {
392
+ throw new Error(await extractErrorMessage(response, "Invalid MFA code"));
393
+ }
394
+ }
395
+
396
+ static async disableMfa(password: string): Promise<void> {
397
+ const response = await apiFetch("/auth/mfa", {
398
+ method: "DELETE",
399
+ headers: { "Content-Type": "application/json" },
400
+ body: JSON.stringify({ password: password.normalize("NFC") }),
401
+ });
402
+ if (!response.ok) {
403
+ throw new Error(await extractErrorMessage(response, "Failed to disable MFA"));
404
+ }
405
+ }
406
+
407
+ static async forgotPassword(email: string): Promise<void> {
408
+ // Multi-tenant IdP scopes the reset request to a tenant.
409
+ const body =
410
+ ApiConfig.getAuthMode() === "multi-tenant"
411
+ ? { tenant: ApiConfig.getTenant(), email }
412
+ : { email };
413
+ const response = await apiFetch("/auth/forgot-password", {
414
+ method: "POST",
415
+ headers: { "Content-Type": "application/json" },
416
+ body: JSON.stringify(body),
417
+ });
418
+ if (!response.ok) {
419
+ throw new Error(await extractErrorMessage(response, "Failed to send reset email"));
420
+ }
421
+ }
422
+
423
+ static async validateResetCode(code: string): Promise<void> {
424
+ const response = await apiFetch("/auth/validate-reset-code", {
425
+ method: "POST",
426
+ headers: { "Content-Type": "application/json" },
427
+ body: JSON.stringify({ code }),
428
+ });
429
+ if (!response.ok) {
430
+ throw await extractApiError(response, "Invalid or expired code");
431
+ }
432
+ }
433
+
434
+ static async resetPassword(code: string, newPassword: string): Promise<void> {
435
+ const response = await apiFetch("/auth/reset-password", {
436
+ method: "POST",
437
+ headers: { "Content-Type": "application/json" },
438
+ body: JSON.stringify({ code, new_password: newPassword.normalize("NFC") }),
439
+ });
440
+ if (!response.ok) {
441
+ throw await extractApiError(response, "Failed to reset password");
442
+ }
443
+ }
444
+
445
+ static async verifyEmail(code: string): Promise<void> {
446
+ const response = await apiFetch("/auth/verify-email", {
447
+ method: "POST",
448
+ headers: { "Content-Type": "application/json" },
449
+ body: JSON.stringify({ code }),
450
+ });
451
+ if (!response.ok) {
452
+ throw new Error(await extractErrorMessage(response, "Email verification failed"));
453
+ }
454
+ }
455
+
456
+ static async resendVerification(): Promise<void> {
457
+ const response = await apiFetch("/auth/resend-verification", { method: "POST" });
458
+ if (!response.ok) {
459
+ throw new Error(await extractErrorMessage(response, "Failed to resend verification email"));
460
+ }
461
+ }
462
+
463
+ static async completeOnboarding(): Promise<void> {
464
+ const response = await apiFetch("/auth/complete-onboarding", { method: "POST" });
465
+ if (!response.ok) {
466
+ throw new Error(await extractErrorMessage(response, "Failed to complete onboarding"));
467
+ }
468
+ }
469
+
470
+ /**
471
+ * Public server configuration (feature flags) — read pre-auth to adapt the
472
+ * UI, e.g. whether MFA enrollment is mandatory during onboarding. Returns
473
+ * permissive defaults if the endpoint is unavailable so onboarding never
474
+ * hard-blocks on a missing/older server.
475
+ */
476
+ static async getServerConfig(): Promise<ServerConfig> {
477
+ try {
478
+ const response = await apiFetch("/config", { method: "GET" });
479
+ if (!response.ok) {
480
+ return { require_mfa_enrollment: false, registration_enabled: true };
481
+ }
482
+ return (await response.json()) as ServerConfig;
483
+ } catch {
484
+ return { require_mfa_enrollment: false, registration_enabled: true };
485
+ }
486
+ }
487
+
488
+ // Validator hints kept on the class as re-exports of the canonical
489
+ // free functions in `utils/validation`. The server enforces the same
490
+ // rules authoritatively (see entropy-auth's `password::policy`); the
491
+ // versions here are UI-only previews so the user sees feedback before
492
+ // the round-trip.
493
+ static validatePassword = validatePasswordUtil;
494
+ static validateEmail = validateEmailUtil;
495
+ static validateUsername = validateUsernameUtil;
496
+ }
497
+
498
+ export default AuthService;
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Favicon-resolution helpers shared between mobile + extension.
3
+ *
4
+ * Items in the vault (and analogues in other Entropy apps) store an
5
+ * `icon_url` reference that may be:
6
+ *
7
+ * * a `custom:<uploadId>` marker for a server-side upload — resolved
8
+ * at render time through the auth-aware transport;
9
+ * * an arbitrary public URL (logo CDN, etc.) — used verbatim;
10
+ * * `null` / empty — the renderer derives a favicon URL from the
11
+ * item's site URL via the server's favicon-proxy endpoint.
12
+ */
13
+
14
+ import { getBaseUrl } from "./api-config";
15
+
16
+ const CUSTOM_ICON_PREFIX = "custom:";
17
+
18
+ /**
19
+ * Build the favicon-proxy URL for a site, or return `null` when no
20
+ * proxy URL can be built. The caller falls back to its default
21
+ * placeholder when null is returned.
22
+ */
23
+ export function buildFaviconUrl(
24
+ siteUrl: string | null | undefined,
25
+ faviconPath = "/favicon"
26
+ ): string | null {
27
+ if (!siteUrl) {
28
+ return null;
29
+ }
30
+ const trimmed = siteUrl.trim();
31
+ if (!trimmed) {
32
+ return null;
33
+ }
34
+ let normalized = trimmed;
35
+ // Reject non-http(s) schemes up front. If we skipped this and just
36
+ // prepended `https://`, an `ftp:` input would parse as
37
+ // `https://ftp://…` — `URL` accepts it, the protocol comes out as
38
+ // `https:`, and a non-web origin leaks into the favicon proxy query.
39
+ const explicitScheme = /^([a-z][a-z0-9+.-]*):\/\//i.exec(normalized)?.[1];
40
+ if (explicitScheme && !/^https?$/i.test(explicitScheme)) {
41
+ return null;
42
+ }
43
+ if (!/^https?:\/\//i.test(normalized)) {
44
+ normalized = `https://${normalized}`;
45
+ }
46
+ try {
47
+ const parsed = new URL(normalized);
48
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
49
+ return null;
50
+ }
51
+ return `${getBaseUrl()}${faviconPath}?url=${encodeURIComponent(parsed.toString())}`;
52
+ } catch {
53
+ return null;
54
+ }
55
+ }
56
+
57
+ /** Wrap a raw upload id as a `custom:<id>` reference for storage. */
58
+ export function customIconRef(uploadId: string): string {
59
+ return `${CUSTOM_ICON_PREFIX}${uploadId}`;
60
+ }
61
+
62
+ /** True if the stored icon reference points at a custom upload. */
63
+ export function isCustomIconRef(iconRef: string | null | undefined): boolean {
64
+ return !!iconRef && iconRef.startsWith(CUSTOM_ICON_PREFIX);
65
+ }
66
+
67
+ /** Extract the upload id from a `custom:<id>` reference. */
68
+ export function customIconId(iconRef: string | null | undefined): string | null {
69
+ if (!iconRef || !iconRef.startsWith(CUSTOM_ICON_PREFIX)) {
70
+ return null;
71
+ }
72
+ const id = iconRef.slice(CUSTOM_ICON_PREFIX.length);
73
+ return id || null;
74
+ }
75
+
76
+ /** Tagged result describing how the renderer should source an item's icon. */
77
+ export type ResolvedIcon =
78
+ | { kind: "upload"; uploadId: string }
79
+ | { kind: "url"; uri: string }
80
+ | { kind: "none" };
81
+
82
+ /**
83
+ * Pick the right source for an item's icon. Returning a tagged variant
84
+ * (rather than always coercing to a URL string) lets the renderer
85
+ * handle the auth-required upload case differently from public-favicon
86
+ * URLs without sniffing path patterns at the call site.
87
+ */
88
+ export function resolveIcon(opts: {
89
+ iconUrl?: string | null;
90
+ siteUrl?: string | null;
91
+ faviconPath?: string;
92
+ }): ResolvedIcon {
93
+ if (opts.iconUrl) {
94
+ const uploadId = customIconId(opts.iconUrl);
95
+ if (uploadId) {
96
+ return { kind: "upload", uploadId };
97
+ }
98
+ return { kind: "url", uri: opts.iconUrl };
99
+ }
100
+ const fav = buildFaviconUrl(opts.siteUrl, opts.faviconPath);
101
+ return fav ? { kind: "url", uri: fav } : { kind: "none" };
102
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Shared HIBP policy cache — separated to avoid circular imports between
3
+ * `auth.service` (which clears it) and the consumer's policy fetcher.
4
+ */
5
+ import { apiFetch } from "../utils/api-fetch";
6
+
7
+ export interface HibpPolicy {
8
+ enabled: boolean;
9
+ enforce: boolean;
10
+ block_threshold: number;
11
+ }
12
+
13
+ interface ServerConfig {
14
+ hibp: HibpPolicy;
15
+ }
16
+
17
+ /** Conservative default — block re-use of widely-leaked passwords. */
18
+ const DEFAULT_POLICY: HibpPolicy = {
19
+ enabled: true,
20
+ enforce: true,
21
+ block_threshold: 1000,
22
+ };
23
+
24
+ let cachedPolicy: HibpPolicy | null = null;
25
+
26
+ export function getCachedPolicy(): HibpPolicy | null {
27
+ return cachedPolicy;
28
+ }
29
+
30
+ export function setCachedPolicy(policy: HibpPolicy): void {
31
+ cachedPolicy = policy;
32
+ }
33
+
34
+ export function clearCachedPolicy(): void {
35
+ cachedPolicy = null;
36
+ }
37
+
38
+ /**
39
+ * Fetch the HIBP policy from the server (`GET /config`). Result is
40
+ * memoized in the in-process cache for the lifetime of the app — call
41
+ * once at startup (typically from `<AuthProvider onBeforeCheckAuth>`).
42
+ * Server-unreachable falls back to `DEFAULT_POLICY` so the caller is
43
+ * never blocked.
44
+ */
45
+ export async function fetchHibpPolicy(): Promise<HibpPolicy> {
46
+ const cached = cachedPolicy;
47
+ if (cached) {
48
+ return cached;
49
+ }
50
+ try {
51
+ const response = await apiFetch("/config");
52
+ if (response.ok) {
53
+ const config: ServerConfig = await response.json();
54
+ cachedPolicy = config.hibp;
55
+ return config.hibp;
56
+ }
57
+ } catch {
58
+ // Server unreachable — fall through to default.
59
+ }
60
+ cachedPolicy = DEFAULT_POLICY;
61
+ return DEFAULT_POLICY;
62
+ }
63
+
64
+ /**
65
+ * Synchronous view of the cached policy. Returns the default when
66
+ * `fetchHibpPolicy` hasn't completed yet; suitable for render-time
67
+ * gating where blocking is unacceptable.
68
+ */
69
+ export function getHibpPolicy(): HibpPolicy {
70
+ return cachedPolicy ?? DEFAULT_POLICY;
71
+ }