hazo_auth 0.3.0 → 1.0.0
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 +39 -0
- package/instrumentation.ts +1 -1
- package/next.config.mjs +1 -1
- package/package.json +3 -1
- package/src/app/api/{auth → hazo_auth/auth}/upload_profile_picture/route.ts +2 -2
- package/src/app/api/{auth → hazo_auth}/change_password/route.ts +23 -0
- package/src/app/api/hazo_auth/get_auth/route.ts +89 -0
- package/src/app/api/hazo_auth/invalidate_cache/route.ts +139 -0
- package/src/app/api/{auth → hazo_auth}/logout/route.ts +27 -0
- package/src/app/api/hazo_auth/upload_profile_picture/route.ts +268 -0
- package/src/app/api/hazo_auth/user_management/permissions/route.ts +367 -0
- package/src/app/api/hazo_auth/user_management/roles/route.ts +442 -0
- package/src/app/api/hazo_auth/user_management/users/roles/route.ts +367 -0
- package/src/app/api/hazo_auth/user_management/users/route.ts +239 -0
- package/src/app/api/{auth → hazo_auth}/validate_reset_token/route.ts +3 -0
- package/src/app/api/{auth → hazo_auth}/verify_email/route.ts +3 -0
- package/src/app/globals.css +1 -1
- package/src/app/hazo_auth/user_management/page.tsx +14 -0
- package/src/app/hazo_auth/user_management/user_management_page_client.tsx +16 -0
- package/src/app/hazo_connect/api/sqlite/data/route.ts +7 -1
- package/src/app/hazo_connect/api/sqlite/schema/route.ts +14 -4
- package/src/app/hazo_connect/api/sqlite/tables/route.ts +14 -4
- package/src/app/hazo_connect/sqlite_admin/sqlite-admin-client.tsx +40 -3
- package/src/app/layout.tsx +1 -1
- package/src/app/page.tsx +4 -4
- package/src/components/layouts/email_verification/hooks/use_email_verification.ts +4 -4
- package/src/components/layouts/email_verification/index.tsx +1 -1
- package/src/components/layouts/forgot_password/hooks/use_forgot_password_form.ts +1 -1
- package/src/components/layouts/login/hooks/use_login_form.ts +2 -2
- package/src/components/layouts/my_settings/components/profile_picture_dialog.tsx +1 -1
- package/src/components/layouts/my_settings/components/profile_picture_library_tab.tsx +2 -2
- package/src/components/layouts/my_settings/hooks/use_my_settings.ts +5 -5
- package/src/components/layouts/my_settings/index.tsx +1 -1
- package/src/components/layouts/register/hooks/use_register_form.ts +1 -1
- package/src/components/layouts/reset_password/hooks/use_reset_password_form.ts +3 -3
- package/src/components/layouts/reset_password/index.tsx +2 -2
- package/src/components/layouts/shared/components/logout_button.tsx +1 -1
- package/src/components/layouts/shared/components/profile_pic_menu.tsx +4 -4
- package/src/components/layouts/shared/components/sidebar_layout_wrapper.tsx +19 -7
- package/src/components/layouts/shared/components/unauthorized_guard.tsx +1 -1
- package/src/components/layouts/shared/hooks/use_auth_status.ts +1 -1
- package/src/components/layouts/shared/hooks/use_hazo_auth.ts +158 -0
- package/src/components/layouts/user_management/components/roles_matrix.tsx +607 -0
- package/src/components/layouts/user_management/index.tsx +1295 -0
- package/src/components/ui/alert-dialog.tsx +141 -0
- package/src/components/ui/checkbox.tsx +30 -0
- package/src/components/ui/table.tsx +120 -0
- package/src/lib/auth/auth_cache.ts +220 -0
- package/src/lib/auth/auth_rate_limiter.ts +121 -0
- package/src/lib/auth/auth_types.ts +65 -0
- package/src/lib/auth/hazo_get_auth.server.ts +333 -0
- package/src/lib/auth_utility_config.server.ts +136 -0
- package/src/lib/hazo_connect_setup.server.ts +2 -3
- package/src/lib/my_settings_config.server.ts +1 -1
- package/src/lib/profile_pic_menu_config.server.ts +4 -4
- package/src/lib/reset_password_config.server.ts +5 -5
- package/src/lib/services/email_service.ts +2 -2
- package/src/lib/services/profile_picture_remove_service.ts +1 -1
- package/src/lib/services/token_service.ts +2 -2
- package/src/lib/user_management_config.server.ts +40 -0
- package/src/lib/utils.ts +1 -1
- package/src/middleware.ts +15 -13
- package/src/server/types/express.d.ts +1 -0
- package/src/stories/project_overview.stories.tsx +1 -1
- package/tailwind.config.ts +1 -1
- /package/src/app/api/{auth → hazo_auth}/forgot_password/route.ts +0 -0
- /package/src/app/api/{auth → hazo_auth}/library_photos/route.ts +0 -0
- /package/src/app/api/{auth → hazo_auth}/login/route.ts +0 -0
- /package/src/app/api/{auth → hazo_auth}/me/route.ts +0 -0
- /package/src/app/api/{auth → hazo_auth}/profile_picture/[filename]/route.ts +0 -0
- /package/src/app/api/{auth → hazo_auth}/register/route.ts +0 -0
- /package/src/app/api/{auth → hazo_auth}/remove_profile_picture/route.ts +0 -0
- /package/src/app/api/{auth → hazo_auth}/resend_verification/route.ts +0 -0
- /package/src/app/api/{auth → hazo_auth}/reset_password/route.ts +0 -0
- /package/src/app/api/{auth → hazo_auth}/update_user/route.ts +0 -0
- /package/src/app/{forgot_password → hazo_auth/forgot_password}/forgot_password_page_client.tsx +0 -0
- /package/src/app/{forgot_password → hazo_auth/forgot_password}/page.tsx +0 -0
- /package/src/app/{login → hazo_auth/login}/login_page_client.tsx +0 -0
- /package/src/app/{login → hazo_auth/login}/page.tsx +0 -0
- /package/src/app/{my_settings → hazo_auth/my_settings}/my_settings_page_client.tsx +0 -0
- /package/src/app/{my_settings → hazo_auth/my_settings}/page.tsx +0 -0
- /package/src/app/{register → hazo_auth/register}/page.tsx +0 -0
- /package/src/app/{register → hazo_auth/register}/register_page_client.tsx +0 -0
- /package/src/app/{reset_password → hazo_auth/reset_password}/page.tsx +0 -0
- /package/src/app/{reset_password → hazo_auth/reset_password}/reset_password_page_client.tsx +0 -0
- /package/src/app/{verify_email → hazo_auth/verify_email}/page.tsx +0 -0
- /package/src/app/{verify_email → hazo_auth/verify_email}/verify_email_page_client.tsx +0 -0
|
@@ -19,11 +19,21 @@ export async function GET(request: NextRequest) {
|
|
|
19
19
|
// Get singleton hazo_connect instance (initializes admin service if needed)
|
|
20
20
|
get_hazo_connect_instance();
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
let service;
|
|
23
|
+
try {
|
|
24
|
+
service = getSqliteAdminService();
|
|
25
|
+
} catch (serviceError) {
|
|
26
|
+
const errorMessage = serviceError instanceof Error ? serviceError.message : "Unknown error";
|
|
27
|
+
return NextResponse.json(
|
|
28
|
+
{ error: `SQLite Admin Service not available: ${errorMessage}. Make sure enable_admin_ui is set to true in hazo_auth_config.ini.` },
|
|
29
|
+
{ status: 500 }
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const schema = await service.getTableSchema(table);
|
|
34
|
+
return NextResponse.json({ data: schema });
|
|
25
35
|
} catch (error) {
|
|
26
|
-
return toErrorResponse(error, `Failed to fetch schema for table '${table}'`)
|
|
36
|
+
return toErrorResponse(error, `Failed to fetch schema for table '${table}'`);
|
|
27
37
|
}
|
|
28
38
|
}
|
|
29
39
|
|
|
@@ -10,11 +10,21 @@ export async function GET() {
|
|
|
10
10
|
// Get singleton hazo_connect instance (initializes admin service if needed)
|
|
11
11
|
get_hazo_connect_instance();
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
let service;
|
|
14
|
+
try {
|
|
15
|
+
service = getSqliteAdminService();
|
|
16
|
+
} catch (serviceError) {
|
|
17
|
+
const errorMessage = serviceError instanceof Error ? serviceError.message : "Unknown error";
|
|
18
|
+
return NextResponse.json(
|
|
19
|
+
{ error: `SQLite Admin Service not available: ${errorMessage}. Make sure enable_admin_ui is set to true in hazo_auth_config.ini.` },
|
|
20
|
+
{ status: 500 }
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const tables = await service.listTables();
|
|
25
|
+
return NextResponse.json({ data: tables });
|
|
16
26
|
} catch (error) {
|
|
17
|
-
return toErrorResponse(error, "Failed to list SQLite tables")
|
|
27
|
+
return toErrorResponse(error, "Failed to list SQLite tables");
|
|
18
28
|
}
|
|
19
29
|
}
|
|
20
30
|
|
|
@@ -91,7 +91,19 @@ export default function SqliteAdminClient({
|
|
|
91
91
|
try {
|
|
92
92
|
const schemaResponse = await fetch(`/hazo_connect/api/sqlite/schema?table=${encodeURIComponent(tableName)}`)
|
|
93
93
|
if (!schemaResponse.ok) {
|
|
94
|
-
|
|
94
|
+
const contentType = schemaResponse.headers.get("content-type");
|
|
95
|
+
if (contentType && contentType.includes("application/json")) {
|
|
96
|
+
const errorData = await schemaResponse.json();
|
|
97
|
+
throw new Error(errorData.error || `Failed to fetch schema: ${schemaResponse.statusText}`);
|
|
98
|
+
} else {
|
|
99
|
+
const errorText = await schemaResponse.text();
|
|
100
|
+
throw new Error(`Failed to fetch schema: ${errorText.substring(0, 200)}`);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const contentType = schemaResponse.headers.get("content-type");
|
|
104
|
+
if (!contentType || !contentType.includes("application/json")) {
|
|
105
|
+
const text = await schemaResponse.text();
|
|
106
|
+
throw new Error(`Expected JSON but received ${contentType || "unknown content type"}. Response: ${text.substring(0, 200)}`);
|
|
95
107
|
}
|
|
96
108
|
const schemaJson = await schemaResponse.json()
|
|
97
109
|
setSchema(schemaJson.data as TableSchema)
|
|
@@ -170,7 +182,20 @@ export default function SqliteAdminClient({
|
|
|
170
182
|
|
|
171
183
|
const response = await fetch(`/hazo_connect/api/sqlite/data?${params.toString()}`)
|
|
172
184
|
if (!response.ok) {
|
|
173
|
-
|
|
185
|
+
const contentType = response.headers.get("content-type");
|
|
186
|
+
if (contentType && contentType.includes("application/json")) {
|
|
187
|
+
const errorData = await response.json();
|
|
188
|
+
throw new Error(errorData.error || `Failed to load data: ${response.statusText}`);
|
|
189
|
+
} else {
|
|
190
|
+
const errorText = await response.text();
|
|
191
|
+
throw new Error(`Failed to load data: ${errorText.substring(0, 200)}`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const contentType = response.headers.get("content-type");
|
|
196
|
+
if (!contentType || !contentType.includes("application/json")) {
|
|
197
|
+
const text = await response.text();
|
|
198
|
+
throw new Error(`Expected JSON but received ${contentType || "unknown content type"}. Response: ${text.substring(0, 200)}`);
|
|
174
199
|
}
|
|
175
200
|
|
|
176
201
|
const json = (await response.json()) as DataResponse
|
|
@@ -197,7 +222,19 @@ export default function SqliteAdminClient({
|
|
|
197
222
|
try {
|
|
198
223
|
const response = await fetch("/hazo_connect/api/sqlite/tables")
|
|
199
224
|
if (!response.ok) {
|
|
200
|
-
|
|
225
|
+
const contentType = response.headers.get("content-type");
|
|
226
|
+
if (contentType && contentType.includes("application/json")) {
|
|
227
|
+
const errorData = await response.json();
|
|
228
|
+
throw new Error(errorData.error || `Failed to refresh tables: ${response.statusText}`);
|
|
229
|
+
} else {
|
|
230
|
+
const errorText = await response.text();
|
|
231
|
+
throw new Error(`Failed to refresh tables: ${errorText.substring(0, 200)}`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
const contentType = response.headers.get("content-type");
|
|
235
|
+
if (!contentType || !contentType.includes("application/json")) {
|
|
236
|
+
const text = await response.text();
|
|
237
|
+
throw new Error(`Expected JSON but received ${contentType || "unknown content type"}. Response: ${text.substring(0, 200)}`);
|
|
201
238
|
}
|
|
202
239
|
const json = await response.json()
|
|
203
240
|
setTables(json.data ?? [])
|
package/src/app/layout.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// file_description: define the root layout wrapper for the
|
|
1
|
+
// file_description: define the root layout wrapper for the hazo_auth project
|
|
2
2
|
import type { Metadata } from "next";
|
|
3
3
|
import local_font from "next/font/local";
|
|
4
4
|
import { Toaster } from "@/components/ui/sonner";
|
package/src/app/page.tsx
CHANGED
|
@@ -41,7 +41,7 @@ export default function home_page() {
|
|
|
41
41
|
<SidebarMenuItem className="cls_home_sidebar_test_login_item">
|
|
42
42
|
<SidebarMenuButton asChild>
|
|
43
43
|
<Link
|
|
44
|
-
href="/login"
|
|
44
|
+
href="/hazo_auth/login"
|
|
45
45
|
className="cls_home_sidebar_test_login_link flex items-center gap-2"
|
|
46
46
|
aria-label="Test login layout component"
|
|
47
47
|
>
|
|
@@ -53,7 +53,7 @@ export default function home_page() {
|
|
|
53
53
|
<SidebarMenuItem className="cls_home_sidebar_test_register_item">
|
|
54
54
|
<SidebarMenuButton asChild>
|
|
55
55
|
<Link
|
|
56
|
-
href="/register"
|
|
56
|
+
href="/hazo_auth/register"
|
|
57
57
|
className="cls_home_sidebar_test_register_link flex items-center gap-2"
|
|
58
58
|
aria-label="Test register layout component"
|
|
59
59
|
>
|
|
@@ -65,7 +65,7 @@ export default function home_page() {
|
|
|
65
65
|
<SidebarMenuItem className="cls_home_sidebar_test_forgot_password_item">
|
|
66
66
|
<SidebarMenuButton asChild>
|
|
67
67
|
<Link
|
|
68
|
-
href="/forgot_password"
|
|
68
|
+
href="/hazo_auth/forgot_password"
|
|
69
69
|
className="cls_home_sidebar_test_forgot_password_link flex items-center gap-2"
|
|
70
70
|
aria-label="Test forgot password layout component"
|
|
71
71
|
>
|
|
@@ -77,7 +77,7 @@ export default function home_page() {
|
|
|
77
77
|
<SidebarMenuItem className="cls_home_sidebar_test_email_verification_item">
|
|
78
78
|
<SidebarMenuButton asChild>
|
|
79
79
|
<Link
|
|
80
|
-
href="/verify_email"
|
|
80
|
+
href="/hazo_auth/verify_email"
|
|
81
81
|
className="cls_home_sidebar_test_email_verification_link flex items-center gap-2"
|
|
82
82
|
aria-label="Test email verification layout component"
|
|
83
83
|
>
|
|
@@ -47,7 +47,7 @@ const buildInitialValues = (initialEmail?: string): EmailVerificationFormValues
|
|
|
47
47
|
export const use_email_verification = <TClient,>({
|
|
48
48
|
dataClient,
|
|
49
49
|
redirectDelay = 5,
|
|
50
|
-
loginPath = "/login",
|
|
50
|
+
loginPath = "/hazo_auth/login",
|
|
51
51
|
}: UseEmailVerificationParams<TClient>): UseEmailVerificationResult => {
|
|
52
52
|
const router = useRouter();
|
|
53
53
|
const searchParams = useSearchParams();
|
|
@@ -91,7 +91,7 @@ export const use_email_verification = <TClient,>({
|
|
|
91
91
|
setErrorMessage(undefined);
|
|
92
92
|
|
|
93
93
|
try {
|
|
94
|
-
const response = await fetch(`/api/
|
|
94
|
+
const response = await fetch(`/api/hazo_auth/verify_email?token=${encodeURIComponent(token)}`, {
|
|
95
95
|
method: "GET",
|
|
96
96
|
});
|
|
97
97
|
|
|
@@ -131,7 +131,7 @@ export const use_email_verification = <TClient,>({
|
|
|
131
131
|
|
|
132
132
|
// Try to extract email from error response if available
|
|
133
133
|
try {
|
|
134
|
-
const response = await fetch(`/api/
|
|
134
|
+
const response = await fetch(`/api/hazo_auth/verify_email?token=${encodeURIComponent(token)}`, {
|
|
135
135
|
method: "GET",
|
|
136
136
|
});
|
|
137
137
|
const data = await response.json();
|
|
@@ -221,7 +221,7 @@ export const use_email_verification = <TClient,>({
|
|
|
221
221
|
setErrors({});
|
|
222
222
|
|
|
223
223
|
try {
|
|
224
|
-
const response = await fetch("/api/
|
|
224
|
+
const response = await fetch("/api/hazo_auth/resend_verification", {
|
|
225
225
|
method: "POST",
|
|
226
226
|
headers: {
|
|
227
227
|
"Content-Type": "application/json",
|
|
@@ -113,7 +113,7 @@ export const use_forgot_password_form = <TClient,>({
|
|
|
113
113
|
setErrors({});
|
|
114
114
|
|
|
115
115
|
try {
|
|
116
|
-
const response = await fetch("/api/
|
|
116
|
+
const response = await fetch("/api/hazo_auth/forgot_password", {
|
|
117
117
|
method: "POST",
|
|
118
118
|
headers: {
|
|
119
119
|
"Content-Type": "application/json",
|
|
@@ -181,7 +181,7 @@ export const use_login_form = <TClient,>({
|
|
|
181
181
|
setClientIp(currentIp);
|
|
182
182
|
|
|
183
183
|
// Attempt login via API route
|
|
184
|
-
const response = await fetch("/api/
|
|
184
|
+
const response = await fetch("/api/hazo_auth/login", {
|
|
185
185
|
method: "POST",
|
|
186
186
|
headers: {
|
|
187
187
|
"Content-Type": "application/json",
|
|
@@ -202,7 +202,7 @@ export const use_login_form = <TClient,>({
|
|
|
202
202
|
const messageParam = encodeURIComponent(
|
|
203
203
|
"Your email address has not been verified. Please verify your email to continue."
|
|
204
204
|
);
|
|
205
|
-
router.push(`/verify_email?email=${emailParam}&message=${messageParam}`);
|
|
205
|
+
router.push(`/hazo_auth/verify_email?email=${emailParam}&message=${messageParam}`);
|
|
206
206
|
return;
|
|
207
207
|
}
|
|
208
208
|
|
|
@@ -188,7 +188,7 @@ export function ProfilePictureDialog({
|
|
|
188
188
|
const formData = new FormData();
|
|
189
189
|
formData.append("file", file);
|
|
190
190
|
|
|
191
|
-
const response = await fetch("/api/
|
|
191
|
+
const response = await fetch("/api/hazo_auth/upload_profile_picture", {
|
|
192
192
|
method: "POST",
|
|
193
193
|
credentials: "include",
|
|
194
194
|
body: formData,
|
|
@@ -57,7 +57,7 @@ export function ProfilePictureLibraryTab({
|
|
|
57
57
|
const loadCategories = async () => {
|
|
58
58
|
setLoadingCategories(true);
|
|
59
59
|
try {
|
|
60
|
-
const response = await fetch("/api/
|
|
60
|
+
const response = await fetch("/api/hazo_auth/library_photos");
|
|
61
61
|
const data = await response.json();
|
|
62
62
|
if (data.success && data.categories) {
|
|
63
63
|
setCategories(data.categories);
|
|
@@ -99,7 +99,7 @@ export function ProfilePictureLibraryTab({
|
|
|
99
99
|
const loadPhotos = async () => {
|
|
100
100
|
setLoadingPhotos(true);
|
|
101
101
|
try {
|
|
102
|
-
const response = await fetch(`/api/
|
|
102
|
+
const response = await fetch(`/api/hazo_auth/library_photos?category=${encodeURIComponent(selectedCategory)}`);
|
|
103
103
|
const data = await response.json();
|
|
104
104
|
if (data.success && data.photos) {
|
|
105
105
|
setPhotos(data.photos);
|
|
@@ -134,7 +134,7 @@ export function use_my_settings({
|
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
try {
|
|
137
|
-
const response = await fetch("/api/
|
|
137
|
+
const response = await fetch("/api/hazo_auth/update_user", {
|
|
138
138
|
method: "PATCH",
|
|
139
139
|
headers: {
|
|
140
140
|
"Content-Type": "application/json",
|
|
@@ -171,7 +171,7 @@ export function use_my_settings({
|
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
try {
|
|
174
|
-
const response = await fetch("/api/
|
|
174
|
+
const response = await fetch("/api/hazo_auth/update_user", {
|
|
175
175
|
method: "PATCH",
|
|
176
176
|
headers: {
|
|
177
177
|
"Content-Type": "application/json",
|
|
@@ -304,7 +304,7 @@ export function use_my_settings({
|
|
|
304
304
|
}
|
|
305
305
|
|
|
306
306
|
try {
|
|
307
|
-
const response = await fetch("/api/
|
|
307
|
+
const response = await fetch("/api/hazo_auth/change_password", {
|
|
308
308
|
method: "POST",
|
|
309
309
|
headers: {
|
|
310
310
|
"Content-Type": "application/json",
|
|
@@ -371,7 +371,7 @@ export function use_my_settings({
|
|
|
371
371
|
*/
|
|
372
372
|
const handleProfilePictureSave = useCallback(async (profilePictureUrl: string, profileSource: "upload" | "library" | "gravatar") => {
|
|
373
373
|
try {
|
|
374
|
-
const response = await fetch("/api/
|
|
374
|
+
const response = await fetch("/api/hazo_auth/update_user", {
|
|
375
375
|
method: "PATCH",
|
|
376
376
|
headers: {
|
|
377
377
|
"Content-Type": "application/json",
|
|
@@ -408,7 +408,7 @@ export function use_my_settings({
|
|
|
408
408
|
*/
|
|
409
409
|
const handleProfilePictureRemove = useCallback(async () => {
|
|
410
410
|
try {
|
|
411
|
-
const response = await fetch("/api/
|
|
411
|
+
const response = await fetch("/api/hazo_auth/remove_profile_picture", {
|
|
412
412
|
method: "DELETE",
|
|
413
413
|
headers: {
|
|
414
414
|
"Content-Type": "application/json",
|
|
@@ -94,7 +94,7 @@ export default function my_settings_layout({
|
|
|
94
94
|
userFields,
|
|
95
95
|
unauthorizedMessage = "You must be logged in to access this page.",
|
|
96
96
|
loginButtonLabel = "Go to login",
|
|
97
|
-
loginPath = "/login",
|
|
97
|
+
loginPath = "/hazo_auth/login",
|
|
98
98
|
heading = "Account Settings",
|
|
99
99
|
subHeading = "Manage your profile, password, and email preferences.",
|
|
100
100
|
profilePhotoLabel = "Profile Photo",
|
|
@@ -194,7 +194,7 @@ export const use_register_form = <TClient,>({
|
|
|
194
194
|
setErrors({});
|
|
195
195
|
|
|
196
196
|
try {
|
|
197
|
-
const response = await fetch("/api/
|
|
197
|
+
const response = await fetch("/api/hazo_auth/register", {
|
|
198
198
|
method: "POST",
|
|
199
199
|
headers: {
|
|
200
200
|
"Content-Type": "application/json",
|
|
@@ -54,7 +54,7 @@ const buildInitialValues = (): ResetPasswordFormValues => ({
|
|
|
54
54
|
export const use_reset_password_form = <TClient,>({
|
|
55
55
|
passwordRequirements,
|
|
56
56
|
dataClient,
|
|
57
|
-
loginPath = "/login",
|
|
57
|
+
loginPath = "/hazo_auth/login",
|
|
58
58
|
}: UseResetPasswordFormParams<TClient>): UseResetPasswordFormResult => {
|
|
59
59
|
const router = useRouter();
|
|
60
60
|
const searchParams = useSearchParams();
|
|
@@ -85,7 +85,7 @@ export const use_reset_password_form = <TClient,>({
|
|
|
85
85
|
setTokenError(null);
|
|
86
86
|
|
|
87
87
|
try {
|
|
88
|
-
const response = await fetch(`/api/
|
|
88
|
+
const response = await fetch(`/api/hazo_auth/validate_reset_token?token=${encodeURIComponent(tokenParam)}`, {
|
|
89
89
|
method: "GET",
|
|
90
90
|
});
|
|
91
91
|
|
|
@@ -217,7 +217,7 @@ export const use_reset_password_form = <TClient,>({
|
|
|
217
217
|
setIsSubmitting(true);
|
|
218
218
|
|
|
219
219
|
try {
|
|
220
|
-
const response = await fetch("/api/
|
|
220
|
+
const response = await fetch("/api/hazo_auth/reset_password", {
|
|
221
221
|
method: "POST",
|
|
222
222
|
headers: {
|
|
223
223
|
"Content-Type": "application/json",
|
|
@@ -75,8 +75,8 @@ export default function reset_password_layout<TClient>({
|
|
|
75
75
|
returnHomePath = "/",
|
|
76
76
|
errorMessage = "Reset password link invalid or has expired. Please go to Reset Password page to get a new link.",
|
|
77
77
|
successMessage = "Password reset successfully. Redirecting to login...",
|
|
78
|
-
loginPath = "/login",
|
|
79
|
-
forgotPasswordPath = "/forgot_password",
|
|
78
|
+
loginPath = "/hazo_auth/login",
|
|
79
|
+
forgotPasswordPath = "/hazo_auth/forgot_password",
|
|
80
80
|
}: ResetPasswordLayoutProps<TClient>) {
|
|
81
81
|
const fieldDefinitions = createResetPasswordFieldDefinitions(field_overrides);
|
|
82
82
|
const resolvedLabels = resolveResetPasswordLabels(labels);
|
|
@@ -47,10 +47,10 @@ export function ProfilePicMenu({
|
|
|
47
47
|
show_single_button = false,
|
|
48
48
|
sign_up_label = "Sign Up",
|
|
49
49
|
sign_in_label = "Sign In",
|
|
50
|
-
register_path = "/register",
|
|
51
|
-
login_path = "/login",
|
|
52
|
-
settings_path = "/my_settings",
|
|
53
|
-
logout_path = "/api/
|
|
50
|
+
register_path = "/hazo_auth/register",
|
|
51
|
+
login_path = "/hazo_auth/login",
|
|
52
|
+
settings_path = "/hazo_auth/my_settings",
|
|
53
|
+
logout_path = "/api/hazo_auth/logout",
|
|
54
54
|
custom_menu_items = [],
|
|
55
55
|
className,
|
|
56
56
|
avatar_size = "default",
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
SidebarTrigger,
|
|
18
18
|
SidebarInset,
|
|
19
19
|
} from "@/components/ui/sidebar";
|
|
20
|
-
import { LogIn, UserPlus, BookOpen, ExternalLink, Database, KeyRound, MailCheck, Key, Settings } from "lucide-react";
|
|
20
|
+
import { LogIn, UserPlus, BookOpen, ExternalLink, Database, KeyRound, MailCheck, Key, Settings, User } from "lucide-react";
|
|
21
21
|
import { use_auth_status } from "@/components/layouts/shared/hooks/use_auth_status";
|
|
22
22
|
import { ProfilePicMenu } from "@/components/layouts/shared/components/profile_pic_menu";
|
|
23
23
|
|
|
@@ -50,7 +50,7 @@ export function SidebarLayoutWrapper({ children }: SidebarLayoutWrapperProps) {
|
|
|
50
50
|
<SidebarMenuItem className="cls_sidebar_layout_test_login_item">
|
|
51
51
|
<SidebarMenuButton asChild>
|
|
52
52
|
<Link
|
|
53
|
-
href="/login"
|
|
53
|
+
href="/hazo_auth/login"
|
|
54
54
|
className="cls_sidebar_layout_test_login_link flex items-center gap-2"
|
|
55
55
|
aria-label="Test login layout component"
|
|
56
56
|
>
|
|
@@ -62,7 +62,7 @@ export function SidebarLayoutWrapper({ children }: SidebarLayoutWrapperProps) {
|
|
|
62
62
|
<SidebarMenuItem className="cls_sidebar_layout_test_register_item">
|
|
63
63
|
<SidebarMenuButton asChild>
|
|
64
64
|
<Link
|
|
65
|
-
href="/register"
|
|
65
|
+
href="/hazo_auth/register"
|
|
66
66
|
className="cls_sidebar_layout_test_register_link flex items-center gap-2"
|
|
67
67
|
aria-label="Test register layout component"
|
|
68
68
|
>
|
|
@@ -74,7 +74,7 @@ export function SidebarLayoutWrapper({ children }: SidebarLayoutWrapperProps) {
|
|
|
74
74
|
<SidebarMenuItem className="cls_sidebar_layout_test_forgot_password_item">
|
|
75
75
|
<SidebarMenuButton asChild>
|
|
76
76
|
<Link
|
|
77
|
-
href="/forgot_password"
|
|
77
|
+
href="/hazo_auth/forgot_password"
|
|
78
78
|
className="cls_sidebar_layout_test_forgot_password_link flex items-center gap-2"
|
|
79
79
|
aria-label="Test forgot password layout component"
|
|
80
80
|
>
|
|
@@ -86,7 +86,7 @@ export function SidebarLayoutWrapper({ children }: SidebarLayoutWrapperProps) {
|
|
|
86
86
|
<SidebarMenuItem className="cls_sidebar_layout_test_reset_password_item">
|
|
87
87
|
<SidebarMenuButton asChild>
|
|
88
88
|
<Link
|
|
89
|
-
href="/reset_password"
|
|
89
|
+
href="/hazo_auth/reset_password"
|
|
90
90
|
className="cls_sidebar_layout_test_reset_password_link flex items-center gap-2"
|
|
91
91
|
aria-label="Test reset password layout component"
|
|
92
92
|
>
|
|
@@ -98,7 +98,7 @@ export function SidebarLayoutWrapper({ children }: SidebarLayoutWrapperProps) {
|
|
|
98
98
|
<SidebarMenuItem className="cls_sidebar_layout_test_email_verification_item">
|
|
99
99
|
<SidebarMenuButton asChild>
|
|
100
100
|
<Link
|
|
101
|
-
href="/verify_email"
|
|
101
|
+
href="/hazo_auth/verify_email"
|
|
102
102
|
className="cls_sidebar_layout_test_email_verification_link flex items-center gap-2"
|
|
103
103
|
aria-label="Test email verification layout component"
|
|
104
104
|
>
|
|
@@ -119,6 +119,18 @@ export function SidebarLayoutWrapper({ children }: SidebarLayoutWrapperProps) {
|
|
|
119
119
|
</Link>
|
|
120
120
|
</SidebarMenuButton>
|
|
121
121
|
</SidebarMenuItem>
|
|
122
|
+
<SidebarMenuItem className="cls_sidebar_layout_user_management_item">
|
|
123
|
+
<SidebarMenuButton asChild>
|
|
124
|
+
<Link
|
|
125
|
+
href="/hazo_auth/user_management"
|
|
126
|
+
className="cls_sidebar_layout_user_management_link flex items-center gap-2"
|
|
127
|
+
aria-label="Open User Management to manage users, roles, and permissions"
|
|
128
|
+
>
|
|
129
|
+
<User className="h-4 w-4" aria-hidden="true" />
|
|
130
|
+
<span>User Management</span>
|
|
131
|
+
</Link>
|
|
132
|
+
</SidebarMenuButton>
|
|
133
|
+
</SidebarMenuItem>
|
|
122
134
|
</SidebarMenu>
|
|
123
135
|
</SidebarGroup>
|
|
124
136
|
{authStatus.authenticated && (
|
|
@@ -130,7 +142,7 @@ export function SidebarLayoutWrapper({ children }: SidebarLayoutWrapperProps) {
|
|
|
130
142
|
<SidebarMenuItem className="cls_sidebar_layout_my_settings_item">
|
|
131
143
|
<SidebarMenuButton asChild>
|
|
132
144
|
<Link
|
|
133
|
-
href="/my_settings"
|
|
145
|
+
href="/hazo_auth/my_settings"
|
|
134
146
|
className="cls_sidebar_layout_my_settings_link flex items-center gap-2"
|
|
135
147
|
aria-label="Open my settings page"
|
|
136
148
|
>
|
|
@@ -26,7 +26,7 @@ export type UnauthorizedGuardProps = {
|
|
|
26
26
|
export function UnauthorizedGuard({
|
|
27
27
|
message = "You must be logged in to access this page.",
|
|
28
28
|
loginButtonLabel = "Go to login",
|
|
29
|
-
loginPath = "/login",
|
|
29
|
+
loginPath = "/hazo_auth/login",
|
|
30
30
|
children,
|
|
31
31
|
}: UnauthorizedGuardProps) {
|
|
32
32
|
const router = useRouter();
|
|
@@ -46,7 +46,7 @@ export function use_auth_status(): AuthStatus {
|
|
|
46
46
|
setAuthStatus((prev) => ({ ...prev, loading: true }));
|
|
47
47
|
|
|
48
48
|
try {
|
|
49
|
-
const response = await fetch("/api/
|
|
49
|
+
const response = await fetch("/api/hazo_auth/me", {
|
|
50
50
|
method: "GET",
|
|
51
51
|
credentials: "include",
|
|
52
52
|
});
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
// file_description: React hook for hazo_get_auth utility (client-side)
|
|
2
|
+
// section: client_directive
|
|
3
|
+
"use client";
|
|
4
|
+
|
|
5
|
+
// section: imports
|
|
6
|
+
import { useState, useEffect, useCallback } from "react";
|
|
7
|
+
import type { HazoAuthResult } from "@/lib/auth/auth_types";
|
|
8
|
+
|
|
9
|
+
// section: types
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Options for use_hazo_auth hook
|
|
13
|
+
*/
|
|
14
|
+
export type UseHazoAuthOptions = {
|
|
15
|
+
/**
|
|
16
|
+
* Array of required permissions to check
|
|
17
|
+
*/
|
|
18
|
+
required_permissions?: string[];
|
|
19
|
+
/**
|
|
20
|
+
* If true, throws error when permissions are missing (default: false)
|
|
21
|
+
*/
|
|
22
|
+
strict?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Skip fetch (for conditional use)
|
|
25
|
+
*/
|
|
26
|
+
skip?: boolean;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Result type for use_hazo_auth hook
|
|
31
|
+
*/
|
|
32
|
+
export type UseHazoAuthResult = HazoAuthResult & {
|
|
33
|
+
/**
|
|
34
|
+
* Loading state
|
|
35
|
+
*/
|
|
36
|
+
loading: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Error state
|
|
39
|
+
*/
|
|
40
|
+
error: Error | null;
|
|
41
|
+
/**
|
|
42
|
+
* Manual refetch function
|
|
43
|
+
*/
|
|
44
|
+
refetch: () => Promise<void>;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// section: constants
|
|
48
|
+
const AUTH_STATUS_CHANGE_EVENT = "hazo_auth_status_change";
|
|
49
|
+
|
|
50
|
+
// section: helpers
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Triggers a refresh of hazo_auth status across all components
|
|
54
|
+
* Dispatches a custom event that all use_hazo_auth hooks listen to
|
|
55
|
+
*/
|
|
56
|
+
export function trigger_hazo_auth_refresh(): void {
|
|
57
|
+
if (typeof window !== "undefined") {
|
|
58
|
+
window.dispatchEvent(
|
|
59
|
+
new CustomEvent(AUTH_STATUS_CHANGE_EVENT),
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// section: hook
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* React hook for hazo_get_auth utility
|
|
68
|
+
* Fetches authentication status and permissions from /api/auth/get_auth
|
|
69
|
+
* @param options - Optional parameters for permission checking
|
|
70
|
+
* @returns UseHazoAuthResult with auth data, loading state, and refetch function
|
|
71
|
+
*/
|
|
72
|
+
export function use_hazo_auth(
|
|
73
|
+
options?: UseHazoAuthOptions,
|
|
74
|
+
): UseHazoAuthResult {
|
|
75
|
+
const [authResult, setAuthResult] = useState<HazoAuthResult>({
|
|
76
|
+
authenticated: false,
|
|
77
|
+
user: null,
|
|
78
|
+
permissions: [],
|
|
79
|
+
permission_ok: false,
|
|
80
|
+
});
|
|
81
|
+
const [loading, setLoading] = useState<boolean>(true);
|
|
82
|
+
const [error, setError] = useState<Error | null>(null);
|
|
83
|
+
|
|
84
|
+
const fetchAuth = useCallback(async () => {
|
|
85
|
+
if (options?.skip) {
|
|
86
|
+
setLoading(false);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
setLoading(true);
|
|
91
|
+
setError(null);
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
const response = await fetch("/api/hazo_auth/get_auth", {
|
|
95
|
+
method: "POST",
|
|
96
|
+
headers: {
|
|
97
|
+
"Content-Type": "application/json",
|
|
98
|
+
},
|
|
99
|
+
credentials: "include",
|
|
100
|
+
body: JSON.stringify({
|
|
101
|
+
required_permissions: options?.required_permissions,
|
|
102
|
+
strict: options?.strict || false,
|
|
103
|
+
}),
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
if (!response.ok) {
|
|
107
|
+
const error_data = await response.json();
|
|
108
|
+
throw new Error(
|
|
109
|
+
error_data.user_friendly_message ||
|
|
110
|
+
error_data.error ||
|
|
111
|
+
"Failed to fetch authentication status",
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const data = await response.json();
|
|
116
|
+
setAuthResult(data);
|
|
117
|
+
} catch (err) {
|
|
118
|
+
const error_message =
|
|
119
|
+
err instanceof Error ? err : new Error("Unknown error");
|
|
120
|
+
setError(error_message);
|
|
121
|
+
setAuthResult({
|
|
122
|
+
authenticated: false,
|
|
123
|
+
user: null,
|
|
124
|
+
permissions: [],
|
|
125
|
+
permission_ok: false,
|
|
126
|
+
});
|
|
127
|
+
} finally {
|
|
128
|
+
setLoading(false);
|
|
129
|
+
}
|
|
130
|
+
}, [options?.required_permissions, options?.strict, options?.skip]);
|
|
131
|
+
|
|
132
|
+
useEffect(() => {
|
|
133
|
+
// Fetch auth status on mount
|
|
134
|
+
void fetchAuth();
|
|
135
|
+
|
|
136
|
+
// Listen for auth status change events
|
|
137
|
+
const handleAuthChange = () => {
|
|
138
|
+
void fetchAuth();
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
window.addEventListener(AUTH_STATUS_CHANGE_EVENT, handleAuthChange);
|
|
142
|
+
|
|
143
|
+
return () => {
|
|
144
|
+
window.removeEventListener(
|
|
145
|
+
AUTH_STATUS_CHANGE_EVENT,
|
|
146
|
+
handleAuthChange,
|
|
147
|
+
);
|
|
148
|
+
};
|
|
149
|
+
}, [fetchAuth]);
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
...authResult,
|
|
153
|
+
loading,
|
|
154
|
+
error,
|
|
155
|
+
refetch: fetchAuth,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|