hazo_auth 1.0.2 → 1.0.4
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/hazo_auth_config.example.ini +5 -0
- package/package.json +3 -3
- package/src/app/api/hazo_auth/resend_verification/route.ts +6 -5
- package/src/components/layouts/email_verification/hooks/use_email_verification.ts +11 -5
- package/src/components/layouts/shared/components/already_logged_in_guard.tsx +4 -4
- package/src/components/layouts/shared/components/form_action_buttons.tsx +2 -2
- package/src/components/layouts/shared/components/form_field_wrapper.tsx +2 -2
- package/src/components/layouts/shared/components/logout_button.tsx +2 -2
- package/src/components/layouts/shared/components/password_field.tsx +3 -3
- package/src/components/layouts/shared/components/profile_pic_menu.tsx +5 -5
- package/src/components/layouts/shared/components/profile_pic_menu_wrapper.tsx +1 -1
- package/src/components/layouts/shared/components/sidebar_layout_wrapper.tsx +3 -3
- package/src/components/layouts/shared/components/two_column_auth_layout.tsx +1 -1
- package/src/components/layouts/shared/components/unauthorized_guard.tsx +2 -2
- package/src/components/layouts/shared/hooks/use_hazo_auth.ts +1 -1
- package/src/components/layouts/shared/utils/validation.ts +1 -1
- package/src/lib/hazo_connect_instance.server.ts +38 -14
- package/src/lib/hazo_connect_setup.server.ts +18 -4
- package/src/lib/services/email_verification_service.ts +6 -0
- package/src/middleware.ts +1 -0
|
@@ -19,6 +19,11 @@ enable_admin_ui = true
|
|
|
19
19
|
|
|
20
20
|
# PostgREST configuration (uncomment if using postgrest type)
|
|
21
21
|
# postgrest_url =
|
|
22
|
+
|
|
23
|
+
# PostgREST API Key (REQUIRED if PostgREST uses authentication)
|
|
24
|
+
# IMPORTANT: API keys should ONLY be set in environment variables for security
|
|
25
|
+
# Set HAZO_CONNECT_POSTGREST_API_KEY or POSTGREST_API_KEY in your .env file
|
|
26
|
+
# DO NOT put API keys in this config file
|
|
22
27
|
# postgrest_api_key =
|
|
23
28
|
|
|
24
29
|
[hazo_auth__register_layout]
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hazo_auth",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"files": [
|
|
5
5
|
"src/**/*",
|
|
6
6
|
"public/file.svg",
|
|
@@ -101,8 +101,8 @@
|
|
|
101
101
|
"@types/react-dom": "^18",
|
|
102
102
|
"better-sqlite3": "^12.4.1",
|
|
103
103
|
"cross-env": "^10.1.0",
|
|
104
|
-
"eslint": "^
|
|
105
|
-
"eslint-config-next": "^
|
|
104
|
+
"eslint": "^9.0.0",
|
|
105
|
+
"eslint-config-next": "^16.0.3",
|
|
106
106
|
"eslint-plugin-storybook": "^10.0.6",
|
|
107
107
|
"jest": "^30.2.0",
|
|
108
108
|
"jest-environment-jsdom": "^29.7.0",
|
|
@@ -52,20 +52,21 @@ export async function POST(request: NextRequest) {
|
|
|
52
52
|
});
|
|
53
53
|
|
|
54
54
|
if (!result.success) {
|
|
55
|
-
logger.
|
|
55
|
+
logger.error("resend_verification_failed", {
|
|
56
56
|
filename: get_filename(),
|
|
57
57
|
line_number: get_line_number(),
|
|
58
58
|
email,
|
|
59
59
|
error: result.error,
|
|
60
60
|
});
|
|
61
61
|
|
|
62
|
-
//
|
|
62
|
+
// Return error response (500) when email sending fails
|
|
63
|
+
// This is a technical error, not a security issue, so we can reveal it
|
|
63
64
|
return NextResponse.json(
|
|
64
65
|
{
|
|
65
|
-
success:
|
|
66
|
-
|
|
66
|
+
success: false,
|
|
67
|
+
error: result.error || "Failed to resend verification email",
|
|
67
68
|
},
|
|
68
|
-
{ status:
|
|
69
|
+
{ status: 500 }
|
|
69
70
|
);
|
|
70
71
|
}
|
|
71
72
|
|
|
@@ -155,10 +155,10 @@ export const use_email_verification = <TClient,>({
|
|
|
155
155
|
return true;
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
|
|
159
|
-
const hasErrors =
|
|
160
|
-
return
|
|
161
|
-
}, [errors,
|
|
158
|
+
// Only disable if there are active errors
|
|
159
|
+
const hasErrors = !!errors[EMAIL_VERIFICATION_FIELD_IDS.EMAIL];
|
|
160
|
+
return hasErrors;
|
|
161
|
+
}, [errors, isSubmitting]);
|
|
162
162
|
|
|
163
163
|
const handleFieldChange = useCallback((fieldId: EmailVerificationFieldId, value: string) => {
|
|
164
164
|
setValues((previousValues) => {
|
|
@@ -231,7 +231,13 @@ export const use_email_verification = <TClient,>({
|
|
|
231
231
|
}),
|
|
232
232
|
});
|
|
233
233
|
|
|
234
|
-
|
|
234
|
+
let data;
|
|
235
|
+
try {
|
|
236
|
+
data = await response.json();
|
|
237
|
+
} catch (jsonError) {
|
|
238
|
+
// If JSON parsing fails, the response is likely HTML (e.g., error page)
|
|
239
|
+
throw new Error("Server returned an invalid response. Please try again later.");
|
|
240
|
+
}
|
|
235
241
|
|
|
236
242
|
if (!response.ok) {
|
|
237
243
|
throw new Error(data.error || "Failed to resend verification email");
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
"use client";
|
|
4
4
|
|
|
5
5
|
// section: imports
|
|
6
|
-
import { use_auth_status } from "
|
|
7
|
-
import { LogoutButton } from "
|
|
8
|
-
import { Button } from "
|
|
9
|
-
import { TwoColumnAuthLayout } from "
|
|
6
|
+
import { use_auth_status } from "../hooks/use_auth_status";
|
|
7
|
+
import { LogoutButton } from "./logout_button";
|
|
8
|
+
import { Button } from "../../../ui/button";
|
|
9
|
+
import { TwoColumnAuthLayout } from "./two_column_auth_layout";
|
|
10
10
|
import { useRouter } from "next/navigation";
|
|
11
11
|
import { Home } from "lucide-react";
|
|
12
12
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// file_description: reusable form action buttons component with submit (positive, left) and cancel (negative, right) buttons
|
|
2
2
|
// section: imports
|
|
3
3
|
import { CircleCheckBig, CircleX } from "lucide-react";
|
|
4
|
-
import { Button } from "
|
|
5
|
-
import type { ButtonPaletteDefaults } from "
|
|
4
|
+
import { Button } from "../../../ui/button";
|
|
5
|
+
import type { ButtonPaletteDefaults } from "../config/layout_customization";
|
|
6
6
|
|
|
7
7
|
// section: types
|
|
8
8
|
type FormActionButtonsProps = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// file_description: reusable wrapper component for form fields that standardizes label, input, and error message structure
|
|
2
2
|
// section: imports
|
|
3
|
-
import { Label } from "
|
|
4
|
-
import { FieldErrorMessage } from "
|
|
3
|
+
import { Label } from "../../../ui/label";
|
|
4
|
+
import { FieldErrorMessage } from "./field_error_message";
|
|
5
5
|
|
|
6
6
|
// section: types
|
|
7
7
|
type FormFieldWrapperProps = {
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
// section: imports
|
|
6
6
|
import { useState } from "react";
|
|
7
7
|
import { useRouter } from "next/navigation";
|
|
8
|
-
import { Button } from "
|
|
8
|
+
import { Button } from "../../../ui/button";
|
|
9
9
|
import { LogOut } from "lucide-react";
|
|
10
10
|
import { toast } from "sonner";
|
|
11
|
-
import { trigger_auth_status_refresh } from "
|
|
11
|
+
import { trigger_auth_status_refresh } from "../hooks/use_auth_status";
|
|
12
12
|
|
|
13
13
|
// section: types
|
|
14
14
|
export type LogoutButtonProps = {
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
// section: imports
|
|
6
6
|
import { Eye, EyeOff } from "lucide-react";
|
|
7
|
-
import { Button } from "
|
|
8
|
-
import { Input } from "
|
|
9
|
-
import { FieldErrorMessage } from "
|
|
7
|
+
import { Button } from "../../../ui/button";
|
|
8
|
+
import { Input } from "../../../ui/input";
|
|
9
|
+
import { FieldErrorMessage } from "./field_error_message";
|
|
10
10
|
|
|
11
11
|
// section: types
|
|
12
12
|
export type PasswordFieldProps = {
|
|
@@ -6,20 +6,20 @@
|
|
|
6
6
|
import { useState, useMemo } from "react";
|
|
7
7
|
import { useRouter } from "next/navigation";
|
|
8
8
|
import Link from "next/link";
|
|
9
|
-
import { Avatar, AvatarImage, AvatarFallback } from "
|
|
10
|
-
import { Button } from "
|
|
9
|
+
import { Avatar, AvatarImage, AvatarFallback } from "../../../ui/avatar";
|
|
10
|
+
import { Button } from "../../../ui/button";
|
|
11
11
|
import {
|
|
12
12
|
DropdownMenu,
|
|
13
13
|
DropdownMenuContent,
|
|
14
14
|
DropdownMenuItem,
|
|
15
15
|
DropdownMenuSeparator,
|
|
16
16
|
DropdownMenuTrigger,
|
|
17
|
-
} from "
|
|
17
|
+
} from "../../../ui/dropdown-menu";
|
|
18
18
|
import { Settings, LogOut } from "lucide-react";
|
|
19
19
|
import { toast } from "sonner";
|
|
20
|
-
import { use_auth_status, trigger_auth_status_refresh } from "
|
|
20
|
+
import { use_auth_status, trigger_auth_status_refresh } from "../hooks/use_auth_status";
|
|
21
21
|
// Type-only import from server file is safe (types are erased at runtime)
|
|
22
|
-
import type { ProfilePicMenuMenuItem } from "
|
|
22
|
+
import type { ProfilePicMenuMenuItem } from "../../../../lib/profile_pic_menu_config.server";
|
|
23
23
|
|
|
24
24
|
// section: types
|
|
25
25
|
export type ProfilePicMenuProps = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// file_description: server wrapper component that loads profile picture menu configuration and passes to client component
|
|
2
2
|
// section: imports
|
|
3
3
|
import { ProfilePicMenu } from "./profile_pic_menu";
|
|
4
|
-
import { get_profile_pic_menu_config } from "
|
|
4
|
+
import { get_profile_pic_menu_config } from "../../../../lib/profile_pic_menu_config.server";
|
|
5
5
|
|
|
6
6
|
// section: types
|
|
7
7
|
export type ProfilePicMenuWrapperProps = {
|
|
@@ -16,10 +16,10 @@ import {
|
|
|
16
16
|
SidebarProvider,
|
|
17
17
|
SidebarTrigger,
|
|
18
18
|
SidebarInset,
|
|
19
|
-
} from "
|
|
19
|
+
} from "../../../ui/sidebar";
|
|
20
20
|
import { LogIn, UserPlus, BookOpen, ExternalLink, Database, KeyRound, MailCheck, Key, Settings, User } from "lucide-react";
|
|
21
|
-
import { use_auth_status } from "
|
|
22
|
-
import { ProfilePicMenu } from "
|
|
21
|
+
import { use_auth_status } from "../hooks/use_auth_status";
|
|
22
|
+
import { ProfilePicMenu } from "./profile_pic_menu";
|
|
23
23
|
|
|
24
24
|
// section: types
|
|
25
25
|
type SidebarLayoutWrapperProps = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// file_description: reusable two-column authentication layout shell that combines visual panel and form content
|
|
2
2
|
// section: imports
|
|
3
|
-
import { VisualPanel } from "
|
|
3
|
+
import { VisualPanel } from "./visual_panel";
|
|
4
4
|
|
|
5
5
|
// section: types
|
|
6
6
|
type TwoColumnAuthLayoutProps = {
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
"use client";
|
|
4
4
|
|
|
5
5
|
// section: imports
|
|
6
|
-
import { use_auth_status } from "
|
|
7
|
-
import { Button } from "
|
|
6
|
+
import { use_auth_status } from "../hooks/use_auth_status";
|
|
7
|
+
import { Button } from "../../../ui/button";
|
|
8
8
|
import { useRouter } from "next/navigation";
|
|
9
9
|
import { LogIn } from "lucide-react";
|
|
10
10
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// file_description: provide shared validation utilities for email and password fields across layout components
|
|
2
2
|
// section: imports
|
|
3
|
-
import type { PasswordRequirementOptions } from "
|
|
3
|
+
import type { PasswordRequirementOptions } from "../config/layout_customization";
|
|
4
4
|
|
|
5
5
|
// section: constants
|
|
6
6
|
const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
@@ -38,36 +38,60 @@ export function get_hazo_connect_instance(): HazoConnectAdapter {
|
|
|
38
38
|
try {
|
|
39
39
|
// Get configuration from hazo_auth_config.ini (falls back to environment variables)
|
|
40
40
|
const config_options = get_hazo_connect_config_options();
|
|
41
|
+
const logger = create_app_logger();
|
|
42
|
+
logger.debug("hazo_connect_singleton_attempt", {
|
|
43
|
+
filename: "hazo_connect_instance.server.ts",
|
|
44
|
+
line_number: 38,
|
|
45
|
+
config_options,
|
|
46
|
+
note: "Attempting to get singleton with these options",
|
|
47
|
+
});
|
|
41
48
|
return getHazoConnectSingleton(config_options);
|
|
42
49
|
} catch (error) {
|
|
50
|
+
const logger = create_app_logger();
|
|
51
|
+
const error_message = error instanceof Error ? error.message : "Unknown error";
|
|
52
|
+
logger.error("hazo_connect_singleton_failed", {
|
|
53
|
+
filename: "hazo_connect_instance.server.ts",
|
|
54
|
+
line_number: 45,
|
|
55
|
+
error: error_message,
|
|
56
|
+
error_stack: error instanceof Error ? error.stack : undefined,
|
|
57
|
+
note: "Falling back to manual singleton implementation",
|
|
58
|
+
});
|
|
59
|
+
|
|
43
60
|
// Fallback: Manual singleton implementation if new API fails
|
|
44
61
|
// This should not happen with the updated package, but kept for safety
|
|
45
62
|
if (!hazoConnectInstance) {
|
|
46
|
-
//
|
|
47
|
-
|
|
63
|
+
// Get config options to determine database type
|
|
64
|
+
const config_options = get_hazo_connect_config_options();
|
|
65
|
+
const db_type = config_options.type;
|
|
66
|
+
|
|
67
|
+
// Only initialize SQLite admin service for SQLite databases
|
|
68
|
+
if (db_type === "sqlite" && !isInitialized) {
|
|
48
69
|
initializeAdminService({ enable_admin_ui: true });
|
|
49
70
|
isInitialized = true;
|
|
50
71
|
}
|
|
51
72
|
|
|
52
73
|
// Create the adapter instance (reads from hazo_auth_config.ini)
|
|
74
|
+
// Note: Despite the name, this function supports both SQLite and PostgREST
|
|
53
75
|
hazoConnectInstance = create_sqlite_hazo_connect_server();
|
|
54
76
|
|
|
55
77
|
// Note: Database migrations should be applied manually via SQLite Admin UI
|
|
56
78
|
// or through a separate migration script. The token_service has fallback
|
|
57
79
|
// logic to work without the token_type column if migration hasn't been applied.
|
|
58
80
|
|
|
59
|
-
// Finalize initialization by getting the admin service
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
81
|
+
// Finalize initialization by getting the admin service (only for SQLite)
|
|
82
|
+
if (db_type === "sqlite") {
|
|
83
|
+
try {
|
|
84
|
+
getSqliteAdminService();
|
|
85
|
+
} catch (adminError) {
|
|
86
|
+
const logger = create_app_logger();
|
|
87
|
+
const error_message = adminError instanceof Error ? adminError.message : "Unknown error";
|
|
88
|
+
logger.warn("hazo_connect_instance_admin_service_init_failed", {
|
|
89
|
+
filename: "hazo_connect_instance.server.ts",
|
|
90
|
+
line_number: 0,
|
|
91
|
+
error: error_message,
|
|
92
|
+
note: "Could not get SqliteAdminService during initialization, continuing...",
|
|
93
|
+
});
|
|
94
|
+
}
|
|
71
95
|
}
|
|
72
96
|
}
|
|
73
97
|
|
|
@@ -100,8 +100,10 @@ function get_hazo_connect_config(): {
|
|
|
100
100
|
hazo_connect_section?.postgrest_url ||
|
|
101
101
|
process.env.HAZO_CONNECT_POSTGREST_URL ||
|
|
102
102
|
process.env.POSTGREST_URL;
|
|
103
|
+
// API key must only come from environment variables for security
|
|
104
|
+
// Check multiple possible env var names for compatibility
|
|
103
105
|
const postgrest_api_key =
|
|
104
|
-
|
|
106
|
+
process.env.postgrest_api_key || // hazo_connect package expects this
|
|
105
107
|
process.env.HAZO_CONNECT_POSTGREST_API_KEY ||
|
|
106
108
|
process.env.POSTGREST_API_KEY;
|
|
107
109
|
|
|
@@ -141,10 +143,13 @@ export function create_sqlite_hazo_connect_server() {
|
|
|
141
143
|
}
|
|
142
144
|
|
|
143
145
|
if (config.type === "postgrest") {
|
|
146
|
+
// Ensure we have a value (empty string if not set, for PostgREST instances without auth)
|
|
147
|
+
const apiKey = config.postgrestApiKey || "";
|
|
148
|
+
|
|
144
149
|
return createHazoConnect({
|
|
145
150
|
type: "postgrest",
|
|
146
151
|
baseUrl: config.postgrestUrl!,
|
|
147
|
-
apiKey:
|
|
152
|
+
apiKey: apiKey, // Pass empty string if not set
|
|
148
153
|
});
|
|
149
154
|
}
|
|
150
155
|
|
|
@@ -161,6 +166,8 @@ export function get_hazo_connect_config_options(): {
|
|
|
161
166
|
sqlitePath?: string;
|
|
162
167
|
enableAdminUi?: boolean;
|
|
163
168
|
readOnly?: boolean;
|
|
169
|
+
baseUrl?: string;
|
|
170
|
+
apiKey?: string;
|
|
164
171
|
} {
|
|
165
172
|
const config = get_hazo_connect_config();
|
|
166
173
|
|
|
@@ -173,8 +180,15 @@ export function get_hazo_connect_config_options(): {
|
|
|
173
180
|
};
|
|
174
181
|
}
|
|
175
182
|
|
|
176
|
-
|
|
177
|
-
|
|
183
|
+
if (config.type === "postgrest") {
|
|
184
|
+
return {
|
|
185
|
+
type: "postgrest",
|
|
186
|
+
baseUrl: config.postgrestUrl, // Corrected from postgrestUrl
|
|
187
|
+
apiKey: config.postgrestApiKey || "", // Corrected from postgrestApiKey and ensured string value
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Fallback: return empty object to let it use environment variables
|
|
178
192
|
return {};
|
|
179
193
|
}
|
|
180
194
|
|
|
@@ -245,6 +245,12 @@ export async function resend_verification_email(
|
|
|
245
245
|
error: email_result.error,
|
|
246
246
|
note: "Verification token created but email failed to send",
|
|
247
247
|
});
|
|
248
|
+
|
|
249
|
+
// Return error if email sending failed (this is a technical error, not a security issue)
|
|
250
|
+
return {
|
|
251
|
+
success: false,
|
|
252
|
+
error: email_result.error || "Failed to send verification email",
|
|
253
|
+
};
|
|
248
254
|
}
|
|
249
255
|
}
|
|
250
256
|
|
package/src/middleware.ts
CHANGED
|
@@ -46,6 +46,7 @@ export async function middleware(request: NextRequest) {
|
|
|
46
46
|
"/api/hazo_auth/reset_password",
|
|
47
47
|
"/api/hazo_auth/verify_email",
|
|
48
48
|
"/api/hazo_auth/validate_reset_token",
|
|
49
|
+
"/api/hazo_auth/resend_verification", // Allow resend verification email without auth
|
|
49
50
|
"/api/hazo_auth/me", // Allow /api/hazo_auth/me to be public (returns authenticated: false if not logged in)
|
|
50
51
|
"/hazo_connect/api/sqlite", // SQLite Admin API routes (admin tool, should be accessible)
|
|
51
52
|
"/hazo_connect/sqlite_admin", // SQLite Admin UI page
|