hazo_auth 1.4.2 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -19
- package/SETUP_CHECKLIST.md +779 -0
- package/dist/app/api/hazo_auth/change_password/route.d.ts +8 -0
- package/dist/app/api/hazo_auth/change_password/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/change_password/route.js +98 -0
- package/dist/app/api/hazo_auth/forgot_password/route.d.ts +8 -0
- package/dist/app/api/hazo_auth/forgot_password/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/forgot_password/route.js +78 -0
- package/dist/app/api/hazo_auth/get_auth/route.d.ts +10 -0
- package/dist/app/api/hazo_auth/get_auth/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/get_auth/route.js +63 -0
- package/dist/app/api/hazo_auth/invalidate_cache/route.d.ts +14 -0
- package/dist/app/api/hazo_auth/invalidate_cache/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/invalidate_cache/route.js +96 -0
- package/dist/app/api/hazo_auth/library_photo/[category]/[filename]/route.d.ts +9 -0
- package/dist/app/api/hazo_auth/library_photo/[category]/[filename]/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/library_photo/[category]/[filename]/route.js +82 -0
- package/dist/app/api/hazo_auth/library_photos/route.d.ts +22 -0
- package/dist/app/api/hazo_auth/library_photos/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/library_photos/route.js +80 -0
- package/dist/app/api/hazo_auth/login/route.d.ts +12 -0
- package/dist/app/api/hazo_auth/login/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/login/route.js +140 -0
- package/dist/app/api/hazo_auth/logout/route.d.ts +8 -0
- package/dist/app/api/hazo_auth/logout/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/logout/route.js +71 -0
- package/dist/app/api/hazo_auth/me/route.d.ts +3 -0
- package/dist/app/api/hazo_auth/me/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/me/route.js +34 -0
- package/dist/app/api/hazo_auth/profile_picture/[filename]/route.d.ts +7 -0
- package/dist/app/api/hazo_auth/profile_picture/[filename]/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/profile_picture/[filename]/route.js +43 -0
- package/dist/app/api/hazo_auth/register/route.d.ts +9 -0
- package/dist/app/api/hazo_auth/register/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/register/route.js +80 -0
- package/dist/app/api/hazo_auth/remove_profile_picture/route.d.ts +8 -0
- package/dist/app/api/hazo_auth/remove_profile_picture/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/remove_profile_picture/route.js +64 -0
- package/dist/app/api/hazo_auth/resend_verification/route.d.ts +8 -0
- package/dist/app/api/hazo_auth/resend_verification/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/resend_verification/route.js +79 -0
- package/dist/app/api/hazo_auth/reset_password/route.d.ts +8 -0
- package/dist/app/api/hazo_auth/reset_password/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/reset_password/route.js +76 -0
- package/dist/app/api/hazo_auth/update_user/route.d.ts +9 -0
- package/dist/app/api/hazo_auth/update_user/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/update_user/route.js +95 -0
- package/dist/app/api/hazo_auth/upload_profile_picture/route.d.ts +9 -0
- package/dist/app/api/hazo_auth/upload_profile_picture/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/upload_profile_picture/route.js +204 -0
- package/dist/app/api/hazo_auth/validate_reset_token/route.d.ts +6 -0
- package/dist/app/api/hazo_auth/validate_reset_token/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/validate_reset_token/route.js +58 -0
- package/dist/app/api/hazo_auth/verify_email/route.d.ts +11 -0
- package/dist/app/api/hazo_auth/verify_email/route.d.ts.map +1 -0
- package/dist/app/api/hazo_auth/verify_email/route.js +63 -0
- package/dist/cli/generate.d.ts +7 -0
- package/dist/cli/generate.d.ts.map +1 -0
- package/dist/cli/generate.js +184 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +173 -0
- package/dist/cli/init.d.ts +2 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +201 -0
- package/dist/cli/validate.d.ts +15 -0
- package/dist/cli/validate.d.ts.map +1 -0
- package/dist/cli/validate.js +509 -0
- package/dist/components/ui/card.d.ts +9 -0
- package/dist/components/ui/card.d.ts.map +1 -0
- package/dist/components/ui/card.js +45 -0
- package/dist/hooks/use-mobile.d.ts.map +1 -1
- package/dist/hooks/use-mobile.js +17 -3
- package/dist/lib/services/profile_picture_service.d.ts +34 -2
- package/dist/lib/services/profile_picture_service.d.ts.map +1 -1
- package/dist/lib/services/profile_picture_service.js +157 -15
- package/dist/page_components/forgot_password.d.ts +19 -0
- package/dist/page_components/forgot_password.d.ts.map +1 -0
- package/dist/page_components/forgot_password.js +36 -0
- package/dist/page_components/index.d.ts +7 -0
- package/dist/page_components/index.d.ts.map +1 -0
- package/dist/page_components/index.js +9 -0
- package/dist/page_components/login.d.ts +26 -0
- package/dist/page_components/login.d.ts.map +1 -0
- package/dist/page_components/login.js +40 -0
- package/dist/page_components/my_settings.d.ts +64 -0
- package/dist/page_components/my_settings.d.ts.map +1 -0
- package/dist/page_components/my_settings.js +67 -0
- package/dist/page_components/register.d.ts +25 -0
- package/dist/page_components/register.d.ts.map +1 -0
- package/dist/page_components/register.js +43 -0
- package/dist/page_components/reset_password.d.ts +25 -0
- package/dist/page_components/reset_password.d.ts.map +1 -0
- package/dist/page_components/reset_password.js +43 -0
- package/dist/page_components/verify_email.d.ts +21 -0
- package/dist/page_components/verify_email.d.ts.map +1 -0
- package/dist/page_components/verify_email.js +36 -0
- package/dist/server/routes/change_password.d.ts +2 -0
- package/dist/server/routes/change_password.d.ts.map +1 -0
- package/dist/server/routes/change_password.js +2 -0
- package/dist/server/routes/forgot_password.d.ts +2 -0
- package/dist/server/routes/forgot_password.d.ts.map +1 -0
- package/dist/server/routes/forgot_password.js +2 -0
- package/dist/server/routes/get_auth.d.ts +2 -0
- package/dist/server/routes/get_auth.d.ts.map +1 -0
- package/dist/server/routes/get_auth.js +2 -0
- package/dist/server/routes/index.d.ts +19 -0
- package/dist/server/routes/index.d.ts.map +1 -0
- package/dist/server/routes/index.js +25 -0
- package/dist/server/routes/invalidate_cache.d.ts +2 -0
- package/dist/server/routes/invalidate_cache.d.ts.map +1 -0
- package/dist/server/routes/invalidate_cache.js +2 -0
- package/dist/server/routes/library_photo.d.ts +2 -0
- package/dist/server/routes/library_photo.d.ts.map +1 -0
- package/dist/server/routes/library_photo.js +3 -0
- package/dist/server/routes/library_photos.d.ts +2 -0
- package/dist/server/routes/library_photos.d.ts.map +1 -0
- package/dist/server/routes/library_photos.js +2 -0
- package/dist/server/routes/login.d.ts +2 -0
- package/dist/server/routes/login.d.ts.map +1 -0
- package/dist/server/routes/login.js +2 -0
- package/dist/server/routes/logout.d.ts +2 -0
- package/dist/server/routes/logout.d.ts.map +1 -0
- package/dist/server/routes/logout.js +2 -0
- package/dist/server/routes/me.d.ts +2 -0
- package/dist/server/routes/me.d.ts.map +1 -0
- package/dist/server/routes/me.js +2 -0
- package/dist/server/routes/profile_picture_filename.d.ts +2 -0
- package/dist/server/routes/profile_picture_filename.d.ts.map +1 -0
- package/dist/server/routes/profile_picture_filename.js +3 -0
- package/dist/server/routes/register.d.ts +2 -0
- package/dist/server/routes/register.d.ts.map +1 -0
- package/dist/server/routes/register.js +2 -0
- package/dist/server/routes/remove_profile_picture.d.ts +2 -0
- package/dist/server/routes/remove_profile_picture.d.ts.map +1 -0
- package/dist/server/routes/remove_profile_picture.js +2 -0
- package/dist/server/routes/resend_verification.d.ts +2 -0
- package/dist/server/routes/resend_verification.d.ts.map +1 -0
- package/dist/server/routes/resend_verification.js +2 -0
- package/dist/server/routes/reset_password.d.ts +2 -0
- package/dist/server/routes/reset_password.d.ts.map +1 -0
- package/dist/server/routes/reset_password.js +2 -0
- package/dist/server/routes/update_user.d.ts +2 -0
- package/dist/server/routes/update_user.d.ts.map +1 -0
- package/dist/server/routes/update_user.js +2 -0
- package/dist/server/routes/upload_profile_picture.d.ts +2 -0
- package/dist/server/routes/upload_profile_picture.d.ts.map +1 -0
- package/dist/server/routes/upload_profile_picture.js +2 -0
- package/dist/server/routes/validate_reset_token.d.ts +2 -0
- package/dist/server/routes/validate_reset_token.d.ts.map +1 -0
- package/dist/server/routes/validate_reset_token.js +2 -0
- package/dist/server/routes/verify_email.d.ts +2 -0
- package/dist/server/routes/verify_email.d.ts.map +1 -0
- package/dist/server/routes/verify_email.js +2 -0
- package/package.json +40 -17
- package/components.json +0 -22
- package/instrumentation.ts +0 -32
- package/migrations/001_add_token_type_to_refresh_tokens.sql +0 -14
- package/migrations/002_add_name_to_hazo_users.sql +0 -7
- package/migrations/003_add_url_on_logon_to_hazo_users.sql +0 -8
- package/next.config.mjs +0 -67
- package/postcss.config.mjs +0 -8
- package/public/file.svg +0 -1
- package/public/globe.svg +0 -1
- package/public/next.svg +0 -1
- package/public/vercel.svg +0 -1
- package/public/window.svg +0 -1
- package/scripts/apply_migration.ts +0 -118
- package/scripts/init_users.ts +0 -378
- package/src/app/api/hazo_auth/auth/upload_profile_picture/route.ts +0 -268
- package/src/app/api/hazo_auth/change_password/route.ts +0 -132
- package/src/app/api/hazo_auth/forgot_password/route.ts +0 -107
- package/src/app/api/hazo_auth/get_auth/route.ts +0 -89
- package/src/app/api/hazo_auth/invalidate_cache/route.ts +0 -139
- package/src/app/api/hazo_auth/library_photos/route.ts +0 -73
- package/src/app/api/hazo_auth/login/route.ts +0 -181
- package/src/app/api/hazo_auth/logout/route.ts +0 -89
- package/src/app/api/hazo_auth/me/route.ts +0 -47
- package/src/app/api/hazo_auth/profile_picture/[filename]/route.ts +0 -67
- package/src/app/api/hazo_auth/register/route.ts +0 -109
- package/src/app/api/hazo_auth/remove_profile_picture/route.ts +0 -86
- package/src/app/api/hazo_auth/resend_verification/route.ts +0 -108
- package/src/app/api/hazo_auth/reset_password/route.ts +0 -107
- package/src/app/api/hazo_auth/update_user/route.ts +0 -126
- package/src/app/api/hazo_auth/upload_profile_picture/route.ts +0 -268
- package/src/app/api/hazo_auth/user_management/permissions/route.ts +0 -367
- package/src/app/api/hazo_auth/user_management/roles/route.ts +0 -442
- package/src/app/api/hazo_auth/user_management/users/roles/route.ts +0 -367
- package/src/app/api/hazo_auth/user_management/users/route.ts +0 -239
- package/src/app/api/hazo_auth/validate_reset_token/route.ts +0 -83
- package/src/app/api/hazo_auth/verify_email/route.ts +0 -88
- package/src/app/api/migrations/apply/route.ts +0 -91
- package/src/app/favicon.ico +0 -0
- package/src/app/fonts/GeistMonoVF.woff +0 -0
- package/src/app/fonts/GeistVF.woff +0 -0
- package/src/app/globals.css +0 -89
- package/src/app/hazo_auth/forgot_password/forgot_password_page_client.tsx +0 -60
- package/src/app/hazo_auth/forgot_password/page.tsx +0 -24
- package/src/app/hazo_auth/login/login_page_client.tsx +0 -86
- package/src/app/hazo_auth/login/page.tsx +0 -38
- package/src/app/hazo_auth/my_settings/my_settings_page_client.tsx +0 -120
- package/src/app/hazo_auth/my_settings/page.tsx +0 -40
- package/src/app/hazo_auth/register/page.tsx +0 -36
- package/src/app/hazo_auth/register/register_page_client.tsx +0 -81
- package/src/app/hazo_auth/reset_password/page.tsx +0 -29
- package/src/app/hazo_auth/reset_password/reset_password_page_client.tsx +0 -81
- package/src/app/hazo_auth/user_management/page.tsx +0 -14
- package/src/app/hazo_auth/user_management/user_management_page_client.tsx +0 -16
- package/src/app/hazo_auth/verify_email/page.tsx +0 -24
- package/src/app/hazo_auth/verify_email/verify_email_page_client.tsx +0 -60
- package/src/app/hazo_connect/api/sqlite/data/route.ts +0 -203
- package/src/app/hazo_connect/api/sqlite/schema/route.ts +0 -45
- package/src/app/hazo_connect/api/sqlite/tables/route.ts +0 -36
- package/src/app/hazo_connect/sqlite_admin/page.tsx +0 -51
- package/src/app/hazo_connect/sqlite_admin/sqlite-admin-client.tsx +0 -984
- package/src/app/layout.tsx +0 -43
- package/src/app/page.tsx +0 -170
- package/src/components/index.ts +0 -7
- package/src/components/layouts/email_verification/config/email_verification_field_config.ts +0 -86
- package/src/components/layouts/email_verification/hooks/use_email_verification.ts +0 -297
- package/src/components/layouts/email_verification/index.tsx +0 -297
- package/src/components/layouts/forgot_password/config/forgot_password_field_config.ts +0 -58
- package/src/components/layouts/forgot_password/hooks/use_forgot_password_form.ts +0 -179
- package/src/components/layouts/forgot_password/index.tsx +0 -168
- package/src/components/layouts/index.ts +0 -26
- package/src/components/layouts/login/config/login_field_config.ts +0 -67
- package/src/components/layouts/login/hooks/use_login_form.ts +0 -286
- package/src/components/layouts/login/index.tsx +0 -252
- package/src/components/layouts/my_settings/components/editable_field.tsx +0 -177
- package/src/components/layouts/my_settings/components/password_change_dialog.tsx +0 -301
- package/src/components/layouts/my_settings/components/profile_picture_dialog.tsx +0 -385
- package/src/components/layouts/my_settings/components/profile_picture_display.tsx +0 -66
- package/src/components/layouts/my_settings/components/profile_picture_gravatar_tab.tsx +0 -143
- package/src/components/layouts/my_settings/components/profile_picture_library_tab.tsx +0 -311
- package/src/components/layouts/my_settings/components/profile_picture_upload_tab.tsx +0 -341
- package/src/components/layouts/my_settings/config/my_settings_field_config.ts +0 -61
- package/src/components/layouts/my_settings/hooks/use_my_settings.ts +0 -458
- package/src/components/layouts/my_settings/index.tsx +0 -351
- package/src/components/layouts/register/config/register_field_config.ts +0 -101
- package/src/components/layouts/register/hooks/use_register_form.ts +0 -275
- package/src/components/layouts/register/index.tsx +0 -226
- package/src/components/layouts/reset_password/config/reset_password_field_config.ts +0 -86
- package/src/components/layouts/reset_password/hooks/use_reset_password_form.ts +0 -276
- package/src/components/layouts/reset_password/index.tsx +0 -294
- package/src/components/layouts/shared/components/already_logged_in_guard.tsx +0 -95
- package/src/components/layouts/shared/components/auth_page_shell.tsx +0 -36
- package/src/components/layouts/shared/components/field_error_message.tsx +0 -29
- package/src/components/layouts/shared/components/form_action_buttons.tsx +0 -64
- package/src/components/layouts/shared/components/form_field_wrapper.tsx +0 -44
- package/src/components/layouts/shared/components/form_header.tsx +0 -36
- package/src/components/layouts/shared/components/logout_button.tsx +0 -76
- package/src/components/layouts/shared/components/password_field.tsx +0 -72
- package/src/components/layouts/shared/components/profile_pic_menu.tsx +0 -321
- package/src/components/layouts/shared/components/profile_pic_menu_wrapper.tsx +0 -40
- package/src/components/layouts/shared/components/sidebar_layout_wrapper.tsx +0 -214
- package/src/components/layouts/shared/components/standalone_layout_wrapper.tsx +0 -53
- package/src/components/layouts/shared/components/two_column_auth_layout.tsx +0 -44
- package/src/components/layouts/shared/components/unauthorized_guard.tsx +0 -78
- package/src/components/layouts/shared/components/visual_panel.tsx +0 -41
- package/src/components/layouts/shared/config/layout_customization.ts +0 -95
- package/src/components/layouts/shared/data/layout_data_client.ts +0 -19
- package/src/components/layouts/shared/hooks/use_auth_status.ts +0 -103
- package/src/components/layouts/shared/hooks/use_hazo_auth.ts +0 -158
- package/src/components/layouts/shared/index.ts +0 -34
- package/src/components/layouts/shared/utils/ip_address.ts +0 -37
- package/src/components/layouts/shared/utils/validation.ts +0 -66
- package/src/components/layouts/user_management/components/roles_matrix.tsx +0 -607
- package/src/components/layouts/user_management/index.tsx +0 -1295
- package/src/components/ui/alert-dialog.tsx +0 -141
- package/src/components/ui/avatar.tsx +0 -50
- package/src/components/ui/button.tsx +0 -57
- package/src/components/ui/checkbox.tsx +0 -30
- package/src/components/ui/dialog.tsx +0 -122
- package/src/components/ui/dropdown-menu.tsx +0 -201
- package/src/components/ui/hazo_ui_tooltip.tsx +0 -67
- package/src/components/ui/index.ts +0 -22
- package/src/components/ui/input.tsx +0 -22
- package/src/components/ui/label.tsx +0 -26
- package/src/components/ui/separator.tsx +0 -31
- package/src/components/ui/sheet.tsx +0 -139
- package/src/components/ui/sidebar.tsx +0 -773
- package/src/components/ui/skeleton.tsx +0 -15
- package/src/components/ui/sonner.tsx +0 -31
- package/src/components/ui/switch.tsx +0 -29
- package/src/components/ui/table.tsx +0 -120
- package/src/components/ui/tabs.tsx +0 -55
- package/src/components/ui/tooltip.tsx +0 -32
- package/src/components/ui/vertical-tabs.tsx +0 -59
- package/src/hooks/use-mobile.tsx +0 -19
- package/src/index.ts +0 -7
- package/src/lib/already_logged_in_config.server.ts +0 -46
- package/src/lib/app_logger.ts +0 -24
- package/src/lib/auth/auth_cache.ts +0 -220
- package/src/lib/auth/auth_rate_limiter.ts +0 -121
- package/src/lib/auth/auth_types.ts +0 -65
- package/src/lib/auth/auth_utils.server.ts +0 -196
- package/src/lib/auth/hazo_get_auth.server.ts +0 -333
- package/src/lib/auth/index.ts +0 -23
- package/src/lib/auth/server_auth.ts +0 -88
- package/src/lib/auth_utility_config.server.ts +0 -136
- package/src/lib/config/config_loader.server.ts +0 -164
- package/src/lib/email_verification_config.server.ts +0 -32
- package/src/lib/file_types_config.server.ts +0 -25
- package/src/lib/forgot_password_config.server.ts +0 -32
- package/src/lib/hazo_connect_instance.server.ts +0 -101
- package/src/lib/hazo_connect_setup.server.ts +0 -194
- package/src/lib/hazo_connect_setup.ts +0 -54
- package/src/lib/index.ts +0 -44
- package/src/lib/login_config.server.ts +0 -71
- package/src/lib/messages_config.server.ts +0 -45
- package/src/lib/migrations/apply_migration.ts +0 -105
- package/src/lib/my_settings_config.server.ts +0 -135
- package/src/lib/password_requirements_config.server.ts +0 -39
- package/src/lib/profile_pic_menu_config.server.ts +0 -138
- package/src/lib/profile_picture_config.server.ts +0 -56
- package/src/lib/register_config.server.ts +0 -73
- package/src/lib/reset_password_config.server.ts +0 -75
- package/src/lib/services/email_service.ts +0 -581
- package/src/lib/services/email_verification_service.ts +0 -270
- package/src/lib/services/index.ts +0 -15
- package/src/lib/services/login_service.ts +0 -134
- package/src/lib/services/password_change_service.ts +0 -154
- package/src/lib/services/password_reset_service.ts +0 -405
- package/src/lib/services/profile_picture_remove_service.ts +0 -120
- package/src/lib/services/profile_picture_service.ts +0 -215
- package/src/lib/services/profile_picture_source_mapper.ts +0 -62
- package/src/lib/services/registration_service.ts +0 -184
- package/src/lib/services/token_service.ts +0 -240
- package/src/lib/services/user_profiles_service.ts +0 -143
- package/src/lib/services/user_update_service.ts +0 -141
- package/src/lib/ui_shell_config.server.ts +0 -73
- package/src/lib/ui_sizes_config.server.ts +0 -37
- package/src/lib/user_fields_config.server.ts +0 -31
- package/src/lib/user_management_config.server.ts +0 -39
- package/src/lib/utils/api_route_helpers.ts +0 -60
- package/src/lib/utils/error_sanitizer.ts +0 -75
- package/src/lib/utils.ts +0 -11
- package/src/middleware.ts +0 -94
- package/src/routes/index.ts +0 -34
- package/src/server/config/config_loader.ts +0 -496
- package/src/server/index.ts +0 -38
- package/src/server/logging/logger_service.ts +0 -56
- package/src/server/routes/root_router.ts +0 -16
- package/src/server/server.ts +0 -28
- package/src/server/types/app_types.ts +0 -74
- package/src/server/types/express.d.ts +0 -16
- package/src/stories/email_verification_layout.stories.tsx +0 -137
- package/src/stories/forgot_password_layout.stories.tsx +0 -85
- package/src/stories/login_layout.stories.tsx +0 -85
- package/src/stories/project_overview.stories.tsx +0 -33
- package/src/stories/register_layout.stories.tsx +0 -107
- package/tailwind.config.ts +0 -77
- package/tsconfig.build.json +0 -36
- package/tsconfig.json +0 -28
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
// file_description: Simple in-memory rate limiter for hazo_get_auth API endpoint
|
|
2
|
-
// section: types
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Rate limit entry structure
|
|
6
|
-
*/
|
|
7
|
-
type RateLimitEntry = {
|
|
8
|
-
count: number;
|
|
9
|
-
window_start: number; // Unix timestamp in milliseconds
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Simple in-memory rate limiter
|
|
14
|
-
* Tracks request counts per key within a time window
|
|
15
|
-
*/
|
|
16
|
-
class RateLimiter {
|
|
17
|
-
private limits: Map<string, RateLimitEntry>;
|
|
18
|
-
private window_ms: number; // 1 minute = 60000ms
|
|
19
|
-
|
|
20
|
-
constructor() {
|
|
21
|
-
this.limits = new Map();
|
|
22
|
-
this.window_ms = 60 * 1000; // 1 minute window
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Checks if a request should be allowed
|
|
27
|
-
* @param key - Rate limit key (e.g., "user:123" or "ip:192.168.1.1")
|
|
28
|
-
* @param max_requests - Maximum requests allowed per window
|
|
29
|
-
* @returns true if allowed, false if rate limited
|
|
30
|
-
*/
|
|
31
|
-
check(key: string, max_requests: number): boolean {
|
|
32
|
-
const now = Date.now();
|
|
33
|
-
const entry = this.limits.get(key);
|
|
34
|
-
|
|
35
|
-
if (!entry) {
|
|
36
|
-
// First request for this key
|
|
37
|
-
this.limits.set(key, {
|
|
38
|
-
count: 1,
|
|
39
|
-
window_start: now,
|
|
40
|
-
});
|
|
41
|
-
return true;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
// Check if window has expired
|
|
45
|
-
if (now - entry.window_start >= this.window_ms) {
|
|
46
|
-
// Reset window
|
|
47
|
-
this.limits.set(key, {
|
|
48
|
-
count: 1,
|
|
49
|
-
window_start: now,
|
|
50
|
-
});
|
|
51
|
-
return true;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Check if limit exceeded
|
|
55
|
-
if (entry.count >= max_requests) {
|
|
56
|
-
return false;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Increment count
|
|
60
|
-
entry.count++;
|
|
61
|
-
return true;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Cleans up old entries (call periodically to prevent memory leak)
|
|
66
|
-
* Removes entries older than 2 windows
|
|
67
|
-
*/
|
|
68
|
-
cleanup(): void {
|
|
69
|
-
const now = Date.now();
|
|
70
|
-
const cutoff = now - 2 * this.window_ms;
|
|
71
|
-
|
|
72
|
-
const keys_to_delete: string[] = [];
|
|
73
|
-
for (const [key, entry] of this.limits.entries()) {
|
|
74
|
-
if (entry.window_start < cutoff) {
|
|
75
|
-
keys_to_delete.push(key);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
for (const key of keys_to_delete) {
|
|
80
|
-
this.limits.delete(key);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Gets rate limit statistics
|
|
86
|
-
* @returns Object with current limit entries count
|
|
87
|
-
*/
|
|
88
|
-
get_stats(): { active_limits: number } {
|
|
89
|
-
return {
|
|
90
|
-
active_limits: this.limits.size,
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// section: singleton
|
|
96
|
-
// Global rate limiter instance
|
|
97
|
-
let rate_limiter_instance: RateLimiter | null = null;
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Gets or creates the global rate limiter instance
|
|
101
|
-
* @returns Rate limiter instance
|
|
102
|
-
*/
|
|
103
|
-
export function get_rate_limiter(): RateLimiter {
|
|
104
|
-
if (!rate_limiter_instance) {
|
|
105
|
-
rate_limiter_instance = new RateLimiter();
|
|
106
|
-
|
|
107
|
-
// Cleanup old entries every 5 minutes
|
|
108
|
-
setInterval(() => {
|
|
109
|
-
rate_limiter_instance?.cleanup();
|
|
110
|
-
}, 5 * 60 * 1000);
|
|
111
|
-
}
|
|
112
|
-
return rate_limiter_instance;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Resets the global rate limiter instance (useful for testing)
|
|
117
|
-
*/
|
|
118
|
-
export function reset_rate_limiter(): void {
|
|
119
|
-
rate_limiter_instance = null;
|
|
120
|
-
}
|
|
121
|
-
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
// file_description: Type definitions and error classes for hazo_get_auth utility
|
|
2
|
-
// section: types
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* User data structure returned by hazo_get_auth
|
|
6
|
-
*/
|
|
7
|
-
export type HazoAuthUser = {
|
|
8
|
-
id: string;
|
|
9
|
-
name: string | null;
|
|
10
|
-
email_address: string;
|
|
11
|
-
is_active: boolean;
|
|
12
|
-
profile_picture_url: string | null;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Result type for hazo_get_auth function
|
|
17
|
-
* Returns authenticated state with user data and permissions, or unauthenticated state
|
|
18
|
-
*/
|
|
19
|
-
export type HazoAuthResult =
|
|
20
|
-
| {
|
|
21
|
-
authenticated: true;
|
|
22
|
-
user: HazoAuthUser;
|
|
23
|
-
permissions: string[];
|
|
24
|
-
permission_ok: boolean;
|
|
25
|
-
missing_permissions?: string[];
|
|
26
|
-
}
|
|
27
|
-
| {
|
|
28
|
-
authenticated: false;
|
|
29
|
-
user: null;
|
|
30
|
-
permissions: [];
|
|
31
|
-
permission_ok: false;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Options for hazo_get_auth function
|
|
36
|
-
*/
|
|
37
|
-
export type HazoAuthOptions = {
|
|
38
|
-
/**
|
|
39
|
-
* Array of required permissions to check
|
|
40
|
-
* If provided, permission_ok will be set based on whether user has all required permissions
|
|
41
|
-
*/
|
|
42
|
-
required_permissions?: string[];
|
|
43
|
-
/**
|
|
44
|
-
* If true, throws PermissionError when user lacks required permissions
|
|
45
|
-
* If false (default), returns permission_ok: false without throwing
|
|
46
|
-
*/
|
|
47
|
-
strict?: boolean;
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Custom error class for permission denials
|
|
52
|
-
* Includes technical and user-friendly error messages
|
|
53
|
-
*/
|
|
54
|
-
export class PermissionError extends Error {
|
|
55
|
-
constructor(
|
|
56
|
-
public missing_permissions: string[],
|
|
57
|
-
public user_permissions: string[],
|
|
58
|
-
public required_permissions: string[],
|
|
59
|
-
public user_friendly_message?: string,
|
|
60
|
-
) {
|
|
61
|
-
super(`Missing permissions: ${missing_permissions.join(", ")}`);
|
|
62
|
-
this.name = "PermissionError";
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
// file_description: server-side authentication utilities for checking login status in API routes
|
|
2
|
-
// section: imports
|
|
3
|
-
import { NextRequest, NextResponse } from "next/server";
|
|
4
|
-
import { get_hazo_connect_instance } from "../hazo_connect_instance.server";
|
|
5
|
-
import { createCrudService } from "hazo_connect/server";
|
|
6
|
-
import { map_db_source_to_ui } from "../services/profile_picture_source_mapper";
|
|
7
|
-
|
|
8
|
-
// section: types
|
|
9
|
-
export type AuthUser = {
|
|
10
|
-
authenticated: true;
|
|
11
|
-
user_id: string;
|
|
12
|
-
email: string;
|
|
13
|
-
name?: string;
|
|
14
|
-
email_verified: boolean;
|
|
15
|
-
is_active: boolean;
|
|
16
|
-
last_logon?: string;
|
|
17
|
-
profile_picture_url?: string;
|
|
18
|
-
profile_source?: "upload" | "library" | "gravatar" | "custom";
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
export type AuthResult =
|
|
22
|
-
| AuthUser
|
|
23
|
-
| { authenticated: false };
|
|
24
|
-
|
|
25
|
-
// section: helpers
|
|
26
|
-
/**
|
|
27
|
-
* Clears authentication cookies from response
|
|
28
|
-
* @param response - NextResponse object to clear cookies from
|
|
29
|
-
* @returns The response with cleared cookies
|
|
30
|
-
*/
|
|
31
|
-
function clear_auth_cookies(response: NextResponse): NextResponse {
|
|
32
|
-
response.cookies.set("hazo_auth_user_email", "", {
|
|
33
|
-
expires: new Date(0),
|
|
34
|
-
path: "/",
|
|
35
|
-
});
|
|
36
|
-
response.cookies.set("hazo_auth_user_id", "", {
|
|
37
|
-
expires: new Date(0),
|
|
38
|
-
path: "/",
|
|
39
|
-
});
|
|
40
|
-
return response;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// section: functions
|
|
44
|
-
/**
|
|
45
|
-
* Checks if a user is authenticated from request cookies
|
|
46
|
-
* Validates user exists, is active, and cookies match
|
|
47
|
-
* @param request - NextRequest object
|
|
48
|
-
* @returns AuthResult with user info or authenticated: false
|
|
49
|
-
*/
|
|
50
|
-
export async function get_authenticated_user(request: NextRequest): Promise<AuthResult> {
|
|
51
|
-
const user_id = request.cookies.get("hazo_auth_user_id")?.value;
|
|
52
|
-
const user_email = request.cookies.get("hazo_auth_user_email")?.value;
|
|
53
|
-
|
|
54
|
-
if (!user_id || !user_email) {
|
|
55
|
-
return { authenticated: false };
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
try {
|
|
59
|
-
const hazoConnect = get_hazo_connect_instance();
|
|
60
|
-
const users_service = createCrudService(hazoConnect, "hazo_users");
|
|
61
|
-
|
|
62
|
-
const users = await users_service.findBy({
|
|
63
|
-
id: user_id,
|
|
64
|
-
email_address: user_email,
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
if (!Array.isArray(users) || users.length === 0) {
|
|
68
|
-
return { authenticated: false };
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const user = users[0];
|
|
72
|
-
|
|
73
|
-
// Check if user is active
|
|
74
|
-
if (user.is_active === false) {
|
|
75
|
-
return { authenticated: false };
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Map database profile_source to UI representation
|
|
79
|
-
const profile_source_db = user.profile_source as string | null | undefined;
|
|
80
|
-
const profile_source_ui = profile_source_db ? map_db_source_to_ui(profile_source_db) : undefined;
|
|
81
|
-
|
|
82
|
-
return {
|
|
83
|
-
authenticated: true,
|
|
84
|
-
user_id: user.id as string,
|
|
85
|
-
email: user.email_address as string,
|
|
86
|
-
name: (user.name as string | null | undefined) || undefined,
|
|
87
|
-
email_verified: user.email_verified === true,
|
|
88
|
-
is_active: user.is_active === true,
|
|
89
|
-
last_logon: (user.last_logon as string | null | undefined) || undefined,
|
|
90
|
-
profile_picture_url: (user.profile_picture_url as string | null | undefined) || undefined,
|
|
91
|
-
profile_source: profile_source_ui,
|
|
92
|
-
};
|
|
93
|
-
} catch (error) {
|
|
94
|
-
return { authenticated: false };
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Checks if user is authenticated (simple boolean check)
|
|
100
|
-
* @param request - NextRequest object
|
|
101
|
-
* @returns true if authenticated, false otherwise
|
|
102
|
-
*/
|
|
103
|
-
export async function is_authenticated(request: NextRequest): Promise<boolean> {
|
|
104
|
-
const result = await get_authenticated_user(request);
|
|
105
|
-
return result.authenticated;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Requires authentication - throws error if not authenticated
|
|
110
|
-
* Use in API routes that require authentication
|
|
111
|
-
* @param request - NextRequest object
|
|
112
|
-
* @returns AuthUser (never returns authenticated: false, throws instead)
|
|
113
|
-
* @throws Error if not authenticated
|
|
114
|
-
*/
|
|
115
|
-
export async function require_auth(request: NextRequest): Promise<AuthUser> {
|
|
116
|
-
const result = await get_authenticated_user(request);
|
|
117
|
-
|
|
118
|
-
if (!result.authenticated) {
|
|
119
|
-
throw new Error("Authentication required");
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
return result;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* Gets authenticated user and returns response with cleared cookies if invalid
|
|
127
|
-
* Useful for /api/auth/me endpoint that needs to clear cookies on invalid auth
|
|
128
|
-
* @param request - NextRequest object
|
|
129
|
-
* @returns Object with auth_result and response (with cleared cookies if invalid)
|
|
130
|
-
*/
|
|
131
|
-
export async function get_authenticated_user_with_response(request: NextRequest): Promise<{
|
|
132
|
-
auth_result: AuthResult;
|
|
133
|
-
response?: NextResponse;
|
|
134
|
-
}> {
|
|
135
|
-
const user_id = request.cookies.get("hazo_auth_user_id")?.value;
|
|
136
|
-
const user_email = request.cookies.get("hazo_auth_user_email")?.value;
|
|
137
|
-
|
|
138
|
-
if (!user_id || !user_email) {
|
|
139
|
-
return { auth_result: { authenticated: false } };
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
try {
|
|
143
|
-
const hazoConnect = get_hazo_connect_instance();
|
|
144
|
-
const users_service = createCrudService(hazoConnect, "hazo_users");
|
|
145
|
-
|
|
146
|
-
const users = await users_service.findBy({
|
|
147
|
-
id: user_id,
|
|
148
|
-
email_address: user_email,
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
if (!Array.isArray(users) || users.length === 0) {
|
|
152
|
-
// User not found - clear cookies
|
|
153
|
-
const response = NextResponse.json(
|
|
154
|
-
{ authenticated: false },
|
|
155
|
-
{ status: 200 }
|
|
156
|
-
);
|
|
157
|
-
clear_auth_cookies(response);
|
|
158
|
-
return { auth_result: { authenticated: false }, response };
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const user = users[0];
|
|
162
|
-
|
|
163
|
-
// Check if user is still active
|
|
164
|
-
if (user.is_active === false) {
|
|
165
|
-
// User is inactive - clear cookies
|
|
166
|
-
const response = NextResponse.json(
|
|
167
|
-
{ authenticated: false },
|
|
168
|
-
{ status: 200 }
|
|
169
|
-
);
|
|
170
|
-
clear_auth_cookies(response);
|
|
171
|
-
return { auth_result: { authenticated: false }, response };
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Map database profile_source to UI representation
|
|
175
|
-
const profile_source_db = user.profile_source as string | null | undefined;
|
|
176
|
-
const profile_source_ui = profile_source_db ? map_db_source_to_ui(profile_source_db) : undefined;
|
|
177
|
-
|
|
178
|
-
return {
|
|
179
|
-
auth_result: {
|
|
180
|
-
authenticated: true,
|
|
181
|
-
user_id: user.id as string,
|
|
182
|
-
email: user.email_address as string,
|
|
183
|
-
name: (user.name as string | null | undefined) || undefined,
|
|
184
|
-
email_verified: user.email_verified === true,
|
|
185
|
-
is_active: user.is_active === true,
|
|
186
|
-
last_logon: (user.last_logon as string | null | undefined) || undefined,
|
|
187
|
-
profile_picture_url: (user.profile_picture_url as string | null | undefined) || undefined,
|
|
188
|
-
profile_source: profile_source_ui,
|
|
189
|
-
},
|
|
190
|
-
};
|
|
191
|
-
} catch (error) {
|
|
192
|
-
// On error, assume not authenticated
|
|
193
|
-
return { auth_result: { authenticated: false } };
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
@@ -1,333 +0,0 @@
|
|
|
1
|
-
// file_description: server-side implementation of hazo_get_auth utility for API routes
|
|
2
|
-
// section: imports
|
|
3
|
-
import { NextRequest } from "next/server";
|
|
4
|
-
import { get_hazo_connect_instance } from "../hazo_connect_instance.server";
|
|
5
|
-
import { createCrudService } from "hazo_connect/server";
|
|
6
|
-
import { create_app_logger } from "../app_logger";
|
|
7
|
-
import { get_filename, get_line_number } from "../utils/api_route_helpers";
|
|
8
|
-
import type { HazoAuthResult, HazoAuthUser, HazoAuthOptions } from "./auth_types";
|
|
9
|
-
import { PermissionError } from "./auth_types";
|
|
10
|
-
import { get_auth_cache } from "./auth_cache";
|
|
11
|
-
import { get_rate_limiter } from "./auth_rate_limiter";
|
|
12
|
-
import { get_auth_utility_config } from "../auth_utility_config.server";
|
|
13
|
-
|
|
14
|
-
// section: helpers
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Gets client IP address from request
|
|
18
|
-
* @param request - NextRequest object
|
|
19
|
-
* @returns IP address string
|
|
20
|
-
*/
|
|
21
|
-
function get_client_ip(request: NextRequest): string {
|
|
22
|
-
const forwarded = request.headers.get("x-forwarded-for");
|
|
23
|
-
if (forwarded) {
|
|
24
|
-
return forwarded.split(",")[0].trim();
|
|
25
|
-
}
|
|
26
|
-
const real_ip = request.headers.get("x-real-ip");
|
|
27
|
-
if (real_ip) {
|
|
28
|
-
return real_ip;
|
|
29
|
-
}
|
|
30
|
-
return "unknown";
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Fetches user data and permissions from database
|
|
35
|
-
* @param user_id - User ID
|
|
36
|
-
* @returns Object with user, permissions, and role_ids
|
|
37
|
-
*/
|
|
38
|
-
async function fetch_user_data_from_db(user_id: string): Promise<{
|
|
39
|
-
user: HazoAuthUser;
|
|
40
|
-
permissions: string[];
|
|
41
|
-
role_ids: number[];
|
|
42
|
-
}> {
|
|
43
|
-
const hazoConnect = get_hazo_connect_instance();
|
|
44
|
-
const users_service = createCrudService(hazoConnect, "hazo_users");
|
|
45
|
-
const user_roles_service = createCrudService(hazoConnect, "hazo_user_roles");
|
|
46
|
-
const role_permissions_service = createCrudService(
|
|
47
|
-
hazoConnect,
|
|
48
|
-
"hazo_role_permissions",
|
|
49
|
-
);
|
|
50
|
-
const permissions_service = createCrudService(
|
|
51
|
-
hazoConnect,
|
|
52
|
-
"hazo_permissions",
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
// Fetch user
|
|
56
|
-
const users = await users_service.findBy({ id: user_id });
|
|
57
|
-
if (!Array.isArray(users) || users.length === 0) {
|
|
58
|
-
throw new Error("User not found");
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const user_db = users[0];
|
|
62
|
-
|
|
63
|
-
// Check if user is active
|
|
64
|
-
if (user_db.is_active === false) {
|
|
65
|
-
throw new Error("User is inactive");
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Build user object
|
|
69
|
-
const user: HazoAuthUser = {
|
|
70
|
-
id: user_db.id as string,
|
|
71
|
-
name: (user_db.name as string | null) || null,
|
|
72
|
-
email_address: user_db.email_address as string,
|
|
73
|
-
is_active: user_db.is_active === true,
|
|
74
|
-
profile_picture_url:
|
|
75
|
-
(user_db.profile_picture_url as string | null) || null,
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
// Fetch user roles
|
|
79
|
-
const user_roles = await user_roles_service.findBy({ user_id });
|
|
80
|
-
const role_ids: number[] = [];
|
|
81
|
-
if (Array.isArray(user_roles)) {
|
|
82
|
-
for (const ur of user_roles) {
|
|
83
|
-
const role_id = ur.role_id as number | undefined;
|
|
84
|
-
if (role_id !== undefined) {
|
|
85
|
-
role_ids.push(role_id);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Fetch role permissions
|
|
91
|
-
const permissions_set = new Set<string>();
|
|
92
|
-
if (role_ids.length > 0) {
|
|
93
|
-
const role_permissions = await role_permissions_service.findBy({});
|
|
94
|
-
if (Array.isArray(role_permissions)) {
|
|
95
|
-
// Filter role_permissions for user's roles
|
|
96
|
-
const user_role_permissions = role_permissions.filter((rp) =>
|
|
97
|
-
role_ids.includes(rp.role_id as number),
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
// Get permission IDs
|
|
101
|
-
const permission_ids = new Set<number>();
|
|
102
|
-
for (const rp of user_role_permissions) {
|
|
103
|
-
const perm_id = rp.permission_id as number | undefined;
|
|
104
|
-
if (perm_id !== undefined) {
|
|
105
|
-
permission_ids.add(perm_id);
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Fetch permission names
|
|
110
|
-
if (permission_ids.size > 0) {
|
|
111
|
-
const permissions = await permissions_service.findBy({});
|
|
112
|
-
if (Array.isArray(permissions)) {
|
|
113
|
-
for (const perm of permissions) {
|
|
114
|
-
const perm_id = perm.id as number | undefined;
|
|
115
|
-
if (perm_id !== undefined && permission_ids.has(perm_id)) {
|
|
116
|
-
const perm_name = perm.permission_name as string | undefined;
|
|
117
|
-
if (perm_name) {
|
|
118
|
-
permissions_set.add(perm_name);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const permissions = Array.from(permissions_set);
|
|
128
|
-
|
|
129
|
-
return { user, permissions, role_ids };
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Checks if user has required permissions
|
|
134
|
-
* @param user_permissions - User's permissions
|
|
135
|
-
* @param required_permissions - Required permissions
|
|
136
|
-
* @returns Object with permission_ok and missing_permissions
|
|
137
|
-
*/
|
|
138
|
-
function check_permissions(
|
|
139
|
-
user_permissions: string[],
|
|
140
|
-
required_permissions: string[],
|
|
141
|
-
): { permission_ok: boolean; missing_permissions: string[] } {
|
|
142
|
-
const user_perms_set = new Set(user_permissions);
|
|
143
|
-
const missing = required_permissions.filter(
|
|
144
|
-
(perm) => !user_perms_set.has(perm),
|
|
145
|
-
);
|
|
146
|
-
|
|
147
|
-
return {
|
|
148
|
-
permission_ok: missing.length === 0,
|
|
149
|
-
missing_permissions: missing,
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Gets user-friendly error message for missing permissions
|
|
155
|
-
* @param missing_permissions - Array of missing permission names
|
|
156
|
-
* @param config - Auth utility config
|
|
157
|
-
* @returns User-friendly message or undefined
|
|
158
|
-
*/
|
|
159
|
-
function get_friendly_error_message(
|
|
160
|
-
missing_permissions: string[],
|
|
161
|
-
config: ReturnType<typeof get_auth_utility_config>,
|
|
162
|
-
): string | undefined {
|
|
163
|
-
if (!config.enable_friendly_error_messages) {
|
|
164
|
-
return undefined;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Try to get messages for each missing permission
|
|
168
|
-
const messages: string[] = [];
|
|
169
|
-
for (const perm of missing_permissions) {
|
|
170
|
-
const message = config.permission_error_messages.get(perm);
|
|
171
|
-
if (message) {
|
|
172
|
-
messages.push(message);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
if (messages.length > 0) {
|
|
177
|
-
return messages.join(". ");
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// Default message if no specific mapping
|
|
181
|
-
return "You don't have the required permissions to perform this action. Please contact your administrator.";
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// section: main_function
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Main hazo_get_auth function for server-side use in API routes
|
|
188
|
-
* Returns user details, permissions, and checks required permissions
|
|
189
|
-
* @param request - NextRequest object
|
|
190
|
-
* @param options - Optional parameters for permission checking
|
|
191
|
-
* @returns HazoAuthResult with user data and permissions
|
|
192
|
-
* @throws PermissionError if strict mode and permissions are missing
|
|
193
|
-
*/
|
|
194
|
-
export async function hazo_get_auth(
|
|
195
|
-
request: NextRequest,
|
|
196
|
-
options?: HazoAuthOptions,
|
|
197
|
-
): Promise<HazoAuthResult> {
|
|
198
|
-
const logger = create_app_logger();
|
|
199
|
-
const config = get_auth_utility_config();
|
|
200
|
-
const cache = get_auth_cache(
|
|
201
|
-
config.cache_max_users,
|
|
202
|
-
config.cache_ttl_minutes,
|
|
203
|
-
config.cache_max_age_minutes,
|
|
204
|
-
);
|
|
205
|
-
const rate_limiter = get_rate_limiter();
|
|
206
|
-
|
|
207
|
-
// Fast path: Check for authentication cookies
|
|
208
|
-
const user_id = request.cookies.get("hazo_auth_user_id")?.value;
|
|
209
|
-
const user_email = request.cookies.get("hazo_auth_user_email")?.value;
|
|
210
|
-
|
|
211
|
-
if (!user_id || !user_email) {
|
|
212
|
-
// Unauthenticated - check rate limit by IP
|
|
213
|
-
const client_ip = get_client_ip(request);
|
|
214
|
-
const ip_key = `ip:${client_ip}`;
|
|
215
|
-
if (!rate_limiter.check(ip_key, config.rate_limit_per_ip)) {
|
|
216
|
-
logger.warn("auth_utility_rate_limit_exceeded_ip", {
|
|
217
|
-
filename: get_filename(),
|
|
218
|
-
line_number: get_line_number(),
|
|
219
|
-
ip: client_ip,
|
|
220
|
-
});
|
|
221
|
-
throw new Error("Rate limit exceeded. Please try again later.");
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
return {
|
|
225
|
-
authenticated: false,
|
|
226
|
-
user: null,
|
|
227
|
-
permissions: [],
|
|
228
|
-
permission_ok: false,
|
|
229
|
-
};
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// Authenticated - check rate limit by user
|
|
233
|
-
const user_key = `user:${user_id}`;
|
|
234
|
-
if (!rate_limiter.check(user_key, config.rate_limit_per_user)) {
|
|
235
|
-
logger.warn("auth_utility_rate_limit_exceeded_user", {
|
|
236
|
-
filename: get_filename(),
|
|
237
|
-
line_number: get_line_number(),
|
|
238
|
-
user_id,
|
|
239
|
-
});
|
|
240
|
-
throw new Error("Rate limit exceeded. Please try again later.");
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// Check cache
|
|
244
|
-
let cached_entry = cache.get(user_id);
|
|
245
|
-
let user: HazoAuthUser;
|
|
246
|
-
let permissions: string[];
|
|
247
|
-
let role_ids: number[];
|
|
248
|
-
|
|
249
|
-
if (cached_entry) {
|
|
250
|
-
// Cache hit
|
|
251
|
-
user = cached_entry.user;
|
|
252
|
-
permissions = cached_entry.permissions;
|
|
253
|
-
role_ids = cached_entry.role_ids;
|
|
254
|
-
} else {
|
|
255
|
-
// Cache miss - fetch from database
|
|
256
|
-
try {
|
|
257
|
-
const user_data = await fetch_user_data_from_db(user_id);
|
|
258
|
-
user = user_data.user;
|
|
259
|
-
permissions = user_data.permissions;
|
|
260
|
-
role_ids = user_data.role_ids;
|
|
261
|
-
|
|
262
|
-
// Update cache
|
|
263
|
-
cache.set(user_id, user, permissions, role_ids);
|
|
264
|
-
} catch (error) {
|
|
265
|
-
const error_message =
|
|
266
|
-
error instanceof Error ? error.message : "Unknown error";
|
|
267
|
-
logger.error("auth_utility_fetch_user_failed", {
|
|
268
|
-
filename: get_filename(),
|
|
269
|
-
line_number: get_line_number(),
|
|
270
|
-
user_id,
|
|
271
|
-
error: error_message,
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
return {
|
|
275
|
-
authenticated: false,
|
|
276
|
-
user: null,
|
|
277
|
-
permissions: [],
|
|
278
|
-
permission_ok: false,
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
// Check permissions if required
|
|
284
|
-
let permission_ok = true;
|
|
285
|
-
let missing_permissions: string[] | undefined;
|
|
286
|
-
|
|
287
|
-
if (options?.required_permissions && options.required_permissions.length > 0) {
|
|
288
|
-
const check_result = check_permissions(
|
|
289
|
-
permissions,
|
|
290
|
-
options.required_permissions,
|
|
291
|
-
);
|
|
292
|
-
permission_ok = check_result.permission_ok;
|
|
293
|
-
missing_permissions = check_result.missing_permissions;
|
|
294
|
-
|
|
295
|
-
// Log permission denial if enabled
|
|
296
|
-
if (!permission_ok && config.log_permission_denials) {
|
|
297
|
-
const client_ip = get_client_ip(request);
|
|
298
|
-
logger.warn("auth_utility_permission_denied", {
|
|
299
|
-
filename: get_filename(),
|
|
300
|
-
line_number: get_line_number(),
|
|
301
|
-
user_id,
|
|
302
|
-
requested_permissions: options.required_permissions,
|
|
303
|
-
missing_permissions,
|
|
304
|
-
user_permissions: permissions,
|
|
305
|
-
ip: client_ip,
|
|
306
|
-
});
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// Throw error if strict mode
|
|
310
|
-
if (!permission_ok && options.strict) {
|
|
311
|
-
const friendly_message = get_friendly_error_message(
|
|
312
|
-
missing_permissions,
|
|
313
|
-
config,
|
|
314
|
-
);
|
|
315
|
-
|
|
316
|
-
throw new PermissionError(
|
|
317
|
-
missing_permissions,
|
|
318
|
-
permissions,
|
|
319
|
-
options.required_permissions,
|
|
320
|
-
friendly_message,
|
|
321
|
-
);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
return {
|
|
326
|
-
authenticated: true,
|
|
327
|
-
user,
|
|
328
|
-
permissions,
|
|
329
|
-
permission_ok,
|
|
330
|
-
missing_permissions,
|
|
331
|
-
};
|
|
332
|
-
}
|
|
333
|
-
|