hazo_auth 4.1.0 → 4.3.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/README.md +230 -0
- package/SETUP_CHECKLIST.md +202 -0
- package/bin/hazo_auth.mjs +35 -0
- package/cli-src/assets/images/forgot_password_default.jpg +0 -0
- package/cli-src/assets/images/login_default.jpg +0 -0
- package/cli-src/assets/images/register_default.jpg +0 -0
- package/cli-src/assets/images/reset_password_default.jpg +0 -0
- package/cli-src/assets/images/verify_email_default.jpg +0 -0
- package/cli-src/cli/generate.ts +276 -0
- package/cli-src/cli/index.ts +207 -0
- package/cli-src/cli/init.ts +254 -0
- package/cli-src/cli/init_users.ts +376 -0
- package/cli-src/cli/validate.ts +581 -0
- package/cli-src/lib/already_logged_in_config.server.ts +46 -0
- package/cli-src/lib/app_logger.ts +24 -0
- package/cli-src/lib/auth/auth_cache.ts +220 -0
- package/cli-src/lib/auth/auth_rate_limiter.ts +121 -0
- package/cli-src/lib/auth/auth_types.ts +110 -0
- package/cli-src/lib/auth/auth_utils.server.ts +196 -0
- package/cli-src/lib/auth/hazo_get_auth.server.ts +512 -0
- package/cli-src/lib/auth/index.ts +23 -0
- package/cli-src/lib/auth/nextauth_config.ts +227 -0
- package/cli-src/lib/auth/scope_cache.ts +233 -0
- package/cli-src/lib/auth/server_auth.ts +88 -0
- package/cli-src/lib/auth/session_token_validator.edge.ts +91 -0
- package/cli-src/lib/auth_utility_config.server.ts +136 -0
- package/cli-src/lib/config/config_loader.server.ts +164 -0
- package/cli-src/lib/config/default_config.ts +199 -0
- package/cli-src/lib/email_verification_config.server.ts +63 -0
- package/cli-src/lib/file_types_config.server.ts +25 -0
- package/cli-src/lib/forgot_password_config.server.ts +63 -0
- package/cli-src/lib/hazo_connect_instance.server.ts +101 -0
- package/cli-src/lib/hazo_connect_setup.server.ts +194 -0
- package/cli-src/lib/hazo_connect_setup.ts +54 -0
- package/cli-src/lib/index.ts +46 -0
- package/cli-src/lib/login_config.server.ts +106 -0
- package/cli-src/lib/messages_config.server.ts +45 -0
- package/cli-src/lib/migrations/apply_migration.ts +105 -0
- package/cli-src/lib/my_settings_config.server.ts +135 -0
- package/cli-src/lib/oauth_config.server.ts +87 -0
- package/cli-src/lib/password_requirements_config.server.ts +40 -0
- package/cli-src/lib/profile_pic_menu_config.server.ts +138 -0
- package/cli-src/lib/profile_picture_config.server.ts +56 -0
- package/cli-src/lib/register_config.server.ts +101 -0
- package/cli-src/lib/reset_password_config.server.ts +103 -0
- package/cli-src/lib/scope_hierarchy_config.server.ts +151 -0
- package/cli-src/lib/services/email_service.ts +587 -0
- package/cli-src/lib/services/email_verification_service.ts +270 -0
- package/cli-src/lib/services/index.ts +16 -0
- package/cli-src/lib/services/login_service.ts +150 -0
- package/cli-src/lib/services/oauth_service.ts +494 -0
- package/cli-src/lib/services/password_change_service.ts +154 -0
- package/cli-src/lib/services/password_reset_service.ts +418 -0
- package/cli-src/lib/services/profile_picture_remove_service.ts +120 -0
- package/cli-src/lib/services/profile_picture_service.ts +451 -0
- package/cli-src/lib/services/profile_picture_source_mapper.ts +62 -0
- package/cli-src/lib/services/registration_service.ts +185 -0
- package/cli-src/lib/services/scope_labels_service.ts +348 -0
- package/cli-src/lib/services/scope_service.ts +778 -0
- package/cli-src/lib/services/session_token_service.ts +177 -0
- package/cli-src/lib/services/token_service.ts +240 -0
- package/cli-src/lib/services/user_profiles_cache.ts +189 -0
- package/cli-src/lib/services/user_profiles_service.ts +264 -0
- package/cli-src/lib/services/user_scope_service.ts +554 -0
- package/cli-src/lib/services/user_update_service.ts +141 -0
- package/cli-src/lib/ui_shell_config.server.ts +73 -0
- package/cli-src/lib/ui_sizes_config.server.ts +37 -0
- package/cli-src/lib/user_fields_config.server.ts +31 -0
- package/cli-src/lib/user_management_config.server.ts +39 -0
- package/cli-src/lib/user_profiles_config.server.ts +55 -0
- package/cli-src/lib/utils/api_route_helpers.ts +60 -0
- package/cli-src/lib/utils/error_sanitizer.ts +75 -0
- package/cli-src/lib/utils/password_validator.ts +65 -0
- package/cli-src/lib/utils.ts +11 -0
- package/cli-src/server/logging/logger_service.ts +56 -0
- package/dist/app/api/hazo_auth/forgot_password/route.d.ts.map +1 -1
- package/dist/app/api/hazo_auth/forgot_password/route.js +15 -0
- package/dist/app/api/hazo_auth/logout/route.d.ts.map +1 -1
- package/dist/app/api/hazo_auth/logout/route.js +31 -0
- package/dist/app/api/hazo_auth/me/route.d.ts.map +1 -1
- package/dist/app/api/hazo_auth/me/route.js +10 -0
- package/dist/cli/index.js +18 -0
- package/dist/cli/init_users.d.ts +17 -0
- package/dist/cli/init_users.d.ts.map +1 -0
- package/dist/cli/init_users.js +307 -0
- package/dist/components/layouts/forgot_password/hooks/use_forgot_password_form.d.ts +2 -0
- package/dist/components/layouts/forgot_password/hooks/use_forgot_password_form.d.ts.map +1 -1
- package/dist/components/layouts/forgot_password/hooks/use_forgot_password_form.js +8 -0
- package/dist/components/layouts/forgot_password/index.d.ts +7 -1
- package/dist/components/layouts/forgot_password/index.d.ts.map +1 -1
- package/dist/components/layouts/forgot_password/index.js +7 -2
- package/dist/components/layouts/login/index.d.ts +13 -1
- package/dist/components/layouts/login/index.d.ts.map +1 -1
- package/dist/components/layouts/login/index.js +11 -2
- package/dist/components/layouts/my_settings/components/connected_accounts_section.d.ts +17 -0
- package/dist/components/layouts/my_settings/components/connected_accounts_section.d.ts.map +1 -0
- package/dist/components/layouts/my_settings/components/connected_accounts_section.js +17 -0
- package/dist/components/layouts/my_settings/components/set_password_section.d.ts +26 -0
- package/dist/components/layouts/my_settings/components/set_password_section.d.ts.map +1 -0
- package/dist/components/layouts/my_settings/components/set_password_section.js +127 -0
- package/dist/components/layouts/my_settings/hooks/use_my_settings.d.ts +3 -0
- package/dist/components/layouts/my_settings/hooks/use_my_settings.d.ts.map +1 -1
- package/dist/components/layouts/my_settings/hooks/use_my_settings.js +9 -0
- package/dist/components/layouts/my_settings/index.d.ts.map +1 -1
- package/dist/components/layouts/my_settings/index.js +4 -2
- package/dist/components/layouts/shared/components/google_icon.d.ts +12 -0
- package/dist/components/layouts/shared/components/google_icon.d.ts.map +1 -0
- package/dist/components/layouts/shared/components/google_icon.js +9 -0
- package/dist/components/layouts/shared/components/google_sign_in_button.d.ts +21 -0
- package/dist/components/layouts/shared/components/google_sign_in_button.d.ts.map +1 -0
- package/dist/components/layouts/shared/components/google_sign_in_button.js +50 -0
- package/dist/components/layouts/shared/components/oauth_divider.d.ts +13 -0
- package/dist/components/layouts/shared/components/oauth_divider.d.ts.map +1 -0
- package/dist/components/layouts/shared/components/oauth_divider.js +13 -0
- package/dist/components/layouts/shared/config/layout_customization.d.ts +2 -7
- package/dist/components/layouts/shared/config/layout_customization.d.ts.map +1 -1
- package/dist/components/layouts/shared/hooks/use_auth_status.d.ts +3 -0
- package/dist/components/layouts/shared/hooks/use_auth_status.d.ts.map +1 -1
- package/dist/components/layouts/shared/hooks/use_auth_status.js +4 -0
- package/dist/components/layouts/shared/index.d.ts +5 -0
- package/dist/components/layouts/shared/index.d.ts.map +1 -1
- package/dist/components/layouts/shared/index.js +3 -0
- package/dist/components/ui/button.d.ts +1 -1
- package/dist/lib/auth/nextauth_config.d.ts +34 -0
- package/dist/lib/auth/nextauth_config.d.ts.map +1 -0
- package/dist/lib/auth/nextauth_config.js +171 -0
- package/dist/lib/config/default_config.d.ts +24 -0
- package/dist/lib/config/default_config.d.ts.map +1 -1
- package/dist/lib/config/default_config.js +14 -0
- package/dist/lib/index.d.ts +2 -0
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +1 -0
- package/dist/lib/login_config.server.d.ts +3 -0
- package/dist/lib/login_config.server.d.ts.map +1 -1
- package/dist/lib/login_config.server.js +4 -0
- package/dist/lib/oauth_config.server.d.ts +29 -0
- package/dist/lib/oauth_config.server.d.ts.map +1 -0
- package/dist/lib/oauth_config.server.js +40 -0
- package/dist/lib/services/login_service.d.ts.map +1 -1
- package/dist/lib/services/login_service.js +16 -1
- package/dist/lib/services/oauth_service.d.ts +88 -0
- package/dist/lib/services/oauth_service.d.ts.map +1 -0
- package/dist/lib/services/oauth_service.js +376 -0
- package/dist/lib/services/password_reset_service.d.ts +2 -0
- package/dist/lib/services/password_reset_service.d.ts.map +1 -1
- package/dist/lib/services/password_reset_service.js +10 -0
- package/dist/lib/services/registration_service.d.ts.map +1 -1
- package/dist/lib/services/registration_service.js +1 -0
- package/dist/lib/utils/password_validator.d.ts +19 -0
- package/dist/lib/utils/password_validator.d.ts.map +1 -0
- package/dist/lib/utils/password_validator.js +36 -0
- package/dist/server_pages/login.d.ts.map +1 -1
- package/dist/server_pages/login.js +6 -1
- package/dist/server_pages/login_client_wrapper.d.ts +5 -2
- package/dist/server_pages/login_client_wrapper.d.ts.map +1 -1
- package/dist/server_pages/login_client_wrapper.js +2 -2
- package/package.json +6 -2
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
// file_description: CLI command to initialize users, roles, and permissions from configuration
|
|
2
|
+
// section: imports
|
|
3
|
+
import { get_hazo_connect_instance } from "../lib/hazo_connect_instance.server.js";
|
|
4
|
+
import { createCrudService } from "hazo_connect/server";
|
|
5
|
+
import { get_user_management_config } from "../lib/user_management_config.server.js";
|
|
6
|
+
import { get_config_value } from "../lib/config/config_loader.server.js";
|
|
7
|
+
import { create_app_logger } from "../lib/app_logger.js";
|
|
8
|
+
// section: helpers
|
|
9
|
+
/**
|
|
10
|
+
* Prints a summary of what was inserted vs what already existed
|
|
11
|
+
*/
|
|
12
|
+
function print_summary(summary) {
|
|
13
|
+
console.log("=".repeat(60));
|
|
14
|
+
console.log("INITIALIZATION SUMMARY");
|
|
15
|
+
console.log("=".repeat(60));
|
|
16
|
+
console.log();
|
|
17
|
+
// Permissions summary
|
|
18
|
+
console.log("Permissions:");
|
|
19
|
+
if (summary.permissions.inserted.length > 0) {
|
|
20
|
+
console.log(` ✓ Inserted (${summary.permissions.inserted.length}):`);
|
|
21
|
+
summary.permissions.inserted.forEach((name) => console.log(` - ${name}`));
|
|
22
|
+
}
|
|
23
|
+
if (summary.permissions.existing.length > 0) {
|
|
24
|
+
console.log(` ⊙ Already existed (${summary.permissions.existing.length}):`);
|
|
25
|
+
summary.permissions.existing.forEach((name) => console.log(` - ${name}`));
|
|
26
|
+
}
|
|
27
|
+
console.log();
|
|
28
|
+
// Role summary
|
|
29
|
+
console.log("Role:");
|
|
30
|
+
if (summary.role.inserted) {
|
|
31
|
+
console.log(` ✓ Inserted: default_super_user_role (ID: ${summary.role.role_id})`);
|
|
32
|
+
}
|
|
33
|
+
if (summary.role.existing) {
|
|
34
|
+
console.log(` ⊙ Already existed: default_super_user_role (ID: ${summary.role.role_id})`);
|
|
35
|
+
}
|
|
36
|
+
console.log();
|
|
37
|
+
// Role permissions summary
|
|
38
|
+
console.log("Role-Permission Assignments:");
|
|
39
|
+
if (summary.role_permissions.inserted > 0) {
|
|
40
|
+
console.log(` ✓ Inserted: ${summary.role_permissions.inserted} assignment(s)`);
|
|
41
|
+
}
|
|
42
|
+
if (summary.role_permissions.existing > 0) {
|
|
43
|
+
console.log(` ⊙ Already existed: ${summary.role_permissions.existing} assignment(s)`);
|
|
44
|
+
}
|
|
45
|
+
console.log();
|
|
46
|
+
// User role summary
|
|
47
|
+
console.log("User-Role Assignment:");
|
|
48
|
+
if (summary.user_role.inserted) {
|
|
49
|
+
console.log(` ✓ Inserted: Super user role assigned to user`);
|
|
50
|
+
}
|
|
51
|
+
if (summary.user_role.existing) {
|
|
52
|
+
console.log(` ⊙ Already existed: User already has super user role`);
|
|
53
|
+
}
|
|
54
|
+
console.log();
|
|
55
|
+
console.log("=".repeat(60));
|
|
56
|
+
}
|
|
57
|
+
// section: main_function
|
|
58
|
+
/**
|
|
59
|
+
* Initializes users, roles, and permissions from configuration
|
|
60
|
+
* This function reads from hazo_auth_config.ini and sets up:
|
|
61
|
+
* 1. Permissions from [hazo_auth__user_management] application_permission_list_defaults
|
|
62
|
+
* 2. A default_super_user_role with all permissions
|
|
63
|
+
* 3. Assigns the role to user from --email parameter or [hazo_auth__initial_setup] default_super_user_email
|
|
64
|
+
*/
|
|
65
|
+
export async function handle_init_users(options = {}) {
|
|
66
|
+
var _a;
|
|
67
|
+
const logger = create_app_logger();
|
|
68
|
+
const summary = {
|
|
69
|
+
permissions: {
|
|
70
|
+
inserted: [],
|
|
71
|
+
existing: [],
|
|
72
|
+
},
|
|
73
|
+
role: {
|
|
74
|
+
inserted: false,
|
|
75
|
+
existing: false,
|
|
76
|
+
role_id: null,
|
|
77
|
+
},
|
|
78
|
+
role_permissions: {
|
|
79
|
+
inserted: 0,
|
|
80
|
+
existing: 0,
|
|
81
|
+
},
|
|
82
|
+
user_role: {
|
|
83
|
+
inserted: false,
|
|
84
|
+
existing: false,
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
try {
|
|
88
|
+
console.log("\n🐸 hazo_auth init-users\n");
|
|
89
|
+
console.log("Initializing users, roles, and permissions from configuration...\n");
|
|
90
|
+
// Get hazo_connect instance
|
|
91
|
+
const hazoConnect = get_hazo_connect_instance();
|
|
92
|
+
const permissions_service = createCrudService(hazoConnect, "hazo_permissions");
|
|
93
|
+
const roles_service = createCrudService(hazoConnect, "hazo_roles");
|
|
94
|
+
const role_permissions_service = createCrudService(hazoConnect, "hazo_role_permissions");
|
|
95
|
+
const users_service = createCrudService(hazoConnect, "hazo_users");
|
|
96
|
+
const user_roles_service = createCrudService(hazoConnect, "hazo_user_roles");
|
|
97
|
+
// 1. Get permissions from config
|
|
98
|
+
const config = get_user_management_config();
|
|
99
|
+
const permission_names = config.application_permission_list_defaults || [];
|
|
100
|
+
if (permission_names.length === 0) {
|
|
101
|
+
console.log("⚠ No permissions found in configuration.");
|
|
102
|
+
console.log(" Add permissions to [hazo_auth__user_management] application_permission_list_defaults\n");
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
console.log(`Found ${permission_names.length} permission(s) in configuration:`);
|
|
106
|
+
permission_names.forEach((name) => console.log(` - ${name}`));
|
|
107
|
+
console.log();
|
|
108
|
+
// 2. Add permissions to hazo_permissions table
|
|
109
|
+
const permission_id_map = {};
|
|
110
|
+
const now = new Date().toISOString();
|
|
111
|
+
for (const permission_name of permission_names) {
|
|
112
|
+
const trimmed_name = permission_name.trim();
|
|
113
|
+
if (!trimmed_name)
|
|
114
|
+
continue;
|
|
115
|
+
// Check if permission already exists
|
|
116
|
+
const existing_permissions = await permissions_service.findBy({
|
|
117
|
+
permission_name: trimmed_name,
|
|
118
|
+
});
|
|
119
|
+
if (Array.isArray(existing_permissions) && existing_permissions.length > 0) {
|
|
120
|
+
const existing_permission = existing_permissions[0];
|
|
121
|
+
const perm_id = existing_permission.id;
|
|
122
|
+
permission_id_map[trimmed_name] = perm_id;
|
|
123
|
+
summary.permissions.existing.push(trimmed_name);
|
|
124
|
+
console.log(`✓ Permission already exists: ${trimmed_name} (ID: ${perm_id})`);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
// Insert new permission
|
|
128
|
+
const new_permission = await permissions_service.insert({
|
|
129
|
+
permission_name: trimmed_name,
|
|
130
|
+
description: `Permission for ${trimmed_name}`,
|
|
131
|
+
created_at: now,
|
|
132
|
+
changed_at: now,
|
|
133
|
+
});
|
|
134
|
+
const perm_id = Array.isArray(new_permission)
|
|
135
|
+
? new_permission[0].id
|
|
136
|
+
: new_permission.id;
|
|
137
|
+
permission_id_map[trimmed_name] = perm_id;
|
|
138
|
+
summary.permissions.inserted.push(trimmed_name);
|
|
139
|
+
console.log(`✓ Inserted permission: ${trimmed_name} (ID: ${perm_id})`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
console.log();
|
|
143
|
+
// 3. Create or get default_super_user_role
|
|
144
|
+
const role_name = "default_super_user_role";
|
|
145
|
+
const existing_roles = await roles_service.findBy({
|
|
146
|
+
role_name,
|
|
147
|
+
});
|
|
148
|
+
let role_id;
|
|
149
|
+
if (Array.isArray(existing_roles) && existing_roles.length > 0) {
|
|
150
|
+
role_id = existing_roles[0].id;
|
|
151
|
+
summary.role.existing = true;
|
|
152
|
+
summary.role.role_id = role_id;
|
|
153
|
+
console.log(`✓ Role already exists: ${role_name} (ID: ${role_id})`);
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
const new_role = await roles_service.insert({
|
|
157
|
+
role_name,
|
|
158
|
+
created_at: now,
|
|
159
|
+
changed_at: now,
|
|
160
|
+
});
|
|
161
|
+
role_id = Array.isArray(new_role)
|
|
162
|
+
? new_role[0].id
|
|
163
|
+
: new_role.id;
|
|
164
|
+
summary.role.inserted = true;
|
|
165
|
+
summary.role.role_id = role_id;
|
|
166
|
+
console.log(`✓ Created role: ${role_name} (ID: ${role_id})`);
|
|
167
|
+
}
|
|
168
|
+
console.log();
|
|
169
|
+
// 4. Assign all permissions to the role
|
|
170
|
+
const permission_ids = Object.values(permission_id_map);
|
|
171
|
+
for (const permission_id of permission_ids) {
|
|
172
|
+
// Check if role-permission assignment already exists
|
|
173
|
+
const existing_assignments = await role_permissions_service.findBy({
|
|
174
|
+
role_id,
|
|
175
|
+
permission_id,
|
|
176
|
+
});
|
|
177
|
+
if (Array.isArray(existing_assignments) && existing_assignments.length > 0) {
|
|
178
|
+
summary.role_permissions.existing++;
|
|
179
|
+
const perm_name = Object.keys(permission_id_map).find((key) => permission_id_map[key] === permission_id);
|
|
180
|
+
console.log(`✓ Role-permission already exists: ${role_name} -> ${perm_name}`);
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
await role_permissions_service.insert({
|
|
184
|
+
role_id,
|
|
185
|
+
permission_id,
|
|
186
|
+
created_at: now,
|
|
187
|
+
changed_at: now,
|
|
188
|
+
});
|
|
189
|
+
summary.role_permissions.inserted++;
|
|
190
|
+
const perm_name = Object.keys(permission_id_map).find((key) => permission_id_map[key] === permission_id);
|
|
191
|
+
console.log(`✓ Assigned permission to role: ${role_name} -> ${perm_name}`);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
console.log();
|
|
195
|
+
// 5. Get super user email from options or config
|
|
196
|
+
const super_user_email = ((_a = options.email) === null || _a === void 0 ? void 0 : _a.trim()) || get_config_value("hazo_auth__initial_setup", "default_super_user_email", "").trim();
|
|
197
|
+
if (!super_user_email) {
|
|
198
|
+
console.log("⚠ No super user email provided.");
|
|
199
|
+
console.log(" Either use --email=user@example.com parameter");
|
|
200
|
+
console.log(" Or add [hazo_auth__initial_setup] default_super_user_email to config\n");
|
|
201
|
+
print_summary(summary);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
if (options.email) {
|
|
205
|
+
console.log(`Using email from --email parameter: ${super_user_email}`);
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
console.log(`Using email from config: ${super_user_email}`);
|
|
209
|
+
}
|
|
210
|
+
console.log(`Looking up user with email: ${super_user_email}`);
|
|
211
|
+
// 6. Find user by email
|
|
212
|
+
const users = await users_service.findBy({
|
|
213
|
+
email_address: super_user_email,
|
|
214
|
+
});
|
|
215
|
+
if (!Array.isArray(users) || users.length === 0) {
|
|
216
|
+
console.log(`✗ User not found with email: ${super_user_email}`);
|
|
217
|
+
console.log(" Please ensure the user exists in the database before running this script.\n");
|
|
218
|
+
print_summary(summary);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const user = users[0];
|
|
222
|
+
const user_id = user.id;
|
|
223
|
+
console.log(`✓ Found user: ${super_user_email} (ID: ${user_id})`);
|
|
224
|
+
console.log();
|
|
225
|
+
// 7. Assign role to user
|
|
226
|
+
const existing_user_roles = await user_roles_service.findBy({
|
|
227
|
+
user_id,
|
|
228
|
+
role_id,
|
|
229
|
+
});
|
|
230
|
+
if (Array.isArray(existing_user_roles) && existing_user_roles.length > 0) {
|
|
231
|
+
summary.user_role.existing = true;
|
|
232
|
+
console.log(`✓ User already has role assigned: ${user_id} -> ${role_name}`);
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
await user_roles_service.insert({
|
|
236
|
+
user_id,
|
|
237
|
+
role_id,
|
|
238
|
+
created_at: now,
|
|
239
|
+
changed_at: now,
|
|
240
|
+
});
|
|
241
|
+
summary.user_role.inserted = true;
|
|
242
|
+
console.log(`✓ Assigned role to user: ${user_id} -> ${role_name}`);
|
|
243
|
+
}
|
|
244
|
+
console.log();
|
|
245
|
+
// 8. Print summary
|
|
246
|
+
print_summary(summary);
|
|
247
|
+
logger.info("init_users_completed", {
|
|
248
|
+
filename: "init_users.ts",
|
|
249
|
+
line_number: 0,
|
|
250
|
+
summary,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
catch (error) {
|
|
254
|
+
const error_message = error instanceof Error ? error.message : "Unknown error";
|
|
255
|
+
const error_stack = error instanceof Error ? error.stack : undefined;
|
|
256
|
+
console.error("\n✗ Error initializing users:");
|
|
257
|
+
console.error(` ${error_message}`);
|
|
258
|
+
if (error_stack) {
|
|
259
|
+
console.error("\nStack trace:");
|
|
260
|
+
console.error(error_stack);
|
|
261
|
+
}
|
|
262
|
+
logger.error("init_users_failed", {
|
|
263
|
+
filename: "init_users.ts",
|
|
264
|
+
line_number: 0,
|
|
265
|
+
error_message,
|
|
266
|
+
error_stack,
|
|
267
|
+
});
|
|
268
|
+
process.exit(1);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
// section: help
|
|
272
|
+
/**
|
|
273
|
+
* Shows help for the init-users command
|
|
274
|
+
*/
|
|
275
|
+
export function show_init_users_help() {
|
|
276
|
+
console.log(`
|
|
277
|
+
hazo_auth init-users
|
|
278
|
+
|
|
279
|
+
Initialize users, roles, and permissions from configuration.
|
|
280
|
+
|
|
281
|
+
This command reads from hazo_auth_config.ini and:
|
|
282
|
+
1. Creates permissions from [hazo_auth__user_management] application_permission_list_defaults
|
|
283
|
+
2. Creates a 'default_super_user_role' role
|
|
284
|
+
3. Assigns all permissions to the super user role
|
|
285
|
+
4. Finds user by email (from --email parameter or config)
|
|
286
|
+
5. Assigns the super user role to that user
|
|
287
|
+
|
|
288
|
+
Options:
|
|
289
|
+
--email=<email> Email address of the user to assign super user role
|
|
290
|
+
(overrides [hazo_auth__initial_setup] default_super_user_email)
|
|
291
|
+
--help, -h Show this help message
|
|
292
|
+
|
|
293
|
+
Configuration required in hazo_auth_config.ini:
|
|
294
|
+
|
|
295
|
+
[hazo_auth__user_management]
|
|
296
|
+
application_permission_list_defaults = admin_user_management,admin_role_management,admin_permission_management
|
|
297
|
+
|
|
298
|
+
[hazo_auth__initial_setup]
|
|
299
|
+
default_super_user_email = admin@example.com (optional if using --email)
|
|
300
|
+
|
|
301
|
+
Note: The user must already exist in the database (registered) before running this command.
|
|
302
|
+
|
|
303
|
+
Usage:
|
|
304
|
+
npx hazo_auth init-users
|
|
305
|
+
npx hazo_auth init-users --email=admin@example.com
|
|
306
|
+
`);
|
|
307
|
+
}
|
|
@@ -13,6 +13,8 @@ export type UseForgotPasswordFormResult = {
|
|
|
13
13
|
isSubmitDisabled: boolean;
|
|
14
14
|
isSubmitting: boolean;
|
|
15
15
|
emailTouched: boolean;
|
|
16
|
+
/** True if the submitted email is for a Google-only account (no password set) */
|
|
17
|
+
isGoogleOnlyAccount: boolean;
|
|
16
18
|
handleFieldChange: (fieldId: ForgotPasswordFieldId, value: string) => void;
|
|
17
19
|
handleEmailBlur: () => void;
|
|
18
20
|
handleSubmit: (event: React.FormEvent<HTMLFormElement>) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"use_forgot_password_form.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/forgot_password/hooks/use_forgot_password_form.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAC7E,OAAO,EAA6B,KAAK,qBAAqB,EAAE,MAAM,wCAAwC,CAAC;AAK/G,MAAM,MAAM,wBAAwB,GAAG,MAAM,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AAC7E,MAAM,MAAM,wBAAwB,GAAG,OAAO,CAAC,MAAM,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC,GAAG;IACtF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,2BAA2B,CAAC,OAAO,GAAG,OAAO,IAAI;IAC3D,UAAU,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,MAAM,EAAE,wBAAwB,CAAC;IACjC,MAAM,EAAE,wBAAwB,CAAC;IACjC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,YAAY,EAAE,OAAO,CAAC;IACtB,YAAY,EAAE,OAAO,CAAC;IACtB,iBAAiB,EAAE,CAAC,OAAO,EAAE,qBAAqB,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3E,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,YAAY,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC,eAAe,CAAC,KAAK,IAAI,CAAC;IAChE,YAAY,EAAE,MAAM,IAAI,CAAC;CAC1B,CAAC;AAQF,eAAO,MAAM,wBAAwB,GAAI,OAAO,EAAG,iBAEhD,2BAA2B,CAAC,OAAO,CAAC,KAAG,
|
|
1
|
+
{"version":3,"file":"use_forgot_password_form.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/forgot_password/hooks/use_forgot_password_form.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AAC7E,OAAO,EAA6B,KAAK,qBAAqB,EAAE,MAAM,wCAAwC,CAAC;AAK/G,MAAM,MAAM,wBAAwB,GAAG,MAAM,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AAC7E,MAAM,MAAM,wBAAwB,GAAG,OAAO,CAAC,MAAM,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC,GAAG;IACtF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,2BAA2B,CAAC,OAAO,GAAG,OAAO,IAAI;IAC3D,UAAU,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,MAAM,EAAE,wBAAwB,CAAC;IACjC,MAAM,EAAE,wBAAwB,CAAC;IACjC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,YAAY,EAAE,OAAO,CAAC;IACtB,YAAY,EAAE,OAAO,CAAC;IACtB,iFAAiF;IACjF,mBAAmB,EAAE,OAAO,CAAC;IAC7B,iBAAiB,EAAE,CAAC,OAAO,EAAE,qBAAqB,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3E,eAAe,EAAE,MAAM,IAAI,CAAC;IAC5B,YAAY,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC,eAAe,CAAC,KAAK,IAAI,CAAC;IAChE,YAAY,EAAE,MAAM,IAAI,CAAC;CAC1B,CAAC;AAQF,eAAO,MAAM,wBAAwB,GAAI,OAAO,EAAG,iBAEhD,2BAA2B,CAAC,OAAO,CAAC,KAAG,2BAqJzC,CAAC"}
|
|
@@ -16,6 +16,7 @@ export const use_forgot_password_form = ({ dataClient, }) => {
|
|
|
16
16
|
const [errors, setErrors] = useState({});
|
|
17
17
|
const [emailTouched, setEmailTouched] = useState(false);
|
|
18
18
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
19
|
+
const [isGoogleOnlyAccount, setIsGoogleOnlyAccount] = useState(false);
|
|
19
20
|
const isSubmitDisabled = useMemo(() => {
|
|
20
21
|
if (isSubmitting) {
|
|
21
22
|
return true;
|
|
@@ -72,6 +73,7 @@ export const use_forgot_password_form = ({ dataClient, }) => {
|
|
|
72
73
|
}
|
|
73
74
|
setIsSubmitting(true);
|
|
74
75
|
setErrors({});
|
|
76
|
+
setIsGoogleOnlyAccount(false);
|
|
75
77
|
try {
|
|
76
78
|
const response = await fetch(`${apiBasePath}/forgot_password`, {
|
|
77
79
|
method: "POST",
|
|
@@ -86,6 +88,11 @@ export const use_forgot_password_form = ({ dataClient, }) => {
|
|
|
86
88
|
if (!response.ok) {
|
|
87
89
|
throw new Error(data.error || "Password reset request failed");
|
|
88
90
|
}
|
|
91
|
+
// Check if user is a Google-only account (no password set)
|
|
92
|
+
if (data.no_password_set) {
|
|
93
|
+
setIsGoogleOnlyAccount(true);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
89
96
|
// Show success notification
|
|
90
97
|
toast.success("Password reset link sent", {
|
|
91
98
|
description: "If an account with that email exists, a password reset link has been sent.",
|
|
@@ -121,6 +128,7 @@ export const use_forgot_password_form = ({ dataClient, }) => {
|
|
|
121
128
|
isSubmitDisabled,
|
|
122
129
|
isSubmitting,
|
|
123
130
|
emailTouched,
|
|
131
|
+
isGoogleOnlyAccount,
|
|
124
132
|
handleFieldChange,
|
|
125
133
|
handleEmailBlur,
|
|
126
134
|
handleSubmit,
|
|
@@ -16,6 +16,12 @@ export type ForgotPasswordLayoutProps<TClient = unknown> = {
|
|
|
16
16
|
showReturnHomeButton?: boolean;
|
|
17
17
|
returnHomeButtonLabel?: string;
|
|
18
18
|
returnHomePath?: string;
|
|
19
|
+
/** Message shown when user's account is Google-only (no password set) */
|
|
20
|
+
googleOnlyAccountHeading?: string;
|
|
21
|
+
googleOnlyAccountMessage?: string;
|
|
22
|
+
googleOnlyAccountHelpText?: string;
|
|
23
|
+
mySettingsPath?: string;
|
|
24
|
+
mySettingsLabel?: string;
|
|
19
25
|
};
|
|
20
|
-
export default function forgot_password_layout<TClient>({ image_src, image_alt, image_background_color, field_overrides, labels, button_colors, data_client, sign_in_path, sign_in_label, alreadyLoggedInMessage, showLogoutButton, showReturnHomeButton, returnHomeButtonLabel, returnHomePath, }: ForgotPasswordLayoutProps<TClient>): import("react/jsx-runtime").JSX.Element;
|
|
26
|
+
export default function forgot_password_layout<TClient>({ image_src, image_alt, image_background_color, field_overrides, labels, button_colors, data_client, sign_in_path, sign_in_label, alreadyLoggedInMessage, showLogoutButton, showReturnHomeButton, returnHomeButtonLabel, returnHomePath, googleOnlyAccountHeading, googleOnlyAccountMessage, googleOnlyAccountHelpText, mySettingsPath, mySettingsLabel, }: ForgotPasswordLayoutProps<TClient>): import("react/jsx-runtime").JSX.Element;
|
|
21
27
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/layouts/forgot_password/index.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/layouts/forgot_password/index.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAOlD,OAAO,EACL,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,EAC1B,MAAM,uCAAuC,CAAC;AAW/C,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAI1E,MAAM,MAAM,yBAAyB,CAAC,OAAO,GAAG,OAAO,IAAI;IACzD,SAAS,EAAE,MAAM,GAAG,eAAe,CAAC;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,eAAe,CAAC,EAAE,uBAAuB,CAAC;IAC1C,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B,aAAa,CAAC,EAAE,sBAAsB,CAAC;IACvC,WAAW,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACvC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,yEAAyE;IACzE,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AASF,MAAM,CAAC,OAAO,UAAU,sBAAsB,CAAC,OAAO,EAAE,EACtD,SAAS,EACT,SAAS,EACT,sBAAkC,EAClC,eAAe,EACf,MAAM,EACN,aAAa,EACb,WAAW,EACX,YAAiC,EACjC,aAAyB,EACzB,sBAAoD,EACpD,gBAAuB,EACvB,oBAA4B,EAC5B,qBAAqC,EACrC,cAAoB,EACpB,wBAAoD,EACpD,wBAA2G,EAC3G,yBAA0I,EAC1I,cAAyC,EACzC,eAAkC,GACnC,EAAE,yBAAyB,CAAC,OAAO,CAAC,2CAgJpC"}
|
|
@@ -9,6 +9,7 @@ import { FormHeader } from "../shared/components/form_header";
|
|
|
9
9
|
import { FormActionButtons } from "../shared/components/form_action_buttons";
|
|
10
10
|
import { TwoColumnAuthLayout } from "../shared/components/two_column_auth_layout";
|
|
11
11
|
import { AlreadyLoggedInGuard } from "../shared/components/already_logged_in_guard";
|
|
12
|
+
import { GoogleSignInButton } from "../shared/components/google_sign_in_button";
|
|
12
13
|
import { FORGOT_PASSWORD_FIELD_IDS, createForgotPasswordFieldDefinitions, resolveForgotPasswordButtonPalette, resolveForgotPasswordLabels, } from "./config/forgot_password_field_config";
|
|
13
14
|
import { use_forgot_password_form, } from "./hooks/use_forgot_password_form";
|
|
14
15
|
import Link from "next/link";
|
|
@@ -16,7 +17,7 @@ const ORDERED_FIELDS = [
|
|
|
16
17
|
FORGOT_PASSWORD_FIELD_IDS.EMAIL,
|
|
17
18
|
];
|
|
18
19
|
// section: component
|
|
19
|
-
export default function forgot_password_layout({ image_src, image_alt, image_background_color = "#f1f5f9", field_overrides, labels, button_colors, data_client, sign_in_path = "/hazo_auth/login", sign_in_label = "Sign in", alreadyLoggedInMessage = "You are already logged in", showLogoutButton = true, showReturnHomeButton = false, returnHomeButtonLabel = "Return home", returnHomePath = "/", }) {
|
|
20
|
+
export default function forgot_password_layout({ image_src, image_alt, image_background_color = "#f1f5f9", field_overrides, labels, button_colors, data_client, sign_in_path = "/hazo_auth/login", sign_in_label = "Sign in", alreadyLoggedInMessage = "You are already logged in", showLogoutButton = true, showReturnHomeButton = false, returnHomeButtonLabel = "Return home", returnHomePath = "/", googleOnlyAccountHeading = "Google Account Detected", googleOnlyAccountMessage = "Your account was created using Google Sign-In and doesn't have a password set.", googleOnlyAccountHelpText = "Sign in with Google, then set a password in your account settings if you'd like to use email/password login.", mySettingsPath = "/hazo_auth/my_settings", mySettingsLabel = "Go to Settings", }) {
|
|
20
21
|
const fieldDefinitions = createForgotPasswordFieldDefinitions(field_overrides);
|
|
21
22
|
const resolvedLabels = resolveForgotPasswordLabels(labels);
|
|
22
23
|
const resolvedButtonPalette = resolveForgotPasswordButtonPalette(button_colors);
|
|
@@ -40,5 +41,9 @@ export default function forgot_password_layout({ image_src, image_alt, image_bac
|
|
|
40
41
|
return (_jsx(FormFieldWrapper, { fieldId: fieldDefinition.id, label: fieldDefinition.label, input: inputElement, errorMessage: shouldShowError }, fieldId));
|
|
41
42
|
});
|
|
42
43
|
};
|
|
43
|
-
return (_jsx(AlreadyLoggedInGuard, { image_src: image_src, image_alt: image_alt, image_background_color: image_background_color, message: alreadyLoggedInMessage, showLogoutButton: showLogoutButton, showReturnHomeButton: showReturnHomeButton, returnHomeButtonLabel: returnHomeButtonLabel, returnHomePath: returnHomePath, children: _jsx(TwoColumnAuthLayout, { imageSrc: image_src, imageAlt: image_alt, imageBackgroundColor: image_background_color, formContent:
|
|
44
|
+
return (_jsx(AlreadyLoggedInGuard, { image_src: image_src, image_alt: image_alt, image_background_color: image_background_color, message: alreadyLoggedInMessage, showLogoutButton: showLogoutButton, showReturnHomeButton: showReturnHomeButton, returnHomeButtonLabel: returnHomeButtonLabel, returnHomePath: returnHomePath, children: _jsx(TwoColumnAuthLayout, { imageSrc: image_src, imageAlt: image_alt, imageBackgroundColor: image_background_color, formContent: _jsx(_Fragment, { children: form.isGoogleOnlyAccount ? (
|
|
45
|
+
/* Google-only account message */
|
|
46
|
+
_jsxs("div", { className: "cls_forgot_password_google_only flex flex-col gap-5", children: [_jsx(FormHeader, { heading: googleOnlyAccountHeading, subHeading: googleOnlyAccountMessage }), _jsx("p", { className: "cls_forgot_password_google_only_help text-sm text-slate-600", children: googleOnlyAccountHelpText }), _jsxs("div", { className: "cls_forgot_password_google_only_actions flex flex-col gap-3", children: [_jsx(GoogleSignInButton, { label: "Sign in with Google" }), _jsx(Link, { href: mySettingsPath, className: "cls_forgot_password_settings_link text-center text-sm font-medium text-slate-700 hover:text-slate-900 hover:underline", children: mySettingsLabel })] }), _jsx("div", { className: "cls_forgot_password_sign_in_link mt-2 text-center text-sm text-slate-600", children: _jsxs(Link, { href: sign_in_path, className: "font-medium text-slate-900 hover:underline", children: ["Back to ", sign_in_label] }) })] })) : (
|
|
47
|
+
/* Normal forgot password form */
|
|
48
|
+
_jsxs(_Fragment, { children: [_jsx(FormHeader, { heading: resolvedLabels.heading, subHeading: resolvedLabels.subHeading }), _jsxs("form", { className: "cls_forgot_password_layout_form_fields flex flex-col gap-5", onSubmit: form.handleSubmit, "aria-label": "Forgot password form", children: [renderFields(form), _jsx(FormActionButtons, { submitLabel: resolvedLabels.submitButton, cancelLabel: resolvedLabels.cancelButton, buttonPalette: resolvedButtonPalette, isSubmitDisabled: form.isSubmitDisabled, onCancel: form.handleCancel, submitAriaLabel: "Submit forgot password form", cancelAriaLabel: "Cancel forgot password form" }), form.isSubmitting && (_jsx("div", { className: "cls_forgot_password_submitting_indicator text-sm text-slate-600 text-center", children: "Sending reset link..." }))] }), _jsxs("div", { className: "cls_forgot_password_sign_in_link mt-4 text-center text-sm text-slate-600", children: ["Remember your password?", " ", _jsx(Link, { href: sign_in_path, className: "font-medium text-slate-900 hover:underline", children: sign_in_label })] })] })) }) }) }));
|
|
44
49
|
}
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import type { StaticImageData } from "next/image";
|
|
2
2
|
import { type ButtonPaletteOverrides, type LayoutFieldMapOverrides, type LayoutLabelOverrides } from "../shared/config/layout_customization";
|
|
3
3
|
import { type LayoutDataClient } from "../shared/data/layout_data_client";
|
|
4
|
+
export type OAuthLayoutConfig = {
|
|
5
|
+
/** Enable Google OAuth login */
|
|
6
|
+
enable_google: boolean;
|
|
7
|
+
/** Enable traditional email/password login */
|
|
8
|
+
enable_email_password: boolean;
|
|
9
|
+
/** Text displayed on the Google sign-in button */
|
|
10
|
+
google_button_text: string;
|
|
11
|
+
/** Text displayed on the divider between OAuth and email/password form */
|
|
12
|
+
oauth_divider_text: string;
|
|
13
|
+
};
|
|
4
14
|
export type LoginLayoutProps<TClient = unknown> = {
|
|
5
15
|
image_src: string | StaticImageData;
|
|
6
16
|
image_alt: string;
|
|
@@ -27,6 +37,8 @@ export type LoginLayoutProps<TClient = unknown> = {
|
|
|
27
37
|
create_account_path?: string;
|
|
28
38
|
create_account_label?: string;
|
|
29
39
|
urlOnLogon?: string;
|
|
40
|
+
/** OAuth configuration */
|
|
41
|
+
oauth?: OAuthLayoutConfig;
|
|
30
42
|
};
|
|
31
|
-
export default function login_layout<TClient>({ image_src, image_alt, image_background_color, field_overrides, labels, button_colors, data_client, logger, redirectRoute, successMessage, alreadyLoggedInMessage, showLogoutButton, showReturnHomeButton, returnHomeButtonLabel, returnHomePath, forgot_password_path, forgot_password_label, create_account_path, create_account_label, urlOnLogon, }: LoginLayoutProps<TClient>): import("react/jsx-runtime").JSX.Element;
|
|
43
|
+
export default function login_layout<TClient>({ image_src, image_alt, image_background_color, field_overrides, labels, button_colors, data_client, logger, redirectRoute, successMessage, alreadyLoggedInMessage, showLogoutButton, showReturnHomeButton, returnHomeButtonLabel, returnHomePath, forgot_password_path, forgot_password_label, create_account_path, create_account_label, urlOnLogon, oauth, }: LoginLayoutProps<TClient>): import("react/jsx-runtime").JSX.Element;
|
|
32
44
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/layouts/login/index.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/layouts/login/index.tsx"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAWlD,OAAO,EACL,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,EAC1B,MAAM,uCAAuC,CAAC;AAW/C,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAG1E,MAAM,MAAM,iBAAiB,GAAG;IAC9B,gCAAgC;IAChC,aAAa,EAAE,OAAO,CAAC;IACvB,8CAA8C;IAC9C,qBAAqB,EAAE,OAAO,CAAC;IAC/B,kDAAkD;IAClD,kBAAkB,EAAE,MAAM,CAAC;IAC3B,0EAA0E;IAC1E,kBAAkB,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,gBAAgB,CAAC,OAAO,GAAG,OAAO,IAAI;IAChD,SAAS,EAAE,MAAM,GAAG,eAAe,CAAC;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,eAAe,CAAC,EAAE,uBAAuB,CAAC;IAC1C,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B,aAAa,CAAC,EAAE,sBAAsB,CAAC;IACvC,WAAW,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACvC,MAAM,CAAC,EAAE;QACP,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;QAChE,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;QACjE,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;QAChE,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;KAClE,CAAC;IACF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0BAA0B;IAC1B,KAAK,CAAC,EAAE,iBAAiB,CAAC;CAC3B,CAAC;AAUF,MAAM,CAAC,OAAO,UAAU,YAAY,CAAC,OAAO,EAAE,EAC5C,SAAS,EACT,SAAS,EACT,sBAAkC,EAClC,eAAe,EACf,MAAM,EACN,aAAa,EACb,WAAW,EACX,MAAM,EACN,aAAa,EACb,cAAyC,EACzC,sBAAoD,EACpD,gBAAuB,EACvB,oBAA4B,EAC5B,qBAAqC,EACrC,cAAoB,EACpB,oBAAmD,EACnD,qBAA0C,EAC1C,mBAA2C,EAC3C,oBAAuC,EACvC,UAAU,EACV,KAAK,GACN,EAAE,gBAAgB,CAAC,OAAO,CAAC,2CAuM3B"}
|
|
@@ -12,6 +12,8 @@ import { FormActionButtons } from "../shared/components/form_action_buttons";
|
|
|
12
12
|
import { TwoColumnAuthLayout } from "../shared/components/two_column_auth_layout";
|
|
13
13
|
import { CheckCircle } from "lucide-react";
|
|
14
14
|
import { AlreadyLoggedInGuard } from "../shared/components/already_logged_in_guard";
|
|
15
|
+
import { GoogleSignInButton } from "../shared/components/google_sign_in_button";
|
|
16
|
+
import { OAuthDivider } from "../shared/components/oauth_divider";
|
|
15
17
|
import { LOGIN_FIELD_IDS, createLoginFieldDefinitions, resolveLoginButtonPalette, resolveLoginLabels, } from "./config/login_field_config";
|
|
16
18
|
import { use_login_form, } from "./hooks/use_login_form";
|
|
17
19
|
const ORDERED_FIELDS = [
|
|
@@ -19,7 +21,14 @@ const ORDERED_FIELDS = [
|
|
|
19
21
|
LOGIN_FIELD_IDS.PASSWORD,
|
|
20
22
|
];
|
|
21
23
|
// section: component
|
|
22
|
-
export default function login_layout({ image_src, image_alt, image_background_color = "#f1f5f9", field_overrides, labels, button_colors, data_client, logger, redirectRoute, successMessage = "Successfully logged in", alreadyLoggedInMessage = "You are already logged in", showLogoutButton = true, showReturnHomeButton = false, returnHomeButtonLabel = "Return home", returnHomePath = "/", forgot_password_path = "/hazo_auth/forgot_password", forgot_password_label = "Forgot password?", create_account_path = "/hazo_auth/register", create_account_label = "Create account", urlOnLogon, }) {
|
|
24
|
+
export default function login_layout({ image_src, image_alt, image_background_color = "#f1f5f9", field_overrides, labels, button_colors, data_client, logger, redirectRoute, successMessage = "Successfully logged in", alreadyLoggedInMessage = "You are already logged in", showLogoutButton = true, showReturnHomeButton = false, returnHomeButtonLabel = "Return home", returnHomePath = "/", forgot_password_path = "/hazo_auth/forgot_password", forgot_password_label = "Forgot password?", create_account_path = "/hazo_auth/register", create_account_label = "Create account", urlOnLogon, oauth, }) {
|
|
25
|
+
// Default OAuth config: both enabled
|
|
26
|
+
const oauthConfig = oauth || {
|
|
27
|
+
enable_google: true,
|
|
28
|
+
enable_email_password: true,
|
|
29
|
+
google_button_text: "Continue with Google",
|
|
30
|
+
oauth_divider_text: "or continue with email",
|
|
31
|
+
};
|
|
23
32
|
const fieldDefinitions = createLoginFieldDefinitions(field_overrides);
|
|
24
33
|
const resolvedLabels = resolveLoginLabels(labels);
|
|
25
34
|
const resolvedButtonPalette = resolveLoginButtonPalette(button_colors);
|
|
@@ -54,5 +63,5 @@ export default function login_layout({ image_src, image_alt, image_background_co
|
|
|
54
63
|
if (form.isSuccess) {
|
|
55
64
|
return (_jsx(TwoColumnAuthLayout, { imageSrc: image_src, imageAlt: image_alt, imageBackgroundColor: image_background_color, formContent: _jsxs(_Fragment, { children: [_jsx(FormHeader, { heading: resolvedLabels.heading, subHeading: resolvedLabels.subHeading }), _jsxs("div", { className: "cls_login_layout_success flex flex-col items-center justify-center gap-4 p-8 text-center", children: [_jsx(CheckCircle, { className: "cls_login_layout_success_icon h-16 w-16 text-green-600", "aria-hidden": "true" }), _jsx("p", { className: "cls_login_layout_success_message text-lg font-medium text-slate-900", children: successMessage })] })] }) }));
|
|
56
65
|
}
|
|
57
|
-
return (_jsx(AlreadyLoggedInGuard, { image_src: image_src, image_alt: image_alt, image_background_color: image_background_color, message: alreadyLoggedInMessage, showLogoutButton: showLogoutButton, showReturnHomeButton: showReturnHomeButton, returnHomeButtonLabel: returnHomeButtonLabel, returnHomePath: returnHomePath, children: _jsx(TwoColumnAuthLayout, { imageSrc: image_src, imageAlt: image_alt, imageBackgroundColor: image_background_color, formContent: _jsxs(_Fragment, { children: [_jsx(FormHeader, { heading: resolvedLabels.heading, subHeading: resolvedLabels.subHeading }), _jsxs("form", { className: "cls_login_layout_form_fields flex flex-col gap-5", onSubmit: form.handleSubmit, "aria-label": "Login form", children: [renderFields(form), _jsx(FormActionButtons, { submitLabel: resolvedLabels.submitButton, cancelLabel: resolvedLabels.cancelButton, buttonPalette: resolvedButtonPalette, isSubmitDisabled: form.isSubmitDisabled, onCancel: form.handleCancel, submitAriaLabel: "Submit login form", cancelAriaLabel: "Cancel login form" }), _jsxs("div", { className: "cls_login_layout_support_links flex flex-col gap-1 text-sm text-muted-foreground", children: [_jsx(Link, { href: forgot_password_path, className: "cls_login_layout_forgot_password_link text-primary underline-offset-4 hover:underline", "aria-label": "Go to forgot password page", children: forgot_password_label }), _jsx(Link, { href: create_account_path, className: "cls_login_layout_create_account_link text-primary underline-offset-4 hover:underline", "aria-label": "Go to create account page", children: create_account_label })] })] })] }) }) }));
|
|
66
|
+
return (_jsx(AlreadyLoggedInGuard, { image_src: image_src, image_alt: image_alt, image_background_color: image_background_color, message: alreadyLoggedInMessage, showLogoutButton: showLogoutButton, showReturnHomeButton: showReturnHomeButton, returnHomeButtonLabel: returnHomeButtonLabel, returnHomePath: returnHomePath, children: _jsx(TwoColumnAuthLayout, { imageSrc: image_src, imageAlt: image_alt, imageBackgroundColor: image_background_color, formContent: _jsxs(_Fragment, { children: [_jsx(FormHeader, { heading: resolvedLabels.heading, subHeading: resolvedLabels.subHeading }), oauthConfig.enable_google && (_jsx("div", { className: "cls_login_layout_oauth_section", children: _jsx(GoogleSignInButton, { label: oauthConfig.google_button_text }) })), oauthConfig.enable_google && oauthConfig.enable_email_password && (_jsx(OAuthDivider, { text: oauthConfig.oauth_divider_text })), oauthConfig.enable_email_password && (_jsxs("form", { className: "cls_login_layout_form_fields flex flex-col gap-5", onSubmit: form.handleSubmit, "aria-label": "Login form", children: [renderFields(form), _jsx(FormActionButtons, { submitLabel: resolvedLabels.submitButton, cancelLabel: resolvedLabels.cancelButton, buttonPalette: resolvedButtonPalette, isSubmitDisabled: form.isSubmitDisabled, onCancel: form.handleCancel, submitAriaLabel: "Submit login form", cancelAriaLabel: "Cancel login form" }), _jsxs("div", { className: "cls_login_layout_support_links flex flex-col gap-1 text-sm text-muted-foreground", children: [_jsx(Link, { href: forgot_password_path, className: "cls_login_layout_forgot_password_link text-primary underline-offset-4 hover:underline", "aria-label": "Go to forgot password page", children: forgot_password_label }), _jsx(Link, { href: create_account_path, className: "cls_login_layout_create_account_link text-primary underline-offset-4 hover:underline", "aria-label": "Go to create account page", children: create_account_label })] })] })), !oauthConfig.enable_email_password && oauthConfig.enable_google && (_jsx("div", { className: "cls_login_layout_support_links mt-4 text-center text-sm text-muted-foreground", children: _jsx(Link, { href: create_account_path, className: "cls_login_layout_create_account_link text-primary underline-offset-4 hover:underline", "aria-label": "Go to create account page", children: create_account_label }) }))] }) }) }));
|
|
58
67
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type ConnectedAccountsSectionProps = {
|
|
2
|
+
/** Whether Google account is connected */
|
|
3
|
+
googleConnected: boolean;
|
|
4
|
+
/** User's email address (shown when Google is connected) */
|
|
5
|
+
email?: string;
|
|
6
|
+
/** Section heading */
|
|
7
|
+
heading?: string;
|
|
8
|
+
/** Loading state */
|
|
9
|
+
loading?: boolean;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Connected Accounts Section for My Settings
|
|
13
|
+
* Shows which OAuth providers are linked to the user's account
|
|
14
|
+
* Currently supports Google, designed for future extensibility
|
|
15
|
+
*/
|
|
16
|
+
export declare function ConnectedAccountsSection({ googleConnected, email, heading, loading, }: ConnectedAccountsSectionProps): import("react/jsx-runtime").JSX.Element;
|
|
17
|
+
//# sourceMappingURL=connected_accounts_section.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connected_accounts_section.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/my_settings/components/connected_accounts_section.tsx"],"names":[],"mappings":"AAQA,MAAM,MAAM,6BAA6B,GAAG;IAC1C,0CAA0C;IAC1C,eAAe,EAAE,OAAO,CAAC;IACzB,4DAA4D;IAC5D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sBAAsB;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oBAAoB;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAGF;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,EACvC,eAAe,EACf,KAAK,EACL,OAA8B,EAC9B,OAAe,GAChB,EAAE,6BAA6B,2CAkD/B"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// file_description: Connected accounts section showing linked OAuth providers
|
|
2
|
+
// section: client_directive
|
|
3
|
+
"use client";
|
|
4
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
5
|
+
// section: imports
|
|
6
|
+
import { GoogleIcon } from "../../shared/components/google_icon";
|
|
7
|
+
// section: component
|
|
8
|
+
/**
|
|
9
|
+
* Connected Accounts Section for My Settings
|
|
10
|
+
* Shows which OAuth providers are linked to the user's account
|
|
11
|
+
* Currently supports Google, designed for future extensibility
|
|
12
|
+
*/
|
|
13
|
+
export function ConnectedAccountsSection({ googleConnected, email, heading = "Connected Accounts", loading = false, }) {
|
|
14
|
+
return (_jsxs("div", { className: "cls_my_settings_connected_accounts_section bg-white rounded-lg border border-[var(--hazo-border)] p-6", children: [_jsx("h2", { className: "cls_my_settings_section_heading text-lg font-semibold text-[var(--hazo-text-primary)] mb-4", children: heading }), _jsx("div", { className: "cls_connected_accounts_list flex flex-col gap-3", children: _jsxs("div", { className: "cls_connected_account_item flex items-center justify-between p-3 rounded-md border border-[var(--hazo-border)] bg-[var(--hazo-surface-muted)]", children: [_jsxs("div", { className: "cls_connected_account_info flex items-center gap-3", children: [_jsx("div", { className: "cls_connected_account_icon flex items-center justify-center w-10 h-10 rounded-full bg-white border border-[var(--hazo-border)]", children: _jsx(GoogleIcon, { width: 20, height: 20 }) }), _jsxs("div", { className: "cls_connected_account_details flex flex-col", children: [_jsx("span", { className: "cls_connected_account_name font-medium text-[var(--hazo-text-primary)]", children: "Google" }), googleConnected && email && (_jsx("span", { className: "cls_connected_account_email text-sm text-[var(--hazo-text-muted)]", children: email }))] })] }), _jsx("div", { className: "cls_connected_account_status", children: loading ? (_jsx("span", { className: "cls_connected_account_loading text-sm text-[var(--hazo-text-muted)] animate-pulse", children: "Loading..." })) : googleConnected ? (_jsxs("span", { className: "cls_connected_account_connected inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium bg-green-100 text-green-700", children: [_jsx("span", { className: "cls_connected_indicator w-1.5 h-1.5 rounded-full bg-green-500" }), "Connected"] })) : (_jsxs("span", { className: "cls_connected_account_not_connected inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-xs font-medium bg-slate-100 text-slate-600", children: [_jsx("span", { className: "cls_not_connected_indicator w-1.5 h-1.5 rounded-full bg-slate-400" }), "Not connected"] })) })] }) }), _jsx("p", { className: "cls_connected_accounts_help text-sm text-[var(--hazo-text-muted)] mt-4", children: googleConnected
|
|
15
|
+
? "Your Google account is linked. You can sign in using either your password or Google."
|
|
16
|
+
: "Link your Google account to enable quick sign-in with Google." })] }));
|
|
17
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { PasswordRequirementOptions } from "../../shared/config/layout_customization";
|
|
2
|
+
export type SetPasswordSectionProps = {
|
|
3
|
+
/** Password requirements for validation */
|
|
4
|
+
passwordRequirements: PasswordRequirementOptions;
|
|
5
|
+
/** Callback when password is successfully set */
|
|
6
|
+
onPasswordSet?: () => void;
|
|
7
|
+
/** Section heading */
|
|
8
|
+
heading?: string;
|
|
9
|
+
/** Description text */
|
|
10
|
+
description?: string;
|
|
11
|
+
/** New password label */
|
|
12
|
+
newPasswordLabel?: string;
|
|
13
|
+
/** Confirm password label */
|
|
14
|
+
confirmPasswordLabel?: string;
|
|
15
|
+
/** Submit button label */
|
|
16
|
+
submitButtonLabel?: string;
|
|
17
|
+
/** Loading state from parent */
|
|
18
|
+
parentLoading?: boolean;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Set Password Section for My Settings
|
|
22
|
+
* Allows Google-only users to set a password for email/password login
|
|
23
|
+
* Only shown when user has no password set (Google-only account)
|
|
24
|
+
*/
|
|
25
|
+
export declare function SetPasswordSection({ passwordRequirements, onPasswordSet, heading, description, newPasswordLabel, confirmPasswordLabel, submitButtonLabel, parentLoading, }: SetPasswordSectionProps): import("react/jsx-runtime").JSX.Element;
|
|
26
|
+
//# sourceMappingURL=set_password_section.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"set_password_section.d.ts","sourceRoot":"","sources":["../../../../../src/components/layouts/my_settings/components/set_password_section.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,0CAA0C,CAAC;AAI3F,MAAM,MAAM,uBAAuB,GAAG;IACpC,2CAA2C;IAC3C,oBAAoB,EAAE,0BAA0B,CAAC;IACjD,iDAAiD;IACjD,aAAa,CAAC,EAAE,MAAM,IAAI,CAAC;IAC3B,sBAAsB;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uBAAuB;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yBAAyB;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,6BAA6B;IAC7B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,0BAA0B;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gCAAgC;IAChC,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAGF;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,oBAAoB,EACpB,aAAa,EACb,OAAwB,EACxB,WAA4G,EAC5G,gBAAiC,EACjC,oBAAyC,EACzC,iBAAkC,EAClC,aAAqB,GACtB,EAAE,uBAAuB,2CAoNzB"}
|