@ackplus/nest-auth 0.1.51 → 1.1.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 +6 -513
- package/eslint.config.mjs +59 -0
- package/jest.config.ts +10 -0
- package/package.json +14 -44
- package/project.json +86 -0
- package/src/index.ts +30 -0
- package/src/lib/admin-console/admin-console.module.ts +62 -0
- package/src/lib/admin-console/controllers/admin-auth.controller.ts +339 -0
- package/src/lib/admin-console/controllers/admin-console.controller.ts +82 -0
- package/src/lib/admin-console/controllers/admin-permissions.controller.ts +180 -0
- package/src/lib/admin-console/controllers/admin-roles.controller.ts +89 -0
- package/src/lib/admin-console/controllers/admin-tenants.controller.ts +68 -0
- package/src/lib/admin-console/controllers/admin-users.controller.ts +379 -0
- package/src/lib/admin-console/decorators/current-admin.decorator.ts +9 -0
- package/src/lib/admin-console/dto/admin-permission.dto.ts +106 -0
- package/src/lib/admin-console/dto/admin-role.dto.ts +45 -0
- package/src/lib/admin-console/dto/admin-tenant.dto.ts +43 -0
- package/src/lib/admin-console/dto/admin-user.dto.ts +87 -0
- package/src/lib/admin-console/dto/create-dashboard-admin.dto.ts +34 -0
- package/src/lib/admin-console/dto/login.dto.ts +10 -0
- package/src/lib/admin-console/dto/reset-password.dto.ts +21 -0
- package/src/lib/admin-console/dto/setup-admin.dto.ts +23 -0
- package/src/lib/admin-console/dto/signup.dto.ts +51 -0
- package/src/lib/admin-console/entities/admin-user.entity.ts +74 -0
- package/src/lib/admin-console/guards/admin-session.guard.ts +47 -0
- package/src/lib/admin-console/services/admin-auth.service.ts +82 -0
- package/src/lib/admin-console/services/admin-console-config.service.ts +62 -0
- package/src/lib/admin-console/services/admin-session.service.ts +106 -0
- package/src/lib/admin-console/services/admin-user.service.ts +96 -0
- package/src/lib/admin-console/static/index.html +771 -0
- package/src/lib/auth/auth.module.ts +58 -0
- package/src/lib/auth/controllers/auth.controller.ts +393 -0
- package/src/lib/auth/controllers/mfa.controller.ts +200 -0
- package/src/lib/auth/dto/credentials/email-credentials.dto.ts +24 -0
- package/src/lib/auth/dto/credentials/phone-credentials.dto.ts +24 -0
- package/src/lib/auth/dto/credentials/social-credentials.dto.ts +15 -0
- package/src/lib/auth/dto/index.ts +1 -0
- package/src/lib/auth/dto/requests/change-password.request.dto.ts +34 -0
- package/src/lib/auth/dto/requests/forgot-password.request.dto.ts +30 -0
- package/src/lib/auth/dto/requests/initialize-admin.request.dto.ts +51 -0
- package/src/lib/auth/dto/requests/login.request.dto.ts +65 -0
- package/src/lib/auth/dto/requests/refresh-token.request.dto.ts +12 -0
- package/src/lib/auth/dto/requests/reset-password-with-token.request.dto.ts +22 -0
- package/src/lib/auth/dto/requests/reset-password.request.dto.ts +50 -0
- package/src/lib/auth/dto/requests/send-email-verification.request.dto.ts +12 -0
- package/src/lib/auth/dto/requests/send-mfa-code.request.dto.ts +19 -0
- package/src/lib/auth/dto/requests/signup.request.dto.ts +42 -0
- package/src/lib/auth/dto/requests/toggle-mfa.request.dto.ts +12 -0
- package/src/lib/auth/dto/requests/verify-2fa.request.dto.ts +24 -0
- package/src/lib/auth/dto/requests/verify-email.request.dto.ts +22 -0
- package/src/lib/auth/dto/requests/verify-forgot-password-otp-request-dto.ts +41 -0
- package/src/lib/auth/dto/requests/verify-totp-setup.request.dto.ts +22 -0
- package/src/lib/auth/dto/responses/auth-cookie.response.dto.ts +58 -0
- package/src/lib/auth/dto/responses/auth-success.response.dto.ts +58 -0
- package/src/lib/auth/dto/responses/auth.response.dto.ts +99 -0
- package/src/lib/auth/dto/responses/client-config.response.dto.ts +153 -0
- package/src/lib/auth/dto/responses/initialize-admin.response.dto.ts +22 -0
- package/src/lib/auth/dto/responses/mfa-code-response.dto.ts +27 -0
- package/src/lib/auth/dto/responses/mfa-status.response.dto.ts +89 -0
- package/src/lib/auth/dto/responses/verify-otp.response.dto.ts +9 -0
- package/src/lib/auth/entities/mfa-secret.entity.ts +33 -0
- package/src/lib/auth/entities/otp.entity.ts +33 -0
- package/src/lib/auth/events/{logged-out-all.event.d.ts → logged-out-all.event.ts} +6 -3
- package/src/lib/auth/events/{logged-out.event.d.ts → logged-out.event.ts} +5 -3
- package/src/lib/auth/events/{password-reset-requested.event.d.ts → password-reset-requested.event.ts} +6 -3
- package/src/lib/auth/events/{password-reset.event.d.ts → password-reset.event.ts} +6 -3
- package/src/lib/auth/events/{user-2fa-verified.event.d.ts → user-2fa-verified.event.ts} +6 -3
- package/src/lib/auth/events/{user-logged-in.event.d.ts → user-logged-in.event.ts} +7 -3
- package/src/lib/auth/events/{user-refresh-token.event.d.ts → user-refresh-token.event.ts} +6 -3
- package/src/lib/auth/events/{user-registered.event.d.ts → user-registered.event.ts} +7 -3
- package/src/lib/auth/guards/auth.guard.ts +386 -0
- package/src/lib/auth/{index.d.ts → index.ts} +28 -1
- package/src/lib/auth/interceptors/refresh-token.interceptor.ts +117 -0
- package/src/lib/auth/services/auth.service.ts +947 -0
- package/src/lib/auth/services/client-config.service.ts +157 -0
- package/src/lib/auth/services/cookie.service.ts +43 -0
- package/src/lib/auth/services/mfa.service.ts +391 -0
- package/src/lib/auth.constants.ts +63 -0
- package/src/lib/core/core.module.ts +50 -0
- package/src/lib/core/decorators/auth.decorator.ts +38 -0
- package/src/lib/core/decorators/permissions.decorator.ts +17 -0
- package/src/lib/core/decorators/public.decorator.ts +33 -0
- package/src/lib/core/decorators/role.decorator.ts +12 -0
- package/src/lib/core/decorators/skip-mfa.decorator.ts +4 -0
- package/src/lib/core/dto/message.response.dto.ts +6 -0
- package/src/lib/core/{entities.d.ts → entities.ts} +18 -1
- package/src/lib/core/{index.d.ts → index.ts} +17 -0
- package/src/lib/core/interfaces/auth-module-options.interface.ts +211 -0
- package/src/lib/core/interfaces/mfa-options.interface.ts +46 -0
- package/src/lib/core/interfaces/otp.interface.ts +6 -0
- package/src/lib/core/interfaces/session-options.interface.ts +19 -0
- package/src/lib/core/interfaces/{token-payload.interface.d.ts → token-payload.interface.ts} +4 -1
- package/src/lib/core/providers/apple-auth.provider.ts +61 -0
- package/src/lib/core/providers/base-auth.provider.ts +74 -0
- package/src/lib/core/providers/email-auth.provider.ts +71 -0
- package/src/lib/core/providers/facebook-auth.provider.ts +55 -0
- package/src/lib/core/providers/github-auth.provider.ts +79 -0
- package/src/lib/core/providers/google-auth.provider.ts +61 -0
- package/src/lib/core/providers/jwt-auth.provider.ts +50 -0
- package/src/lib/core/providers/phone-auth.provider.ts +45 -0
- package/src/lib/core/services/auth-config.service.ts +184 -0
- package/src/lib/core/services/auth-provider-registry.service.ts +93 -0
- package/src/lib/core/services/{debug-logger.service.js → debug-logger.service.ts} +92 -59
- package/src/lib/core/services/initialization.service.ts +29 -0
- package/src/lib/core/services/jwt.service.ts +137 -0
- package/src/lib/nest-auth.module.ts +152 -0
- package/src/lib/permission/entities/permission.entity.ts +56 -0
- package/src/lib/permission/index.ts +4 -0
- package/src/lib/permission/permission.module.ts +14 -0
- package/src/lib/permission/services/permission.service.ts +233 -0
- package/src/lib/request-context/index.ts +2 -0
- package/src/lib/request-context/request-context.middleware.ts +13 -0
- package/src/lib/request-context/{request-context.js → request-context.ts} +51 -27
- package/src/lib/role/entities/role.entity.ts +103 -0
- package/src/lib/role/{index.d.ts → index.ts} +2 -0
- package/src/lib/role/role.module.ts +15 -0
- package/src/lib/role/services/{role.service.js → role.service.ts} +117 -52
- package/src/lib/session/entities/session.entity.ts +54 -0
- package/src/lib/session/index.ts +20 -0
- package/src/lib/session/interfaces/session-repository.interface.ts +58 -0
- package/src/lib/session/repositories/base-session.repository.ts +74 -0
- package/src/lib/session/repositories/memory-session.repository.ts +153 -0
- package/src/lib/session/repositories/redis-session.repository.ts +171 -0
- package/src/lib/session/repositories/typeorm-session.repository.ts +86 -0
- package/src/lib/session/services/session-manager.service.ts +261 -0
- package/src/lib/session/session.module.ts +102 -0
- package/src/lib/session/utils/session.util.ts +166 -0
- package/src/lib/tenant/entities/tenant.entity.ts +40 -0
- package/src/lib/tenant/events/tenant-created.event.ts +9 -0
- package/src/lib/tenant/events/tenant-deleted.event.ts +11 -0
- package/src/lib/tenant/events/{tenant-updated.event.d.ts → tenant-updated.event.ts} +6 -3
- package/src/lib/tenant/index.ts +9 -0
- package/src/lib/tenant/services/tenant.service.ts +336 -0
- package/src/lib/tenant/tenant.module.ts +19 -0
- package/src/lib/types/express.d.ts +14 -0
- package/src/lib/user/dto/requests/update-user.dto.ts +15 -0
- package/src/lib/user/entities/access-key.entity.ts +53 -0
- package/src/lib/user/entities/identity.entity.ts +31 -0
- package/src/lib/user/entities/user.entity.ts +212 -0
- package/src/lib/user/events/{user-created.event.d.ts → user-created.event.ts} +4 -3
- package/src/lib/user/events/{user-deleted.event.d.ts → user-deleted.event.ts} +6 -3
- package/src/lib/user/events/{user-updated.event.d.ts → user-updated.event.ts} +6 -3
- package/src/lib/user/index.ts +11 -0
- package/src/lib/user/services/access-key.service.ts +145 -0
- package/src/lib/user/services/{user.service.js → user.service.ts} +199 -95
- package/src/lib/user/user.module.ts +26 -0
- package/src/lib/utils/database.utils.ts +6 -0
- package/src/lib/utils/date.util.ts +106 -0
- package/src/lib/utils/device.util.ts +111 -0
- package/src/lib/utils/index.ts +6 -0
- package/src/lib/utils/otp.ts +3 -0
- package/src/lib/utils/security.util.ts +27 -0
- package/src/lib/utils/slug.util.ts +58 -0
- package/src/types/ms.d.ts +1 -0
- package/test/access-key.service.spec.ts +204 -0
- package/test/auth.service.spec.ts +541 -0
- package/test/mfa.service.spec.ts +359 -0
- package/test/role.service.spec.ts +418 -0
- package/test/tenant.service.spec.ts +218 -0
- package/test/test.setup.ts +66 -0
- package/test/user.service.spec.ts +374 -0
- package/tsconfig.json +17 -0
- package/tsconfig.lib.json +15 -0
- package/tsconfig.spec.json +15 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/ui/.env +1 -0
- package/ui/.env.example +1 -0
- package/ui/.eslintignore +7 -0
- package/ui/README.md +288 -0
- package/ui/index.html +17 -0
- package/ui/package.json +34 -0
- package/ui/postcss.config.js +6 -0
- package/ui/src/App.tsx +245 -0
- package/ui/src/components/AuthGuard.tsx +59 -0
- package/ui/src/components/AuthProvider.tsx +76 -0
- package/ui/src/components/Button.tsx +37 -0
- package/ui/src/components/Card.tsx +37 -0
- package/ui/src/components/ErrorMessage.tsx +15 -0
- package/ui/src/components/FormDialog.tsx +61 -0
- package/ui/src/components/FormFooter.tsx +37 -0
- package/ui/src/components/Layout.tsx +112 -0
- package/ui/src/components/LoadingMessage.tsx +11 -0
- package/ui/src/components/Modal.tsx +97 -0
- package/ui/src/components/MultiSelect.tsx +145 -0
- package/ui/src/components/PageHeader.tsx +42 -0
- package/ui/src/components/PanelHeader.tsx +28 -0
- package/ui/src/components/PermissionInput.tsx +473 -0
- package/ui/src/components/SearchInput.tsx +69 -0
- package/ui/src/components/Select.tsx +51 -0
- package/ui/src/components/SwaggerUIWrapper.tsx +316 -0
- package/ui/src/components/Table.tsx +207 -0
- package/ui/src/components/Tag.tsx +9 -0
- package/ui/src/components/TagsInput.tsx +96 -0
- package/ui/src/components/admin/AdminForm.tsx +170 -0
- package/ui/src/components/admin/CreateAdminDialog.tsx +38 -0
- package/ui/src/components/auth/LoginFooter.tsx +17 -0
- package/ui/src/components/auth/LoginHeader.tsx +14 -0
- package/ui/src/components/auth/components/CodeBlock.tsx +43 -0
- package/ui/src/components/auth/components/CreateAccountCodeExamples.tsx +60 -0
- package/ui/src/components/auth/components/PasswordRequirements.tsx +16 -0
- package/ui/src/components/auth/components/PasswordStrengthIndicator.tsx +48 -0
- package/ui/src/components/auth/components/ResetPasswordCodeExamples.tsx +76 -0
- package/ui/src/components/auth/components/Tabs.tsx +32 -0
- package/ui/src/components/auth/dialogs/CreateAccountDialog.tsx +79 -0
- package/ui/src/components/auth/dialogs/ForgotPasswordDialog.tsx +79 -0
- package/ui/src/components/auth/forms/CreateAccountForm.tsx +226 -0
- package/ui/src/components/auth/forms/LoginForm.tsx +149 -0
- package/ui/src/components/auth/forms/ResetPasswordForm.tsx +202 -0
- package/ui/src/components/auth/types.ts +17 -0
- package/ui/src/components/auth/utils/security.ts +82 -0
- package/ui/src/components/auth/utils/utils.ts +25 -0
- package/ui/src/components/form/EmailField.tsx +25 -0
- package/ui/src/components/form/FormField.tsx +102 -0
- package/ui/src/components/form/FormMultiSelect.tsx +46 -0
- package/ui/src/components/form/FormSelect.tsx +60 -0
- package/ui/src/components/form/FormTagsInput.tsx +42 -0
- package/ui/src/components/form/FormTextarea.tsx +42 -0
- package/ui/src/components/form/PasswordField.tsx +93 -0
- package/ui/src/components/form/SecretKeyField.tsx +49 -0
- package/ui/src/components/permission/CreatePermissionDialog.tsx +44 -0
- package/ui/src/components/permission/EditPermissionDialog.tsx +55 -0
- package/ui/src/components/permission/PermissionForm.tsx +251 -0
- package/ui/src/components/role/CreateRoleDialog.tsx +45 -0
- package/ui/src/components/role/EditRoleDialog.tsx +55 -0
- package/ui/src/components/role/RoleDialog.tsx +252 -0
- package/ui/src/components/role/RoleForm.tsx +246 -0
- package/ui/src/components/tenant/CreateTenantDialog.tsx +41 -0
- package/ui/src/components/tenant/EditTenantDialog.tsx +52 -0
- package/ui/src/components/tenant/TenantForm.tsx +160 -0
- package/ui/src/components/user/CreateUserDialog.tsx +45 -0
- package/ui/src/components/user/UserDetailModal.tsx +815 -0
- package/ui/src/components/user/UserForm.tsx +191 -0
- package/ui/src/data/nest-auth.json +1687 -0
- package/ui/src/hooks/useApi.ts +69 -0
- package/ui/src/hooks/useAuth.ts +100 -0
- package/ui/src/hooks/useConfirm.tsx +105 -0
- package/ui/src/hooks/useFormFooter.tsx +42 -0
- package/ui/src/hooks/usePagination.ts +69 -0
- package/ui/src/index.css +59 -0
- package/ui/src/main.tsx +13 -0
- package/ui/src/pages/AdminsPage.tsx +178 -0
- package/ui/src/pages/ApiPage.tsx +89 -0
- package/ui/src/pages/DashboardPage.tsx +281 -0
- package/ui/src/pages/LoginPage.tsx +39 -0
- package/ui/src/pages/PermissionsPage.tsx +376 -0
- package/ui/src/pages/RolesPage.tsx +274 -0
- package/ui/src/pages/TenantsPage.tsx +221 -0
- package/ui/src/pages/UsersPage.tsx +387 -0
- package/ui/src/services/api.ts +115 -0
- package/ui/src/types/index.ts +136 -0
- package/ui/src/vite-env.d.ts +9 -0
- package/ui/tailwind.config.js +45 -0
- package/ui/tsconfig.json +24 -0
- package/ui/tsconfig.node.json +10 -0
- package/ui/vite.config.ts +37 -0
- package/ui/yarn.lock +3137 -0
- package/src/index.d.ts +0 -11
- package/src/index.js +0 -18
- package/src/index.js.map +0 -1
- package/src/lib/auth/auth.module.d.ts +0 -2
- package/src/lib/auth/auth.module.js +0 -54
- package/src/lib/auth/auth.module.js.map +0 -1
- package/src/lib/auth/controllers/auth.controller.d.ts +0 -29
- package/src/lib/auth/controllers/auth.controller.js +0 -206
- package/src/lib/auth/controllers/auth.controller.js.map +0 -1
- package/src/lib/auth/controllers/mfa.controller.d.ts +0 -23
- package/src/lib/auth/controllers/mfa.controller.js +0 -131
- package/src/lib/auth/controllers/mfa.controller.js.map +0 -1
- package/src/lib/auth/dto/index.d.ts +0 -0
- package/src/lib/auth/dto/index.js +0 -1
- package/src/lib/auth/dto/index.js.map +0 -1
- package/src/lib/auth/dto/requests/forgot-password.request.dto.d.ts +0 -5
- package/src/lib/auth/dto/requests/forgot-password.request.dto.js +0 -30
- package/src/lib/auth/dto/requests/forgot-password.request.dto.js.map +0 -1
- package/src/lib/auth/dto/requests/login.request.dto.d.ts +0 -6
- package/src/lib/auth/dto/requests/login.request.dto.js +0 -38
- package/src/lib/auth/dto/requests/login.request.dto.js.map +0 -1
- package/src/lib/auth/dto/requests/refresh-token.request.dto.d.ts +0 -3
- package/src/lib/auth/dto/requests/refresh-token.request.dto.js +0 -15
- package/src/lib/auth/dto/requests/refresh-token.request.dto.js.map +0 -1
- package/src/lib/auth/dto/requests/reset-password.request.dto.d.ts +0 -7
- package/src/lib/auth/dto/requests/reset-password.request.dto.js +0 -42
- package/src/lib/auth/dto/requests/reset-password.request.dto.js.map +0 -1
- package/src/lib/auth/dto/requests/send-mfa-code.request.dto.d.ts +0 -4
- package/src/lib/auth/dto/requests/send-mfa-code.request.dto.js +0 -16
- package/src/lib/auth/dto/requests/send-mfa-code.request.dto.js.map +0 -1
- package/src/lib/auth/dto/requests/signup.request.dto.d.ts +0 -7
- package/src/lib/auth/dto/requests/signup.request.dto.js +0 -37
- package/src/lib/auth/dto/requests/signup.request.dto.js.map +0 -1
- package/src/lib/auth/dto/requests/social-login.request.dto.d.ts +0 -3
- package/src/lib/auth/dto/requests/social-login.request.dto.js +0 -16
- package/src/lib/auth/dto/requests/social-login.request.dto.js.map +0 -1
- package/src/lib/auth/dto/requests/verify-2fa.request.dto.d.ts +0 -5
- package/src/lib/auth/dto/requests/verify-2fa.request.dto.js +0 -21
- package/src/lib/auth/dto/requests/verify-2fa.request.dto.js.map +0 -1
- package/src/lib/auth/dto/requests/verify-forgot-password-otp-request-dto.d.ts +0 -6
- package/src/lib/auth/dto/requests/verify-forgot-password-otp-request-dto.js +0 -35
- package/src/lib/auth/dto/requests/verify-forgot-password-otp-request-dto.js.map +0 -1
- package/src/lib/auth/dto/requests/verify-totp-setup.request.dto.d.ts +0 -4
- package/src/lib/auth/dto/requests/verify-totp-setup.request.dto.js +0 -20
- package/src/lib/auth/dto/requests/verify-totp-setup.request.dto.js.map +0 -1
- package/src/lib/auth/dto/responses/auth.response.dto.d.ts +0 -16
- package/src/lib/auth/dto/responses/auth.response.dto.js +0 -50
- package/src/lib/auth/dto/responses/auth.response.dto.js.map +0 -1
- package/src/lib/auth/entities/mfa-secret.entity.d.ts +0 -12
- package/src/lib/auth/entities/mfa-secret.entity.js +0 -50
- package/src/lib/auth/entities/mfa-secret.entity.js.map +0 -1
- package/src/lib/auth/entities/otp.entity.d.ts +0 -13
- package/src/lib/auth/entities/otp.entity.js +0 -50
- package/src/lib/auth/entities/otp.entity.js.map +0 -1
- package/src/lib/auth/events/logged-out-all.event.js +0 -10
- package/src/lib/auth/events/logged-out-all.event.js.map +0 -1
- package/src/lib/auth/events/logged-out.event.js +0 -10
- package/src/lib/auth/events/logged-out.event.js.map +0 -1
- package/src/lib/auth/events/password-reset-requested.event.js +0 -10
- package/src/lib/auth/events/password-reset-requested.event.js.map +0 -1
- package/src/lib/auth/events/password-reset.event.js +0 -10
- package/src/lib/auth/events/password-reset.event.js.map +0 -1
- package/src/lib/auth/events/user-2fa-verified.event.js +0 -10
- package/src/lib/auth/events/user-2fa-verified.event.js.map +0 -1
- package/src/lib/auth/events/user-logged-in.event.js +0 -10
- package/src/lib/auth/events/user-logged-in.event.js.map +0 -1
- package/src/lib/auth/events/user-refresh-token.event.js +0 -10
- package/src/lib/auth/events/user-refresh-token.event.js.map +0 -1
- package/src/lib/auth/events/user-registered.event.js +0 -10
- package/src/lib/auth/events/user-registered.event.js.map +0 -1
- package/src/lib/auth/guards/auth.guard.d.ts +0 -28
- package/src/lib/auth/guards/auth.guard.js +0 -304
- package/src/lib/auth/guards/auth.guard.js.map +0 -1
- package/src/lib/auth/index.js +0 -31
- package/src/lib/auth/index.js.map +0 -1
- package/src/lib/auth/services/auth.service.d.ts +0 -53
- package/src/lib/auth/services/auth.service.js +0 -522
- package/src/lib/auth/services/auth.service.js.map +0 -1
- package/src/lib/auth/services/cookie.service.d.ts +0 -9
- package/src/lib/auth/services/cookie.service.js +0 -43
- package/src/lib/auth/services/cookie.service.js.map +0 -1
- package/src/lib/auth/services/mfa.service.d.ts +0 -38
- package/src/lib/auth/services/mfa.service.js +0 -254
- package/src/lib/auth/services/mfa.service.js.map +0 -1
- package/src/lib/auth.constants.d.ts +0 -39
- package/src/lib/auth.constants.js +0 -43
- package/src/lib/auth.constants.js.map +0 -1
- package/src/lib/core/core.module.d.ts +0 -2
- package/src/lib/core/core.module.js +0 -53
- package/src/lib/core/core.module.js.map +0 -1
- package/src/lib/core/decorators/auth.decorator.d.ts +0 -1
- package/src/lib/core/decorators/auth.decorator.js +0 -8
- package/src/lib/core/decorators/auth.decorator.js.map +0 -1
- package/src/lib/core/decorators/permissions.decorator.d.ts +0 -2
- package/src/lib/core/decorators/permissions.decorator.js +0 -14
- package/src/lib/core/decorators/permissions.decorator.js.map +0 -1
- package/src/lib/core/decorators/role.decorator.d.ts +0 -3
- package/src/lib/core/decorators/role.decorator.js +0 -14
- package/src/lib/core/decorators/role.decorator.js.map +0 -1
- package/src/lib/core/decorators/skip-mfa.decorator.d.ts +0 -2
- package/src/lib/core/decorators/skip-mfa.decorator.js +0 -8
- package/src/lib/core/decorators/skip-mfa.decorator.js.map +0 -1
- package/src/lib/core/dto/message.response.dto.d.ts +0 -3
- package/src/lib/core/dto/message.response.dto.js +0 -13
- package/src/lib/core/dto/message.response.dto.js.map +0 -1
- package/src/lib/core/entities.js +0 -31
- package/src/lib/core/entities.js.map +0 -1
- package/src/lib/core/index.js +0 -27
- package/src/lib/core/index.js.map +0 -1
- package/src/lib/core/interfaces/auth-module-options.interface.d.ts +0 -62
- package/src/lib/core/interfaces/auth-module-options.interface.js +0 -3
- package/src/lib/core/interfaces/auth-module-options.interface.js.map +0 -1
- package/src/lib/core/interfaces/mfa-options.interface.d.ts +0 -25
- package/src/lib/core/interfaces/mfa-options.interface.js +0 -10
- package/src/lib/core/interfaces/mfa-options.interface.js.map +0 -1
- package/src/lib/core/interfaces/otp.interface.d.ts +0 -5
- package/src/lib/core/interfaces/otp.interface.js +0 -10
- package/src/lib/core/interfaces/otp.interface.js.map +0 -1
- package/src/lib/core/interfaces/session-options.interface.d.ts +0 -12
- package/src/lib/core/interfaces/session-options.interface.js +0 -9
- package/src/lib/core/interfaces/session-options.interface.js.map +0 -1
- package/src/lib/core/interfaces/token-payload.interface.js +0 -3
- package/src/lib/core/interfaces/token-payload.interface.js.map +0 -1
- package/src/lib/core/providers/apple-auth.provider.d.ts +0 -18
- package/src/lib/core/providers/apple-auth.provider.js +0 -57
- package/src/lib/core/providers/apple-auth.provider.js.map +0 -1
- package/src/lib/core/providers/base-auth.provider.d.ts +0 -26
- package/src/lib/core/providers/base-auth.provider.js +0 -43
- package/src/lib/core/providers/base-auth.provider.js.map +0 -1
- package/src/lib/core/providers/email-auth.provider.d.ts +0 -17
- package/src/lib/core/providers/email-auth.provider.js +0 -40
- package/src/lib/core/providers/email-auth.provider.js.map +0 -1
- package/src/lib/core/providers/facebook-auth.provider.d.ts +0 -18
- package/src/lib/core/providers/facebook-auth.provider.js +0 -56
- package/src/lib/core/providers/facebook-auth.provider.js.map +0 -1
- package/src/lib/core/providers/google-auth.provider.d.ts +0 -21
- package/src/lib/core/providers/google-auth.provider.js +0 -58
- package/src/lib/core/providers/google-auth.provider.js.map +0 -1
- package/src/lib/core/providers/jwt-auth.provider.d.ts +0 -33
- package/src/lib/core/providers/jwt-auth.provider.js +0 -50
- package/src/lib/core/providers/jwt-auth.provider.js.map +0 -1
- package/src/lib/core/providers/phone-auth.provider.d.ts +0 -18
- package/src/lib/core/providers/phone-auth.provider.js +0 -43
- package/src/lib/core/providers/phone-auth.provider.js.map +0 -1
- package/src/lib/core/services/auth-config.service.d.ts +0 -12
- package/src/lib/core/services/auth-config.service.js +0 -79
- package/src/lib/core/services/auth-config.service.js.map +0 -1
- package/src/lib/core/services/auth-provider-registry.service.d.ts +0 -24
- package/src/lib/core/services/auth-provider-registry.service.js +0 -71
- package/src/lib/core/services/auth-provider-registry.service.js.map +0 -1
- package/src/lib/core/services/debug-logger.service.d.ts +0 -38
- package/src/lib/core/services/debug-logger.service.js.map +0 -1
- package/src/lib/core/services/initialization.service.d.ts +0 -10
- package/src/lib/core/services/initialization.service.js +0 -34
- package/src/lib/core/services/initialization.service.js.map +0 -1
- package/src/lib/core/services/jwt.service.d.ts +0 -14
- package/src/lib/core/services/jwt.service.js +0 -92
- package/src/lib/core/services/jwt.service.js.map +0 -1
- package/src/lib/nest-auth.module.d.ts +0 -11
- package/src/lib/nest-auth.module.js +0 -177
- package/src/lib/nest-auth.module.js.map +0 -1
- package/src/lib/request-context/request-context.d.ts +0 -22
- package/src/lib/request-context/request-context.js.map +0 -1
- package/src/lib/request-context/request-context.middleware.d.ts +0 -4
- package/src/lib/request-context/request-context.middleware.js +0 -16
- package/src/lib/request-context/request-context.middleware.js.map +0 -1
- package/src/lib/role/entities/role.entity.d.ts +0 -20
- package/src/lib/role/entities/role.entity.js +0 -110
- package/src/lib/role/entities/role.entity.js.map +0 -1
- package/src/lib/role/index.js +0 -5
- package/src/lib/role/index.js.map +0 -1
- package/src/lib/role/role.module.d.ts +0 -2
- package/src/lib/role/role.module.js +0 -23
- package/src/lib/role/role.module.js.map +0 -1
- package/src/lib/role/services/role.service.d.ts +0 -20
- package/src/lib/role/services/role.service.js.map +0 -1
- package/src/lib/session/entities/session.entity.d.ts +0 -16
- package/src/lib/session/entities/session.entity.js +0 -63
- package/src/lib/session/entities/session.entity.js.map +0 -1
- package/src/lib/session/index.d.ts +0 -3
- package/src/lib/session/index.js +0 -7
- package/src/lib/session/index.js.map +0 -1
- package/src/lib/session/services/base-session.service.d.ts +0 -23
- package/src/lib/session/services/base-session.service.js +0 -64
- package/src/lib/session/services/base-session.service.js.map +0 -1
- package/src/lib/session/services/database-session.service.d.ts +0 -17
- package/src/lib/session/services/database-session.service.js +0 -51
- package/src/lib/session/services/database-session.service.js.map +0 -1
- package/src/lib/session/services/redis-session.service.d.ts +0 -20
- package/src/lib/session/services/redis-session.service.js +0 -117
- package/src/lib/session/services/redis-session.service.js.map +0 -1
- package/src/lib/session/session.module.d.ts +0 -2
- package/src/lib/session/session.module.js +0 -33
- package/src/lib/session/session.module.js.map +0 -1
- package/src/lib/tenant/entities/tenant.entity.d.ts +0 -10
- package/src/lib/tenant/entities/tenant.entity.js +0 -44
- package/src/lib/tenant/entities/tenant.entity.js.map +0 -1
- package/src/lib/tenant/events/tenant-created.event.d.ts +0 -8
- package/src/lib/tenant/events/tenant-created.event.js +0 -10
- package/src/lib/tenant/events/tenant-created.event.js.map +0 -1
- package/src/lib/tenant/events/tenant-deleted.event.d.ts +0 -8
- package/src/lib/tenant/events/tenant-deleted.event.js +0 -10
- package/src/lib/tenant/events/tenant-deleted.event.js.map +0 -1
- package/src/lib/tenant/events/tenant-updated.event.js +0 -10
- package/src/lib/tenant/events/tenant-updated.event.js.map +0 -1
- package/src/lib/tenant/index.d.ts +0 -1
- package/src/lib/tenant/index.js +0 -5
- package/src/lib/tenant/index.js.map +0 -1
- package/src/lib/tenant/services/tenant.service.d.ts +0 -26
- package/src/lib/tenant/services/tenant.service.js +0 -200
- package/src/lib/tenant/services/tenant.service.js.map +0 -1
- package/src/lib/tenant/tenant.module.d.ts +0 -2
- package/src/lib/tenant/tenant.module.js +0 -27
- package/src/lib/tenant/tenant.module.js.map +0 -1
- package/src/lib/user/dto/requests/update-user.dto.d.ts +0 -5
- package/src/lib/user/dto/requests/update-user.dto.js +0 -24
- package/src/lib/user/dto/requests/update-user.dto.js.map +0 -1
- package/src/lib/user/entities/access-key.entity.d.ts +0 -16
- package/src/lib/user/entities/access-key.entity.js +0 -63
- package/src/lib/user/entities/access-key.entity.js.map +0 -1
- package/src/lib/user/entities/identity.entity.d.ts +0 -12
- package/src/lib/user/entities/identity.entity.js +0 -47
- package/src/lib/user/entities/identity.entity.js.map +0 -1
- package/src/lib/user/entities/user.entity.d.ts +0 -39
- package/src/lib/user/entities/user.entity.js +0 -201
- package/src/lib/user/entities/user.entity.js.map +0 -1
- package/src/lib/user/events/user-created.event.js +0 -10
- package/src/lib/user/events/user-created.event.js.map +0 -1
- package/src/lib/user/events/user-deleted.event.js +0 -10
- package/src/lib/user/events/user-deleted.event.js.map +0 -1
- package/src/lib/user/events/user-updated.event.js +0 -10
- package/src/lib/user/events/user-updated.event.js.map +0 -1
- package/src/lib/user/index.d.ts +0 -3
- package/src/lib/user/index.js +0 -7
- package/src/lib/user/index.js.map +0 -1
- package/src/lib/user/services/access-key.service.d.ts +0 -19
- package/src/lib/user/services/access-key.service.js +0 -119
- package/src/lib/user/services/access-key.service.js.map +0 -1
- package/src/lib/user/services/user.service.d.ts +0 -24
- package/src/lib/user/services/user.service.js.map +0 -1
- package/src/lib/user/user.module.d.ts +0 -2
- package/src/lib/user/user.module.js +0 -34
- package/src/lib/user/user.module.js.map +0 -1
- package/src/lib/utils/database.utils.d.ts +0 -2
- package/src/lib/utils/database.utils.js +0 -8
- package/src/lib/utils/database.utils.js.map +0 -1
- package/src/lib/utils/otp.d.ts +0 -1
- package/src/lib/utils/otp.js +0 -7
- package/src/lib/utils/otp.js.map +0 -1
|
@@ -0,0 +1,947 @@
|
|
|
1
|
+
import { Injectable, UnauthorizedException, BadRequestException, Inject, ForbiddenException, InternalServerErrorException } from '@nestjs/common';
|
|
2
|
+
import { InjectRepository } from '@nestjs/typeorm';
|
|
3
|
+
import { Repository } from 'typeorm';
|
|
4
|
+
import { NestAuthUser } from '../../user/entities/user.entity';
|
|
5
|
+
import { NestAuthOTP } from '../../auth/entities/otp.entity';
|
|
6
|
+
import { OTPTypeEnum } from '../../core/interfaces/otp.interface';
|
|
7
|
+
import {
|
|
8
|
+
EMAIL_AUTH_PROVIDER,
|
|
9
|
+
PHONE_AUTH_PROVIDER,
|
|
10
|
+
REFRESH_TOKEN_EXPIRED,
|
|
11
|
+
REFRESH_TOKEN_INVALID,
|
|
12
|
+
SESSION_NOT_FOUND_ERROR,
|
|
13
|
+
INVALID_MFA_EXCEPTION_CODE,
|
|
14
|
+
NestAuthEvents,
|
|
15
|
+
USER_NOT_ACTIVE_ERROR,
|
|
16
|
+
} from '../../auth.constants';
|
|
17
|
+
import { MoreThan } from 'typeorm';
|
|
18
|
+
import { MfaService } from './mfa.service';
|
|
19
|
+
import { JwtService } from '../../core/services/jwt.service';
|
|
20
|
+
import { EventEmitter2 } from '@nestjs/event-emitter';
|
|
21
|
+
import { SessionManagerService } from '../../session/services/session-manager.service';
|
|
22
|
+
import { RequestContext } from '../../request-context/request-context';
|
|
23
|
+
import { SignupRequestDto } from '../dto/requests/signup.request.dto';
|
|
24
|
+
import { AuthResponseDto } from '../dto/responses/auth.response.dto';
|
|
25
|
+
import { LoginRequestDto } from '../dto/requests/login.request.dto';
|
|
26
|
+
import { Verify2faRequestDto } from '../dto/requests/verify-2fa.request.dto';
|
|
27
|
+
import { MFAMethodEnum } from '../../core/interfaces/mfa-options.interface';
|
|
28
|
+
import { JWTTokenPayload, SessionPayload } from '../../core/interfaces/token-payload.interface';
|
|
29
|
+
import { ForgotPasswordRequestDto } from '../dto/requests/forgot-password.request.dto';
|
|
30
|
+
import { generateOtp } from '../../utils/otp';
|
|
31
|
+
import { ResetPasswordRequestDto } from '../dto/requests/reset-password.request.dto';
|
|
32
|
+
import { UserRegisteredEvent } from '../events/user-registered.event';
|
|
33
|
+
import { UserLoggedInEvent } from '../events/user-logged-in.event';
|
|
34
|
+
import { User2faVerifiedEvent } from '../events/user-2fa-verified.event';
|
|
35
|
+
import { UserRefreshTokenEvent } from '../events/user-refresh-token.event';
|
|
36
|
+
import { LoggedOutEvent } from '../events/logged-out.event';
|
|
37
|
+
import { LoggedOutAllEvent } from '../events/logged-out-all.event';
|
|
38
|
+
import { PasswordResetRequestedEvent } from '../events/password-reset-requested.event';
|
|
39
|
+
import { PasswordResetEvent } from '../events/password-reset.event';
|
|
40
|
+
import { AuthProviderUser, BaseAuthProvider } from '../../core/providers/base-auth.provider';
|
|
41
|
+
import { AuthProviderRegistryService } from '../../core/services/auth-provider-registry.service';
|
|
42
|
+
import { TenantService } from '../../tenant/services/tenant.service';
|
|
43
|
+
import { DebugLoggerService } from '../../core/services/debug-logger.service';
|
|
44
|
+
import moment from 'moment';
|
|
45
|
+
import { VerifyForgotPasswordOtpRequestDto } from '../dto/requests/verify-forgot-password-otp-request-dto';
|
|
46
|
+
import { ResetPasswordWithTokenRequestDto } from '../dto/requests/reset-password-with-token.request.dto';
|
|
47
|
+
import { ChangePasswordRequestDto } from '../dto/requests/change-password.request.dto';
|
|
48
|
+
import { VerifyOtpResponseDto } from '../dto/responses/verify-otp.response.dto';
|
|
49
|
+
import { SendEmailVerificationRequestDto } from '../dto/requests/send-email-verification.request.dto';
|
|
50
|
+
import { VerifyEmailRequestDto } from '../dto/requests/verify-email.request.dto';
|
|
51
|
+
|
|
52
|
+
@Injectable()
|
|
53
|
+
export class AuthService {
|
|
54
|
+
|
|
55
|
+
constructor(
|
|
56
|
+
@InjectRepository(NestAuthUser)
|
|
57
|
+
private readonly userRepository: Repository<NestAuthUser>,
|
|
58
|
+
|
|
59
|
+
@InjectRepository(NestAuthOTP)
|
|
60
|
+
private otpRepository: Repository<NestAuthOTP>,
|
|
61
|
+
|
|
62
|
+
private readonly authProviderRegistry: AuthProviderRegistryService,
|
|
63
|
+
|
|
64
|
+
private readonly mfaService: MfaService,
|
|
65
|
+
|
|
66
|
+
private readonly sessionManager: SessionManagerService,
|
|
67
|
+
|
|
68
|
+
private readonly jwtService: JwtService,
|
|
69
|
+
|
|
70
|
+
private readonly eventEmitter: EventEmitter2,
|
|
71
|
+
|
|
72
|
+
private readonly tenantService: TenantService,
|
|
73
|
+
|
|
74
|
+
private readonly debugLogger: DebugLoggerService,
|
|
75
|
+
) {
|
|
76
|
+
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
getUserWithRolesAndPermissions(userId: string, relations: string[] = []): Promise<NestAuthUser> {
|
|
80
|
+
return this.userRepository.findOne({
|
|
81
|
+
where: { id: userId },
|
|
82
|
+
relations: [
|
|
83
|
+
'roles',
|
|
84
|
+
...relations
|
|
85
|
+
],
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async getUser() {
|
|
90
|
+
const user = RequestContext.currentUser();
|
|
91
|
+
if (!user) {
|
|
92
|
+
return null
|
|
93
|
+
}
|
|
94
|
+
return this.getUserWithRolesAndPermissions(user.id);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async signup(input: SignupRequestDto): Promise<AuthResponseDto> {
|
|
98
|
+
this.debugLogger.logFunctionEntry('signup', 'AuthService', { email: input.email, phone: input.phone, hasPassword: !!input.password });
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
|
|
102
|
+
const { email, phone, password } = input;
|
|
103
|
+
|
|
104
|
+
let { tenantId = null } = input;
|
|
105
|
+
|
|
106
|
+
// Resolve tenant ID - use provided or default
|
|
107
|
+
tenantId = await this.tenantService.resolveTenantId(tenantId);
|
|
108
|
+
this.debugLogger.logAuthOperation('signup', 'email|phone', undefined, { email, phone, resolvedTenantId: tenantId });
|
|
109
|
+
|
|
110
|
+
if (!email && !phone) {
|
|
111
|
+
this.debugLogger.error('Signup failed: Neither email nor phone provided', 'AuthService');
|
|
112
|
+
throw new BadRequestException('Either email or phone must be provided');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
let provider: BaseAuthProvider | null = null;
|
|
116
|
+
let providerUserId: string | null = null;
|
|
117
|
+
|
|
118
|
+
if (email) {
|
|
119
|
+
provider = this.authProviderRegistry.getProvider(EMAIL_AUTH_PROVIDER);
|
|
120
|
+
providerUserId = email;
|
|
121
|
+
} else if (phone) {
|
|
122
|
+
provider = this.authProviderRegistry.getProvider(PHONE_AUTH_PROVIDER);
|
|
123
|
+
providerUserId = phone;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (!provider) {
|
|
127
|
+
this.debugLogger.error('Provider not found for signup', 'AuthService', { email: !!email, phone: !!phone });
|
|
128
|
+
throw new InternalServerErrorException('Phone or email authentication is not enabled');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
this.debugLogger.debug('Checking for existing identity', 'AuthService', { providerUserId });
|
|
132
|
+
const identity = await provider.findIdentity(providerUserId);
|
|
133
|
+
|
|
134
|
+
if (identity) {
|
|
135
|
+
this.debugLogger.warn('Identity already exists', 'AuthService', { email: !!email, phone: !!phone, tenantId });
|
|
136
|
+
if (email) {
|
|
137
|
+
throw new BadRequestException('Email already exists in this tenant');
|
|
138
|
+
}
|
|
139
|
+
if (phone) {
|
|
140
|
+
throw new BadRequestException('Phone number already exists in this tenant');
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
this.debugLogger.debug('Creating new user', 'AuthService', { email: !!email, phone: !!phone, tenantId });
|
|
145
|
+
let user = this.userRepository.create({
|
|
146
|
+
email,
|
|
147
|
+
phone,
|
|
148
|
+
tenantId,
|
|
149
|
+
isVerified: false,
|
|
150
|
+
});
|
|
151
|
+
await user.setPassword(password);
|
|
152
|
+
await this.userRepository.save(user);
|
|
153
|
+
this.debugLogger.info('User created successfully', 'AuthService', { userId: user.id, tenantId });
|
|
154
|
+
|
|
155
|
+
user = await this.getUserWithRolesAndPermissions(user.id);
|
|
156
|
+
|
|
157
|
+
this.debugLogger.debug('Linking user to provider', 'AuthService', { userId: user.id, providerName: provider.providerName });
|
|
158
|
+
await provider.linkToUser(user.id, providerUserId);
|
|
159
|
+
|
|
160
|
+
this.debugLogger.debug('Creating session for new user', 'AuthService', { userId: user.id });
|
|
161
|
+
const session = await this.sessionManager.createSessionFromUser(user);
|
|
162
|
+
const tokens = await this.generateTokensFromSession(session);
|
|
163
|
+
const isRequiresMfa = await this.mfaService.isRequiresMfa(user.id);
|
|
164
|
+
this.debugLogger.debug('Signup tokens generated', 'AuthService', { userId: user.id, isRequiresMfa });
|
|
165
|
+
|
|
166
|
+
// Emit registration event
|
|
167
|
+
this.debugLogger.debug('Emitting user registration event', 'AuthService', { userId: user.id });
|
|
168
|
+
await this.eventEmitter.emitAsync(
|
|
169
|
+
NestAuthEvents.REGISTERED,
|
|
170
|
+
new UserRegisteredEvent({
|
|
171
|
+
user,
|
|
172
|
+
tenantId: user.tenantId,
|
|
173
|
+
input,
|
|
174
|
+
provider,
|
|
175
|
+
session,
|
|
176
|
+
tokens,
|
|
177
|
+
isRequiresMfa
|
|
178
|
+
})
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
this.debugLogger.logFunctionExit('signup', 'AuthService', { userId: user.id, isRequiresMfa });
|
|
182
|
+
return {
|
|
183
|
+
accessToken: tokens.accessToken,
|
|
184
|
+
refreshToken: tokens.refreshToken,
|
|
185
|
+
isRequiresMfa: isRequiresMfa,
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
} catch (error) {
|
|
189
|
+
this.debugLogger.logError(error, 'signup', { email: input.email, phone: input.phone });
|
|
190
|
+
throw error;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
async login(input: LoginRequestDto): Promise<AuthResponseDto> {
|
|
195
|
+
const { credentials, providerName, createUserIfNotExists = false } = input;
|
|
196
|
+
this.debugLogger.logFunctionEntry('login', 'AuthService', { providerName, createUserIfNotExists });
|
|
197
|
+
let { tenantId = null } = input;
|
|
198
|
+
|
|
199
|
+
// Resolve tenant ID - use provided or default
|
|
200
|
+
tenantId = await this.tenantService.resolveTenantId(tenantId);
|
|
201
|
+
this.debugLogger.logAuthOperation('login', providerName, undefined, { resolvedTenantId: tenantId, createUserIfNotExists });
|
|
202
|
+
|
|
203
|
+
const provider = this.authProviderRegistry.getProvider(providerName);
|
|
204
|
+
|
|
205
|
+
if (!provider) {
|
|
206
|
+
throw new UnauthorizedException('Invalid authentication providerName or provider is not enabled');
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const requiredFields = provider.getRequiredFields();
|
|
210
|
+
|
|
211
|
+
if (!requiredFields.every(field => credentials[field])) {
|
|
212
|
+
throw new BadRequestException(`Missing ${requiredFields.join(', ')} required fields`);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const authProviderUser = await provider.validate(credentials);
|
|
216
|
+
|
|
217
|
+
const identity = await provider.findIdentity(authProviderUser.userId);
|
|
218
|
+
|
|
219
|
+
let user: NestAuthUser | null = identity?.user || null;
|
|
220
|
+
|
|
221
|
+
if (!user) {
|
|
222
|
+
if (!createUserIfNotExists) {
|
|
223
|
+
throw new UnauthorizedException('Invalid credentials');
|
|
224
|
+
}
|
|
225
|
+
// Create new user if not exists and link to provider
|
|
226
|
+
user = await this.handleSocialLogin(provider, authProviderUser, tenantId);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (user.isActive === false) {
|
|
230
|
+
throw new UnauthorizedException({
|
|
231
|
+
message: 'Your account is suspended, please contact support',
|
|
232
|
+
code: USER_NOT_ACTIVE_ERROR,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
user = await this.getUserWithRolesAndPermissions(user.id);
|
|
237
|
+
|
|
238
|
+
const session = await this.sessionManager.createSessionFromUser(user);
|
|
239
|
+
const tokens = await this.generateTokensFromSession(session);
|
|
240
|
+
|
|
241
|
+
const isRequiresMfa = await this.mfaService.isRequiresMfa(user.id);
|
|
242
|
+
|
|
243
|
+
// Emit login event
|
|
244
|
+
await this.eventEmitter.emitAsync(
|
|
245
|
+
NestAuthEvents.LOGGED_IN,
|
|
246
|
+
new UserLoggedInEvent({
|
|
247
|
+
user,
|
|
248
|
+
tenantId: user.tenantId,
|
|
249
|
+
input,
|
|
250
|
+
provider,
|
|
251
|
+
session,
|
|
252
|
+
tokens,
|
|
253
|
+
isRequiresMfa
|
|
254
|
+
})
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
return {
|
|
258
|
+
accessToken: tokens.accessToken,
|
|
259
|
+
refreshToken: tokens.refreshToken,
|
|
260
|
+
isRequiresMfa: isRequiresMfa,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
async verify2fa(input: Verify2faRequestDto) {
|
|
265
|
+
this.debugLogger.logFunctionEntry('verify2fa', 'AuthService', { method: input.method });
|
|
266
|
+
|
|
267
|
+
try {
|
|
268
|
+
const session = RequestContext.currentSession();
|
|
269
|
+
|
|
270
|
+
if (!session) {
|
|
271
|
+
this.debugLogger.error('Session not found for 2FA verification', 'AuthService');
|
|
272
|
+
throw new UnauthorizedException({
|
|
273
|
+
message: 'Session not found',
|
|
274
|
+
code: SESSION_NOT_FOUND_ERROR,
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
this.debugLogger.debug('Verifying MFA code', 'AuthService', { userId: session.userId, method: input.method });
|
|
279
|
+
const isValid = await this.mfaService.verifyMfa(session.userId, input.otp, input.method);
|
|
280
|
+
if (!isValid) {
|
|
281
|
+
this.debugLogger.warn('Invalid MFA code provided', 'AuthService', { userId: session.userId, method: input.method });
|
|
282
|
+
throw new UnauthorizedException({
|
|
283
|
+
message: 'Invalid MFA code',
|
|
284
|
+
code: INVALID_MFA_EXCEPTION_CODE,
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
this.debugLogger.debug('Updating session with MFA verification', 'AuthService', { sessionId: session.id });
|
|
289
|
+
const payload = await this.sessionManager.updateSession(session.id, {
|
|
290
|
+
data: {
|
|
291
|
+
...session.data,
|
|
292
|
+
isMfaVerified: true,
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
const tokens = await this.generateTokensFromSession(payload);
|
|
296
|
+
|
|
297
|
+
const user = await this.getUser();
|
|
298
|
+
|
|
299
|
+
// Emit 2FA verified event
|
|
300
|
+
this.debugLogger.debug('Emitting 2FA verified event', 'AuthService', { userId: user.id });
|
|
301
|
+
await this.eventEmitter.emitAsync(
|
|
302
|
+
NestAuthEvents.TWO_FACTOR_VERIFIED,
|
|
303
|
+
new User2faVerifiedEvent({
|
|
304
|
+
user,
|
|
305
|
+
tenantId: user.tenantId,
|
|
306
|
+
input,
|
|
307
|
+
session,
|
|
308
|
+
tokens
|
|
309
|
+
})
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
this.debugLogger.logFunctionExit('verify2fa', 'AuthService', { userId: user.id });
|
|
313
|
+
return {
|
|
314
|
+
accessToken: tokens.accessToken,
|
|
315
|
+
refreshToken: tokens.refreshToken,
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
} catch (error) {
|
|
319
|
+
this.debugLogger.logError(error, 'verify2fa', { method: input.method });
|
|
320
|
+
throw error;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
async send2faCode(userId: string, method: MFAMethodEnum) {
|
|
325
|
+
const user = await this.userRepository.findOne({ where: { id: userId } });
|
|
326
|
+
|
|
327
|
+
if (!user) {
|
|
328
|
+
throw new UnauthorizedException('User not found');
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
await this.mfaService.sendMfaCode(user.id, method);
|
|
332
|
+
|
|
333
|
+
return true;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
private async handleSocialLogin(
|
|
337
|
+
provider: BaseAuthProvider,
|
|
338
|
+
providerUser: AuthProviderUser,
|
|
339
|
+
tenantId?: string | null,
|
|
340
|
+
): Promise<NestAuthUser> {
|
|
341
|
+
|
|
342
|
+
// Check if identity exists
|
|
343
|
+
let identity = await provider.findIdentity(providerUser.userId);
|
|
344
|
+
|
|
345
|
+
if (identity) {
|
|
346
|
+
return identity.user;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const linkUserWith = provider.linkUserWith();
|
|
350
|
+
|
|
351
|
+
let user = await this.userRepository.findOne({ where: { [linkUserWith]: providerUser.userId } });
|
|
352
|
+
|
|
353
|
+
if (!user) {
|
|
354
|
+
// Create new user
|
|
355
|
+
user = this.userRepository.create({
|
|
356
|
+
[linkUserWith]: providerUser.userId,
|
|
357
|
+
isVerified: true,
|
|
358
|
+
metadata: providerUser.metadata || {},
|
|
359
|
+
tenantId: tenantId,
|
|
360
|
+
});
|
|
361
|
+
await this.userRepository.save(user);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
await provider.linkToUser(user.id, providerUser.userId, providerUser.metadata || {});
|
|
365
|
+
|
|
366
|
+
return user;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
async refreshToken(refreshToken: string) {
|
|
370
|
+
this.debugLogger.logFunctionEntry('refreshToken', 'AuthService', { hasRefreshToken: !!refreshToken });
|
|
371
|
+
|
|
372
|
+
try {
|
|
373
|
+
if (!refreshToken) {
|
|
374
|
+
this.debugLogger.error('No refresh token provided', 'AuthService');
|
|
375
|
+
throw new UnauthorizedException({
|
|
376
|
+
message: 'No refresh token provided',
|
|
377
|
+
code: REFRESH_TOKEN_INVALID,
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
this.debugLogger.debug('Verifying refresh token', 'AuthService');
|
|
382
|
+
let payload: JWTTokenPayload;
|
|
383
|
+
try {
|
|
384
|
+
payload = await this.jwtService.verifyToken(refreshToken);
|
|
385
|
+
} catch (error) {
|
|
386
|
+
this.debugLogger.warn('Invalid or expired refresh token', 'AuthService');
|
|
387
|
+
throw new UnauthorizedException({
|
|
388
|
+
message: 'Invalid or expired refresh token',
|
|
389
|
+
code: REFRESH_TOKEN_EXPIRED,
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
const session = await this.sessionManager.getSession(payload.sessionId);
|
|
394
|
+
|
|
395
|
+
if (!session) {
|
|
396
|
+
throw new UnauthorizedException({
|
|
397
|
+
message: 'Invalid refresh token',
|
|
398
|
+
code: REFRESH_TOKEN_INVALID,
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Generate new session
|
|
403
|
+
const newSession = await this.sessionManager.createSessionFromSession(session);
|
|
404
|
+
|
|
405
|
+
// Revoke old session
|
|
406
|
+
await this.sessionManager.revokeSession(session.id);
|
|
407
|
+
|
|
408
|
+
// Generate new tokens
|
|
409
|
+
this.debugLogger.debug('Generating new tokens from refreshed session', 'AuthService', { sessionId: newSession.id });
|
|
410
|
+
const tokens = await this.generateTokensFromSession(newSession);
|
|
411
|
+
|
|
412
|
+
// Emit refresh token event
|
|
413
|
+
this.debugLogger.debug('Emitting refresh token event', 'AuthService', { sessionId: newSession.id });
|
|
414
|
+
await this.eventEmitter.emitAsync(
|
|
415
|
+
NestAuthEvents.REFRESH_TOKEN,
|
|
416
|
+
new UserRefreshTokenEvent({
|
|
417
|
+
oldRefreshToken: refreshToken,
|
|
418
|
+
session: newSession,
|
|
419
|
+
tokens,
|
|
420
|
+
})
|
|
421
|
+
);
|
|
422
|
+
|
|
423
|
+
this.debugLogger.logFunctionExit('refreshToken', 'AuthService', { sessionId: newSession.id });
|
|
424
|
+
return tokens;
|
|
425
|
+
|
|
426
|
+
} catch (error) {
|
|
427
|
+
this.debugLogger.logError(error, 'refreshToken', { hasRefreshToken: !!refreshToken });
|
|
428
|
+
throw error;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
async changePassword(input: ChangePasswordRequestDto): Promise<AuthResponseDto> {
|
|
434
|
+
this.debugLogger.logFunctionEntry('changePassword', 'AuthService');
|
|
435
|
+
|
|
436
|
+
try {
|
|
437
|
+
const currentUser = RequestContext.currentUser();
|
|
438
|
+
|
|
439
|
+
if (!currentUser?.id) {
|
|
440
|
+
throw new UnauthorizedException('User not found');
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const user = await this.userRepository.findOne({
|
|
444
|
+
where: { id: currentUser.id },
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
if (!user) {
|
|
448
|
+
throw new UnauthorizedException('User not found');
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const isValid = await user.validatePassword(input.currentPassword);
|
|
452
|
+
if (!isValid) {
|
|
453
|
+
throw new BadRequestException('Current password is incorrect');
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
if (input.currentPassword === input.newPassword) {
|
|
457
|
+
throw new BadRequestException('New password must be different from the current password');
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
await user.setPassword(input.newPassword);
|
|
461
|
+
await this.userRepository.save(user);
|
|
462
|
+
|
|
463
|
+
await this.sessionManager.revokeAllUserSessions(user.id);
|
|
464
|
+
|
|
465
|
+
const hydratedUser = await this.getUserWithRolesAndPermissions(user.id);
|
|
466
|
+
const session = await this.sessionManager.createSessionFromUser(hydratedUser);
|
|
467
|
+
const tokens = await this.generateTokensFromSession(session);
|
|
468
|
+
const isRequiresMfa = await this.mfaService.isRequiresMfa(user.id);
|
|
469
|
+
|
|
470
|
+
this.debugLogger.logFunctionExit('changePassword', 'AuthService', { userId: user.id });
|
|
471
|
+
return {
|
|
472
|
+
accessToken: tokens.accessToken,
|
|
473
|
+
refreshToken: tokens.refreshToken,
|
|
474
|
+
isRequiresMfa,
|
|
475
|
+
};
|
|
476
|
+
} catch (error) {
|
|
477
|
+
this.debugLogger.logError(error, 'changePassword');
|
|
478
|
+
throw error;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
async forgotPassword(input: ForgotPasswordRequestDto) {
|
|
483
|
+
this.debugLogger.logFunctionEntry('forgotPassword', 'AuthService', { email: input.email, phone: input.phone });
|
|
484
|
+
|
|
485
|
+
try {
|
|
486
|
+
const { email, phone } = input;
|
|
487
|
+
let { tenantId = null } = input;
|
|
488
|
+
|
|
489
|
+
// Resolve tenant ID - use provided or default
|
|
490
|
+
tenantId = await this.tenantService.resolveTenantId(tenantId);
|
|
491
|
+
let provider: BaseAuthProvider | null = null;
|
|
492
|
+
|
|
493
|
+
if (phone) {
|
|
494
|
+
provider = this.authProviderRegistry.getProvider(PHONE_AUTH_PROVIDER);
|
|
495
|
+
} else if (email) {
|
|
496
|
+
provider = this.authProviderRegistry.getProvider(EMAIL_AUTH_PROVIDER);
|
|
497
|
+
} else {
|
|
498
|
+
throw new BadRequestException('Either email or phone must be provided');
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
if (!provider) {
|
|
502
|
+
throw new BadRequestException('Phone or email authentication is not enabled');
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
if (!provider.enabled) {
|
|
506
|
+
if (email) {
|
|
507
|
+
throw new BadRequestException('Email authentication is not enabled');
|
|
508
|
+
} else if (phone) {
|
|
509
|
+
throw new BadRequestException('Phone authentication is not enabled');
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
const identity = await provider.findIdentity(email || phone);
|
|
514
|
+
|
|
515
|
+
if (!identity) {
|
|
516
|
+
// Return success even if user not found to prevent email/phone enumeration
|
|
517
|
+
return { message: 'If the account exists, a password reset code has been sent' };
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
// Generate OTP
|
|
521
|
+
const otp = generateOtp();
|
|
522
|
+
const expiresAt = new Date();
|
|
523
|
+
expiresAt.setMinutes(expiresAt.getMinutes() + 15); // OTP expires in 15 minutes
|
|
524
|
+
|
|
525
|
+
// Save OTP to database
|
|
526
|
+
const otpEntity = await this.otpRepository.save({
|
|
527
|
+
userId: identity.user?.id,
|
|
528
|
+
code: otp,
|
|
529
|
+
expiresAt,
|
|
530
|
+
type: OTPTypeEnum.PASSWORD_RESET
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
// Emit refresh token event, Send OTP via email or SMS should be handled by the event listener
|
|
535
|
+
await this.eventEmitter.emitAsync(
|
|
536
|
+
NestAuthEvents.PASSWORD_RESET_REQUESTED,
|
|
537
|
+
new PasswordResetRequestedEvent({
|
|
538
|
+
user: identity.user,
|
|
539
|
+
tenantId: identity.user?.tenantId,
|
|
540
|
+
input,
|
|
541
|
+
otp: otpEntity,
|
|
542
|
+
provider,
|
|
543
|
+
})
|
|
544
|
+
);
|
|
545
|
+
|
|
546
|
+
this.debugLogger.logFunctionExit('forgotPassword', 'AuthService', { email: !!email, phone: !!phone });
|
|
547
|
+
return true;
|
|
548
|
+
|
|
549
|
+
} catch (error) {
|
|
550
|
+
this.debugLogger.logError(error, 'forgotPassword', { email: input.email, phone: input.phone });
|
|
551
|
+
throw error;
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
async verifyForgotPasswordOtp(input: VerifyForgotPasswordOtpRequestDto): Promise<VerifyOtpResponseDto> {
|
|
556
|
+
this.debugLogger.logFunctionEntry('verifyForgotPasswordOtp', 'AuthService', { email: input.email, phone: input.phone });
|
|
557
|
+
try {
|
|
558
|
+
const { email, phone, otp } = input;
|
|
559
|
+
let { tenantId = null } = input;
|
|
560
|
+
|
|
561
|
+
// Resolve tenant ID - use provided or default
|
|
562
|
+
tenantId = await this.tenantService.resolveTenantId(tenantId);
|
|
563
|
+
|
|
564
|
+
if (!email && !phone) {
|
|
565
|
+
throw new BadRequestException('Either email or phone must be provided');
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
let provider: BaseAuthProvider | null = null;
|
|
569
|
+
|
|
570
|
+
if (phone) {
|
|
571
|
+
provider = this.authProviderRegistry.getProvider(PHONE_AUTH_PROVIDER);
|
|
572
|
+
} else if (email) {
|
|
573
|
+
provider = this.authProviderRegistry.getProvider(EMAIL_AUTH_PROVIDER);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
if (!provider) {
|
|
577
|
+
throw new BadRequestException('Phone or email authentication is not enabled');
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
const identity = await provider.findIdentity(email || phone);
|
|
581
|
+
|
|
582
|
+
if (!identity) {
|
|
583
|
+
throw new BadRequestException('Invalid reset request');
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
const validOtp = await this.otpRepository.findOne({
|
|
587
|
+
where: {
|
|
588
|
+
userId: identity.user?.id,
|
|
589
|
+
code: otp,
|
|
590
|
+
type: OTPTypeEnum.PASSWORD_RESET,
|
|
591
|
+
used: false
|
|
592
|
+
},
|
|
593
|
+
relations: ['user']
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
if (!validOtp) {
|
|
597
|
+
throw new BadRequestException('Invalid OTP code');
|
|
598
|
+
}
|
|
599
|
+
if (moment(validOtp.expiresAt).isBefore(new Date())) {
|
|
600
|
+
throw new BadRequestException('OTP code expired');
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
const user = validOtp.user;
|
|
604
|
+
|
|
605
|
+
// Generate JWT-based password reset token
|
|
606
|
+
// Include password hash prefix to invalidate token if password changes
|
|
607
|
+
const passwordHashPrefix = user.passwordHash ? user.passwordHash.substring(0, 10) : '';
|
|
608
|
+
const resetToken = await this.jwtService.generatePasswordResetToken({
|
|
609
|
+
userId: user.id,
|
|
610
|
+
passwordHashPrefix,
|
|
611
|
+
type: 'password-reset'
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
// Delete the OTP since it's been verified
|
|
615
|
+
await this.otpRepository.remove(validOtp);
|
|
616
|
+
|
|
617
|
+
this.debugLogger.logFunctionExit('verifyForgotPasswordOtp', 'AuthService', { email: input.email, phone: input.phone });
|
|
618
|
+
return {
|
|
619
|
+
message: 'OTP verified successfully',
|
|
620
|
+
resetToken
|
|
621
|
+
};
|
|
622
|
+
} catch (error) {
|
|
623
|
+
this.debugLogger.logError(error, 'verifyForgotPasswordOtp', { email: input.email, phone: input.phone });
|
|
624
|
+
throw error;
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
async resetPassword(input: ResetPasswordRequestDto) {
|
|
629
|
+
this.debugLogger.logFunctionEntry('resetPassword', 'AuthService', { email: input.email, phone: input.phone });
|
|
630
|
+
|
|
631
|
+
try {
|
|
632
|
+
const { email, phone, otp, newPassword } = input;
|
|
633
|
+
let { tenantId = null } = input;
|
|
634
|
+
|
|
635
|
+
// Resolve tenant ID - use provided or default
|
|
636
|
+
tenantId = await this.tenantService.resolveTenantId(tenantId);
|
|
637
|
+
|
|
638
|
+
if (!email && !phone) {
|
|
639
|
+
throw new BadRequestException('Either email or phone must be provided');
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// Find user by email or phone
|
|
643
|
+
const user = await this.userRepository.findOne({
|
|
644
|
+
where: [
|
|
645
|
+
...(email ? [{ email, tenantId }] : []),
|
|
646
|
+
...(phone ? [{ phone, tenantId }] : [])
|
|
647
|
+
]
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
if (!user) {
|
|
651
|
+
throw new BadRequestException('Invalid reset request');
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
// Find valid OTP
|
|
655
|
+
const validOtp = await this.otpRepository.findOne({
|
|
656
|
+
where: {
|
|
657
|
+
userId: user.id,
|
|
658
|
+
code: otp,
|
|
659
|
+
type: OTPTypeEnum.PASSWORD_RESET,
|
|
660
|
+
expiresAt: MoreThan(new Date()),
|
|
661
|
+
used: false
|
|
662
|
+
}
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
if (!validOtp) {
|
|
666
|
+
throw new BadRequestException('Invalid or expired OTP');
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
// Update password
|
|
670
|
+
await user.setPassword(newPassword);
|
|
671
|
+
await this.userRepository.save(user);
|
|
672
|
+
|
|
673
|
+
// Mark OTP as used
|
|
674
|
+
validOtp.used = true;
|
|
675
|
+
await this.otpRepository.save(validOtp);
|
|
676
|
+
|
|
677
|
+
// Emit refresh token event, If we want to send email or SMS should be handled by the event listener
|
|
678
|
+
await this.eventEmitter.emitAsync(
|
|
679
|
+
NestAuthEvents.PASSWORD_RESET,
|
|
680
|
+
new PasswordResetEvent({
|
|
681
|
+
user,
|
|
682
|
+
tenantId: user.tenantId,
|
|
683
|
+
input,
|
|
684
|
+
})
|
|
685
|
+
);
|
|
686
|
+
|
|
687
|
+
this.debugLogger.logFunctionExit('resetPassword', 'AuthService', { email: !!email, phone: !!phone });
|
|
688
|
+
return true;
|
|
689
|
+
|
|
690
|
+
} catch (error) {
|
|
691
|
+
this.debugLogger.logError(error, 'resetPassword', { email: input.email, phone: input.phone });
|
|
692
|
+
throw error;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
|
|
697
|
+
async resetPasswordWithToken(input: ResetPasswordWithTokenRequestDto) {
|
|
698
|
+
this.debugLogger.logFunctionEntry('resetPasswordWithToken', 'AuthService', { token: '***' });
|
|
699
|
+
|
|
700
|
+
try {
|
|
701
|
+
const { token, newPassword } = input;
|
|
702
|
+
|
|
703
|
+
// Verify JWT token
|
|
704
|
+
let decoded: any;
|
|
705
|
+
try {
|
|
706
|
+
decoded = await this.jwtService.verifyPasswordResetToken(token);
|
|
707
|
+
} catch (error) {
|
|
708
|
+
throw new BadRequestException('Invalid or expired reset token');
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
if (decoded.type !== 'password-reset') {
|
|
712
|
+
throw new BadRequestException('Invalid token type');
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
// Get user
|
|
716
|
+
const user = await this.userRepository.findOne({
|
|
717
|
+
where: { id: decoded.userId }
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
if (!user) {
|
|
721
|
+
throw new BadRequestException('User not found');
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
// Verify password hasn't changed since token was issued
|
|
725
|
+
// This makes the token single-use in practice
|
|
726
|
+
const currentPasswordHashPrefix = user.passwordHash ? user.passwordHash.substring(0, 10) : '';
|
|
727
|
+
if (decoded.passwordHashPrefix !== currentPasswordHashPrefix) {
|
|
728
|
+
throw new BadRequestException('Reset token is no longer valid');
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// Update password
|
|
732
|
+
await user.setPassword(newPassword);
|
|
733
|
+
await this.userRepository.save(user);
|
|
734
|
+
|
|
735
|
+
// Emit password reset event
|
|
736
|
+
await this.eventEmitter.emitAsync(
|
|
737
|
+
NestAuthEvents.PASSWORD_RESET,
|
|
738
|
+
new PasswordResetEvent({
|
|
739
|
+
user,
|
|
740
|
+
tenantId: user.tenantId,
|
|
741
|
+
input: { token, newPassword } as any,
|
|
742
|
+
})
|
|
743
|
+
);
|
|
744
|
+
|
|
745
|
+
this.debugLogger.logFunctionExit('resetPasswordWithToken', 'AuthService');
|
|
746
|
+
return true;
|
|
747
|
+
|
|
748
|
+
} catch (error) {
|
|
749
|
+
this.debugLogger.logError(error, 'resetPasswordWithToken');
|
|
750
|
+
throw error;
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
async logout(logoutType: 'user' | 'admin' | 'system' = 'user', reason?: string) {
|
|
755
|
+
const session = RequestContext.currentSession();
|
|
756
|
+
|
|
757
|
+
const user = await this.getUser();
|
|
758
|
+
|
|
759
|
+
// Emit logout event
|
|
760
|
+
await this.eventEmitter.emitAsync(
|
|
761
|
+
NestAuthEvents.LOGGED_OUT,
|
|
762
|
+
new LoggedOutEvent({
|
|
763
|
+
user,
|
|
764
|
+
tenantId: user?.tenantId,
|
|
765
|
+
session,
|
|
766
|
+
logoutType,
|
|
767
|
+
reason,
|
|
768
|
+
})
|
|
769
|
+
);
|
|
770
|
+
|
|
771
|
+
if (session) {
|
|
772
|
+
await this.sessionManager.revokeSession(session.id);
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
return true;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
async logoutAll(userId: string, logoutType: 'user' | 'admin' | 'system' = 'user', reason?: string) {
|
|
779
|
+
const session = RequestContext.currentSession();
|
|
780
|
+
if (!session) {
|
|
781
|
+
throw new UnauthorizedException('Session not found');
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
const sessions = await this.sessionManager.getUserSessions(userId);
|
|
785
|
+
|
|
786
|
+
await this.sessionManager.revokeAllUserSessions(userId);
|
|
787
|
+
|
|
788
|
+
const user = await this.getUser();
|
|
789
|
+
|
|
790
|
+
// Emit logout event
|
|
791
|
+
await this.eventEmitter.emitAsync(
|
|
792
|
+
NestAuthEvents.LOGGED_OUT_ALL,
|
|
793
|
+
new LoggedOutAllEvent({
|
|
794
|
+
user,
|
|
795
|
+
tenantId: user?.tenantId,
|
|
796
|
+
logoutType,
|
|
797
|
+
reason,
|
|
798
|
+
currentSessionId: session.id,
|
|
799
|
+
sessions,
|
|
800
|
+
})
|
|
801
|
+
);
|
|
802
|
+
|
|
803
|
+
return true;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
async sendEmailVerification(input: SendEmailVerificationRequestDto) {
|
|
807
|
+
this.debugLogger.logFunctionEntry('sendEmailVerification', 'AuthService');
|
|
808
|
+
|
|
809
|
+
try {
|
|
810
|
+
const user = RequestContext.currentUser();
|
|
811
|
+
if (!user) {
|
|
812
|
+
throw new UnauthorizedException('User not authenticated');
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
const fullUser = await this.getUserWithRolesAndPermissions(user.id);
|
|
816
|
+
|
|
817
|
+
if (!fullUser.email) {
|
|
818
|
+
throw new BadRequestException('User does not have an email address');
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
if (fullUser.emailVerifiedAt) {
|
|
822
|
+
throw new BadRequestException('Email is already verified');
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
// Generate OTP
|
|
826
|
+
const otp = generateOtp();
|
|
827
|
+
const expiresAt = new Date();
|
|
828
|
+
expiresAt.setMinutes(expiresAt.getMinutes() + 30); // OTP expires in 30 minutes
|
|
829
|
+
|
|
830
|
+
// Save OTP to database
|
|
831
|
+
const otpEntity = await this.otpRepository.save({
|
|
832
|
+
userId: fullUser.id,
|
|
833
|
+
code: otp,
|
|
834
|
+
expiresAt,
|
|
835
|
+
type: OTPTypeEnum.VERIFICATION
|
|
836
|
+
});
|
|
837
|
+
|
|
838
|
+
// Emit email verification event - email sending should be handled by event listener
|
|
839
|
+
await this.eventEmitter.emitAsync(
|
|
840
|
+
NestAuthEvents.EMAIL_VERIFICATION_REQUESTED,
|
|
841
|
+
{
|
|
842
|
+
user: fullUser,
|
|
843
|
+
tenantId: fullUser.tenantId,
|
|
844
|
+
otp: otpEntity,
|
|
845
|
+
}
|
|
846
|
+
);
|
|
847
|
+
|
|
848
|
+
this.debugLogger.logFunctionExit('sendEmailVerification', 'AuthService');
|
|
849
|
+
return { message: 'Verification email sent successfully' };
|
|
850
|
+
|
|
851
|
+
} catch (error) {
|
|
852
|
+
this.debugLogger.logError(error, 'sendEmailVerification');
|
|
853
|
+
throw error;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
async verifyEmail(input: VerifyEmailRequestDto) {
|
|
858
|
+
this.debugLogger.logFunctionEntry('verifyEmail', 'AuthService');
|
|
859
|
+
|
|
860
|
+
try {
|
|
861
|
+
const user = RequestContext.currentUser();
|
|
862
|
+
if (!user) {
|
|
863
|
+
throw new UnauthorizedException('User not authenticated');
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
const fullUser = await this.getUserWithRolesAndPermissions(user.id);
|
|
867
|
+
|
|
868
|
+
if (!fullUser.email) {
|
|
869
|
+
throw new BadRequestException('User does not have an email address');
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
if (fullUser.emailVerifiedAt) {
|
|
873
|
+
throw new BadRequestException('Email is already verified');
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
// Find valid OTP
|
|
877
|
+
const validOtp = await this.otpRepository.findOne({
|
|
878
|
+
where: {
|
|
879
|
+
userId: fullUser.id,
|
|
880
|
+
code: input.otp,
|
|
881
|
+
type: OTPTypeEnum.VERIFICATION,
|
|
882
|
+
used: false
|
|
883
|
+
}
|
|
884
|
+
});
|
|
885
|
+
|
|
886
|
+
if (!validOtp) {
|
|
887
|
+
throw new BadRequestException('Invalid verification code');
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
if (moment(validOtp.expiresAt).isBefore(new Date())) {
|
|
891
|
+
throw new BadRequestException('Verification code has expired');
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
// Mark OTP as used
|
|
895
|
+
validOtp.used = true;
|
|
896
|
+
await this.otpRepository.save(validOtp);
|
|
897
|
+
|
|
898
|
+
// Verify user email
|
|
899
|
+
fullUser.emailVerifiedAt = new Date();
|
|
900
|
+
fullUser.isVerified = true;
|
|
901
|
+
await this.userRepository.save(fullUser);
|
|
902
|
+
|
|
903
|
+
// Emit email verified event
|
|
904
|
+
await this.eventEmitter.emitAsync(
|
|
905
|
+
NestAuthEvents.EMAIL_VERIFIED,
|
|
906
|
+
{
|
|
907
|
+
user: fullUser,
|
|
908
|
+
tenantId: fullUser.tenantId,
|
|
909
|
+
}
|
|
910
|
+
);
|
|
911
|
+
|
|
912
|
+
this.debugLogger.logFunctionExit('verifyEmail', 'AuthService');
|
|
913
|
+
return { message: 'Email verified successfully' };
|
|
914
|
+
|
|
915
|
+
} catch (error) {
|
|
916
|
+
this.debugLogger.logError(error, 'verifyEmail');
|
|
917
|
+
throw error;
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
|
|
922
|
+
|
|
923
|
+
private generateTokensPayload(session: SessionPayload, otherPayload: Partial<JWTTokenPayload> = {}): JWTTokenPayload {
|
|
924
|
+
|
|
925
|
+
const payload: JWTTokenPayload = {
|
|
926
|
+
id: session.userId,
|
|
927
|
+
sub: session.userId,
|
|
928
|
+
sessionId: session.id,
|
|
929
|
+
email: session.data?.user?.email,
|
|
930
|
+
phone: session.data?.user?.phone,
|
|
931
|
+
isVerified: session.data?.user?.isVerified,
|
|
932
|
+
roles: session.data?.roles,
|
|
933
|
+
tenantId: session.data?.user?.tenantId,
|
|
934
|
+
isMfaEnabled: session.data?.user?.isMfaEnabled,
|
|
935
|
+
isMfaVerified: session.data?.isMfaVerified,
|
|
936
|
+
...otherPayload,
|
|
937
|
+
};
|
|
938
|
+
|
|
939
|
+
return payload;
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
private async generateTokensFromSession(session: SessionPayload): Promise<{ accessToken: string; refreshToken: string }> {
|
|
943
|
+
const payload = this.generateTokensPayload(session);
|
|
944
|
+
const tokens = await this.jwtService.generateTokens(payload);
|
|
945
|
+
return tokens
|
|
946
|
+
}
|
|
947
|
+
}
|