hazo_auth 5.1.3 → 5.1.6

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 (252) hide show
  1. package/README.md +12 -0
  2. package/cli-src/cli/init_users.ts +8 -42
  3. package/cli-src/lib/auth/auth_cache.ts +9 -8
  4. package/cli-src/lib/auth/auth_types.ts +1 -1
  5. package/cli-src/lib/auth/auth_utils.server.ts +6 -6
  6. package/cli-src/lib/auth/hazo_get_auth.server.ts +39 -22
  7. package/cli-src/lib/auth/index.ts +1 -1
  8. package/cli-src/lib/auth/server_auth.ts +3 -3
  9. package/cli-src/lib/index.ts +3 -3
  10. package/cli-src/lib/services/index.ts +20 -20
  11. package/cli-src/lib/services/login_service.ts +2 -2
  12. package/cli-src/lib/services/oauth_service.ts +1 -1
  13. package/cli-src/lib/services/registration_service.ts +1 -1
  14. package/dist/cli/init_users.d.ts.map +1 -1
  15. package/dist/cli/init_users.js +7 -37
  16. package/dist/client.d.ts +7 -7
  17. package/dist/client.js +7 -7
  18. package/dist/components/index.d.ts +2 -2
  19. package/dist/components/index.js +2 -2
  20. package/dist/components/layouts/create_firm/index.d.ts +1 -1
  21. package/dist/components/layouts/create_firm/index.js +7 -7
  22. package/dist/components/layouts/dev_lock/index.js +2 -2
  23. package/dist/components/layouts/email_verification/config/email_verification_field_config.d.ts +1 -1
  24. package/dist/components/layouts/email_verification/config/email_verification_field_config.js +1 -1
  25. package/dist/components/layouts/email_verification/hooks/use_email_verification.d.ts +1 -1
  26. package/dist/components/layouts/email_verification/hooks/use_email_verification.js +3 -3
  27. package/dist/components/layouts/email_verification/index.d.ts +3 -3
  28. package/dist/components/layouts/email_verification/index.js +9 -9
  29. package/dist/components/layouts/forgot_password/config/forgot_password_field_config.d.ts +1 -1
  30. package/dist/components/layouts/forgot_password/config/forgot_password_field_config.js +1 -1
  31. package/dist/components/layouts/forgot_password/hooks/use_forgot_password_form.d.ts +1 -1
  32. package/dist/components/layouts/forgot_password/hooks/use_forgot_password_form.js +3 -3
  33. package/dist/components/layouts/forgot_password/index.d.ts +2 -2
  34. package/dist/components/layouts/forgot_password/index.js +9 -9
  35. package/dist/components/layouts/index.d.ts +9 -9
  36. package/dist/components/layouts/index.js +9 -9
  37. package/dist/components/layouts/login/config/login_field_config.d.ts +1 -1
  38. package/dist/components/layouts/login/config/login_field_config.js +1 -1
  39. package/dist/components/layouts/login/hooks/use_login_form.d.ts +1 -1
  40. package/dist/components/layouts/login/hooks/use_login_form.js +5 -5
  41. package/dist/components/layouts/login/index.d.ts +2 -2
  42. package/dist/components/layouts/login/index.js +11 -11
  43. package/dist/components/layouts/my_settings/components/connected_accounts_section.js +1 -1
  44. package/dist/components/layouts/my_settings/components/editable_field.js +3 -3
  45. package/dist/components/layouts/my_settings/components/password_change_dialog.js +4 -4
  46. package/dist/components/layouts/my_settings/components/profile_picture_dialog.js +8 -8
  47. package/dist/components/layouts/my_settings/components/profile_picture_display.js +1 -1
  48. package/dist/components/layouts/my_settings/components/profile_picture_gravatar_tab.js +3 -3
  49. package/dist/components/layouts/my_settings/components/profile_picture_library_tab.js +6 -6
  50. package/dist/components/layouts/my_settings/components/profile_picture_upload_tab.js +4 -4
  51. package/dist/components/layouts/my_settings/components/set_password_section.js +4 -4
  52. package/dist/components/layouts/my_settings/config/my_settings_field_config.d.ts +1 -1
  53. package/dist/components/layouts/my_settings/config/my_settings_field_config.js +1 -1
  54. package/dist/components/layouts/my_settings/hooks/use_my_settings.js +2 -2
  55. package/dist/components/layouts/my_settings/index.d.ts +1 -1
  56. package/dist/components/layouts/my_settings/index.js +12 -12
  57. package/dist/components/layouts/register/config/register_field_config.d.ts +1 -1
  58. package/dist/components/layouts/register/config/register_field_config.js +1 -1
  59. package/dist/components/layouts/register/hooks/use_register_form.d.ts +1 -1
  60. package/dist/components/layouts/register/hooks/use_register_form.js +3 -3
  61. package/dist/components/layouts/register/index.d.ts +2 -2
  62. package/dist/components/layouts/register/index.js +9 -9
  63. package/dist/components/layouts/reset_password/config/reset_password_field_config.d.ts +1 -1
  64. package/dist/components/layouts/reset_password/config/reset_password_field_config.js +1 -1
  65. package/dist/components/layouts/reset_password/hooks/use_reset_password_form.d.ts +1 -1
  66. package/dist/components/layouts/reset_password/hooks/use_reset_password_form.js +3 -3
  67. package/dist/components/layouts/reset_password/index.d.ts +2 -2
  68. package/dist/components/layouts/reset_password/index.js +8 -8
  69. package/dist/components/layouts/scope_management/components/branding_editor.js +5 -5
  70. package/dist/components/layouts/shared/components/already_logged_in_guard.js +4 -4
  71. package/dist/components/layouts/shared/components/auth_navbar.js +1 -1
  72. package/dist/components/layouts/shared/components/auth_page_shell.js +3 -3
  73. package/dist/components/layouts/shared/components/form_action_buttons.js +1 -1
  74. package/dist/components/layouts/shared/components/form_field_wrapper.js +2 -2
  75. package/dist/components/layouts/shared/components/google_sign_in_button.js +3 -3
  76. package/dist/components/layouts/shared/components/logout_button.js +3 -3
  77. package/dist/components/layouts/shared/components/oauth_divider.js +1 -1
  78. package/dist/components/layouts/shared/components/password_field.js +3 -3
  79. package/dist/components/layouts/shared/components/profile_pic_menu.js +8 -8
  80. package/dist/components/layouts/shared/components/profile_pic_menu_wrapper.js +2 -2
  81. package/dist/components/layouts/shared/components/profile_stamp.js +3 -3
  82. package/dist/components/layouts/shared/components/sidebar_layout_wrapper.js +3 -3
  83. package/dist/components/layouts/shared/components/standalone_layout_wrapper.d.ts +1 -1
  84. package/dist/components/layouts/shared/components/standalone_layout_wrapper.js +2 -2
  85. package/dist/components/layouts/shared/components/two_column_auth_layout.js +1 -1
  86. package/dist/components/layouts/shared/components/unauthorized_guard.js +2 -2
  87. package/dist/components/layouts/shared/hooks/use_auth_status.js +1 -1
  88. package/dist/components/layouts/shared/hooks/use_firm_branding.js +1 -1
  89. package/dist/components/layouts/shared/hooks/use_hazo_auth.js +1 -1
  90. package/dist/components/layouts/shared/index.d.ts +23 -23
  91. package/dist/components/layouts/shared/index.js +23 -23
  92. package/dist/components/layouts/user_management/components/app_user_data_editor.js +5 -5
  93. package/dist/components/layouts/user_management/components/roles_matrix.js +8 -8
  94. package/dist/components/layouts/user_management/components/scope_hierarchy_tab.js +9 -9
  95. package/dist/components/layouts/user_management/components/user_scopes_tab.js +9 -9
  96. package/dist/components/layouts/user_management/index.js +17 -17
  97. package/dist/components/ui/alert-dialog.js +2 -2
  98. package/dist/components/ui/avatar.js +1 -1
  99. package/dist/components/ui/button.js +1 -1
  100. package/dist/components/ui/card.js +1 -1
  101. package/dist/components/ui/checkbox.js +1 -1
  102. package/dist/components/ui/dialog.js +1 -1
  103. package/dist/components/ui/dropdown-menu.js +1 -1
  104. package/dist/components/ui/hazo_ui_tooltip.js +1 -1
  105. package/dist/components/ui/hover-card.js +1 -1
  106. package/dist/components/ui/index.d.ts +20 -20
  107. package/dist/components/ui/index.js +20 -20
  108. package/dist/components/ui/input.js +1 -1
  109. package/dist/components/ui/label.js +1 -1
  110. package/dist/components/ui/select.js +1 -1
  111. package/dist/components/ui/separator.js +1 -1
  112. package/dist/components/ui/sheet.js +1 -1
  113. package/dist/components/ui/sidebar.d.ts +1 -1
  114. package/dist/components/ui/sidebar.js +8 -8
  115. package/dist/components/ui/skeleton.js +1 -1
  116. package/dist/components/ui/switch.js +1 -1
  117. package/dist/components/ui/table.js +1 -1
  118. package/dist/components/ui/tabs.js +1 -1
  119. package/dist/components/ui/tooltip.js +1 -1
  120. package/dist/components/ui/tree-view.js +1 -1
  121. package/dist/components/ui/user-type-badge.js +1 -1
  122. package/dist/components/ui/vertical-tabs.js +1 -1
  123. package/dist/contexts/hazo_auth_provider.d.ts +1 -1
  124. package/dist/contexts/hazo_auth_provider.js +1 -1
  125. package/dist/index.d.ts +4 -4
  126. package/dist/index.js +4 -4
  127. package/dist/lib/already_logged_in_config.server.js +1 -1
  128. package/dist/lib/app_permissions_config.server.js +1 -1
  129. package/dist/lib/app_user_data_config.server.js +1 -1
  130. package/dist/lib/auth/auth_cache.d.ts +7 -6
  131. package/dist/lib/auth/auth_cache.d.ts.map +1 -1
  132. package/dist/lib/auth/auth_cache.js +3 -3
  133. package/dist/lib/auth/auth_utils.server.js +9 -9
  134. package/dist/lib/auth/dev_lock_validator.edge.js +1 -1
  135. package/dist/lib/auth/hazo_get_auth.server.d.ts.map +1 -1
  136. package/dist/lib/auth/hazo_get_auth.server.js +42 -30
  137. package/dist/lib/auth/index.d.ts +6 -6
  138. package/dist/lib/auth/index.js +6 -6
  139. package/dist/lib/auth/nextauth_config.js +4 -4
  140. package/dist/lib/auth/server_auth.js +6 -6
  141. package/dist/lib/auth/session_token_validator.edge.js +1 -1
  142. package/dist/lib/auth_utility_config.server.js +1 -1
  143. package/dist/lib/branding_config.server.js +1 -1
  144. package/dist/lib/config/config_loader.server.js +1 -1
  145. package/dist/lib/cookies_config.server.js +1 -1
  146. package/dist/lib/dev_lock_config.server.js +2 -2
  147. package/dist/lib/email_verification_config.server.js +2 -2
  148. package/dist/lib/file_types_config.server.js +1 -1
  149. package/dist/lib/forgot_password_config.server.js +2 -2
  150. package/dist/lib/hazo_connect_instance.server.js +2 -2
  151. package/dist/lib/hazo_connect_setup.server.js +2 -2
  152. package/dist/lib/index.d.ts +28 -28
  153. package/dist/lib/index.js +28 -28
  154. package/dist/lib/login_config.server.d.ts +1 -1
  155. package/dist/lib/login_config.server.js +3 -3
  156. package/dist/lib/messages_config.server.js +1 -1
  157. package/dist/lib/my_settings_config.server.js +7 -7
  158. package/dist/lib/navbar_config.server.js +2 -2
  159. package/dist/lib/oauth_config.server.js +2 -2
  160. package/dist/lib/password_requirements_config.server.js +2 -2
  161. package/dist/lib/profile_pic_menu_config.server.js +1 -1
  162. package/dist/lib/profile_picture_config.server.js +2 -2
  163. package/dist/lib/register_config.server.js +4 -4
  164. package/dist/lib/reset_password_config.server.js +3 -3
  165. package/dist/lib/scope_hierarchy_config.server.js +2 -2
  166. package/dist/lib/services/app_user_data_service.js +4 -4
  167. package/dist/lib/services/branding_service.d.ts +1 -1
  168. package/dist/lib/services/branding_service.js +3 -3
  169. package/dist/lib/services/email_service.js +2 -2
  170. package/dist/lib/services/email_verification_service.js +3 -3
  171. package/dist/lib/services/firm_service.d.ts +2 -2
  172. package/dist/lib/services/firm_service.js +4 -4
  173. package/dist/lib/services/index.d.ts +20 -20
  174. package/dist/lib/services/index.js +20 -20
  175. package/dist/lib/services/invitation_service.js +4 -4
  176. package/dist/lib/services/login_service.js +5 -5
  177. package/dist/lib/services/oauth_service.js +5 -5
  178. package/dist/lib/services/password_change_service.js +3 -3
  179. package/dist/lib/services/password_reset_service.js +3 -3
  180. package/dist/lib/services/post_verification_service.js +4 -4
  181. package/dist/lib/services/profile_picture_remove_service.js +3 -3
  182. package/dist/lib/services/profile_picture_service.d.ts +1 -1
  183. package/dist/lib/services/profile_picture_service.js +5 -5
  184. package/dist/lib/services/registration_service.js +10 -10
  185. package/dist/lib/services/scope_service.js +2 -2
  186. package/dist/lib/services/session_token_service.js +2 -2
  187. package/dist/lib/services/token_service.js +2 -2
  188. package/dist/lib/services/user_profiles_service.js +4 -4
  189. package/dist/lib/services/user_scope_service.js +3 -3
  190. package/dist/lib/services/user_update_service.d.ts +1 -1
  191. package/dist/lib/services/user_update_service.js +4 -4
  192. package/dist/lib/ui_shell_config.server.d.ts +1 -1
  193. package/dist/lib/ui_shell_config.server.js +2 -2
  194. package/dist/lib/ui_sizes_config.server.js +1 -1
  195. package/dist/lib/user_fields_config.server.js +1 -1
  196. package/dist/lib/user_management_config.server.js +1 -1
  197. package/dist/lib/user_profiles_config.server.js +1 -1
  198. package/dist/lib/user_types_config.server.js +2 -2
  199. package/dist/lib/utils/error_sanitizer.d.ts +1 -1
  200. package/dist/page_components/create_firm.js +3 -3
  201. package/dist/page_components/dev_lock.js +1 -1
  202. package/dist/page_components/forgot_password.js +3 -3
  203. package/dist/page_components/login.js +3 -3
  204. package/dist/page_components/my_settings.js +1 -1
  205. package/dist/page_components/register.js +3 -3
  206. package/dist/page_components/reset_password.js +3 -3
  207. package/dist/page_components/verify_email.js +3 -3
  208. package/dist/server/index.js +1 -1
  209. package/dist/server/routes/app_user_data.js +5 -5
  210. package/dist/server/routes/change_password.js +7 -7
  211. package/dist/server/routes/create_firm.js +6 -6
  212. package/dist/server/routes/forgot_password.js +4 -4
  213. package/dist/server/routes/get_auth.js +4 -4
  214. package/dist/server/routes/invalidate_cache.js +5 -5
  215. package/dist/server/routes/invitations.js +6 -6
  216. package/dist/server/routes/library_photo.js +3 -3
  217. package/dist/server/routes/library_photos.js +3 -3
  218. package/dist/server/routes/login.js +7 -7
  219. package/dist/server/routes/logout.js +5 -5
  220. package/dist/server/routes/me.js +6 -6
  221. package/dist/server/routes/profile_picture_filename.js +1 -1
  222. package/dist/server/routes/register.js +5 -5
  223. package/dist/server/routes/remove_profile_picture.js +4 -4
  224. package/dist/server/routes/resend_verification.js +4 -4
  225. package/dist/server/routes/reset_password.js +5 -5
  226. package/dist/server/routes/update_user.js +6 -6
  227. package/dist/server/routes/upload_profile_picture.js +7 -7
  228. package/dist/server/routes/user_management_permissions.js +4 -4
  229. package/dist/server/routes/user_management_roles.d.ts.map +1 -1
  230. package/dist/server/routes/user_management_roles.js +17 -15
  231. package/dist/server/routes/user_management_users.d.ts +1 -1
  232. package/dist/server/routes/user_management_users.js +10 -10
  233. package/dist/server/routes/user_management_users_roles.d.ts +12 -5
  234. package/dist/server/routes/user_management_users_roles.d.ts.map +1 -1
  235. package/dist/server/routes/user_management_users_roles.js +118 -89
  236. package/dist/server/routes/validate_reset_token.js +4 -4
  237. package/dist/server/routes/verify_email.js +5 -5
  238. package/dist/server/server.js +2 -2
  239. package/dist/server_pages/forgot_password.js +4 -4
  240. package/dist/server_pages/forgot_password_client_wrapper.js +3 -3
  241. package/dist/server_pages/index.d.ts +6 -6
  242. package/dist/server_pages/index.js +6 -6
  243. package/dist/server_pages/login.js +3 -3
  244. package/dist/server_pages/login_client_wrapper.js +3 -3
  245. package/dist/server_pages/my_settings.js +3 -3
  246. package/dist/server_pages/register.js +3 -3
  247. package/dist/server_pages/register_client_wrapper.js +3 -3
  248. package/dist/server_pages/reset_password.js +3 -3
  249. package/dist/server_pages/reset_password_client_wrapper.js +3 -3
  250. package/dist/server_pages/verify_email.js +4 -4
  251. package/dist/server_pages/verify_email_client_wrapper.js +3 -3
  252. package/package.json +1 -1
package/README.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  A reusable authentication UI component package powered by Next.js, TailwindCSS, and shadcn. It integrates `hazo_config` for configuration management and `hazo_connect` for data access, enabling future components to stay aligned with platform conventions.
4
4
 
5
+ ### What's New in v5.1.5 🐛
6
+
7
+ **CRITICAL BUGFIX**: Fixed incomplete migration from v4.x to v5.x - several files were still referencing the deprecated `hazo_user_roles` table instead of `hazo_user_scopes`. This release completes the scope-based role assignment architecture introduced in v5.0.
8
+
9
+ **Key Fixes:**
10
+ - ✅ **hazo_get_auth** - Now correctly fetches roles from `hazo_user_scopes`
11
+ - ✅ **Role IDs** - Changed from `number[]` to `string[]` (UUIDs) throughout codebase
12
+ - ✅ **User Management** - Updated for scope-based role assignments
13
+ - ✅ **Cache System** - Fixed type inconsistencies with UUID role IDs
14
+
15
+ If you're on v5.x and experiencing permission/role issues, upgrade to v5.1.5 immediately.
16
+
5
17
  ### What's New in v5.0 🚀
6
18
 
7
19
  **BREAKING CHANGE: Scope-Based Multi-Tenancy** - Complete architectural redesign for simpler, more flexible multi-tenancy!
@@ -22,10 +22,7 @@ type InitSummary = {
22
22
  inserted: number;
23
23
  existing: number;
24
24
  };
25
- user_role: {
26
- inserted: boolean;
27
- existing: boolean;
28
- };
25
+ // v5.x: Removed user_role - roles are now assigned via hazo_user_scopes
29
26
  super_admin_scope: {
30
27
  inserted: boolean;
31
28
  existing: boolean;
@@ -78,15 +75,7 @@ function print_summary(summary: InitSummary): void {
78
75
  }
79
76
  console.log();
80
77
 
81
- // User role summary
82
- console.log("User-Role Assignment:");
83
- if (summary.user_role.inserted) {
84
- console.log(` ✓ Inserted: Super user role assigned to user`);
85
- }
86
- if (summary.user_role.existing) {
87
- console.log(` ⊙ Already existed: User already has super user role`);
88
- }
89
- console.log();
78
+ // v5.x: User-Role assignments are now handled via User-Scope assignments (see below)
90
79
 
91
80
  // Super admin scope summary
92
81
  console.log("Super Admin Scope:");
@@ -141,10 +130,7 @@ export async function handle_init_users(options: InitUsersOptions = {}): Promise
141
130
  inserted: 0,
142
131
  existing: 0,
143
132
  },
144
- user_role: {
145
- inserted: false,
146
- existing: false,
147
- },
133
+ // v5.x: Removed user_role - roles are now assigned via hazo_user_scopes
148
134
  super_admin_scope: {
149
135
  inserted: false,
150
136
  existing: false,
@@ -165,7 +151,7 @@ export async function handle_init_users(options: InitUsersOptions = {}): Promise
165
151
  const roles_service = createCrudService(hazoConnect, "hazo_roles");
166
152
  const role_permissions_service = createCrudService(hazoConnect, "hazo_role_permissions");
167
153
  const users_service = createCrudService(hazoConnect, "hazo_users");
168
- const user_roles_service = createCrudService(hazoConnect, "hazo_user_roles");
154
+ // v5.x: Removed hazo_user_roles - roles are now assigned via hazo_user_scopes
169
155
  const scopes_service = createCrudService(hazoConnect, "hazo_scopes");
170
156
  // hazo_user_scopes uses composite primary key (user_id, scope_id), no 'id' column
171
157
  const user_scopes_service = createCrudService(hazoConnect, "hazo_user_scopes", {
@@ -328,27 +314,7 @@ export async function handle_init_users(options: InitUsersOptions = {}): Promise
328
314
  console.log(`✓ Found user: ${super_user_email} (ID: ${user_id})`);
329
315
  console.log();
330
316
 
331
- // 7. Assign role to user
332
- const existing_user_roles = await user_roles_service.findBy({
333
- user_id,
334
- role_id,
335
- });
336
-
337
- if (Array.isArray(existing_user_roles) && existing_user_roles.length > 0) {
338
- summary.user_role.existing = true;
339
- console.log(`✓ User already has role assigned: ${user_id} -> ${role_name}`);
340
- } else {
341
- await user_roles_service.insert({
342
- user_id,
343
- role_id,
344
- created_at: now,
345
- changed_at: now,
346
- });
347
- summary.user_role.inserted = true;
348
- console.log(`✓ Assigned role to user: ${user_id} -> ${role_name}`);
349
- }
350
-
351
- console.log();
317
+ // v5.x: Step 7 removed - role assignment now happens via hazo_user_scopes (see step 9)
352
318
 
353
319
  // 8. Ensure super admin scope exists
354
320
  const existing_scopes = await scopes_service.findBy({ id: SUPER_ADMIN_SCOPE_ID });
@@ -440,9 +406,9 @@ This command reads from hazo_auth_config.ini and:
440
406
  2. Creates a 'default_super_user_role' role
441
407
  3. Assigns all permissions to the super user role
442
408
  4. Finds user by email (from --email parameter or config)
443
- 5. Assigns the super user role to that user
444
- 6. Creates the Super Admin scope (${SUPER_ADMIN_SCOPE_ID})
445
- 7. Assigns the user to the Super Admin scope
409
+ 5. Creates the Super Admin scope (${SUPER_ADMIN_SCOPE_ID})
410
+ 6. Assigns the user to the Super Admin scope with the super user role
411
+ (v5.x: Roles are assigned per-scope via hazo_user_scopes table)
446
412
 
447
413
  Options:
448
414
  --email=<email> Email address of the user to assign super user role
@@ -6,11 +6,12 @@ import type { HazoAuthUser } from "./auth_types";
6
6
 
7
7
  /**
8
8
  * Cache entry structure
9
+ * v5.x: role_ids are now string UUIDs (from hazo_user_scopes)
9
10
  */
10
11
  type CacheEntry = {
11
12
  user: HazoAuthUser;
12
13
  permissions: string[];
13
- role_ids: number[];
14
+ role_ids: string[];
14
15
  timestamp: number; // Unix timestamp in milliseconds
15
16
  cache_version: number; // Version number for smart invalidation
16
17
  };
@@ -24,7 +25,7 @@ class AuthCache {
24
25
  private max_size: number;
25
26
  private ttl_ms: number;
26
27
  private max_age_ms: number;
27
- private role_version_map: Map<number, number>; // Track version per role for smart invalidation
28
+ private role_version_map: Map<string, number>; // Track version per role for smart invalidation (v5.x: string UUIDs)
28
29
 
29
30
  constructor(
30
31
  max_size: number,
@@ -81,13 +82,13 @@ class AuthCache {
81
82
  * @param user_id - User ID
82
83
  * @param user - User data
83
84
  * @param permissions - User permissions
84
- * @param role_ids - User role IDs
85
+ * @param role_ids - User role IDs (v5.x: string UUIDs)
85
86
  */
86
87
  set(
87
88
  user_id: string,
88
89
  user: HazoAuthUser,
89
90
  permissions: string[],
90
- role_ids: number[],
91
+ role_ids: string[],
91
92
  ): void {
92
93
  // Evict LRU entries if cache is full
93
94
  while (this.cache.size >= this.max_size) {
@@ -124,9 +125,9 @@ class AuthCache {
124
125
  /**
125
126
  * Invalidates cache for all users with specific roles
126
127
  * Uses cache version to determine if invalidation is needed
127
- * @param role_ids - Array of role IDs to invalidate
128
+ * @param role_ids - Array of role IDs to invalidate (v5.x: string UUIDs)
128
129
  */
129
- invalidate_by_roles(role_ids: number[]): void {
130
+ invalidate_by_roles(role_ids: string[]): void {
130
131
  // Increment version for affected roles
131
132
  for (const role_id of role_ids) {
132
133
  const current_version = this.role_version_map.get(role_id) || 0;
@@ -157,10 +158,10 @@ class AuthCache {
157
158
  /**
158
159
  * Gets the maximum cache version for a set of roles
159
160
  * Used to determine if cache entry is stale
160
- * @param role_ids - Array of role IDs
161
+ * @param role_ids - Array of role IDs (v5.x: string UUIDs)
161
162
  * @returns Maximum version number
162
163
  */
163
- private get_max_role_version(role_ids: number[]): number {
164
+ private get_max_role_version(role_ids: string[]): number {
164
165
  if (role_ids.length === 0) {
165
166
  return 0;
166
167
  }
@@ -8,7 +8,7 @@ export type HazoAuthUser = {
8
8
  id: string;
9
9
  name: string | null;
10
10
  email_address: string;
11
- is_active: boolean; // Derived from status column: status === 'active'
11
+ is_active: boolean; // Derived from status column: status === 'ACTIVE'
12
12
  profile_picture_url: string | null;
13
13
  // App-specific user data (JSON object stored as TEXT in database)
14
14
  app_user_data: Record<string, unknown> | null;
@@ -69,8 +69,8 @@ export async function get_authenticated_user(request: NextRequest): Promise<Auth
69
69
 
70
70
  const user = users[0];
71
71
 
72
- // Check if user is active (status must be 'active')
73
- if (user.status !== "active") {
72
+ // Check if user is active (status must be 'ACTIVE')
73
+ if (user.status !== "ACTIVE") {
74
74
  return { authenticated: false };
75
75
  }
76
76
 
@@ -84,7 +84,7 @@ export async function get_authenticated_user(request: NextRequest): Promise<Auth
84
84
  email: user.email_address as string,
85
85
  name: (user.name as string | null | undefined) || undefined,
86
86
  email_verified: user.email_verified === true,
87
- is_active: user.status === "active", // Derived from status column
87
+ is_active: user.status === "ACTIVE", // Derived from status column
88
88
  last_logon: (user.last_logon as string | null | undefined) || undefined,
89
89
  profile_picture_url: (user.profile_picture_url as string | null | undefined) || undefined,
90
90
  profile_source: profile_source_ui,
@@ -159,8 +159,8 @@ export async function get_authenticated_user_with_response(request: NextRequest)
159
159
 
160
160
  const user = users[0];
161
161
 
162
- // Check if user is still active (status must be 'active')
163
- if (user.status !== "active") {
162
+ // Check if user is still active (status must be 'ACTIVE')
163
+ if (user.status !== "ACTIVE") {
164
164
  // User is inactive - clear cookies
165
165
  const response = NextResponse.json(
166
166
  { authenticated: false },
@@ -181,7 +181,7 @@ export async function get_authenticated_user_with_response(request: NextRequest)
181
181
  email: user.email_address as string,
182
182
  name: (user.name as string | null | undefined) || undefined,
183
183
  email_verified: user.email_verified === true,
184
- is_active: user.status === "active", // Derived from status column
184
+ is_active: user.status === "ACTIVE", // Derived from status column
185
185
  last_logon: (user.last_logon as string | null | undefined) || undefined,
186
186
  profile_picture_url: (user.profile_picture_url as string | null | undefined) || undefined,
187
187
  profile_source: profile_source_ui,
@@ -77,14 +77,29 @@ function get_client_ip(request: NextRequest): string {
77
77
  * @param user_id - User ID
78
78
  * @returns Object with user, permissions, and role_ids
79
79
  */
80
+ /**
81
+ * CRUD service options for hazo_user_scopes table
82
+ * This table uses a composite primary key (user_id, scope_id) and no 'id' column
83
+ */
84
+ const USER_SCOPES_CRUD_OPTIONS = {
85
+ primaryKeys: ["user_id", "scope_id"],
86
+ autoId: false as const,
87
+ };
88
+
80
89
  async function fetch_user_data_from_db(user_id: string): Promise<{
81
90
  user: HazoAuthUser;
82
91
  permissions: string[];
83
- role_ids: number[];
92
+ role_ids: string[];
84
93
  }> {
85
94
  const hazoConnect = get_hazo_connect_instance();
86
95
  const users_service = createCrudService(hazoConnect, "hazo_users");
87
- const user_roles_service = createCrudService(hazoConnect, "hazo_user_roles");
96
+ // v5.x: Use hazo_user_scopes instead of hazo_user_roles
97
+ // Roles are now assigned per-scope via hazo_user_scopes.role_id
98
+ const user_scopes_service = createCrudService(
99
+ hazoConnect,
100
+ "hazo_user_scopes",
101
+ USER_SCOPES_CRUD_OPTIONS,
102
+ );
88
103
  const role_permissions_service = createCrudService(
89
104
  hazoConnect,
90
105
  "hazo_role_permissions",
@@ -102,8 +117,8 @@ async function fetch_user_data_from_db(user_id: string): Promise<{
102
117
 
103
118
  const user_db = users[0];
104
119
 
105
- // Check if user is active (status must be 'active')
106
- if (user_db.status !== "active") {
120
+ // Check if user is active (status must be 'ACTIVE')
121
+ if (user_db.status !== "ACTIVE") {
107
122
  throw new Error("User is inactive");
108
123
  }
109
124
 
@@ -112,40 +127,42 @@ async function fetch_user_data_from_db(user_id: string): Promise<{
112
127
  id: user_db.id as string,
113
128
  name: (user_db.name as string | null) || null,
114
129
  email_address: user_db.email_address as string,
115
- is_active: user_db.status === "active", // Derived from status column
130
+ is_active: user_db.status === "ACTIVE", // Derived from status column
116
131
  profile_picture_url:
117
132
  (user_db.profile_picture_url as string | null) || null,
118
133
  app_user_data: parse_app_user_data(user_db.app_user_data),
119
134
  };
120
135
 
121
- // Fetch user roles
122
- const user_roles = await user_roles_service.findBy({ user_id });
123
- const role_ids: number[] = [];
124
- if (Array.isArray(user_roles)) {
125
- for (const ur of user_roles) {
126
- const role_id = ur.role_id as number | undefined;
127
- if (role_id !== undefined) {
128
- role_ids.push(role_id);
136
+ // v5.x: Fetch user's roles from hazo_user_scopes (scope-based role assignments)
137
+ // Each scope assignment has a role_id (string UUID)
138
+ const user_scopes = await user_scopes_service.findBy({ user_id });
139
+ const role_ids_set = new Set<string>();
140
+ if (Array.isArray(user_scopes)) {
141
+ for (const us of user_scopes) {
142
+ const role_id = us.role_id as string | undefined;
143
+ if (role_id) {
144
+ role_ids_set.add(role_id);
129
145
  }
130
146
  }
131
147
  }
148
+ const role_ids = Array.from(role_ids_set);
132
149
 
133
150
  // Fetch role permissions
134
151
  const permissions_set = new Set<string>();
135
152
  if (role_ids.length > 0) {
136
153
  const role_permissions = await role_permissions_service.findBy({});
137
154
  if (Array.isArray(role_permissions)) {
138
- // Filter role_permissions for user's roles
155
+ // Filter role_permissions for user's roles (role_id is string UUID in v5.x)
139
156
  const user_role_permissions = role_permissions.filter((rp) =>
140
- role_ids.includes(rp.role_id as number),
157
+ role_ids.includes(rp.role_id as string),
141
158
  );
142
159
 
143
- // Get permission IDs
144
- const permission_ids = new Set<number>();
160
+ // Get permission IDs (can be string or number depending on database)
161
+ const permission_ids = new Set<string>();
145
162
  for (const rp of user_role_permissions) {
146
- const perm_id = rp.permission_id as number | undefined;
163
+ const perm_id = rp.permission_id as string | number | undefined;
147
164
  if (perm_id !== undefined) {
148
- permission_ids.add(perm_id);
165
+ permission_ids.add(String(perm_id));
149
166
  }
150
167
  }
151
168
 
@@ -154,8 +171,8 @@ async function fetch_user_data_from_db(user_id: string): Promise<{
154
171
  const permissions = await permissions_service.findBy({});
155
172
  if (Array.isArray(permissions)) {
156
173
  for (const perm of permissions) {
157
- const perm_id = perm.id as number | undefined;
158
- if (perm_id !== undefined && permission_ids.has(perm_id)) {
174
+ const perm_id = perm.id as string | number | undefined;
175
+ if (perm_id !== undefined && permission_ids.has(String(perm_id))) {
159
176
  const perm_name = perm.permission_name as string | undefined;
160
177
  if (perm_name) {
161
178
  permissions_set.add(perm_name);
@@ -398,7 +415,7 @@ export async function hazo_get_auth(
398
415
  let cached_entry = cache.get(user_id);
399
416
  let user: HazoAuthUser;
400
417
  let permissions: string[];
401
- let role_ids: number[];
418
+ let role_ids: string[]; // v5.x: role_ids are now string UUIDs
402
419
 
403
420
  if (cached_entry) {
404
421
  // Cache hit
@@ -1,6 +1,6 @@
1
1
  // file_description: barrel export for auth utilities
2
2
  // section: type_exports
3
- export * from "./auth_types";
3
+ export * from "./auth_types.js";
4
4
 
5
5
  // section: server_exports
6
6
  export { hazo_get_auth } from "./hazo_get_auth.server.js";
@@ -53,8 +53,8 @@ export async function get_server_auth_user(): Promise<ServerAuthResult> {
53
53
 
54
54
  const user = users[0];
55
55
 
56
- // Check if user is active (status must be 'active')
57
- if (user.status !== "active") {
56
+ // Check if user is active (status must be 'ACTIVE')
57
+ if (user.status !== "ACTIVE") {
58
58
  return { authenticated: false };
59
59
  }
60
60
 
@@ -68,7 +68,7 @@ export async function get_server_auth_user(): Promise<ServerAuthResult> {
68
68
  email: user.email_address as string,
69
69
  name: (user.name as string | null | undefined) || undefined,
70
70
  email_verified: user.email_verified === true,
71
- is_active: user.status === "active", // Derived from status column
71
+ is_active: user.status === "ACTIVE", // Derived from status column
72
72
  last_logon: (user.last_logon as string | null | undefined) || undefined,
73
73
  profile_picture_url: (user.profile_picture_url as string | null | undefined) || undefined,
74
74
  profile_source: profile_source_ui,
@@ -1,9 +1,9 @@
1
1
  // file_description: barrel export for lib utilities
2
2
  // section: auth_exports
3
- export * from "./auth/index";
3
+ export * from "./auth/index.js";
4
4
 
5
5
  // section: service_exports
6
- export * from "./services/index";
6
+ export * from "./services/index.js";
7
7
 
8
8
  // section: utility_exports
9
9
  export { cn, merge_class_names } from "./utils.js";
@@ -44,5 +44,5 @@ export type { FirmBrandingConfig } from "./branding_config.server";
44
44
  // section: util_exports
45
45
  export { sanitize_error_for_user } from "./utils/error_sanitizer.js";
46
46
  export type { ErrorSanitizationOptions } from "./utils/error_sanitizer";
47
- export * from "./utils/api_route_helpers";
47
+ export * from "./utils/api_route_helpers.js";
48
48
 
@@ -1,24 +1,24 @@
1
1
  // file_description: barrel export for all services
2
2
  // section: service_exports
3
- export * from "./email_service";
4
- export * from "./email_verification_service";
5
- export * from "./login_service";
6
- export * from "./password_change_service";
7
- export * from "./password_reset_service";
8
- export * from "./profile_picture_remove_service";
9
- export * from "./profile_picture_service";
10
- export * from "./profile_picture_source_mapper";
11
- export * from "./registration_service";
12
- export * from "./token_service";
13
- export * from "./user_profiles_service";
14
- export * from "./user_update_service";
15
- export * from "./app_user_data_service";
16
- export * from "./invitation_service";
17
- export * from "./firm_service";
18
- export * from "./post_verification_service";
19
- export * from "./scope_service";
20
- export * from "./user_scope_service";
21
- export * from "./oauth_service";
22
- export * from "./branding_service";
3
+ export * from "./email_service.js";
4
+ export * from "./email_verification_service.js";
5
+ export * from "./login_service.js";
6
+ export * from "./password_change_service.js";
7
+ export * from "./password_reset_service.js";
8
+ export * from "./profile_picture_remove_service.js";
9
+ export * from "./profile_picture_service.js";
10
+ export * from "./profile_picture_source_mapper.js";
11
+ export * from "./registration_service.js";
12
+ export * from "./token_service.js";
13
+ export * from "./user_profiles_service.js";
14
+ export * from "./user_update_service.js";
15
+ export * from "./app_user_data_service.js";
16
+ export * from "./invitation_service.js";
17
+ export * from "./firm_service.js";
18
+ export * from "./post_verification_service.js";
19
+ export * from "./scope_service.js";
20
+ export * from "./user_scope_service.js";
21
+ export * from "./oauth_service.js";
22
+ export * from "./branding_service.js";
23
23
 
24
24
 
@@ -53,8 +53,8 @@ export async function authenticate_user(
53
53
 
54
54
  const user = users[0];
55
55
 
56
- // Check if user is active (status must be 'active')
57
- if (user.status !== "active") {
56
+ // Check if user is active (status must be 'ACTIVE')
57
+ if (user.status !== "ACTIVE") {
58
58
  return {
59
59
  success: false,
60
60
  error: "Account is inactive. Please contact support.",
@@ -179,7 +179,7 @@ export async function handle_google_oauth_login(
179
179
  email_address: email,
180
180
  password_hash: "", // Empty string for Google-only users
181
181
  email_verified: email_verified, // Trust Google's verification
182
- status: "active",
182
+ status: "ACTIVE",
183
183
  login_attempts: 0,
184
184
  google_id,
185
185
  auth_providers: "google",
@@ -73,7 +73,7 @@ export async function register_user(
73
73
  email_address: email,
74
74
  password_hash: password_hash,
75
75
  email_verified: false,
76
- status: "active",
76
+ status: "ACTIVE",
77
77
  login_attempts: 0,
78
78
  auth_providers: "email", // Track that this user registered with email/password
79
79
  created_at: now,
@@ -1 +1 @@
1
- {"version":3,"file":"init_users.d.ts","sourceRoot":"","sources":["../../src/cli/init_users.ts"],"names":[],"mappings":"AAkHA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAGF;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CA0SrF;AAGD;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAkC3C"}
1
+ {"version":3,"file":"init_users.d.ts","sourceRoot":"","sources":["../../src/cli/init_users.ts"],"names":[],"mappings":"AAuGA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAGF;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAmRrF;AAGD;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAkC3C"}
@@ -44,15 +44,7 @@ function print_summary(summary) {
44
44
  console.log(` ⊙ Already existed: ${summary.role_permissions.existing} assignment(s)`);
45
45
  }
46
46
  console.log();
47
- // User role summary
48
- console.log("User-Role Assignment:");
49
- if (summary.user_role.inserted) {
50
- console.log(` ✓ Inserted: Super user role assigned to user`);
51
- }
52
- if (summary.user_role.existing) {
53
- console.log(` ⊙ Already existed: User already has super user role`);
54
- }
55
- console.log();
47
+ // v5.x: User-Role assignments are now handled via User-Scope assignments (see below)
56
48
  // Super admin scope summary
57
49
  console.log("Super Admin Scope:");
58
50
  if (summary.super_admin_scope.inserted) {
@@ -98,10 +90,7 @@ export async function handle_init_users(options = {}) {
98
90
  inserted: 0,
99
91
  existing: 0,
100
92
  },
101
- user_role: {
102
- inserted: false,
103
- existing: false,
104
- },
93
+ // v5.x: Removed user_role - roles are now assigned via hazo_user_scopes
105
94
  super_admin_scope: {
106
95
  inserted: false,
107
96
  existing: false,
@@ -120,7 +109,7 @@ export async function handle_init_users(options = {}) {
120
109
  const roles_service = createCrudService(hazoConnect, "hazo_roles");
121
110
  const role_permissions_service = createCrudService(hazoConnect, "hazo_role_permissions");
122
111
  const users_service = createCrudService(hazoConnect, "hazo_users");
123
- const user_roles_service = createCrudService(hazoConnect, "hazo_user_roles");
112
+ // v5.x: Removed hazo_user_roles - roles are now assigned via hazo_user_scopes
124
113
  const scopes_service = createCrudService(hazoConnect, "hazo_scopes");
125
114
  // hazo_user_scopes uses composite primary key (user_id, scope_id), no 'id' column
126
115
  const user_scopes_service = createCrudService(hazoConnect, "hazo_user_scopes", {
@@ -255,26 +244,7 @@ export async function handle_init_users(options = {}) {
255
244
  const user_id = user.id;
256
245
  console.log(`✓ Found user: ${super_user_email} (ID: ${user_id})`);
257
246
  console.log();
258
- // 7. Assign role to user
259
- const existing_user_roles = await user_roles_service.findBy({
260
- user_id,
261
- role_id,
262
- });
263
- if (Array.isArray(existing_user_roles) && existing_user_roles.length > 0) {
264
- summary.user_role.existing = true;
265
- console.log(`✓ User already has role assigned: ${user_id} -> ${role_name}`);
266
- }
267
- else {
268
- await user_roles_service.insert({
269
- user_id,
270
- role_id,
271
- created_at: now,
272
- changed_at: now,
273
- });
274
- summary.user_role.inserted = true;
275
- console.log(`✓ Assigned role to user: ${user_id} -> ${role_name}`);
276
- }
277
- console.log();
247
+ // v5.x: Step 7 removed - role assignment now happens via hazo_user_scopes (see step 9)
278
248
  // 8. Ensure super admin scope exists
279
249
  const existing_scopes = await scopes_service.findBy({ id: SUPER_ADMIN_SCOPE_ID });
280
250
  if (Array.isArray(existing_scopes) && existing_scopes.length > 0) {
@@ -357,9 +327,9 @@ This command reads from hazo_auth_config.ini and:
357
327
  2. Creates a 'default_super_user_role' role
358
328
  3. Assigns all permissions to the super user role
359
329
  4. Finds user by email (from --email parameter or config)
360
- 5. Assigns the super user role to that user
361
- 6. Creates the Super Admin scope (${SUPER_ADMIN_SCOPE_ID})
362
- 7. Assigns the user to the Super Admin scope
330
+ 5. Creates the Super Admin scope (${SUPER_ADMIN_SCOPE_ID})
331
+ 6. Assigns the user to the Super Admin scope with the super user role
332
+ (v5.x: Roles are assigned per-scope via hazo_user_scopes table)
363
333
 
364
334
  Options:
365
335
  --email=<email> Email address of the user to assign super user role
package/dist/client.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- export * from "./components/index";
2
- export { cn, merge_class_names } from "./lib/utils";
3
- export * from "./lib/auth/auth_types";
4
- export { use_auth_status, trigger_auth_status_refresh } from "./components/layouts/shared/hooks/use_auth_status";
5
- export { use_hazo_auth, trigger_hazo_auth_refresh } from "./components/layouts/shared/hooks/use_hazo_auth";
1
+ export * from "./components/index.js";
2
+ export { cn, merge_class_names } from "./lib/utils.js";
3
+ export * from "./lib/auth/auth_types.js";
4
+ export { use_auth_status, trigger_auth_status_refresh } from "./components/layouts/shared/hooks/use_auth_status.js";
5
+ export { use_hazo_auth, trigger_hazo_auth_refresh } from "./components/layouts/shared/hooks/use_hazo_auth.js";
6
6
  export type { UseHazoAuthOptions, UseHazoAuthResult } from "./components/layouts/shared/hooks/use_hazo_auth";
7
- export { use_firm_branding, use_current_user_branding } from "./components/layouts/shared/hooks/use_firm_branding";
7
+ export { use_firm_branding, use_current_user_branding } from "./components/layouts/shared/hooks/use_firm_branding.js";
8
8
  export type { FirmBranding, UseFirmBrandingOptions, UseFirmBrandingResult } from "./components/layouts/shared/hooks/use_firm_branding";
9
- export * from "./components/layouts/shared/utils/validation";
9
+ export * from "./components/layouts/shared/utils/validation.js";
10
10
  //# sourceMappingURL=client.d.ts.map
package/dist/client.js CHANGED
@@ -9,18 +9,18 @@
9
9
  // import { hazo_get_auth, get_config_value } from "hazo_auth";
10
10
  // section: component_exports
11
11
  // All UI and layout components are client-safe
12
- export * from "./components/index";
12
+ export * from "./components/index.js";
13
13
  // section: utility_exports
14
14
  // CSS utility functions
15
- export { cn, merge_class_names } from "./lib/utils";
15
+ export { cn, merge_class_names } from "./lib/utils.js";
16
16
  // section: type_exports
17
17
  // Type definitions are always safe (erased at runtime)
18
- export * from "./lib/auth/auth_types";
18
+ export * from "./lib/auth/auth_types.js";
19
19
  // section: client_hook_exports
20
20
  // Re-export from shared hooks (these are already "use client" components)
21
- export { use_auth_status, trigger_auth_status_refresh } from "./components/layouts/shared/hooks/use_auth_status";
22
- export { use_hazo_auth, trigger_hazo_auth_refresh } from "./components/layouts/shared/hooks/use_hazo_auth";
23
- export { use_firm_branding, use_current_user_branding } from "./components/layouts/shared/hooks/use_firm_branding";
21
+ export { use_auth_status, trigger_auth_status_refresh } from "./components/layouts/shared/hooks/use_auth_status.js";
22
+ export { use_hazo_auth, trigger_hazo_auth_refresh } from "./components/layouts/shared/hooks/use_hazo_auth.js";
23
+ export { use_firm_branding, use_current_user_branding } from "./components/layouts/shared/hooks/use_firm_branding.js";
24
24
  // section: validation_exports
25
25
  // Client-side validation utilities
26
- export * from "./components/layouts/shared/utils/validation";
26
+ export * from "./components/layouts/shared/utils/validation.js";
@@ -1,3 +1,3 @@
1
- export * from "./ui/index";
2
- export * from "./layouts/index";
1
+ export * from "./ui/index.js";
2
+ export * from "./layouts/index.js";
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1,5 +1,5 @@
1
1
  // file_description: barrel export for all components (UI and layouts)
2
2
  // section: ui_exports
3
- export * from "./ui/index";
3
+ export * from "./ui/index.js";
4
4
  // section: layout_exports
5
- export * from "./layouts/index";
5
+ export * from "./layouts/index.js";
@@ -1,5 +1,5 @@
1
1
  import type { StaticImageData } from "next/image";
2
- import { type ButtonPaletteOverrides } from "../shared/config/layout_customization";
2
+ import { type ButtonPaletteOverrides } from "../shared/config/layout_customization.js";
3
3
  export type CreateFirmLayoutProps = {
4
4
  /** Image source for the left panel */
5
5
  image_src: string | StaticImageData;