hazo_auth 7.0.1 → 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.
- package/README.md +96 -319
- package/SETUP_CHECKLIST.md +59 -248
- package/cli-src/cli/generate.ts +1 -10
- package/cli-src/cli/validate.ts +0 -4
- package/cli-src/lib/auth/auth_types.ts +15 -21
- package/cli-src/lib/auth/hazo_get_auth.server.ts +19 -0
- package/cli-src/lib/auth/hazo_get_tenant_auth.server.ts +24 -25
- package/cli-src/lib/auth/index.ts +2 -2
- package/cli-src/lib/auth/nextauth_config.ts +27 -67
- package/cli-src/lib/auth/with_auth.server.ts +15 -15
- package/cli-src/lib/config/default_config.ts +8 -0
- package/cli-src/lib/cookies_config.server.ts +1 -1
- package/cli-src/lib/email_verification_config.server.ts +34 -0
- package/cli-src/lib/forgot_password_config.server.ts +34 -0
- package/cli-src/lib/legal/legal_docs_config.server.ts +61 -0
- package/cli-src/lib/legal/legal_docs_reader.server.ts +36 -0
- package/cli-src/lib/legal/legal_docs_service.ts +196 -0
- package/cli-src/lib/legal/legal_docs_types.ts +31 -0
- package/cli-src/lib/login_config.server.ts +29 -14
- package/cli-src/lib/my_settings_config.server.ts +3 -0
- package/cli-src/lib/oauth_config.server.ts +31 -57
- package/cli-src/lib/register_config.server.ts +35 -11
- package/cli-src/lib/reset_password_config.server.ts +31 -0
- package/cli-src/lib/services/email_template_manifest.ts +0 -17
- package/cli-src/lib/services/index.ts +2 -8
- package/cli-src/lib/services/oauth_service.ts +74 -128
- package/cli-src/lib/services/otp_service.ts +7 -2
- package/cli-src/lib/services/registration_service.ts +16 -1
- package/cli-src/lib/services/session_token_service.ts +0 -2
- package/config/hazo_auth_config.example.ini +41 -76
- package/dist/cli/generate.d.ts.map +1 -1
- package/dist/cli/generate.js +1 -10
- package/dist/cli/validate.d.ts.map +1 -1
- package/dist/cli/validate.js +0 -4
- package/dist/client.d.ts +1 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +3 -1
- package/dist/components/layouts/create_firm/index.d.ts +8 -4
- package/dist/components/layouts/create_firm/index.d.ts.map +1 -1
- package/dist/components/layouts/create_firm/index.js +3 -3
- package/dist/components/layouts/email_verification/index.d.ts +5 -4
- package/dist/components/layouts/email_verification/index.d.ts.map +1 -1
- package/dist/components/layouts/email_verification/index.js +4 -4
- package/dist/components/layouts/forgot_password/index.d.ts +5 -4
- package/dist/components/layouts/forgot_password/index.d.ts.map +1 -1
- package/dist/components/layouts/forgot_password/index.js +2 -2
- package/dist/components/layouts/index.d.ts +1 -0
- package/dist/components/layouts/index.d.ts.map +1 -1
- package/dist/components/layouts/index.js +2 -0
- package/dist/components/layouts/legal/index.d.ts +5 -0
- package/dist/components/layouts/legal/index.d.ts.map +1 -0
- package/dist/components/layouts/legal/index.js +4 -0
- package/dist/components/layouts/legal/legal_acceptance_gate.d.ts +7 -0
- package/dist/components/layouts/legal/legal_acceptance_gate.d.ts.map +1 -0
- package/dist/components/layouts/legal/legal_acceptance_gate.js +84 -0
- package/dist/components/layouts/legal/legal_doc_checkbox_list.d.ts +9 -0
- package/dist/components/layouts/legal/legal_doc_checkbox_list.d.ts.map +1 -0
- package/dist/components/layouts/legal/legal_doc_checkbox_list.js +11 -0
- package/dist/components/layouts/legal/legal_doc_combined_view.d.ts +9 -0
- package/dist/components/layouts/legal/legal_doc_combined_view.d.ts.map +1 -0
- package/dist/components/layouts/legal/legal_doc_combined_view.js +11 -0
- package/dist/components/layouts/legal/legal_doc_drawer.d.ts +8 -0
- package/dist/components/layouts/legal/legal_doc_drawer.d.ts.map +1 -0
- package/dist/components/layouts/legal/legal_doc_drawer.js +55 -0
- package/dist/components/layouts/login/index.d.ts +13 -19
- package/dist/components/layouts/login/index.d.ts.map +1 -1
- package/dist/components/layouts/login/index.js +8 -11
- package/dist/components/layouts/otp/index.d.ts +5 -1
- package/dist/components/layouts/otp/index.d.ts.map +1 -1
- package/dist/components/layouts/otp/index.js +2 -2
- package/dist/components/layouts/register/hooks/use_register_form.d.ts +5 -1
- package/dist/components/layouts/register/hooks/use_register_form.d.ts.map +1 -1
- package/dist/components/layouts/register/hooks/use_register_form.js +25 -10
- package/dist/components/layouts/register/index.d.ts +11 -11
- package/dist/components/layouts/register/index.d.ts.map +1 -1
- package/dist/components/layouts/register/index.js +26 -7
- package/dist/components/layouts/reset_password/index.d.ts +5 -4
- package/dist/components/layouts/reset_password/index.d.ts.map +1 -1
- package/dist/components/layouts/reset_password/index.js +5 -5
- package/dist/components/layouts/shared/components/already_logged_in_guard.d.ts +5 -3
- package/dist/components/layouts/shared/components/already_logged_in_guard.d.ts.map +1 -1
- package/dist/components/layouts/shared/components/already_logged_in_guard.js +2 -2
- package/dist/components/layouts/shared/components/facebook_sign_in_button.d.ts +2 -6
- package/dist/components/layouts/shared/components/facebook_sign_in_button.d.ts.map +1 -1
- package/dist/components/layouts/shared/components/facebook_sign_in_button.js +11 -13
- package/dist/components/layouts/shared/components/sidebar_layout_wrapper.d.ts.map +1 -1
- package/dist/components/layouts/shared/components/sidebar_layout_wrapper.js +3 -8
- package/dist/components/layouts/shared/components/two_column_auth_layout.d.ts +6 -3
- package/dist/components/layouts/shared/components/two_column_auth_layout.d.ts.map +1 -1
- package/dist/components/layouts/shared/components/two_column_auth_layout.js +5 -8
- package/dist/components/layouts/shared/index.d.ts +2 -0
- package/dist/components/layouts/shared/index.d.ts.map +1 -1
- package/dist/components/layouts/shared/index.js +1 -0
- package/dist/components/layouts/user_management/index.d.ts.map +1 -1
- package/dist/components/layouts/user_management/index.js +84 -9
- package/dist/components/ui/button.d.ts +1 -1
- package/dist/components/ui/input-otp.d.ts +2 -2
- package/dist/components/ui/sheet.d.ts +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/lib/auth/auth_types.d.ts +14 -13
- package/dist/lib/auth/auth_types.d.ts.map +1 -1
- package/dist/lib/auth/auth_types.js +0 -10
- package/dist/lib/auth/hazo_get_auth.server.d.ts.map +1 -1
- package/dist/lib/auth/hazo_get_auth.server.js +19 -0
- package/dist/lib/auth/hazo_get_tenant_auth.server.d.ts +7 -8
- package/dist/lib/auth/hazo_get_tenant_auth.server.d.ts.map +1 -1
- package/dist/lib/auth/hazo_get_tenant_auth.server.js +22 -23
- package/dist/lib/auth/index.d.ts +2 -2
- package/dist/lib/auth/index.d.ts.map +1 -1
- package/dist/lib/auth/nextauth_config.d.ts +0 -10
- package/dist/lib/auth/nextauth_config.d.ts.map +1 -1
- package/dist/lib/auth/nextauth_config.js +23 -52
- package/dist/lib/auth/with_auth.server.d.ts +13 -13
- package/dist/lib/auth/with_auth.server.d.ts.map +1 -1
- package/dist/lib/auth/with_auth.server.js +2 -2
- package/dist/lib/config/default_config.d.ts +16 -0
- package/dist/lib/config/default_config.d.ts.map +1 -1
- package/dist/lib/config/default_config.js +8 -0
- package/dist/lib/cookies_config.server.d.ts +1 -1
- package/dist/lib/cookies_config.server.js +1 -1
- package/dist/lib/email_verification_config.server.d.ts +3 -0
- package/dist/lib/email_verification_config.server.d.ts.map +1 -1
- package/dist/lib/email_verification_config.server.js +15 -0
- package/dist/lib/forgot_password_config.server.d.ts +3 -0
- package/dist/lib/forgot_password_config.server.d.ts.map +1 -1
- package/dist/lib/forgot_password_config.server.js +15 -0
- package/dist/lib/legal/legal_docs_config.server.d.ts +22 -0
- package/dist/lib/legal/legal_docs_config.server.d.ts.map +1 -0
- package/dist/lib/legal/legal_docs_config.server.js +52 -0
- package/dist/lib/legal/legal_docs_reader.server.d.ts +15 -0
- package/dist/lib/legal/legal_docs_reader.server.d.ts.map +1 -0
- package/dist/lib/legal/legal_docs_reader.server.js +24 -0
- package/dist/lib/legal/legal_docs_service.d.ts +49 -0
- package/dist/lib/legal/legal_docs_service.d.ts.map +1 -0
- package/dist/lib/legal/legal_docs_service.js +140 -0
- package/dist/lib/legal/legal_docs_types.d.ts +25 -0
- package/dist/lib/legal/legal_docs_types.d.ts.map +1 -0
- package/dist/lib/legal/legal_docs_types.js +2 -0
- package/dist/lib/login_config.server.d.ts +3 -6
- package/dist/lib/login_config.server.d.ts.map +1 -1
- package/dist/lib/login_config.server.js +11 -7
- package/dist/lib/my_settings_config.server.d.ts +1 -0
- package/dist/lib/my_settings_config.server.d.ts.map +1 -1
- package/dist/lib/my_settings_config.server.js +2 -0
- package/dist/lib/oauth_config.server.d.ts +8 -17
- package/dist/lib/oauth_config.server.d.ts.map +1 -1
- package/dist/lib/oauth_config.server.js +10 -25
- package/dist/lib/register_config.server.d.ts +5 -2
- package/dist/lib/register_config.server.d.ts.map +1 -1
- package/dist/lib/register_config.server.js +15 -4
- package/dist/lib/reset_password_config.server.d.ts +3 -0
- package/dist/lib/reset_password_config.server.d.ts.map +1 -1
- package/dist/lib/reset_password_config.server.js +13 -0
- package/dist/lib/services/email_template_manifest.d.ts.map +1 -1
- package/dist/lib/services/email_template_manifest.js +0 -17
- package/dist/lib/services/index.d.ts +0 -2
- package/dist/lib/services/index.d.ts.map +1 -1
- package/dist/lib/services/index.js +0 -1
- package/dist/lib/services/oauth_service.d.ts +11 -22
- package/dist/lib/services/oauth_service.d.ts.map +1 -1
- package/dist/lib/services/oauth_service.js +63 -96
- package/dist/lib/services/otp_service.d.ts +1 -1
- package/dist/lib/services/otp_service.d.ts.map +1 -1
- package/dist/lib/services/otp_service.js +6 -1
- package/dist/lib/services/registration_service.d.ts +5 -0
- package/dist/lib/services/registration_service.d.ts.map +1 -1
- package/dist/lib/services/registration_service.js +6 -0
- package/dist/lib/services/session_token_service.d.ts +0 -2
- package/dist/lib/services/session_token_service.d.ts.map +1 -1
- package/dist/lib/services/session_token_service.js +0 -2
- package/dist/page_components/create_firm.d.ts +1 -13
- package/dist/page_components/create_firm.d.ts.map +1 -1
- package/dist/page_components/create_firm.js +6 -10
- package/dist/page_components/forgot_password.d.ts +4 -1
- package/dist/page_components/forgot_password.d.ts.map +1 -1
- package/dist/page_components/forgot_password.js +6 -2
- package/dist/page_components/index.d.ts +0 -5
- package/dist/page_components/index.d.ts.map +1 -1
- package/dist/page_components/index.js +0 -5
- package/dist/page_components/login.d.ts +4 -1
- package/dist/page_components/login.d.ts.map +1 -1
- package/dist/page_components/login.js +6 -2
- package/dist/page_components/register.d.ts +4 -1
- package/dist/page_components/register.d.ts.map +1 -1
- package/dist/page_components/register.js +6 -2
- package/dist/page_components/reset_password.d.ts +4 -1
- package/dist/page_components/reset_password.d.ts.map +1 -1
- package/dist/page_components/reset_password.js +6 -2
- package/dist/page_components/verify_email.d.ts +4 -1
- package/dist/page_components/verify_email.d.ts.map +1 -1
- package/dist/page_components/verify_email.js +6 -2
- package/dist/server/routes/assets.d.ts +8 -0
- package/dist/server/routes/assets.d.ts.map +1 -0
- package/dist/server/routes/assets.js +38 -0
- package/dist/server/routes/consent_me.d.ts +4 -0
- package/dist/server/routes/consent_me.d.ts.map +1 -0
- package/dist/server/routes/consent_me.js +15 -0
- package/dist/server/routes/index.d.ts +9 -4
- package/dist/server/routes/index.d.ts.map +1 -1
- package/dist/server/routes/index.js +13 -5
- package/dist/server/routes/legal_docs_accept.d.ts +3 -0
- package/dist/server/routes/legal_docs_accept.d.ts.map +1 -0
- package/dist/server/routes/legal_docs_accept.js +43 -0
- package/dist/server/routes/legal_docs_get.d.ts +3 -0
- package/dist/server/routes/legal_docs_get.d.ts.map +1 -0
- package/dist/server/routes/legal_docs_get.js +49 -0
- package/dist/server/routes/legal_docs_publish.d.ts +3 -0
- package/dist/server/routes/legal_docs_publish.d.ts.map +1 -0
- package/dist/server/routes/legal_docs_publish.js +35 -0
- package/dist/server/routes/me.d.ts.map +1 -1
- package/dist/server/routes/me.js +1 -43
- package/dist/server/routes/oauth_facebook_callback.d.ts +1 -1
- package/dist/server/routes/oauth_facebook_callback.d.ts.map +1 -1
- package/dist/server/routes/oauth_facebook_callback.js +8 -1
- package/dist/server/routes/oauth_google_callback.js +1 -1
- package/dist/server/routes/otp/verify.js +2 -2
- package/dist/server/routes/register.d.ts.map +1 -1
- package/dist/server/routes/register.js +26 -0
- package/dist/server/routes/strings_defaults.d.ts +4 -0
- package/dist/server/routes/strings_defaults.d.ts.map +1 -0
- package/dist/server/routes/strings_defaults.js +7 -0
- package/dist/server/routes/user_management_users.d.ts +11 -0
- package/dist/server/routes/user_management_users.d.ts.map +1 -1
- package/dist/server/routes/user_management_users.js +94 -0
- package/dist/server-lib.d.ts +0 -3
- package/dist/server-lib.d.ts.map +1 -1
- package/dist/server-lib.js +0 -2
- package/dist/server_pages/forgot_password.d.ts +18 -14
- package/dist/server_pages/forgot_password.d.ts.map +1 -1
- package/dist/server_pages/forgot_password.js +14 -12
- package/dist/server_pages/forgot_password_client_wrapper.d.ts +8 -7
- package/dist/server_pages/forgot_password_client_wrapper.d.ts.map +1 -1
- package/dist/server_pages/forgot_password_client_wrapper.js +2 -2
- package/dist/server_pages/index.d.ts +2 -0
- package/dist/server_pages/index.d.ts.map +1 -1
- package/dist/server_pages/index.js +1 -0
- package/dist/server_pages/login.d.ts +22 -23
- package/dist/server_pages/login.d.ts.map +1 -1
- package/dist/server_pages/login.js +27 -14
- package/dist/server_pages/login_client_wrapper.d.ts +9 -10
- package/dist/server_pages/login_client_wrapper.d.ts.map +1 -1
- package/dist/server_pages/login_client_wrapper.js +2 -2
- package/dist/server_pages/my_settings.d.ts +1 -3
- package/dist/server_pages/my_settings.d.ts.map +1 -1
- package/dist/server_pages/my_settings.js +2 -9
- package/dist/server_pages/register.d.ts +17 -20
- package/dist/server_pages/register.d.ts.map +1 -1
- package/dist/server_pages/register.js +20 -15
- package/dist/server_pages/register_client_wrapper.d.ts +8 -10
- package/dist/server_pages/register_client_wrapper.d.ts.map +1 -1
- package/dist/server_pages/register_client_wrapper.js +2 -2
- package/dist/server_pages/reset_password.d.ts +16 -11
- package/dist/server_pages/reset_password.d.ts.map +1 -1
- package/dist/server_pages/reset_password.js +14 -10
- package/dist/server_pages/reset_password_client_wrapper.d.ts +8 -7
- package/dist/server_pages/reset_password_client_wrapper.d.ts.map +1 -1
- package/dist/server_pages/reset_password_client_wrapper.js +2 -2
- package/dist/server_pages/verify_email.d.ts +18 -12
- package/dist/server_pages/verify_email.d.ts.map +1 -1
- package/dist/server_pages/verify_email.js +13 -11
- package/dist/server_pages/verify_email_client_wrapper.d.ts +8 -7
- package/dist/server_pages/verify_email_client_wrapper.d.ts.map +1 -1
- package/dist/server_pages/verify_email_client_wrapper.js +2 -2
- package/dist/strings.d.ts +2 -0
- package/dist/strings.d.ts.map +1 -0
- package/dist/strings.js +3 -0
- package/dist/themes/index.d.ts +0 -1
- package/dist/themes/index.d.ts.map +1 -1
- package/dist/themes/index.js +1 -1
- package/package.json +30 -61
- package/dist/themes/preset_indigo_sunset.d.ts +0 -3
- package/dist/themes/preset_indigo_sunset.d.ts.map +0 -1
- package/dist/themes/preset_indigo_sunset.js +0 -20
package/SETUP_CHECKLIST.md
CHANGED
|
@@ -2,6 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
This checklist provides step-by-step instructions for setting up the `hazo_auth` package in your Next.js project. AI assistants can follow this guide to ensure complete and correct setup.
|
|
4
4
|
|
|
5
|
+
## v8.0.0 Migration (from v7.x)
|
|
6
|
+
|
|
7
|
+
### Breaking changes
|
|
8
|
+
|
|
9
|
+
Remove the deprecated page-component imports if you used them:
|
|
10
|
+
|
|
11
|
+
```diff
|
|
12
|
+
- import LoginPage from 'hazo_auth/page_components/login';
|
|
13
|
+
- import RegisterPage from 'hazo_auth/page_components/register';
|
|
14
|
+
+ // Use the Layout components directly in a server component instead
|
|
15
|
+
+ import LoginLayout from 'hazo_auth/components/layouts/login';
|
|
16
|
+
+ import RegisterLayout from 'hazo_auth/components/layouts/register';
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### New migrations (run in order)
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm run migrate migrations/017_legal_acceptance_column.sql
|
|
23
|
+
npm run migrate migrations/018_hazo_legal_acceptances.sql
|
|
24
|
+
npm run migrate migrations/019_hazo_legal_doc_versions.sql
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Legal docs setup (optional)
|
|
28
|
+
|
|
29
|
+
1. Add `[hazo_auth__legal_docs]` to `config/hazo_auth_config.ini` (see commented sample at the bottom of that file)
|
|
30
|
+
2. Create markdown files at the configured paths (e.g. `legal/tos.md`, `legal/privacy.md`)
|
|
31
|
+
3. Wrap your app layout with `<LegalAcceptanceGate>` from `hazo_auth/client`
|
|
32
|
+
4. Go to User Management → Legal Docs tab, click "Publish current version" per doc to activate gating for existing users
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
5
36
|
## v5.3.0 Migration (from v5.2.x)
|
|
6
37
|
|
|
7
38
|
If you are already on hazo_auth `5.2.x`, the only mandatory work is wiring up hazo_notify's template manager at boot. Apps that don't use the templated email path (only the plain `send_email`) keep working unchanged after bumping the peer dep.
|
|
@@ -958,7 +989,27 @@ CREATE UNIQUE INDEX IF NOT EXISTS idx_hazo_users_google_id_unique ON hazo_users(
|
|
|
958
989
|
CREATE INDEX IF NOT EXISTS idx_hazo_users_google_id ON hazo_users(google_id);
|
|
959
990
|
```
|
|
960
991
|
|
|
961
|
-
### Step 3.2.4:
|
|
992
|
+
### Step 3.2.4 (Optional): Run Email OTP Migration
|
|
993
|
+
|
|
994
|
+
Required only if you plan to enable `enable_email_otp = true` in `[hazo_auth__otp]`.
|
|
995
|
+
|
|
996
|
+
```bash
|
|
997
|
+
npx hazo_auth migrate migrations/015_email_otp.sql
|
|
998
|
+
```
|
|
999
|
+
|
|
1000
|
+
Creates `hazo_email_otps` table for OTP codes and rate limiting.
|
|
1001
|
+
|
|
1002
|
+
### Step 3.2.4b (Optional): Run Facebook OAuth Migration
|
|
1003
|
+
|
|
1004
|
+
Required only if `enable_facebook_oauth = true` in `[hazo_auth__oauth]`.
|
|
1005
|
+
|
|
1006
|
+
```bash
|
|
1007
|
+
npx hazo_auth migrate migrations/016_add_facebook_id_to_hazo_users.sql
|
|
1008
|
+
```
|
|
1009
|
+
|
|
1010
|
+
Adds `facebook_id` column to `hazo_users`.
|
|
1011
|
+
|
|
1012
|
+
### Step 3.2.5: Configure OAuth in hazo_auth_config.ini
|
|
962
1013
|
|
|
963
1014
|
Add (or modify) the OAuth section:
|
|
964
1015
|
|
|
@@ -1443,246 +1494,6 @@ export { POST } from "hazo_auth/server/routes/pin_login";
|
|
|
1443
1494
|
|
|
1444
1495
|
---
|
|
1445
1496
|
|
|
1446
|
-
## Phase 5.3: Email-OTP Sign-in Setup (Optional)
|
|
1447
|
-
|
|
1448
|
-
Email-OTP sign-in lets users authenticate with a 6-digit code sent to their email — no password or Google OAuth required. Skip this phase if your app uses only password or OAuth authentication.
|
|
1449
|
-
|
|
1450
|
-
### Step 5.3.1: Apply the OTP migration
|
|
1451
|
-
|
|
1452
|
-
```bash
|
|
1453
|
-
npm run migrate migrations/015_email_otp.sql
|
|
1454
|
-
```
|
|
1455
|
-
|
|
1456
|
-
This creates the `hazo_email_otps` table (stores hashed codes with expiry).
|
|
1457
|
-
|
|
1458
|
-
**Verify the table exists:**
|
|
1459
|
-
```bash
|
|
1460
|
-
sqlite3 data/hazo_auth.sqlite ".tables" | tr ' ' '\n' | grep hazo_email_otps
|
|
1461
|
-
# Expected: hazo_email_otps
|
|
1462
|
-
```
|
|
1463
|
-
|
|
1464
|
-
### Step 5.3.2: Add OTP config section
|
|
1465
|
-
|
|
1466
|
-
Add to `config/hazo_auth_config.ini`:
|
|
1467
|
-
|
|
1468
|
-
```ini
|
|
1469
|
-
[hazo_auth__otp]
|
|
1470
|
-
# Whether OTP sign-in is enabled
|
|
1471
|
-
enabled = true
|
|
1472
|
-
|
|
1473
|
-
# Auto-register unknown emails on first successful verify (default: false)
|
|
1474
|
-
otp_auto_register = false
|
|
1475
|
-
|
|
1476
|
-
# Code expiry in minutes (default: 10)
|
|
1477
|
-
code_expiry_minutes = 10
|
|
1478
|
-
```
|
|
1479
|
-
|
|
1480
|
-
### Step 5.3.3: Create OTP API routes
|
|
1481
|
-
|
|
1482
|
-
```bash
|
|
1483
|
-
npx hazo_auth generate-routes
|
|
1484
|
-
```
|
|
1485
|
-
|
|
1486
|
-
Or manually:
|
|
1487
|
-
|
|
1488
|
-
`app/api/hazo_auth/otp/request/route.ts`:
|
|
1489
|
-
```typescript
|
|
1490
|
-
export { otpRequestPOST as POST } from "hazo_auth/server/routes";
|
|
1491
|
-
```
|
|
1492
|
-
|
|
1493
|
-
`app/api/hazo_auth/otp/verify/route.ts`:
|
|
1494
|
-
```typescript
|
|
1495
|
-
export { otpVerifyPOST as POST } from "hazo_auth/server/routes";
|
|
1496
|
-
```
|
|
1497
|
-
|
|
1498
|
-
### Step 5.3.4: Create the OTP sign-in page
|
|
1499
|
-
|
|
1500
|
-
`app/hazo_auth/otp/page.tsx`:
|
|
1501
|
-
```typescript
|
|
1502
|
-
export { default } from "hazo_auth/pages/otp";
|
|
1503
|
-
```
|
|
1504
|
-
|
|
1505
|
-
### Step 5.3.5: Add OTP routes to middleware/proxy public_routes (REQUIRED)
|
|
1506
|
-
|
|
1507
|
-
**This step is critical.** Unauthenticated users arrive at the OTP sign-in page before they have auth cookies. If the OTP routes are not in `public_routes`, your middleware will redirect them to `/login` in a loop.
|
|
1508
|
-
|
|
1509
|
-
Edit your `middleware.ts` or `proxy.ts`:
|
|
1510
|
-
|
|
1511
|
-
```typescript
|
|
1512
|
-
const public_routes = [
|
|
1513
|
-
// ... existing entries ...
|
|
1514
|
-
"/hazo_auth/otp", // OTP sign-in page (public — users arrive unauthenticated)
|
|
1515
|
-
"/api/hazo_auth/otp", // OTP request + verify API routes
|
|
1516
|
-
];
|
|
1517
|
-
```
|
|
1518
|
-
|
|
1519
|
-
### Step 5.3.6: Install optional peer dep (if using OTPVerifyForm)
|
|
1520
|
-
|
|
1521
|
-
If you render `<OTPVerifyForm/>` directly (rather than using the zero-config page), install:
|
|
1522
|
-
|
|
1523
|
-
```bash
|
|
1524
|
-
npm install input-otp
|
|
1525
|
-
```
|
|
1526
|
-
|
|
1527
|
-
**OTP Setup Checklist:**
|
|
1528
|
-
- [ ] `hazo_email_otps` table created (`npm run migrate migrations/015_email_otp.sql`)
|
|
1529
|
-
- [ ] `[hazo_auth__otp]` section added to `hazo_auth_config.ini`
|
|
1530
|
-
- [ ] OTP API routes created (`/api/hazo_auth/otp/request` and `/api/hazo_auth/otp/verify`)
|
|
1531
|
-
- [ ] OTP page created (`/hazo_auth/otp`)
|
|
1532
|
-
- [ ] `/hazo_auth/otp` and `/api/hazo_auth/otp` added to middleware `public_routes`
|
|
1533
|
-
- [ ] `input-otp` installed (only if using `<OTPVerifyForm/>` directly)
|
|
1534
|
-
- [ ] Email delivery configured (`hazo_notify` + valid `ZEPTOMAIL_API_KEY`)
|
|
1535
|
-
|
|
1536
|
-
---
|
|
1537
|
-
|
|
1538
|
-
## Phase 5.4: Theme Setup (v7.0.0+)
|
|
1539
|
-
|
|
1540
|
-
### Step 5.4.1: Mount HazoAuthThemeProvider (Required for themed auth pages)
|
|
1541
|
-
|
|
1542
|
-
Mount `<HazoAuthThemeProvider>` in `app/layout.tsx`. Use `preset_neutral` for the default look, or configure a custom theme.
|
|
1543
|
-
|
|
1544
|
-
**Option A: Default look (no config needed)**
|
|
1545
|
-
```tsx
|
|
1546
|
-
import { HazoAuthThemeProvider } from "hazo_auth/theme";
|
|
1547
|
-
import { preset_neutral } from "hazo_auth/themes";
|
|
1548
|
-
|
|
1549
|
-
export default function RootLayout({ children }) {
|
|
1550
|
-
return (
|
|
1551
|
-
<html>
|
|
1552
|
-
<body>
|
|
1553
|
-
<HazoAuthThemeProvider theme={preset_neutral}>
|
|
1554
|
-
{children}
|
|
1555
|
-
</HazoAuthThemeProvider>
|
|
1556
|
-
</body>
|
|
1557
|
-
</html>
|
|
1558
|
-
);
|
|
1559
|
-
}
|
|
1560
|
-
```
|
|
1561
|
-
|
|
1562
|
-
**Option B: Custom theme**
|
|
1563
|
-
```tsx
|
|
1564
|
-
import { createTheme, HazoAuthThemeProvider } from "hazo_auth/theme";
|
|
1565
|
-
|
|
1566
|
-
const theme = createTheme({
|
|
1567
|
-
colors: { primary: "#2563EB" },
|
|
1568
|
-
layout: "split",
|
|
1569
|
-
brandPanel: { logoSrc: "/logo.svg", tagline: "Welcome." },
|
|
1570
|
-
});
|
|
1571
|
-
|
|
1572
|
-
<HazoAuthThemeProvider theme={theme}>{children}</HazoAuthThemeProvider>
|
|
1573
|
-
```
|
|
1574
|
-
|
|
1575
|
-
**If using `preset_indigo_sunset` (custom fonts required):**
|
|
1576
|
-
|
|
1577
|
-
Wire `next/font` in `app/layout.tsx` to expose `--font-display` and `--font-body` CSS variables. See [MIGRATION.md §3](./MIGRATION.md).
|
|
1578
|
-
|
|
1579
|
-
```tsx
|
|
1580
|
-
import { Crimson_Pro, DM_Sans } from "next/font/google";
|
|
1581
|
-
const display = Crimson_Pro({ subsets: ["latin"], variable: "--font-display" });
|
|
1582
|
-
const body = DM_Sans({ subsets: ["latin"], variable: "--font-body" });
|
|
1583
|
-
|
|
1584
|
-
export default function Layout({ children }) {
|
|
1585
|
-
return <html className={`${display.variable} ${body.variable}`}>{children}</html>;
|
|
1586
|
-
}
|
|
1587
|
-
```
|
|
1588
|
-
|
|
1589
|
-
**Checklist:**
|
|
1590
|
-
- [ ] `<HazoAuthThemeProvider>` mounted in `app/layout.tsx`
|
|
1591
|
-
- [ ] Preset or custom theme configured
|
|
1592
|
-
- [ ] If using `preset_indigo_sunset`: `next/font` wired for `--font-display` and `--font-body`
|
|
1593
|
-
|
|
1594
|
-
---
|
|
1595
|
-
|
|
1596
|
-
## Phase 5.5: Facebook OAuth Setup (Optional, v7.0.0+)
|
|
1597
|
-
|
|
1598
|
-
### Step 5.5.1: Get Facebook App credentials
|
|
1599
|
-
|
|
1600
|
-
1. Go to [Meta Developers](https://developers.facebook.com/)
|
|
1601
|
-
2. Create or select an app
|
|
1602
|
-
3. Add the **Facebook Login** product
|
|
1603
|
-
4. Set authorized redirect URIs: `http://localhost:3000/api/auth/callback/facebook` (dev) and your production URL
|
|
1604
|
-
5. Copy **App ID** and **App Secret**
|
|
1605
|
-
|
|
1606
|
-
### Step 5.5.2: Add Facebook OAuth environment variables
|
|
1607
|
-
|
|
1608
|
-
Add to your `.env.local`:
|
|
1609
|
-
```env
|
|
1610
|
-
HAZO_AUTH_FACEBOOK_APP_ID=your_app_id
|
|
1611
|
-
HAZO_AUTH_FACEBOOK_APP_SECRET=your_app_secret
|
|
1612
|
-
```
|
|
1613
|
-
|
|
1614
|
-
The Facebook sign-in button is hidden when these vars are not set.
|
|
1615
|
-
|
|
1616
|
-
### Step 5.5.3: Enable Facebook in config
|
|
1617
|
-
|
|
1618
|
-
Add to `config/hazo_auth_config.ini`:
|
|
1619
|
-
```ini
|
|
1620
|
-
[hazo_auth__oauth]
|
|
1621
|
-
enable_facebook = true
|
|
1622
|
-
|
|
1623
|
-
; Facebook: default false — stricter security posture
|
|
1624
|
-
; Set true only if you want to auto-link to unverified email accounts
|
|
1625
|
-
auto_link_unverified_accounts_facebook = false
|
|
1626
|
-
```
|
|
1627
|
-
|
|
1628
|
-
### Step 5.5.4: Apply Facebook migration
|
|
1629
|
-
|
|
1630
|
-
```bash
|
|
1631
|
-
npm run migrate migrations/016_facebook_oauth.sql
|
|
1632
|
-
```
|
|
1633
|
-
|
|
1634
|
-
### Step 5.5.5: Create Facebook OAuth callback route
|
|
1635
|
-
|
|
1636
|
-
```typescript
|
|
1637
|
-
// app/api/hazo_auth/oauth/facebook/callback/route.ts
|
|
1638
|
-
export { GET } from "hazo_auth/server/routes/oauth_facebook_callback";
|
|
1639
|
-
```
|
|
1640
|
-
|
|
1641
|
-
**Facebook OAuth Checklist:**
|
|
1642
|
-
- [ ] Facebook App credentials obtained
|
|
1643
|
-
- [ ] `HAZO_AUTH_FACEBOOK_APP_ID` and `HAZO_AUTH_FACEBOOK_APP_SECRET` set in `.env.local`
|
|
1644
|
-
- [ ] `enable_facebook = true` in `[hazo_auth__oauth]`
|
|
1645
|
-
- [ ] Migration `016_facebook_oauth.sql` applied
|
|
1646
|
-
- [ ] Facebook callback route created
|
|
1647
|
-
|
|
1648
|
-
---
|
|
1649
|
-
|
|
1650
|
-
## Phase 5.6: Cookie Consent Setup (Optional, v7.0.0+)
|
|
1651
|
-
|
|
1652
|
-
### Step 5.6.1: Mount CookieConsentBanner
|
|
1653
|
-
|
|
1654
|
-
Mount `<CookieConsentBanner>` in your root layout:
|
|
1655
|
-
|
|
1656
|
-
```tsx
|
|
1657
|
-
import { CookieConsentBanner } from "hazo_auth/consent";
|
|
1658
|
-
|
|
1659
|
-
export default function RootLayout({ children }) {
|
|
1660
|
-
return (
|
|
1661
|
-
<html>
|
|
1662
|
-
<body>
|
|
1663
|
-
<HazoAuthThemeProvider theme={preset_neutral}>
|
|
1664
|
-
{children}
|
|
1665
|
-
<CookieConsentBanner />
|
|
1666
|
-
</HazoAuthThemeProvider>
|
|
1667
|
-
</body>
|
|
1668
|
-
</html>
|
|
1669
|
-
);
|
|
1670
|
-
}
|
|
1671
|
-
```
|
|
1672
|
-
|
|
1673
|
-
**With GTM (optional):**
|
|
1674
|
-
```tsx
|
|
1675
|
-
<CookieConsentBanner enableGTM gtmContainerId="GTM-XXXX" />
|
|
1676
|
-
```
|
|
1677
|
-
|
|
1678
|
-
See the README [Cookie Consent](#cookie-consent-v700) section for server-side `read_consent` usage.
|
|
1679
|
-
|
|
1680
|
-
**Checklist:**
|
|
1681
|
-
- [ ] `<CookieConsentBanner>` mounted in root layout
|
|
1682
|
-
- [ ] GTM container ID configured (if using GTM)
|
|
1683
|
-
|
|
1684
|
-
---
|
|
1685
|
-
|
|
1686
1497
|
## Phase 6: Verification Tests
|
|
1687
1498
|
|
|
1688
1499
|
Run these tests to verify your setup is working correctly.
|
|
@@ -2164,10 +1975,10 @@ import { hazo_get_tenant_auth } from "hazo_auth/server-lib";
|
|
|
2164
1975
|
export async function GET(request: NextRequest) {
|
|
2165
1976
|
const auth = await hazo_get_tenant_auth(request);
|
|
2166
1977
|
|
|
2167
|
-
if (auth.authenticated && auth.
|
|
2168
|
-
// auth.
|
|
1978
|
+
if (auth.authenticated && auth.organization) {
|
|
1979
|
+
// auth.organization contains tenant details
|
|
2169
1980
|
// auth.user_scopes contains all scopes user can access (for UI switcher)
|
|
2170
|
-
const data = await getTenantData(auth.
|
|
1981
|
+
const data = await getTenantData(auth.organization.id);
|
|
2171
1982
|
}
|
|
2172
1983
|
}
|
|
2173
1984
|
```
|
|
@@ -2179,8 +1990,8 @@ import { require_tenant_auth, HazoAuthError } from "hazo_auth/server-lib";
|
|
|
2179
1990
|
export async function GET(request: NextRequest) {
|
|
2180
1991
|
try {
|
|
2181
1992
|
const auth = await require_tenant_auth(request);
|
|
2182
|
-
// auth.
|
|
2183
|
-
return NextResponse.json(await getData(auth.
|
|
1993
|
+
// auth.organization is guaranteed non-null
|
|
1994
|
+
return NextResponse.json(await getData(auth.organization.id));
|
|
2184
1995
|
} catch (error) {
|
|
2185
1996
|
if (error instanceof HazoAuthError) {
|
|
2186
1997
|
return NextResponse.json(
|
|
@@ -2200,7 +2011,7 @@ import { withAuth } from "hazo_auth/server-lib";
|
|
|
2200
2011
|
// Auth + params + error handling all automatic
|
|
2201
2012
|
export const GET = withAuth<{ id: string }>(
|
|
2202
2013
|
async (request, auth, { id }) => {
|
|
2203
|
-
const data = await getData(auth.
|
|
2014
|
+
const data = await getData(auth.organization.id, id);
|
|
2204
2015
|
return NextResponse.json(data);
|
|
2205
2016
|
},
|
|
2206
2017
|
{ require_tenant: true }
|
|
@@ -2249,7 +2060,7 @@ const auth = await hazo_get_tenant_auth(request);
|
|
|
2249
2060
|
|
|
2250
2061
|
// Return available scopes to frontend
|
|
2251
2062
|
return NextResponse.json({
|
|
2252
|
-
current_scope: auth.
|
|
2063
|
+
current_scope: auth.organization,
|
|
2253
2064
|
available_scopes: auth.user_scopes.map(s => ({
|
|
2254
2065
|
id: s.id,
|
|
2255
2066
|
name: s.name,
|
package/cli-src/cli/generate.ts
CHANGED
|
@@ -127,20 +127,11 @@ ${exports}
|
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
function generate_page_content(page: PageDefinition): string {
|
|
130
|
-
// Next 16 requires page default exports to satisfy `PageProps` (i.e. accept
|
|
131
|
-
// `{ params?, searchParams? }` and nothing else). Re-exporting the named
|
|
132
|
-
// component directly fails that check because the component carries custom
|
|
133
|
-
// props (image_src, layout, …). The wrapper below is a parameter-less
|
|
134
|
-
// function that returns the component — Next sees a `() => JSX` signature
|
|
135
|
-
// which trivially satisfies PageProps. Direct JSX consumers still use the
|
|
136
|
-
// named export from `hazo_auth/pages` and get the full custom-prop API.
|
|
137
130
|
return `// Generated by hazo_auth - do not edit manually
|
|
138
131
|
// Page: /${page.path}
|
|
139
132
|
import { ${page.component_name} } from "hazo_auth/pages";
|
|
140
133
|
|
|
141
|
-
export default
|
|
142
|
-
return <${page.component_name} />;
|
|
143
|
-
}
|
|
134
|
+
export default ${page.component_name};
|
|
144
135
|
`;
|
|
145
136
|
}
|
|
146
137
|
|
package/cli-src/cli/validate.ts
CHANGED
|
@@ -64,9 +64,6 @@ const REQUIRED_API_ROUTES = [
|
|
|
64
64
|
{ path: "api/hazo_auth/user_management/users/roles", method: "GET" },
|
|
65
65
|
{ path: "api/hazo_auth/user_management/users/roles", method: "POST" },
|
|
66
66
|
{ path: "api/hazo_auth/user_management/users/roles", method: "PUT" },
|
|
67
|
-
// OTP routes
|
|
68
|
-
{ path: "api/hazo_auth/otp/request", method: "POST" },
|
|
69
|
-
{ path: "api/hazo_auth/otp/verify", method: "POST" },
|
|
70
67
|
];
|
|
71
68
|
|
|
72
69
|
// section: helpers
|
|
@@ -537,7 +534,6 @@ const REQUIRED_TABLES = [
|
|
|
537
534
|
"hazo_role_permissions",
|
|
538
535
|
"hazo_invitations",
|
|
539
536
|
"hazo_refresh_tokens",
|
|
540
|
-
"hazo_email_otps",
|
|
541
537
|
];
|
|
542
538
|
|
|
543
539
|
const TEXT_ID_TABLES = [
|
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
// file_description: Type definitions and error classes for hazo_get_auth utility
|
|
2
|
-
//
|
|
3
|
-
// Naming note (v6.0.0): the field previously called `organization` (and
|
|
4
|
-
// `organization_id`) on `TenantAuthResult` was renamed to `selected_scope`
|
|
5
|
-
// (and `selected_scope_id`), and the type `TenantOrganization` was renamed
|
|
6
|
-
// to `SelectedScope`. The multi-tenancy model is scopes throughout; the
|
|
7
|
-
// old name was a legacy synonym for "the currently selected scope" derived
|
|
8
|
-
// from the scope-selection cookie/header. No deprecation shim is provided.
|
|
9
|
-
//
|
|
10
2
|
// section: types
|
|
3
|
+
import type { LegalAcceptanceMap } from '../legal/legal_docs_types';
|
|
11
4
|
|
|
12
5
|
/**
|
|
13
6
|
* User data structure returned by hazo_get_auth
|
|
@@ -21,6 +14,8 @@ export type HazoAuthUser = {
|
|
|
21
14
|
managed_by_user_id?: string | null;
|
|
22
15
|
// App-specific user data (JSON object stored as TEXT in database)
|
|
23
16
|
app_user_data: Record<string, unknown> | null;
|
|
17
|
+
// Legal document acceptance map (JSON object stored in database)
|
|
18
|
+
legal_acceptance: LegalAcceptanceMap | null;
|
|
24
19
|
};
|
|
25
20
|
|
|
26
21
|
/**
|
|
@@ -131,11 +126,10 @@ export type ScopeDetails = {
|
|
|
131
126
|
};
|
|
132
127
|
|
|
133
128
|
/**
|
|
134
|
-
*
|
|
135
|
-
* Simplified view of
|
|
136
|
-
* `X-Hazo-Scope-Id` header.
|
|
129
|
+
* Tenant/organization information returned in tenant auth results
|
|
130
|
+
* Simplified view of scope for API responses
|
|
137
131
|
*/
|
|
138
|
-
export type
|
|
132
|
+
export type TenantOrganization = {
|
|
139
133
|
id: string;
|
|
140
134
|
name: string;
|
|
141
135
|
slug: string | null;
|
|
@@ -176,9 +170,9 @@ export type TenantAuthResult =
|
|
|
176
170
|
permissions: string[];
|
|
177
171
|
permission_ok: boolean;
|
|
178
172
|
missing_permissions?: string[];
|
|
179
|
-
|
|
180
|
-
/** Shorthand for
|
|
181
|
-
|
|
173
|
+
organization: TenantOrganization | null;
|
|
174
|
+
/** Shorthand for organization?.id - commonly used for DB query filters */
|
|
175
|
+
organization_id: string | null;
|
|
182
176
|
user_scopes: ScopeDetails[];
|
|
183
177
|
scope_ok?: boolean;
|
|
184
178
|
scope_access_via?: ScopeAccessInfo;
|
|
@@ -188,20 +182,20 @@ export type TenantAuthResult =
|
|
|
188
182
|
user: null;
|
|
189
183
|
permissions: [];
|
|
190
184
|
permission_ok: false;
|
|
191
|
-
|
|
192
|
-
/** Shorthand for
|
|
193
|
-
|
|
185
|
+
organization: null;
|
|
186
|
+
/** Shorthand for organization?.id - commonly used for DB query filters */
|
|
187
|
+
organization_id: null;
|
|
194
188
|
user_scopes: [];
|
|
195
189
|
scope_ok?: false;
|
|
196
190
|
};
|
|
197
191
|
|
|
198
192
|
/**
|
|
199
|
-
* Guaranteed authenticated result with non-null
|
|
200
|
-
* Returned by require_tenant_auth when validation passes
|
|
193
|
+
* Guaranteed authenticated result with non-null organization
|
|
194
|
+
* Returned by require_tenant_auth when validation passes
|
|
201
195
|
*/
|
|
202
196
|
export type RequiredTenantAuthResult = TenantAuthResult & {
|
|
203
197
|
authenticated: true;
|
|
204
|
-
|
|
198
|
+
organization: TenantOrganization;
|
|
205
199
|
};
|
|
206
200
|
|
|
207
201
|
// section: tenant_error_classes
|
|
@@ -59,6 +59,24 @@ function parse_app_user_data(
|
|
|
59
59
|
}
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Parse raw legal_acceptance field from DB to LegalAcceptanceMap
|
|
64
|
+
* @param raw - Raw value from database (string or object)
|
|
65
|
+
* @returns Parsed LegalAcceptanceMap or null
|
|
66
|
+
*/
|
|
67
|
+
function parse_legal_acceptance(
|
|
68
|
+
raw: unknown,
|
|
69
|
+
): import('../legal/legal_docs_types').LegalAcceptanceMap | null {
|
|
70
|
+
if (!raw) return null;
|
|
71
|
+
try {
|
|
72
|
+
const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw;
|
|
73
|
+
if (typeof parsed !== 'object' || Array.isArray(parsed)) return null;
|
|
74
|
+
return parsed as import('../legal/legal_docs_types').LegalAcceptanceMap;
|
|
75
|
+
} catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
62
80
|
/**
|
|
63
81
|
* Gets client IP address from request
|
|
64
82
|
* @param request - NextRequest object
|
|
@@ -185,6 +203,7 @@ async function fetch_user_data_from_db(user_id: string): Promise<{
|
|
|
185
203
|
(user_db.profile_picture_url as string | null) || null,
|
|
186
204
|
managed_by_user_id: (user_db.managed_by_user_id as string | undefined) || null,
|
|
187
205
|
app_user_data: parse_app_user_data(user_db.app_user_data),
|
|
206
|
+
legal_acceptance: parse_legal_acceptance(user_db.legal_acceptance),
|
|
188
207
|
};
|
|
189
208
|
|
|
190
209
|
// v5.x: Fetch user's roles from hazo_user_scopes (scope-based role assignments)
|
|
@@ -14,7 +14,7 @@ import type {
|
|
|
14
14
|
TenantAuthOptions,
|
|
15
15
|
TenantAuthResult,
|
|
16
16
|
RequiredTenantAuthResult,
|
|
17
|
-
|
|
17
|
+
TenantOrganization,
|
|
18
18
|
ScopeDetails,
|
|
19
19
|
} from "./auth_types";
|
|
20
20
|
import {
|
|
@@ -62,15 +62,15 @@ export function extract_scope_id_from_request(
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
/**
|
|
65
|
-
* Builds
|
|
65
|
+
* Builds TenantOrganization from scope details and access info
|
|
66
66
|
* @param scope_details - Full scope details from cache
|
|
67
67
|
* @param is_super_admin - Whether user is accessing as super admin
|
|
68
|
-
* @returns
|
|
68
|
+
* @returns TenantOrganization object
|
|
69
69
|
*/
|
|
70
|
-
function
|
|
70
|
+
function build_tenant_organization(
|
|
71
71
|
scope_details: ScopeDetails,
|
|
72
72
|
is_super_admin: boolean,
|
|
73
|
-
):
|
|
73
|
+
): TenantOrganization {
|
|
74
74
|
return {
|
|
75
75
|
id: scope_details.id,
|
|
76
76
|
name: scope_details.name,
|
|
@@ -96,21 +96,20 @@ function build_selected_scope(
|
|
|
96
96
|
* Tenant-aware authentication function
|
|
97
97
|
*
|
|
98
98
|
* Extracts tenant/scope context from request headers or cookies,
|
|
99
|
-
* validates access, and returns enriched result
|
|
100
|
-
* selected scope.
|
|
99
|
+
* validates access, and returns enriched result with organization info.
|
|
101
100
|
*
|
|
102
101
|
* Header priority: X-Hazo-Scope-Id > Cookie
|
|
103
102
|
*
|
|
104
103
|
* @param request - NextRequest object
|
|
105
104
|
* @param options - TenantAuthOptions for customization
|
|
106
|
-
* @returns TenantAuthResult with user, permissions,
|
|
105
|
+
* @returns TenantAuthResult with user, permissions, organization, and user_scopes
|
|
107
106
|
*
|
|
108
107
|
* @example
|
|
109
108
|
* ```typescript
|
|
110
109
|
* const auth = await hazo_get_tenant_auth(request);
|
|
111
|
-
* if (auth.authenticated && auth.
|
|
110
|
+
* if (auth.authenticated && auth.organization) {
|
|
112
111
|
* // Access tenant-specific data
|
|
113
|
-
* const data = await getData(auth.
|
|
112
|
+
* const data = await getData(auth.organization.id);
|
|
114
113
|
* }
|
|
115
114
|
* ```
|
|
116
115
|
*/
|
|
@@ -137,8 +136,8 @@ export async function hazo_get_tenant_auth(
|
|
|
137
136
|
user: null,
|
|
138
137
|
permissions: [],
|
|
139
138
|
permission_ok: false,
|
|
140
|
-
|
|
141
|
-
|
|
139
|
+
organization: null,
|
|
140
|
+
organization_id: null,
|
|
142
141
|
user_scopes: [],
|
|
143
142
|
scope_ok: false,
|
|
144
143
|
};
|
|
@@ -156,8 +155,8 @@ export async function hazo_get_tenant_auth(
|
|
|
156
155
|
// User scopes from cache or empty array
|
|
157
156
|
const user_scopes: ScopeDetails[] = cached?.scopes || [];
|
|
158
157
|
|
|
159
|
-
// Build
|
|
160
|
-
let
|
|
158
|
+
// Build organization info if scope access was successful
|
|
159
|
+
let organization: TenantOrganization | null = null;
|
|
161
160
|
|
|
162
161
|
if (scope_id && auth_result.scope_ok && auth_result.scope_access_via) {
|
|
163
162
|
// Find the scope in user's scopes that matches the access_via scope
|
|
@@ -166,7 +165,7 @@ export async function hazo_get_tenant_auth(
|
|
|
166
165
|
);
|
|
167
166
|
|
|
168
167
|
if (access_scope) {
|
|
169
|
-
|
|
168
|
+
organization = build_tenant_organization(
|
|
170
169
|
access_scope,
|
|
171
170
|
auth_result.scope_access_via.is_super_admin || false,
|
|
172
171
|
);
|
|
@@ -175,7 +174,7 @@ export async function hazo_get_tenant_auth(
|
|
|
175
174
|
const hazoConnect = get_hazo_connect_instance();
|
|
176
175
|
const scope_result = await get_scope_by_id(hazoConnect, scope_id);
|
|
177
176
|
if (scope_result.success && scope_result.scope) {
|
|
178
|
-
|
|
177
|
+
organization = {
|
|
179
178
|
id: scope_result.scope.id,
|
|
180
179
|
name: scope_result.scope.name,
|
|
181
180
|
slug: null, // Could fetch from scope if slug column exists
|
|
@@ -201,8 +200,8 @@ export async function hazo_get_tenant_auth(
|
|
|
201
200
|
permissions: auth_result.permissions,
|
|
202
201
|
permission_ok: auth_result.permission_ok,
|
|
203
202
|
missing_permissions: auth_result.missing_permissions,
|
|
204
|
-
|
|
205
|
-
|
|
203
|
+
organization,
|
|
204
|
+
organization_id: organization?.id || null,
|
|
206
205
|
user_scopes,
|
|
207
206
|
scope_ok: auth_result.scope_ok,
|
|
208
207
|
scope_access_via: auth_result.scope_access_via,
|
|
@@ -219,15 +218,15 @@ export async function hazo_get_tenant_auth(
|
|
|
219
218
|
*
|
|
220
219
|
* @param request - NextRequest object
|
|
221
220
|
* @param options - TenantAuthOptions for customization
|
|
222
|
-
* @returns RequiredTenantAuthResult with guaranteed non-null
|
|
221
|
+
* @returns RequiredTenantAuthResult with guaranteed non-null organization
|
|
223
222
|
* @throws AuthenticationRequiredError, TenantRequiredError, TenantAccessDeniedError
|
|
224
223
|
*
|
|
225
224
|
* @example
|
|
226
225
|
* ```typescript
|
|
227
226
|
* try {
|
|
228
227
|
* const auth = await require_tenant_auth(request);
|
|
229
|
-
* // auth.
|
|
230
|
-
* const data = await getData(auth.
|
|
228
|
+
* // auth.organization is guaranteed non-null here
|
|
229
|
+
* const data = await getData(auth.organization.id);
|
|
231
230
|
* } catch (error) {
|
|
232
231
|
* if (error instanceof HazoAuthError) {
|
|
233
232
|
* return NextResponse.json(
|
|
@@ -258,14 +257,14 @@ export async function require_tenant_auth(
|
|
|
258
257
|
throw new TenantAccessDeniedError(scope_id, result.user_scopes);
|
|
259
258
|
}
|
|
260
259
|
|
|
261
|
-
// Check if
|
|
262
|
-
if (!result.
|
|
260
|
+
// Check if organization context is required but missing
|
|
261
|
+
if (!result.organization) {
|
|
263
262
|
throw new TenantRequiredError(
|
|
264
|
-
"No
|
|
263
|
+
"No organization context provided. Include X-Hazo-Scope-Id header or scope cookie.",
|
|
265
264
|
result.user_scopes,
|
|
266
265
|
);
|
|
267
266
|
}
|
|
268
267
|
|
|
269
|
-
// Type assertion: at this point we know
|
|
268
|
+
// Type assertion: at this point we know organization is non-null
|
|
270
269
|
return result as RequiredTenantAuthResult;
|
|
271
270
|
}
|
|
@@ -22,7 +22,7 @@ export {
|
|
|
22
22
|
} from "./hazo_get_tenant_auth.server.js";
|
|
23
23
|
export type {
|
|
24
24
|
ScopeDetails,
|
|
25
|
-
|
|
25
|
+
TenantOrganization,
|
|
26
26
|
TenantAuthOptions,
|
|
27
27
|
TenantAuthResult,
|
|
28
28
|
RequiredTenantAuthResult,
|
|
@@ -50,7 +50,7 @@ export {
|
|
|
50
50
|
} from "./with_auth.server.js";
|
|
51
51
|
export type {
|
|
52
52
|
AuthenticatedTenantAuth,
|
|
53
|
-
|
|
53
|
+
AuthenticatedTenantAuthWithOrg,
|
|
54
54
|
WithAuthOptions,
|
|
55
55
|
} from "./with_auth.server";
|
|
56
56
|
|