hazo_auth 7.0.2 → 8.0.1

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 (86) hide show
  1. package/README.md +34 -0
  2. package/SETUP_CHECKLIST.md +31 -0
  3. package/cli-src/lib/auth/auth_types.ts +3 -0
  4. package/cli-src/lib/auth/hazo_get_auth.server.ts +19 -0
  5. package/cli-src/lib/legal/legal_docs_config.server.ts +61 -0
  6. package/cli-src/lib/legal/legal_docs_reader.server.ts +36 -0
  7. package/cli-src/lib/legal/legal_docs_service.ts +196 -0
  8. package/cli-src/lib/legal/legal_docs_types.ts +31 -0
  9. package/cli-src/lib/services/registration_service.ts +16 -1
  10. package/dist/client.d.ts +1 -0
  11. package/dist/client.d.ts.map +1 -1
  12. package/dist/client.js +3 -0
  13. package/dist/components/layouts/index.d.ts +1 -0
  14. package/dist/components/layouts/index.d.ts.map +1 -1
  15. package/dist/components/layouts/index.js +2 -0
  16. package/dist/components/layouts/legal/index.d.ts +5 -0
  17. package/dist/components/layouts/legal/index.d.ts.map +1 -0
  18. package/dist/components/layouts/legal/index.js +4 -0
  19. package/dist/components/layouts/legal/legal_acceptance_gate.d.ts +7 -0
  20. package/dist/components/layouts/legal/legal_acceptance_gate.d.ts.map +1 -0
  21. package/dist/components/layouts/legal/legal_acceptance_gate.js +84 -0
  22. package/dist/components/layouts/legal/legal_doc_checkbox_list.d.ts +9 -0
  23. package/dist/components/layouts/legal/legal_doc_checkbox_list.d.ts.map +1 -0
  24. package/dist/components/layouts/legal/legal_doc_checkbox_list.js +11 -0
  25. package/dist/components/layouts/legal/legal_doc_combined_view.d.ts +9 -0
  26. package/dist/components/layouts/legal/legal_doc_combined_view.d.ts.map +1 -0
  27. package/dist/components/layouts/legal/legal_doc_combined_view.js +11 -0
  28. package/dist/components/layouts/legal/legal_doc_drawer.d.ts +8 -0
  29. package/dist/components/layouts/legal/legal_doc_drawer.d.ts.map +1 -0
  30. package/dist/components/layouts/legal/legal_doc_drawer.js +55 -0
  31. package/dist/components/layouts/register/hooks/use_register_form.d.ts +5 -1
  32. package/dist/components/layouts/register/hooks/use_register_form.d.ts.map +1 -1
  33. package/dist/components/layouts/register/hooks/use_register_form.js +25 -10
  34. package/dist/components/layouts/register/index.d.ts.map +1 -1
  35. package/dist/components/layouts/register/index.js +21 -1
  36. package/dist/components/layouts/user_management/index.d.ts.map +1 -1
  37. package/dist/components/layouts/user_management/index.js +45 -7
  38. package/dist/components/ui/button.d.ts +1 -1
  39. package/dist/components/ui/input-otp.d.ts +2 -2
  40. package/dist/components/ui/sheet.d.ts +1 -1
  41. package/dist/index.d.ts +1 -0
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/lib/auth/auth_types.d.ts +2 -0
  44. package/dist/lib/auth/auth_types.d.ts.map +1 -1
  45. package/dist/lib/auth/auth_types.js +0 -2
  46. package/dist/lib/auth/hazo_get_auth.server.d.ts.map +1 -1
  47. package/dist/lib/auth/hazo_get_auth.server.js +19 -0
  48. package/dist/lib/legal/legal_docs_config.server.d.ts +22 -0
  49. package/dist/lib/legal/legal_docs_config.server.d.ts.map +1 -0
  50. package/dist/lib/legal/legal_docs_config.server.js +52 -0
  51. package/dist/lib/legal/legal_docs_reader.server.d.ts +15 -0
  52. package/dist/lib/legal/legal_docs_reader.server.d.ts.map +1 -0
  53. package/dist/lib/legal/legal_docs_reader.server.js +24 -0
  54. package/dist/lib/legal/legal_docs_service.d.ts +49 -0
  55. package/dist/lib/legal/legal_docs_service.d.ts.map +1 -0
  56. package/dist/lib/legal/legal_docs_service.js +140 -0
  57. package/dist/lib/legal/legal_docs_types.d.ts +25 -0
  58. package/dist/lib/legal/legal_docs_types.d.ts.map +1 -0
  59. package/dist/lib/legal/legal_docs_types.js +2 -0
  60. package/dist/lib/services/registration_service.d.ts +5 -0
  61. package/dist/lib/services/registration_service.d.ts.map +1 -1
  62. package/dist/lib/services/registration_service.js +6 -0
  63. package/dist/page_components/index.d.ts +0 -5
  64. package/dist/page_components/index.d.ts.map +1 -1
  65. package/dist/page_components/index.js +0 -5
  66. package/dist/server/routes/index.d.ts +3 -0
  67. package/dist/server/routes/index.d.ts.map +1 -1
  68. package/dist/server/routes/index.js +4 -0
  69. package/dist/server/routes/legal_docs_accept.d.ts +3 -0
  70. package/dist/server/routes/legal_docs_accept.d.ts.map +1 -0
  71. package/dist/server/routes/legal_docs_accept.js +43 -0
  72. package/dist/server/routes/legal_docs_get.d.ts +3 -0
  73. package/dist/server/routes/legal_docs_get.d.ts.map +1 -0
  74. package/dist/server/routes/legal_docs_get.js +49 -0
  75. package/dist/server/routes/legal_docs_publish.d.ts +3 -0
  76. package/dist/server/routes/legal_docs_publish.d.ts.map +1 -0
  77. package/dist/server/routes/legal_docs_publish.js +35 -0
  78. package/dist/server/routes/register.d.ts.map +1 -1
  79. package/dist/server/routes/register.js +26 -0
  80. package/dist/server/routes/user_management_users.d.ts +2 -2
  81. package/dist/server/routes/user_management_users.d.ts.map +1 -1
  82. package/dist/server/routes/user_management_users.js +46 -2
  83. package/dist/strings.d.ts +2 -0
  84. package/dist/strings.d.ts.map +1 -0
  85. package/dist/strings.js +3 -0
  86. package/package.json +5 -22
@@ -0,0 +1,35 @@
1
+ // file_description: POST /api/hazo_auth/legal_docs/publish — admin endpoint to publish a new required version of a legal doc
2
+ import { NextResponse } from 'next/server';
3
+ import { hazo_get_auth } from '../../lib/auth/hazo_get_auth.server.js';
4
+ import { get_legal_docs_config } from '../../lib/legal/legal_docs_config.server.js';
5
+ import { read_legal_doc } from '../../lib/legal/legal_docs_reader.server.js';
6
+ import { publish_doc_version } from '../../lib/legal/legal_docs_service.js';
7
+ import { get_hazo_connect_instance } from '../../lib/hazo_connect_instance.server.js';
8
+ import { create_app_logger } from '../../lib/app_logger.js';
9
+ export async function legalDocsPublishPOST(request) {
10
+ const logger = create_app_logger();
11
+ try {
12
+ const auth = await hazo_get_auth(request, { required_permissions: ['admin_user_management'] });
13
+ if (!auth.permission_ok) {
14
+ return NextResponse.json({ ok: false, error: 'Forbidden' }, { status: 403 });
15
+ }
16
+ const body = await request.json().catch(() => null);
17
+ const doc_key = body === null || body === void 0 ? void 0 : body.doc_key;
18
+ if (!doc_key || typeof doc_key !== 'string') {
19
+ return NextResponse.json({ ok: false, error: 'doc_key is required' }, { status: 400 });
20
+ }
21
+ const config = get_legal_docs_config();
22
+ const doc_config = config.docs.find(d => d.key === doc_key);
23
+ if (!doc_config) {
24
+ return NextResponse.json({ ok: false, error: `Unknown doc key: ${doc_key}` }, { status: 400 });
25
+ }
26
+ const { hash } = read_legal_doc(doc_config.path);
27
+ const adapter = get_hazo_connect_instance();
28
+ const { published_at } = await publish_doc_version(adapter, doc_key, hash, auth.user.id);
29
+ return NextResponse.json({ ok: true, data: { doc_key, required_hash: hash, published_at } });
30
+ }
31
+ catch (err) {
32
+ logger.error('legal_docs_publish: internal server error', { err });
33
+ return NextResponse.json({ ok: false, error: 'Internal server error' }, { status: 500 });
34
+ }
35
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../../src/server/routes/register.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAQxD,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW;;;;;;IAiG9C"}
1
+ {"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../../src/server/routes/register.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAWxD,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW;;;;;;IAgI9C"}
@@ -6,12 +6,17 @@ import { create_app_logger } from "../../lib/app_logger.js";
6
6
  import { register_user } from "../../lib/services/registration_service.js";
7
7
  import { get_filename, get_line_number } from "../../lib/utils/api_route_helpers.js";
8
8
  import { sanitize_error_for_user } from "../../lib/utils/error_sanitizer.js";
9
+ import { get_legal_docs_config } from "../../lib/legal/legal_docs_config.server.js";
10
+ import { read_legal_doc } from "../../lib/legal/legal_docs_reader.server.js";
11
+ import { get_client_ip } from "../../lib/auth/hazo_get_auth.server.js";
9
12
  // section: api_handler
10
13
  export async function POST(request) {
14
+ var _a;
11
15
  const logger = create_app_logger();
12
16
  try {
13
17
  const body = await request.json();
14
18
  const { name, email, password, url_on_logon } = body;
19
+ const legal_accepted = body === null || body === void 0 ? void 0 : body.legal_accepted;
15
20
  // Validate input
16
21
  if (!email || !password) {
17
22
  logger.warn("registration_validation_failed", {
@@ -32,6 +37,24 @@ export async function POST(request) {
32
37
  });
33
38
  return NextResponse.json({ error: "Invalid email address format" }, { status: 400 });
34
39
  }
40
+ // Validate legal acceptance if docs are configured
41
+ const legal_config = get_legal_docs_config();
42
+ if (legal_config.docs.length > 0) {
43
+ const missing_keys = legal_config.docs
44
+ .map(d => d.key)
45
+ .filter(key => !(legal_accepted === null || legal_accepted === void 0 ? void 0 : legal_accepted[key]));
46
+ if (missing_keys.length > 0) {
47
+ return NextResponse.json({ ok: false, error: 'legal_acceptance_required', missing_keys }, { status: 400 });
48
+ }
49
+ // Validate hashes match current file content
50
+ for (const doc_config of legal_config.docs) {
51
+ const submitted_hash = (_a = legal_accepted[doc_config.key]) === null || _a === void 0 ? void 0 : _a.hash;
52
+ const { hash: current_hash } = read_legal_doc(doc_config.path);
53
+ if (submitted_hash !== current_hash) {
54
+ return NextResponse.json({ ok: false, error: `Hash mismatch for "${doc_config.key}"` }, { status: 400 });
55
+ }
56
+ }
57
+ }
35
58
  // Get singleton hazo_connect instance (reuses same connection across all routes)
36
59
  const hazoConnect = get_hazo_connect_instance();
37
60
  // Register user using the registration service
@@ -40,6 +63,9 @@ export async function POST(request) {
40
63
  password,
41
64
  name,
42
65
  url_on_logon,
66
+ legal_accepted,
67
+ ip: get_client_ip(request),
68
+ user_agent: request.headers.get('user-agent'),
43
69
  });
44
70
  if (!result.success) {
45
71
  const status_code = result.error === "Email address already registered" ? 409 : 500;
@@ -26,6 +26,7 @@ export declare function GET(request: NextRequest): Promise<NextResponse<{
26
26
  profile_source: {} | null;
27
27
  user_type: string | null;
28
28
  app_user_data: Record<string, unknown> | null;
29
+ legal_acceptance_status: "current" | "outdated" | "none";
29
30
  }[];
30
31
  }>>;
31
32
  /**
@@ -47,8 +48,7 @@ export declare function POST(request: NextRequest): Promise<NextResponse<{
47
48
  /**
48
49
  * DELETE - Hard-delete a user from hazo_users (cascades to all related rows).
49
50
  * Body: { user_id: string }
50
- * Requires: admin_user_management permission (enforced by UI, not here — same
51
- * pattern as PATCH/POST in this file which also don't re-auth).
51
+ * Requires: admin_user_management permission.
52
52
  */
53
53
  export declare function DELETE(request: NextRequest): Promise<NextResponse<{
54
54
  error: string;
@@ -1 +1 @@
1
- {"version":3,"file":"user_management_users.d.ts","sourceRoot":"","sources":["../../../src/server/routes/user_management_users.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAexD,eAAO,MAAM,OAAO,kBAAkB,CAAC;AAGvC;;;GAGG;AACH,wBAAsB,GAAG,CAAC,OAAO,EAAE,WAAW;;;;;;;;;;;;;;;;;;;;;;;IAyF7C;AAED;;GAEG;AACH,wBAAsB,KAAK,CAAC,OAAO,EAAE,WAAW;;;;IAgI/C;AAED;;GAEG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW;;;;IA2E9C;AAED;;;;;GAKG;AACH,wBAAsB,MAAM,CAAC,OAAO,EAAE,WAAW;;;;IAuDhD"}
1
+ {"version":3,"file":"user_management_users.d.ts","sourceRoot":"","sources":["../../../src/server/routes/user_management_users.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAkBxD,eAAO,MAAM,OAAO,kBAAkB,CAAC;AA+BvC;;;GAGG;AACH,wBAAsB,GAAG,CAAC,OAAO,EAAE,WAAW;;;;;;;;;;;;;;;;;;;;;;;;IAsG7C;AAED;;GAEG;AACH,wBAAsB,KAAK,CAAC,OAAO,EAAE,WAAW;;;;IAgI/C;AAED;;GAEG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW;;;;IA2E9C;AAED;;;;GAIG;AACH,wBAAsB,MAAM,CAAC,OAAO,EAAE,WAAW;;;;IAmEhD"}
@@ -9,8 +9,35 @@ import { request_password_reset } from "../../lib/services/password_reset_servic
9
9
  import { get_auth_cache } from "../../lib/auth/auth_cache.js";
10
10
  import { get_auth_utility_config } from "../../lib/auth_utility_config.server.js";
11
11
  import { is_user_types_enabled, get_all_user_types, get_user_types_config, } from "../../lib/user_types_config.server.js";
12
+ import { get_required_versions } from "../../lib/legal/legal_docs_service.js";
13
+ import { get_legal_docs_config } from "../../lib/legal/legal_docs_config.server.js";
14
+ import { hazo_get_auth } from "../../lib/auth/hazo_get_auth.server.js";
12
15
  // section: route_config
13
16
  export const dynamic = 'force-dynamic';
17
+ // section: helpers
18
+ /**
19
+ * Compute a user's legal compliance status given their raw legal_acceptance JSON,
20
+ * the currently-required hashes, and the configured doc keys.
21
+ */
22
+ function compute_legal_status(raw, required, docs) {
23
+ if (docs.length === 0)
24
+ return 'none';
25
+ if (Object.keys(required).length === 0)
26
+ return 'none';
27
+ let map = {};
28
+ try {
29
+ map = typeof raw === 'string' ? JSON.parse(raw) : (raw !== null && raw !== void 0 ? raw : {});
30
+ }
31
+ catch ( /* ignore */_a) { /* ignore */ }
32
+ const all_current = docs.every(doc => {
33
+ var _a;
34
+ const req = required[doc.key];
35
+ if (!req)
36
+ return true;
37
+ return ((_a = map[doc.key]) === null || _a === void 0 ? void 0 : _a.hash) === req.required_hash;
38
+ });
39
+ return all_current ? 'current' : 'outdated';
40
+ }
14
41
  // section: api_handler
15
42
  /**
16
43
  * GET - Fetch all users with details or a specific user by id
@@ -42,6 +69,13 @@ export async function GET(request) {
42
69
  badge_color: t.badge_color,
43
70
  }))
44
71
  : [];
72
+ // Load legal docs required versions for compliance status
73
+ const legal_config = get_legal_docs_config();
74
+ let required_versions = {};
75
+ if (legal_config.docs.length > 0) {
76
+ const adapter = get_hazo_connect_instance();
77
+ required_versions = await get_required_versions(adapter, legal_config.docs.map(d => d.key));
78
+ }
45
79
  return NextResponse.json({
46
80
  success: true,
47
81
  user_types_enabled,
@@ -74,6 +108,7 @@ export async function GET(request) {
74
108
  profile_source: user.profile_source || null,
75
109
  user_type: user.user_type || null,
76
110
  app_user_data,
111
+ legal_acceptance_status: compute_legal_status(user.legal_acceptance, required_versions, legal_config.docs),
77
112
  };
78
113
  }),
79
114
  }, { status: 200 });
@@ -244,14 +279,23 @@ export async function POST(request) {
244
279
  /**
245
280
  * DELETE - Hard-delete a user from hazo_users (cascades to all related rows).
246
281
  * Body: { user_id: string }
247
- * Requires: admin_user_management permission (enforced by UI, not here — same
248
- * pattern as PATCH/POST in this file which also don't re-auth).
282
+ * Requires: admin_user_management permission.
249
283
  */
250
284
  export async function DELETE(request) {
251
285
  const logger = create_app_logger();
252
286
  try {
287
+ const auth = await hazo_get_auth(request, { required_permissions: ['admin_user_management'] });
288
+ if (!auth.user) {
289
+ return NextResponse.json({ error: 'Authentication required' }, { status: 401 });
290
+ }
291
+ if (!auth.permission_ok) {
292
+ return NextResponse.json({ error: 'Forbidden' }, { status: 403 });
293
+ }
253
294
  const body = await request.json();
254
295
  const { user_id } = body;
296
+ if (auth.user.id === user_id) {
297
+ return NextResponse.json({ error: 'Cannot delete your own account' }, { status: 400 });
298
+ }
255
299
  if (!user_id) {
256
300
  return NextResponse.json({ error: "user_id is required" }, { status: 400 });
257
301
  }
@@ -0,0 +1,2 @@
1
+ export * from "./strings/index.js";
2
+ //# sourceMappingURL=strings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strings.d.ts","sourceRoot":"","sources":["../src/strings.ts"],"names":[],"mappings":"AAEA,cAAc,iBAAiB,CAAC"}
@@ -0,0 +1,3 @@
1
+ // Barrel — gives ESM consumers a flat `../strings.js` import path
2
+ // that resolves correctly at runtime (the directory `strings/` does not).
3
+ export * from "./strings/index.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hazo_auth",
3
- "version": "7.0.2",
3
+ "version": "8.0.1",
4
4
  "description": "Zero-config authentication UI components for Next.js with RBAC, OAuth, scope-based multi-tenancy, and invitations",
5
5
  "keywords": [
6
6
  "authentication",
@@ -157,26 +157,6 @@
157
157
  "types": "./dist/page_components/index.d.ts",
158
158
  "import": "./dist/page_components/index.js"
159
159
  },
160
- "./page_components/login": {
161
- "types": "./dist/page_components/login.d.ts",
162
- "import": "./dist/page_components/login.js"
163
- },
164
- "./page_components/register": {
165
- "types": "./dist/page_components/register.d.ts",
166
- "import": "./dist/page_components/register.js"
167
- },
168
- "./page_components/forgot_password": {
169
- "types": "./dist/page_components/forgot_password.d.ts",
170
- "import": "./dist/page_components/forgot_password.js"
171
- },
172
- "./page_components/reset_password": {
173
- "types": "./dist/page_components/reset_password.d.ts",
174
- "import": "./dist/page_components/reset_password.js"
175
- },
176
- "./page_components/verify_email": {
177
- "types": "./dist/page_components/verify_email.d.ts",
178
- "import": "./dist/page_components/verify_email.js"
179
- },
180
160
  "./page_components/dev_lock": {
181
161
  "types": "./dist/page_components/dev_lock.d.ts",
182
162
  "import": "./dist/page_components/dev_lock.js"
@@ -198,6 +178,8 @@
198
178
  "!dist/components/layouts/rbac_test/**/*",
199
179
  "!dist/components/layouts/auto_test/**/*",
200
180
  "!dist/components/layouts/test_scenarios/**/*",
181
+ "!dist/components/layouts/legal_docs_test/**/*",
182
+ "!dist/components/layouts/legal_register_test/**/*",
201
183
  "!dist/lib/auto_test/**/*",
202
184
  "bin/**/*",
203
185
  "cli-src/**/*",
@@ -238,6 +220,7 @@
238
220
  "jose": "^5.9.6",
239
221
  "jsonwebtoken": "^9.0.2",
240
222
  "mime-types": "^3.0.1",
223
+ "react-markdown": "^10.1.0",
241
224
  "server-only": "^0.0.1",
242
225
  "tailwind-merge": "^3.5.0",
243
226
  "tw-animate-css": "^1.4.0",
@@ -371,6 +354,7 @@
371
354
  "@storybook/addon-onboarding": "^10.0.6",
372
355
  "@storybook/addon-vitest": "^10.0.6",
373
356
  "@storybook/nextjs": "^10.0.6",
357
+ "@tailwindcss/postcss": "^4.2.4",
374
358
  "@testing-library/dom": "^10.4.1",
375
359
  "@testing-library/jest-dom": "^6.6.3",
376
360
  "@testing-library/react": "^16.0.1",
@@ -392,7 +376,6 @@
392
376
  "eslint": "^9.39.1",
393
377
  "eslint-config-next": "^16.0.4",
394
378
  "eslint-plugin-storybook": "^10.0.6",
395
- "@tailwindcss/postcss": "^4.2.4",
396
379
  "hazo_config": "^2.1.0",
397
380
  "hazo_connect": "^2.4.0",
398
381
  "hazo_logs": "^1.0.13",