@nauth-toolkit/core 0.1.14 → 0.1.18
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/dist/adapters/database-columns.d.ts +70 -0
- package/dist/adapters/database-columns.d.ts.map +1 -1
- package/dist/adapters/database-columns.js +76 -2
- package/dist/adapters/database-columns.js.map +1 -1
- package/dist/adapters/express.adapter.d.ts +66 -0
- package/dist/adapters/express.adapter.d.ts.map +1 -1
- package/dist/adapters/express.adapter.js +80 -0
- package/dist/adapters/express.adapter.js.map +1 -1
- package/dist/adapters/fastify.adapter.d.ts +42 -0
- package/dist/adapters/fastify.adapter.d.ts.map +1 -1
- package/dist/adapters/fastify.adapter.js +86 -0
- package/dist/adapters/fastify.adapter.js.map +1 -1
- package/dist/adapters/index.d.ts +5 -0
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +9 -0
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/storage.factory.d.ts +107 -0
- package/dist/adapters/storage.factory.d.ts.map +1 -1
- package/dist/adapters/storage.factory.js +114 -0
- package/dist/adapters/storage.factory.js.map +1 -1
- package/dist/adapters.d.ts +8 -0
- package/dist/adapters.d.ts.map +1 -1
- package/dist/adapters.js +8 -0
- package/dist/adapters.js.map +1 -1
- package/dist/bootstrap.d.ts +82 -0
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +106 -0
- package/dist/bootstrap.js.map +1 -1
- package/dist/dto/admin-set-password.dto.d.ts +90 -0
- package/dist/dto/admin-set-password.dto.d.ts.map +1 -1
- package/dist/dto/admin-set-password.dto.js +91 -0
- package/dist/dto/admin-set-password.dto.js.map +1 -1
- package/dist/dto/auth-challenge.dto.d.ts +170 -0
- package/dist/dto/auth-challenge.dto.d.ts.map +1 -1
- package/dist/dto/auth-challenge.dto.js +170 -0
- package/dist/dto/auth-challenge.dto.js.map +1 -1
- package/dist/dto/auth-response.dto.d.ts +196 -0
- package/dist/dto/auth-response.dto.d.ts.map +1 -1
- package/dist/dto/auth-response.dto.js +149 -0
- package/dist/dto/auth-response.dto.js.map +1 -1
- package/dist/dto/challenge-response.dto.d.ts +155 -0
- package/dist/dto/challenge-response.dto.d.ts.map +1 -1
- package/dist/dto/challenge-response.dto.js +8 -0
- package/dist/dto/challenge-response.dto.js.map +1 -1
- package/dist/dto/change-password-request.dto.d.ts +35 -0
- package/dist/dto/change-password-request.dto.d.ts.map +1 -1
- package/dist/dto/change-password-request.dto.js +35 -0
- package/dist/dto/change-password-request.dto.js.map +1 -1
- package/dist/dto/change-password-response.dto.d.ts +25 -0
- package/dist/dto/change-password-response.dto.d.ts.map +1 -1
- package/dist/dto/change-password-response.dto.js +25 -0
- package/dist/dto/change-password-response.dto.js.map +1 -1
- package/dist/dto/change-password.dto.d.ts +45 -0
- package/dist/dto/change-password.dto.d.ts.map +1 -1
- package/dist/dto/change-password.dto.js +45 -0
- package/dist/dto/change-password.dto.js.map +1 -1
- package/dist/dto/confirm-forgot-password.dto.d.ts +59 -0
- package/dist/dto/confirm-forgot-password.dto.d.ts.map +1 -1
- package/dist/dto/confirm-forgot-password.dto.js +59 -0
- package/dist/dto/confirm-forgot-password.dto.js.map +1 -1
- package/dist/dto/error-response.dto.d.ts +103 -0
- package/dist/dto/error-response.dto.d.ts.map +1 -1
- package/dist/dto/error-response.dto.js +103 -0
- package/dist/dto/error-response.dto.js.map +1 -1
- package/dist/dto/forgot-password.dto.d.ts +58 -0
- package/dist/dto/forgot-password.dto.d.ts.map +1 -1
- package/dist/dto/forgot-password.dto.js +58 -0
- package/dist/dto/forgot-password.dto.js.map +1 -1
- package/dist/dto/get-available-methods.dto.d.ts +37 -0
- package/dist/dto/get-available-methods.dto.d.ts.map +1 -1
- package/dist/dto/get-available-methods.dto.js +37 -0
- package/dist/dto/get-available-methods.dto.js.map +1 -1
- package/dist/dto/get-challenge-data-response.dto.d.ts +24 -0
- package/dist/dto/get-challenge-data-response.dto.d.ts.map +1 -1
- package/dist/dto/get-challenge-data-response.dto.js +24 -0
- package/dist/dto/get-challenge-data-response.dto.js.map +1 -1
- package/dist/dto/get-challenge-data.dto.d.ts +46 -0
- package/dist/dto/get-challenge-data.dto.d.ts.map +1 -1
- package/dist/dto/get-challenge-data.dto.js +46 -0
- package/dist/dto/get-challenge-data.dto.js.map +1 -1
- package/dist/dto/get-client-info.dto.d.ts +74 -0
- package/dist/dto/get-client-info.dto.d.ts.map +1 -1
- package/dist/dto/get-client-info.dto.js +74 -0
- package/dist/dto/get-client-info.dto.js.map +1 -1
- package/dist/dto/get-device-token-response.dto.d.ts +21 -0
- package/dist/dto/get-device-token-response.dto.d.ts.map +1 -1
- package/dist/dto/get-device-token-response.dto.js +21 -0
- package/dist/dto/get-device-token-response.dto.js.map +1 -1
- package/dist/dto/get-events-by-type.dto.d.ts +50 -0
- package/dist/dto/get-events-by-type.dto.d.ts.map +1 -1
- package/dist/dto/get-events-by-type.dto.js +50 -0
- package/dist/dto/get-events-by-type.dto.js.map +1 -1
- package/dist/dto/get-ip-address-response.dto.d.ts +20 -0
- package/dist/dto/get-ip-address-response.dto.d.ts.map +1 -1
- package/dist/dto/get-ip-address-response.dto.js +20 -0
- package/dist/dto/get-ip-address-response.dto.js.map +1 -1
- package/dist/dto/get-mfa-status.dto.d.ts +59 -0
- package/dist/dto/get-mfa-status.dto.d.ts.map +1 -1
- package/dist/dto/get-mfa-status.dto.js +59 -0
- package/dist/dto/get-mfa-status.dto.js.map +1 -1
- package/dist/dto/get-risk-assessment-history.dto.d.ts +28 -0
- package/dist/dto/get-risk-assessment-history.dto.d.ts.map +1 -1
- package/dist/dto/get-risk-assessment-history.dto.js +28 -0
- package/dist/dto/get-risk-assessment-history.dto.js.map +1 -1
- package/dist/dto/get-session-id-response.dto.d.ts +21 -0
- package/dist/dto/get-session-id-response.dto.d.ts.map +1 -1
- package/dist/dto/get-session-id-response.dto.js +21 -0
- package/dist/dto/get-session-id-response.dto.js.map +1 -1
- package/dist/dto/get-setup-data-response.dto.d.ts +27 -0
- package/dist/dto/get-setup-data-response.dto.d.ts.map +1 -1
- package/dist/dto/get-setup-data-response.dto.js +27 -0
- package/dist/dto/get-setup-data-response.dto.js.map +1 -1
- package/dist/dto/get-setup-data.dto.d.ts +51 -0
- package/dist/dto/get-setup-data.dto.d.ts.map +1 -1
- package/dist/dto/get-setup-data.dto.js +51 -0
- package/dist/dto/get-setup-data.dto.js.map +1 -1
- package/dist/dto/get-suspicious-activity.dto.d.ts +31 -0
- package/dist/dto/get-suspicious-activity.dto.d.ts.map +1 -1
- package/dist/dto/get-suspicious-activity.dto.js +31 -0
- package/dist/dto/get-suspicious-activity.dto.js.map +1 -1
- package/dist/dto/get-user-agent-response.dto.d.ts +19 -0
- package/dist/dto/get-user-agent-response.dto.d.ts.map +1 -1
- package/dist/dto/get-user-agent-response.dto.js +19 -0
- package/dist/dto/get-user-agent-response.dto.js.map +1 -1
- package/dist/dto/get-user-auth-history.dto.d.ts +64 -0
- package/dist/dto/get-user-auth-history.dto.d.ts.map +1 -1
- package/dist/dto/get-user-auth-history.dto.js +64 -0
- package/dist/dto/get-user-auth-history.dto.js.map +1 -1
- package/dist/dto/get-user-by-email.dto.d.ts +42 -0
- package/dist/dto/get-user-by-email.dto.d.ts.map +1 -1
- package/dist/dto/get-user-by-email.dto.js +42 -0
- package/dist/dto/get-user-by-email.dto.js.map +1 -1
- package/dist/dto/get-user-by-id.dto.d.ts +32 -0
- package/dist/dto/get-user-by-id.dto.d.ts.map +1 -1
- package/dist/dto/get-user-by-id.dto.js +32 -0
- package/dist/dto/get-user-by-id.dto.js.map +1 -1
- package/dist/dto/get-user-devices.dto.d.ts +34 -0
- package/dist/dto/get-user-devices.dto.d.ts.map +1 -1
- package/dist/dto/get-user-devices.dto.js +34 -0
- package/dist/dto/get-user-devices.dto.js.map +1 -1
- package/dist/dto/get-user-response.dto.d.ts +14 -0
- package/dist/dto/get-user-response.dto.d.ts.map +1 -1
- package/dist/dto/get-user-response.dto.js +15 -0
- package/dist/dto/get-user-response.dto.js.map +1 -1
- package/dist/dto/has-provider.dto.d.ts +33 -0
- package/dist/dto/has-provider.dto.d.ts.map +1 -1
- package/dist/dto/has-provider.dto.js +33 -0
- package/dist/dto/has-provider.dto.js.map +1 -1
- package/dist/dto/index.js +5 -0
- package/dist/dto/index.js.map +1 -1
- package/dist/dto/is-trusted-device-response.dto.d.ts +28 -0
- package/dist/dto/is-trusted-device-response.dto.d.ts.map +1 -1
- package/dist/dto/is-trusted-device-response.dto.js +28 -0
- package/dist/dto/is-trusted-device-response.dto.js.map +1 -1
- package/dist/dto/list-providers-response.dto.d.ts +19 -0
- package/dist/dto/list-providers-response.dto.d.ts.map +1 -1
- package/dist/dto/list-providers-response.dto.js +19 -0
- package/dist/dto/list-providers-response.dto.js.map +1 -1
- package/dist/dto/login.dto.d.ts +48 -0
- package/dist/dto/login.dto.d.ts.map +1 -1
- package/dist/dto/login.dto.js +50 -1
- package/dist/dto/login.dto.js.map +1 -1
- package/dist/dto/logout-all-response.dto.d.ts +20 -0
- package/dist/dto/logout-all-response.dto.d.ts.map +1 -1
- package/dist/dto/logout-all-response.dto.js +20 -0
- package/dist/dto/logout-all-response.dto.js.map +1 -1
- package/dist/dto/logout-all.dto.d.ts +42 -0
- package/dist/dto/logout-all.dto.d.ts.map +1 -1
- package/dist/dto/logout-all.dto.js +42 -0
- package/dist/dto/logout-all.dto.js.map +1 -1
- package/dist/dto/logout-response.dto.d.ts +21 -0
- package/dist/dto/logout-response.dto.d.ts.map +1 -1
- package/dist/dto/logout-response.dto.js +21 -0
- package/dist/dto/logout-response.dto.js.map +1 -1
- package/dist/dto/logout.dto.d.ts +45 -0
- package/dist/dto/logout.dto.d.ts.map +1 -1
- package/dist/dto/logout.dto.js +45 -0
- package/dist/dto/logout.dto.js.map +1 -1
- package/dist/dto/refresh-token.dto.d.ts +28 -0
- package/dist/dto/refresh-token.dto.d.ts.map +1 -1
- package/dist/dto/refresh-token.dto.js +28 -0
- package/dist/dto/refresh-token.dto.js.map +1 -1
- package/dist/dto/remove-devices.dto.d.ts +51 -0
- package/dist/dto/remove-devices.dto.d.ts.map +1 -1
- package/dist/dto/remove-devices.dto.js +51 -0
- package/dist/dto/remove-devices.dto.js.map +1 -1
- package/dist/dto/resend-code-response.dto.d.ts +28 -0
- package/dist/dto/resend-code-response.dto.d.ts.map +1 -1
- package/dist/dto/resend-code-response.dto.js +28 -0
- package/dist/dto/resend-code-response.dto.js.map +1 -1
- package/dist/dto/resend-code.dto.d.ts +37 -0
- package/dist/dto/resend-code.dto.d.ts.map +1 -1
- package/dist/dto/resend-code.dto.js +37 -0
- package/dist/dto/resend-code.dto.js.map +1 -1
- package/dist/dto/reset-password.dto.d.ts +74 -0
- package/dist/dto/reset-password.dto.d.ts.map +1 -1
- package/dist/dto/reset-password.dto.js +76 -1
- package/dist/dto/reset-password.dto.js.map +1 -1
- package/dist/dto/respond-challenge.dto.d.ts +147 -0
- package/dist/dto/respond-challenge.dto.d.ts.map +1 -1
- package/dist/dto/respond-challenge.dto.js +162 -0
- package/dist/dto/respond-challenge.dto.js.map +1 -1
- package/dist/dto/set-mfa-exemption.dto.d.ts +65 -0
- package/dist/dto/set-mfa-exemption.dto.d.ts.map +1 -1
- package/dist/dto/set-mfa-exemption.dto.js +65 -0
- package/dist/dto/set-mfa-exemption.dto.js.map +1 -1
- package/dist/dto/set-must-change-password-response.dto.d.ts +23 -0
- package/dist/dto/set-must-change-password-response.dto.d.ts.map +1 -1
- package/dist/dto/set-must-change-password-response.dto.js +23 -0
- package/dist/dto/set-must-change-password-response.dto.js.map +1 -1
- package/dist/dto/set-must-change-password.dto.d.ts +32 -0
- package/dist/dto/set-must-change-password.dto.d.ts.map +1 -1
- package/dist/dto/set-must-change-password.dto.js +32 -0
- package/dist/dto/set-must-change-password.dto.js.map +1 -1
- package/dist/dto/set-preferred-method.dto.d.ts +48 -0
- package/dist/dto/set-preferred-method.dto.d.ts.map +1 -1
- package/dist/dto/set-preferred-method.dto.js +48 -0
- package/dist/dto/set-preferred-method.dto.js.map +1 -1
- package/dist/dto/setup-mfa.dto.d.ts +62 -0
- package/dist/dto/setup-mfa.dto.d.ts.map +1 -1
- package/dist/dto/setup-mfa.dto.js +62 -0
- package/dist/dto/setup-mfa.dto.js.map +1 -1
- package/dist/dto/signup.dto.d.ts +92 -0
- package/dist/dto/signup.dto.d.ts.map +1 -1
- package/dist/dto/signup.dto.js +93 -0
- package/dist/dto/signup.dto.js.map +1 -1
- package/dist/dto/social-auth.dto.d.ts +234 -0
- package/dist/dto/social-auth.dto.d.ts.map +1 -1
- package/dist/dto/social-auth.dto.js +234 -0
- package/dist/dto/social-auth.dto.js.map +1 -1
- package/dist/dto/trust-device-response.dto.d.ts +26 -0
- package/dist/dto/trust-device-response.dto.d.ts.map +1 -1
- package/dist/dto/trust-device-response.dto.js +26 -0
- package/dist/dto/trust-device-response.dto.js.map +1 -1
- package/dist/dto/trust-device.dto.d.ts +9 -0
- package/dist/dto/trust-device.dto.d.ts.map +1 -1
- package/dist/dto/trust-device.dto.js +9 -0
- package/dist/dto/trust-device.dto.js.map +1 -1
- package/dist/dto/update-user-attributes-request.dto.d.ts +36 -0
- package/dist/dto/update-user-attributes-request.dto.d.ts.map +1 -1
- package/dist/dto/update-user-attributes-request.dto.js +36 -0
- package/dist/dto/update-user-attributes-request.dto.js.map +1 -1
- package/dist/dto/user-response.dto.d.ts +81 -0
- package/dist/dto/user-response.dto.d.ts.map +1 -1
- package/dist/dto/user-response.dto.js +84 -2
- package/dist/dto/user-response.dto.js.map +1 -1
- package/dist/dto/user-update.dto.d.ts +132 -0
- package/dist/dto/user-update.dto.d.ts.map +1 -1
- package/dist/dto/user-update.dto.js +133 -0
- package/dist/dto/user-update.dto.js.map +1 -1
- package/dist/dto/verify-email.dto.d.ts +171 -0
- package/dist/dto/verify-email.dto.d.ts.map +1 -1
- package/dist/dto/verify-email.dto.js +173 -1
- package/dist/dto/verify-email.dto.js.map +1 -1
- package/dist/dto/verify-mfa-code.dto.d.ts +65 -0
- package/dist/dto/verify-mfa-code.dto.d.ts.map +1 -1
- package/dist/dto/verify-mfa-code.dto.js +65 -0
- package/dist/dto/verify-mfa-code.dto.js.map +1 -1
- package/dist/dto/verify-phone-by-sub.dto.d.ts +49 -0
- package/dist/dto/verify-phone-by-sub.dto.d.ts.map +1 -1
- package/dist/dto/verify-phone-by-sub.dto.js +49 -0
- package/dist/dto/verify-phone-by-sub.dto.js.map +1 -1
- package/dist/dto/verify-phone.dto.d.ts +139 -0
- package/dist/dto/verify-phone.dto.d.ts.map +1 -1
- package/dist/dto/verify-phone.dto.js +142 -1
- package/dist/dto/verify-phone.dto.js.map +1 -1
- package/dist/dto.d.ts +10 -0
- package/dist/dto.d.ts.map +1 -1
- package/dist/dto.js +10 -0
- package/dist/dto.js.map +1 -1
- package/dist/entities/auth-audit.entity.d.ts +159 -0
- package/dist/entities/auth-audit.entity.d.ts.map +1 -1
- package/dist/entities/auth-audit.entity.js +166 -0
- package/dist/entities/auth-audit.entity.js.map +1 -1
- package/dist/entities/challenge-session.entity.d.ts +87 -0
- package/dist/entities/challenge-session.entity.d.ts.map +1 -1
- package/dist/entities/challenge-session.entity.js +87 -0
- package/dist/entities/challenge-session.entity.js.map +1 -1
- package/dist/entities/index.d.ts +18 -0
- package/dist/entities/index.d.ts.map +1 -1
- package/dist/entities/index.js +18 -0
- package/dist/entities/index.js.map +1 -1
- package/dist/entities/login-attempt.entity.d.ts +43 -0
- package/dist/entities/login-attempt.entity.d.ts.map +1 -1
- package/dist/entities/login-attempt.entity.js +43 -0
- package/dist/entities/login-attempt.entity.js.map +1 -1
- package/dist/entities/mfa-device.entity.d.ts +112 -0
- package/dist/entities/mfa-device.entity.d.ts.map +1 -1
- package/dist/entities/mfa-device.entity.js +112 -0
- package/dist/entities/mfa-device.entity.js.map +1 -1
- package/dist/entities/rate-limit.entity.d.ts +31 -0
- package/dist/entities/rate-limit.entity.d.ts.map +1 -1
- package/dist/entities/rate-limit.entity.js +31 -0
- package/dist/entities/rate-limit.entity.js.map +1 -1
- package/dist/entities/session.entity.d.ts +121 -0
- package/dist/entities/session.entity.d.ts.map +1 -1
- package/dist/entities/session.entity.js +121 -0
- package/dist/entities/session.entity.js.map +1 -1
- package/dist/entities/social-account.entity.d.ts +75 -0
- package/dist/entities/social-account.entity.d.ts.map +1 -1
- package/dist/entities/social-account.entity.js +75 -0
- package/dist/entities/social-account.entity.js.map +1 -1
- package/dist/entities/storage-lock.entity.d.ts +28 -0
- package/dist/entities/storage-lock.entity.d.ts.map +1 -1
- package/dist/entities/storage-lock.entity.js +28 -0
- package/dist/entities/storage-lock.entity.js.map +1 -1
- package/dist/entities/trusted-device.entity.d.ts +83 -0
- package/dist/entities/trusted-device.entity.d.ts.map +1 -1
- package/dist/entities/trusted-device.entity.js +83 -0
- package/dist/entities/trusted-device.entity.js.map +1 -1
- package/dist/entities/user.entity.d.ts +166 -0
- package/dist/entities/user.entity.d.ts.map +1 -1
- package/dist/entities/user.entity.js +166 -0
- package/dist/entities/user.entity.js.map +1 -1
- package/dist/entities/verification-token.entity.d.ts +102 -0
- package/dist/entities/verification-token.entity.d.ts.map +1 -1
- package/dist/entities/verification-token.entity.js +102 -0
- package/dist/entities/verification-token.entity.js.map +1 -1
- package/dist/entities.d.ts +8 -0
- package/dist/entities.d.ts.map +1 -1
- package/dist/entities.js +8 -0
- package/dist/entities.js.map +1 -1
- package/dist/enums/auth-audit-event-type.enum.d.ts +211 -0
- package/dist/enums/auth-audit-event-type.enum.d.ts.map +1 -1
- package/dist/enums/auth-audit-event-type.enum.js +244 -0
- package/dist/enums/auth-audit-event-type.enum.js.map +1 -1
- package/dist/enums/error-codes.enum.d.ts +296 -0
- package/dist/enums/error-codes.enum.d.ts.map +1 -1
- package/dist/enums/error-codes.enum.js +332 -0
- package/dist/enums/error-codes.enum.js.map +1 -1
- package/dist/enums/mfa-method.enum.d.ts +74 -0
- package/dist/enums/mfa-method.enum.d.ts.map +1 -1
- package/dist/enums/mfa-method.enum.js +64 -0
- package/dist/enums/mfa-method.enum.js.map +1 -1
- package/dist/enums/risk-factor.enum.d.ts +91 -0
- package/dist/enums/risk-factor.enum.d.ts.map +1 -1
- package/dist/enums/risk-factor.enum.js +97 -0
- package/dist/enums/risk-factor.enum.js.map +1 -1
- package/dist/exceptions/nauth.exception.d.ts +149 -0
- package/dist/exceptions/nauth.exception.d.ts.map +1 -1
- package/dist/exceptions/nauth.exception.js +159 -0
- package/dist/exceptions/nauth.exception.js.map +1 -1
- package/dist/handlers/auth.handler.d.ts +32 -0
- package/dist/handlers/auth.handler.d.ts.map +1 -1
- package/dist/handlers/auth.handler.js +47 -1
- package/dist/handlers/auth.handler.js.map +1 -1
- package/dist/handlers/client-info.handler.d.ts +25 -0
- package/dist/handlers/client-info.handler.d.ts.map +1 -1
- package/dist/handlers/client-info.handler.js +36 -2
- package/dist/handlers/client-info.handler.js.map +1 -1
- package/dist/handlers/csrf.handler.d.ts +32 -0
- package/dist/handlers/csrf.handler.d.ts.map +1 -1
- package/dist/handlers/csrf.handler.js +49 -1
- package/dist/handlers/csrf.handler.js.map +1 -1
- package/dist/handlers/token-delivery.handler.d.ts +16 -0
- package/dist/handlers/token-delivery.handler.d.ts.map +1 -1
- package/dist/handlers/token-delivery.handler.js +22 -1
- package/dist/handlers/token-delivery.handler.js.map +1 -1
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +67 -0
- package/dist/index.js.map +1 -1
- package/dist/interfaces/client-info.interface.d.ts +58 -0
- package/dist/interfaces/client-info.interface.d.ts.map +1 -1
- package/dist/interfaces/config.interface.d.ts +1780 -0
- package/dist/interfaces/config.interface.d.ts.map +1 -1
- package/dist/interfaces/config.interface.js +16 -0
- package/dist/interfaces/config.interface.js.map +1 -1
- package/dist/interfaces/entities.interface.d.ts +48 -0
- package/dist/interfaces/entities.interface.d.ts.map +1 -1
- package/dist/interfaces/entities.interface.js +8 -0
- package/dist/interfaces/entities.interface.js.map +1 -1
- package/dist/interfaces/index.js +5 -0
- package/dist/interfaces/index.js.map +1 -1
- package/dist/interfaces/logger.interface.d.ts +213 -0
- package/dist/interfaces/logger.interface.d.ts.map +1 -1
- package/dist/interfaces/logger.interface.js +35 -0
- package/dist/interfaces/logger.interface.js.map +1 -1
- package/dist/interfaces/mfa-provider.interface.d.ts +134 -0
- package/dist/interfaces/mfa-provider.interface.d.ts.map +1 -1
- package/dist/interfaces/oauth.interface.d.ts +110 -0
- package/dist/interfaces/oauth.interface.d.ts.map +1 -1
- package/dist/interfaces/provider.interface.d.ts +83 -0
- package/dist/interfaces/provider.interface.d.ts.map +1 -1
- package/dist/interfaces/sms-template.interface.d.ts +246 -0
- package/dist/interfaces/sms-template.interface.d.ts.map +1 -1
- package/dist/interfaces/sms-template.interface.js +26 -0
- package/dist/interfaces/sms-template.interface.js.map +1 -1
- package/dist/interfaces/social-auth-provider.interface.d.ts +115 -0
- package/dist/interfaces/social-auth-provider.interface.d.ts.map +1 -1
- package/dist/interfaces/storage-adapter.interface.d.ts +37 -0
- package/dist/interfaces/storage-adapter.interface.d.ts.map +1 -1
- package/dist/interfaces/template.interface.d.ts +351 -0
- package/dist/interfaces/template.interface.d.ts.map +1 -1
- package/dist/interfaces/template.interface.js +13 -0
- package/dist/interfaces/template.interface.js.map +1 -1
- package/dist/interfaces/token-verifier.interface.d.ts +101 -0
- package/dist/interfaces/token-verifier.interface.d.ts.map +1 -1
- package/dist/interfaces.d.ts +8 -0
- package/dist/interfaces.d.ts.map +1 -1
- package/dist/interfaces.js +8 -0
- package/dist/interfaces.js.map +1 -1
- package/dist/internal.d.ts +120 -0
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +138 -0
- package/dist/internal.js.map +1 -1
- package/dist/platform/interfaces.d.ts +187 -0
- package/dist/platform/interfaces.d.ts.map +1 -1
- package/dist/platform/interfaces.js +11 -0
- package/dist/platform/interfaces.js.map +1 -1
- package/dist/schemas/auth-config.schema.d.ts +62 -0
- package/dist/schemas/auth-config.schema.d.ts.map +1 -1
- package/dist/schemas/auth-config.schema.js +189 -9
- package/dist/schemas/auth-config.schema.js.map +1 -1
- package/dist/services/adaptive-mfa-decision.service.d.ts +144 -0
- package/dist/services/adaptive-mfa-decision.service.d.ts.map +1 -1
- package/dist/services/adaptive-mfa-decision.service.js +151 -5
- package/dist/services/adaptive-mfa-decision.service.js.map +1 -1
- package/dist/services/auth-audit.service.d.ts +195 -0
- package/dist/services/auth-audit.service.d.ts.map +1 -1
- package/dist/services/auth-audit.service.js +228 -1
- package/dist/services/auth-audit.service.js.map +1 -1
- package/dist/services/auth-challenge-helper.service.d.ts +144 -1
- package/dist/services/auth-challenge-helper.service.d.ts.map +1 -1
- package/dist/services/auth-challenge-helper.service.js +295 -16
- package/dist/services/auth-challenge-helper.service.js.map +1 -1
- package/dist/services/auth-flow-context-builder.service.d.ts +120 -1
- package/dist/services/auth-flow-context-builder.service.d.ts.map +1 -1
- package/dist/services/auth-flow-context-builder.service.js +184 -5
- package/dist/services/auth-flow-context-builder.service.js.map +1 -1
- package/dist/services/auth-flow-rules.d.ts +136 -0
- package/dist/services/auth-flow-rules.d.ts.map +1 -1
- package/dist/services/auth-flow-rules.js +137 -0
- package/dist/services/auth-flow-rules.js.map +1 -1
- package/dist/services/auth-flow-state-definitions.d.ts +40 -0
- package/dist/services/auth-flow-state-definitions.d.ts.map +1 -1
- package/dist/services/auth-flow-state-definitions.js +98 -0
- package/dist/services/auth-flow-state-definitions.js.map +1 -1
- package/dist/services/auth-flow-state-machine.service.d.ts +91 -0
- package/dist/services/auth-flow-state-machine.service.d.ts.map +1 -1
- package/dist/services/auth-flow-state-machine.service.js +102 -0
- package/dist/services/auth-flow-state-machine.service.js.map +1 -1
- package/dist/services/auth-flow-state-machine.types.d.ts +221 -0
- package/dist/services/auth-flow-state-machine.types.d.ts.map +1 -1
- package/dist/services/auth-flow-state-machine.types.js +47 -0
- package/dist/services/auth-flow-state-machine.types.js.map +1 -1
- package/dist/services/auth.service.d.ts +397 -1
- package/dist/services/auth.service.d.ts.map +1 -1
- package/dist/services/auth.service.js +943 -27
- package/dist/services/auth.service.js.map +1 -1
- package/dist/services/challenge.service.d.ts +255 -1
- package/dist/services/challenge.service.d.ts.map +1 -1
- package/dist/services/challenge.service.js +327 -3
- package/dist/services/challenge.service.js.map +1 -1
- package/dist/services/client-info.service.d.ts +143 -0
- package/dist/services/client-info.service.d.ts.map +1 -1
- package/dist/services/client-info.service.js +161 -0
- package/dist/services/client-info.service.js.map +1 -1
- package/dist/services/csrf.service.d.ts +15 -0
- package/dist/services/csrf.service.d.ts.map +1 -1
- package/dist/services/csrf.service.js +16 -0
- package/dist/services/csrf.service.js.map +1 -1
- package/dist/services/email-verification.service.d.ts +52 -0
- package/dist/services/email-verification.service.d.ts.map +1 -1
- package/dist/services/email-verification.service.js +152 -12
- package/dist/services/email-verification.service.js.map +1 -1
- package/dist/services/geo-location.service.d.ts +105 -0
- package/dist/services/geo-location.service.d.ts.map +1 -1
- package/dist/services/geo-location.service.js +188 -2
- package/dist/services/geo-location.service.js.map +1 -1
- package/dist/services/jwt.service.d.ts +257 -0
- package/dist/services/jwt.service.d.ts.map +1 -1
- package/dist/services/jwt.service.js +284 -1
- package/dist/services/jwt.service.js.map +1 -1
- package/dist/services/mfa-base.service.d.ts +179 -1
- package/dist/services/mfa-base.service.d.ts.map +1 -1
- package/dist/services/mfa-base.service.js +256 -2
- package/dist/services/mfa-base.service.js.map +1 -1
- package/dist/services/mfa.service.d.ts +304 -0
- package/dist/services/mfa.service.d.ts.map +1 -1
- package/dist/services/mfa.service.js +380 -0
- package/dist/services/mfa.service.js.map +1 -1
- package/dist/services/password-reset.service.d.ts +46 -0
- package/dist/services/password-reset.service.d.ts.map +1 -1
- package/dist/services/password-reset.service.js +79 -0
- package/dist/services/password-reset.service.js.map +1 -1
- package/dist/services/password.service.d.ts +139 -0
- package/dist/services/password.service.d.ts.map +1 -1
- package/dist/services/password.service.js +167 -9
- package/dist/services/password.service.js.map +1 -1
- package/dist/services/phone-verification.service.d.ts +75 -0
- package/dist/services/phone-verification.service.d.ts.map +1 -1
- package/dist/services/phone-verification.service.js +188 -6
- package/dist/services/phone-verification.service.js.map +1 -1
- package/dist/services/risk-detection.service.d.ts +198 -0
- package/dist/services/risk-detection.service.d.ts.map +1 -1
- package/dist/services/risk-detection.service.js +358 -11
- package/dist/services/risk-detection.service.js.map +1 -1
- package/dist/services/risk-scoring.service.d.ts +84 -0
- package/dist/services/risk-scoring.service.d.ts.map +1 -1
- package/dist/services/risk-scoring.service.js +87 -0
- package/dist/services/risk-scoring.service.js.map +1 -1
- package/dist/services/session.service.d.ts +204 -0
- package/dist/services/session.service.d.ts.map +1 -1
- package/dist/services/session.service.js +289 -4
- package/dist/services/session.service.js.map +1 -1
- package/dist/services/social-auth-base.service.d.ts +123 -1
- package/dist/services/social-auth-base.service.d.ts.map +1 -1
- package/dist/services/social-auth-base.service.js +155 -2
- package/dist/services/social-auth-base.service.js.map +1 -1
- package/dist/services/social-auth.service.d.ts +191 -0
- package/dist/services/social-auth.service.d.ts.map +1 -1
- package/dist/services/social-auth.service.js +215 -2
- package/dist/services/social-auth.service.js.map +1 -1
- package/dist/services/social-provider-registry.service.d.ts +86 -0
- package/dist/services/social-provider-registry.service.d.ts.map +1 -1
- package/dist/services/social-provider-registry.service.js +86 -0
- package/dist/services/social-provider-registry.service.js.map +1 -1
- package/dist/services/trusted-device.service.d.ts +105 -0
- package/dist/services/trusted-device.service.d.ts.map +1 -1
- package/dist/services/trusted-device.service.js +133 -4
- package/dist/services/trusted-device.service.js.map +1 -1
- package/dist/storage/account-lockout-storage.service.d.ts +35 -0
- package/dist/storage/account-lockout-storage.service.d.ts.map +1 -1
- package/dist/storage/account-lockout-storage.service.js +35 -0
- package/dist/storage/account-lockout-storage.service.js.map +1 -1
- package/dist/storage/memory-storage.adapter.d.ts +148 -0
- package/dist/storage/memory-storage.adapter.d.ts.map +1 -1
- package/dist/storage/memory-storage.adapter.js +201 -6
- package/dist/storage/memory-storage.adapter.js.map +1 -1
- package/dist/storage/rate-limit-storage.service.d.ts +3 -0
- package/dist/storage/rate-limit-storage.service.d.ts.map +1 -1
- package/dist/storage/rate-limit-storage.service.js +4 -0
- package/dist/storage/rate-limit-storage.service.js.map +1 -1
- package/dist/storage.d.ts +8 -0
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +8 -0
- package/dist/storage.js.map +1 -1
- package/dist/templates/html-template.engine.d.ts +110 -0
- package/dist/templates/html-template.engine.d.ts.map +1 -1
- package/dist/templates/html-template.engine.js +147 -0
- package/dist/templates/html-template.engine.js.map +1 -1
- package/dist/templates/index.d.ts +5 -0
- package/dist/templates/index.d.ts.map +1 -1
- package/dist/templates/index.js +5 -0
- package/dist/templates/index.js.map +1 -1
- package/dist/templates/sms-template.engine.d.ts +151 -0
- package/dist/templates/sms-template.engine.d.ts.map +1 -1
- package/dist/templates/sms-template.engine.js +171 -0
- package/dist/templates/sms-template.engine.js.map +1 -1
- package/dist/templates.d.ts +8 -0
- package/dist/templates.d.ts.map +1 -1
- package/dist/templates.js +8 -0
- package/dist/templates.js.map +1 -1
- package/dist/utils/common-passwords.d.ts +42 -0
- package/dist/utils/common-passwords.d.ts.map +1 -1
- package/dist/utils/common-passwords.js +88 -0
- package/dist/utils/common-passwords.js.map +1 -1
- package/dist/utils/context-storage.d.ts +129 -0
- package/dist/utils/context-storage.d.ts.map +1 -1
- package/dist/utils/context-storage.js +129 -0
- package/dist/utils/context-storage.js.map +1 -1
- package/dist/utils/cookie-names.util.d.ts +35 -0
- package/dist/utils/cookie-names.util.d.ts.map +1 -1
- package/dist/utils/cookie-names.util.js +37 -0
- package/dist/utils/cookie-names.util.js.map +1 -1
- package/dist/utils/cookies.util.d.ts +19 -0
- package/dist/utils/cookies.util.d.ts.map +1 -1
- package/dist/utils/cookies.util.js +30 -3
- package/dist/utils/cookies.util.js.map +1 -1
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +4 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/ip-extractor.d.ts +88 -0
- package/dist/utils/ip-extractor.d.ts.map +1 -1
- package/dist/utils/ip-extractor.js +109 -16
- package/dist/utils/ip-extractor.js.map +1 -1
- package/dist/utils/nauth-logger.d.ts +70 -0
- package/dist/utils/nauth-logger.d.ts.map +1 -1
- package/dist/utils/nauth-logger.js +82 -4
- package/dist/utils/nauth-logger.js.map +1 -1
- package/dist/utils/pii-redactor.d.ts +70 -0
- package/dist/utils/pii-redactor.d.ts.map +1 -1
- package/dist/utils/pii-redactor.js +102 -0
- package/dist/utils/pii-redactor.js.map +1 -1
- package/dist/utils/setup/get-repositories.d.ts +16 -0
- package/dist/utils/setup/get-repositories.d.ts.map +1 -1
- package/dist/utils/setup/get-repositories.js +21 -0
- package/dist/utils/setup/get-repositories.js.map +1 -1
- package/dist/utils/setup/init-services.d.ts +40 -1
- package/dist/utils/setup/init-services.d.ts.map +1 -1
- package/dist/utils/setup/init-services.js +98 -0
- package/dist/utils/setup/init-services.js.map +1 -1
- package/dist/utils/setup/init-social.d.ts +27 -0
- package/dist/utils/setup/init-social.d.ts.map +1 -1
- package/dist/utils/setup/init-social.js +49 -0
- package/dist/utils/setup/init-social.js.map +1 -1
- package/dist/utils/setup/init-storage.d.ts +22 -0
- package/dist/utils/setup/init-storage.d.ts.map +1 -1
- package/dist/utils/setup/init-storage.js +36 -0
- package/dist/utils/setup/init-storage.js.map +1 -1
- package/dist/utils/setup/register-mfa.d.ts +22 -0
- package/dist/utils/setup/register-mfa.d.ts.map +1 -1
- package/dist/utils/setup/register-mfa.js +41 -0
- package/dist/utils/setup/register-mfa.js.map +1 -1
- package/dist/utils/setup/run-nauth-migrations.d.ts +7 -0
- package/dist/utils/setup/run-nauth-migrations.d.ts.map +1 -1
- package/dist/utils/setup/run-nauth-migrations.js +8 -0
- package/dist/utils/setup/run-nauth-migrations.js.map +1 -1
- package/dist/utils/token-delivery-policy.d.ts +17 -0
- package/dist/utils/token-delivery-policy.d.ts.map +1 -1
- package/dist/utils/token-delivery-policy.js +17 -0
- package/dist/utils/token-delivery-policy.js.map +1 -1
- package/dist/utils.d.ts +8 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +8 -0
- package/dist/utils.js.map +1 -1
- package/dist/validators/template.validator.d.ts +80 -0
- package/dist/validators/template.validator.d.ts.map +1 -1
- package/dist/validators/template.validator.js +94 -0
- package/dist/validators/template.validator.js.map +1 -1
- package/package.json +7 -2
|
@@ -6,6 +6,35 @@ const error_codes_enum_1 = require("../enums/error-codes.enum");
|
|
|
6
6
|
const mfa_method_enum_1 = require("../enums/mfa-method.enum");
|
|
7
7
|
const auth_challenge_dto_1 = require("../dto/auth-challenge.dto");
|
|
8
8
|
const auth_audit_event_type_enum_1 = require("../enums/auth-audit-event-type.enum");
|
|
9
|
+
/**
|
|
10
|
+
* MFA Service Registry
|
|
11
|
+
*
|
|
12
|
+
* Central registry for managing MFA provider services.
|
|
13
|
+
* Routes requests to the appropriate provider based on method name.
|
|
14
|
+
*
|
|
15
|
+
* Provider services (TOTP, SMS, Passkey) automatically register themselves
|
|
16
|
+
* when their modules are imported via OnModuleInit.
|
|
17
|
+
*
|
|
18
|
+
* **Key Features:**
|
|
19
|
+
* - Provider registration and lookup
|
|
20
|
+
* - Unified interface for MFA operations
|
|
21
|
+
* - Routing verification requests to correct provider
|
|
22
|
+
* - Device management operations
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* @Controller('auth')
|
|
27
|
+
* export class AuthController {
|
|
28
|
+
* constructor(private readonly mfaService: MFAService) {}
|
|
29
|
+
*
|
|
30
|
+
* @Post('mfa/verify')
|
|
31
|
+
* async verifyMFA(@Body() dto: { method: string; code: string }) {
|
|
32
|
+
* const provider = this.mfaService.getProvider(dto.method);
|
|
33
|
+
* return await provider.verify(user, dto.code);
|
|
34
|
+
* }
|
|
35
|
+
* }
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
9
38
|
class MFAService {
|
|
10
39
|
mfaDeviceRepository;
|
|
11
40
|
userRepository;
|
|
@@ -24,6 +53,21 @@ class MFAService {
|
|
|
24
53
|
this.auditService = auditService;
|
|
25
54
|
this.clientInfoService = clientInfoService;
|
|
26
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Register an MFA provider
|
|
58
|
+
*
|
|
59
|
+
* Called automatically by provider modules during initialization.
|
|
60
|
+
* Provider method names must be unique.
|
|
61
|
+
*
|
|
62
|
+
* @param provider - Provider service instance (must have methodName property)
|
|
63
|
+
* @throws {NAuthException} If provider is already registered
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```typescript
|
|
67
|
+
* // In provider module's OnModuleInit
|
|
68
|
+
* this.mfaService.registerProvider(this.totpProvider);
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
27
71
|
registerProvider(provider) {
|
|
28
72
|
const name = provider.methodName;
|
|
29
73
|
if (this.providers.has(name)) {
|
|
@@ -31,6 +75,19 @@ class MFAService {
|
|
|
31
75
|
}
|
|
32
76
|
this.providers.set(name, provider);
|
|
33
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* Get a provider by method name
|
|
80
|
+
*
|
|
81
|
+
* @param methodName - Method name (e.g., 'totp', 'sms', 'passkey')
|
|
82
|
+
* @returns Provider service instance
|
|
83
|
+
* @throws {NAuthException} If provider is not registered
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```typescript
|
|
87
|
+
* const totpProvider = this.mfaService.getProvider('totp');
|
|
88
|
+
* const setupData = await totpProvider.setup(user);
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
34
91
|
getProvider(methodName) {
|
|
35
92
|
const provider = this.providers.get(methodName);
|
|
36
93
|
if (!provider) {
|
|
@@ -38,39 +95,115 @@ class MFAService {
|
|
|
38
95
|
}
|
|
39
96
|
return provider;
|
|
40
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* Check if a provider is registered
|
|
100
|
+
*
|
|
101
|
+
* @param dto - Request DTO with method name
|
|
102
|
+
* @returns Response DTO with hasProvider flag
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```typescript
|
|
106
|
+
* const result = await this.mfaService.hasProvider({ methodName: 'totp' });
|
|
107
|
+
* if (result.hasProvider) {
|
|
108
|
+
* // TOTP is available
|
|
109
|
+
* }
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
41
112
|
hasProvider(dto) {
|
|
42
113
|
return {
|
|
43
114
|
hasProvider: this.providers.has(dto.methodName),
|
|
44
115
|
};
|
|
45
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* Get all registered provider method names
|
|
119
|
+
*
|
|
120
|
+
* @returns Response DTO with array of method names
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```typescript
|
|
124
|
+
* const result = this.mfaService.listProviders(); // { providers: ['totp', 'sms', 'passkey'] }
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
46
127
|
listProviders() {
|
|
47
128
|
return {
|
|
48
129
|
providers: Array.from(this.providers.keys()),
|
|
49
130
|
};
|
|
50
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* Get available MFA methods for a user
|
|
134
|
+
*
|
|
135
|
+
* Returns list of methods that are:
|
|
136
|
+
* - Registered as providers
|
|
137
|
+
* - Allowed by configuration
|
|
138
|
+
*
|
|
139
|
+
* This returns ALL methods that can be set up, not just ones the user has configured.
|
|
140
|
+
* Use getUserDevices() to check which methods the user has actually set up.
|
|
141
|
+
*
|
|
142
|
+
* @param dto - Request DTO with user sub
|
|
143
|
+
* @returns Response DTO with array of available method names
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* ```typescript
|
|
147
|
+
* const result = await this.mfaService.getAvailableMethods({ sub: user.sub });
|
|
148
|
+
* // Returns: { availableMethods: ['totp', 'sms', 'passkey'] }
|
|
149
|
+
* ```
|
|
150
|
+
*/
|
|
51
151
|
async getAvailableMethods(dto) {
|
|
152
|
+
// Look up user by sub to validate user exists
|
|
52
153
|
const userEntity = await this.userRepository.findOne({ where: { sub: dto.sub } });
|
|
53
154
|
if (!userEntity) {
|
|
54
155
|
throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.NOT_FOUND, 'User not found');
|
|
55
156
|
}
|
|
56
157
|
const available = [];
|
|
57
158
|
for (const [methodName, provider] of this.providers.entries()) {
|
|
159
|
+
// Check if method is allowed by configuration
|
|
58
160
|
if (!provider.isMethodAllowed()) {
|
|
59
161
|
continue;
|
|
60
162
|
}
|
|
163
|
+
// Return all allowed methods (whether user has set them up or not)
|
|
61
164
|
available.push(methodName);
|
|
62
165
|
}
|
|
63
166
|
return {
|
|
64
167
|
availableMethods: available,
|
|
65
168
|
};
|
|
66
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* Verify MFA code using appropriate provider
|
|
172
|
+
*
|
|
173
|
+
* Routes the verification request to the correct provider based on method name.
|
|
174
|
+
*
|
|
175
|
+
* @param dto - Request DTO with user sub, method name, code, and optional device ID
|
|
176
|
+
* @returns Response DTO with verification result
|
|
177
|
+
* @throws {NAuthException} If method is not available or verification fails
|
|
178
|
+
*
|
|
179
|
+
* @example
|
|
180
|
+
* ```typescript
|
|
181
|
+
* // Verify TOTP code
|
|
182
|
+
* const result = await this.mfaService.verifyCode({
|
|
183
|
+
* sub: user.sub,
|
|
184
|
+
* methodName: 'totp',
|
|
185
|
+
* code: '123456'
|
|
186
|
+
* });
|
|
187
|
+
*
|
|
188
|
+
* // Verify backup code
|
|
189
|
+
* const result = await this.mfaService.verifyCode({
|
|
190
|
+
* sub: user.sub,
|
|
191
|
+
* methodName: 'backup',
|
|
192
|
+
* code: 'ABC12345'
|
|
193
|
+
* });
|
|
194
|
+
* ```
|
|
195
|
+
*/
|
|
67
196
|
async verifyCode(dto) {
|
|
197
|
+
// Look up user by sub
|
|
68
198
|
const userEntity = await this.userRepository.findOne({ where: { sub: dto.sub } });
|
|
69
199
|
if (!userEntity) {
|
|
70
200
|
throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.NOT_FOUND, 'User not found');
|
|
71
201
|
}
|
|
72
202
|
const user = userEntity;
|
|
203
|
+
// Handle backup codes specially (not a provider, uses base class helper)
|
|
73
204
|
if (dto.methodName === mfa_method_enum_1.MFAMethod.BACKUP) {
|
|
205
|
+
// Get any provider to access backup code verification
|
|
206
|
+
// All providers extend BaseMFAProviderService which has verifyBackupCode
|
|
74
207
|
const firstProvider = Array.from(this.providers.values())[0];
|
|
75
208
|
if (firstProvider && 'verifyBackupCode' in firstProvider) {
|
|
76
209
|
const providerWithBackup = firstProvider;
|
|
@@ -79,11 +212,28 @@ class MFAService {
|
|
|
79
212
|
}
|
|
80
213
|
throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.VALIDATION_FAILED, 'Backup code verification not available');
|
|
81
214
|
}
|
|
215
|
+
// Get provider and verify
|
|
82
216
|
const provider = this.getProvider(dto.methodName);
|
|
83
217
|
const isValid = await provider.verify(user, dto.code, dto.deviceId);
|
|
84
218
|
return { valid: isValid };
|
|
85
219
|
}
|
|
220
|
+
/**
|
|
221
|
+
* Setup MFA device using appropriate provider
|
|
222
|
+
*
|
|
223
|
+
* @param dto - Request DTO with user sub, method name, and optional setup data
|
|
224
|
+
* @returns Response DTO with provider-specific setup data
|
|
225
|
+
*
|
|
226
|
+
* @example
|
|
227
|
+
* ```typescript
|
|
228
|
+
* const result = await this.mfaService.setup({
|
|
229
|
+
* sub: user.sub,
|
|
230
|
+
* methodName: 'totp'
|
|
231
|
+
* });
|
|
232
|
+
* // Returns: { setupData: { secret, qrCode, manualEntryKey } }
|
|
233
|
+
* ```
|
|
234
|
+
*/
|
|
86
235
|
async setup(dto) {
|
|
236
|
+
// Look up user by sub
|
|
87
237
|
const userEntity = await this.userRepository.findOne({ where: { sub: dto.sub } });
|
|
88
238
|
if (!userEntity) {
|
|
89
239
|
throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.NOT_FOUND, 'User not found');
|
|
@@ -95,11 +245,25 @@ class MFAService {
|
|
|
95
245
|
setupData: setupData,
|
|
96
246
|
};
|
|
97
247
|
}
|
|
248
|
+
/**
|
|
249
|
+
* Get user's MFA devices
|
|
250
|
+
*
|
|
251
|
+
* @param dto - Request DTO with user sub
|
|
252
|
+
* @returns Response DTO with array of MFA devices
|
|
253
|
+
*
|
|
254
|
+
* @example
|
|
255
|
+
* ```typescript
|
|
256
|
+
* const result = await this.mfaService.getUserDevices({ sub: user.sub });
|
|
257
|
+
* // Returns: { devices: [...] }
|
|
258
|
+
* ```
|
|
259
|
+
*/
|
|
98
260
|
async getUserDevices(dto) {
|
|
261
|
+
// Look up user by sub to get internal ID
|
|
99
262
|
const userEntity = await this.userRepository.findOne({ where: { sub: dto.sub } });
|
|
100
263
|
if (!userEntity) {
|
|
101
264
|
throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.NOT_FOUND, 'User not found');
|
|
102
265
|
}
|
|
266
|
+
// Only fetch active devices (inactive devices are soft-deleted)
|
|
103
267
|
const devices = await this.mfaDeviceRepository.find({
|
|
104
268
|
where: { userId: userEntity.id, isActive: true },
|
|
105
269
|
order: { createdAt: 'DESC' },
|
|
@@ -108,7 +272,33 @@ class MFAService {
|
|
|
108
272
|
devices: devices,
|
|
109
273
|
};
|
|
110
274
|
}
|
|
275
|
+
/**
|
|
276
|
+
* Get comprehensive MFA status for a user
|
|
277
|
+
*
|
|
278
|
+
* Returns complete MFA configuration status including:
|
|
279
|
+
* - Whether MFA is enabled/required
|
|
280
|
+
* - Configured and available methods
|
|
281
|
+
* - Preferred method
|
|
282
|
+
* - Backup codes status
|
|
283
|
+
* - MFA exemption information
|
|
284
|
+
*
|
|
285
|
+
* This method encapsulates all business logic for MFA status,
|
|
286
|
+
* ensuring consumer apps don't need to query databases or build responses manually.
|
|
287
|
+
*
|
|
288
|
+
* @param dto - Request DTO with user sub
|
|
289
|
+
* @returns Response DTO with complete MFA status
|
|
290
|
+
*
|
|
291
|
+
* @example
|
|
292
|
+
* ```typescript
|
|
293
|
+
* @Get('mfa/status')
|
|
294
|
+
* async getMFAStatus(@CurrentUser() user: IUser) {
|
|
295
|
+
* return await this.mfaService.getMFAStatus({ sub: user.sub });
|
|
296
|
+
* }
|
|
297
|
+
* ```
|
|
298
|
+
*/
|
|
111
299
|
async getMFAStatus(dto) {
|
|
300
|
+
// Get user entity with MFA-related fields
|
|
301
|
+
// Note: mfaExemptGrantedBy is intentionally excluded as it's sensitive admin information
|
|
112
302
|
const userEntity = await this.userRepository.findOne({
|
|
113
303
|
select: [
|
|
114
304
|
'id',
|
|
@@ -125,18 +315,23 @@ class MFAService {
|
|
|
125
315
|
throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.NOT_FOUND, 'User not found');
|
|
126
316
|
}
|
|
127
317
|
const enabled = userEntity.mfaEnabled || false;
|
|
318
|
+
// Get available methods (all registered & allowed methods)
|
|
128
319
|
const availableMethodsResult = await this.getAvailableMethods({ sub: dto.sub });
|
|
320
|
+
// Add 'backup' to available methods if backup codes are enabled in config
|
|
129
321
|
const finalAvailableMethods = [...availableMethodsResult.availableMethods];
|
|
130
322
|
if (this.config?.mfa?.backup?.enabled) {
|
|
131
323
|
if (!finalAvailableMethods.includes(mfa_method_enum_1.MFAMethod.BACKUP)) {
|
|
132
324
|
finalAvailableMethods.push(mfa_method_enum_1.MFAMethod.BACKUP);
|
|
133
325
|
}
|
|
134
326
|
}
|
|
327
|
+
// Get user's configured devices
|
|
135
328
|
const devicesResult = await this.getUserDevices({ sub: dto.sub });
|
|
136
329
|
const configuredMethods = [
|
|
137
330
|
...new Set(devicesResult.devices.filter((d) => d.isActive).map((d) => d.type)),
|
|
138
331
|
];
|
|
332
|
+
// Determine if MFA is required based on config and user state
|
|
139
333
|
const required = enabled && configuredMethods.length > 0;
|
|
334
|
+
// Check backup codes
|
|
140
335
|
const hasBackupCodes = !!userEntity.backupCodes && userEntity.backupCodes.length > 0;
|
|
141
336
|
return {
|
|
142
337
|
enabled,
|
|
@@ -150,12 +345,43 @@ class MFAService {
|
|
|
150
345
|
mfaExemptGrantedAt: userEntity.mfaExemptGrantedAt || null,
|
|
151
346
|
};
|
|
152
347
|
}
|
|
348
|
+
/**
|
|
349
|
+
* Remove MFA devices by method type
|
|
350
|
+
*
|
|
351
|
+
* Comprehensive method that handles all aspects of MFA device removal:
|
|
352
|
+
* - Looks up user by sub (consumer apps should pass user.sub from @CurrentUser())
|
|
353
|
+
* - Validates method type
|
|
354
|
+
* - Removes all active devices of the specified method type
|
|
355
|
+
* - Updates user's preferred method if the removed method was preferred
|
|
356
|
+
* - Updates device primary flags
|
|
357
|
+
* - Disables MFA if this was the last device
|
|
358
|
+
* - Creates MFA_SETUP_REQUIRED challenge if MFA enforcement requires it
|
|
359
|
+
*
|
|
360
|
+
* This method encapsulates all database operations related to MFA device removal,
|
|
361
|
+
* ensuring the consumer app doesn't need to directly manipulate nauth_* tables.
|
|
362
|
+
*
|
|
363
|
+
* @param dto - Request DTO with user sub and method type
|
|
364
|
+
* @returns Response DTO with deletedCount and whether MFA was disabled
|
|
365
|
+
* @throws {NAuthException} If user not found, invalid method type, or no devices found
|
|
366
|
+
*
|
|
367
|
+
* @example
|
|
368
|
+
* ```typescript
|
|
369
|
+
* // Consumer app controller
|
|
370
|
+
* @Delete('mfa/devices/:method')
|
|
371
|
+
* async removeMFAMethod(@CurrentUser() user: IUser, @Param('method') method: string) {
|
|
372
|
+
* const result = await this.mfaService.removeDevices({ userSub: user.sub, methodType: method });
|
|
373
|
+
* return { message: 'MFA method removed successfully', ...result };
|
|
374
|
+
* }
|
|
375
|
+
* ```
|
|
376
|
+
*/
|
|
153
377
|
async removeDevices(dto) {
|
|
378
|
+
// Validate method type
|
|
154
379
|
const validMethods = [mfa_method_enum_1.MFAMethod.TOTP, mfa_method_enum_1.MFAMethod.SMS, mfa_method_enum_1.MFAMethod.EMAIL, mfa_method_enum_1.MFAMethod.PASSKEY];
|
|
155
380
|
const normalizedMethod = dto.methodType.toLowerCase();
|
|
156
381
|
if (!validMethods.includes(normalizedMethod)) {
|
|
157
382
|
throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.VALIDATION_FAILED, `Invalid MFA method: ${dto.methodType}. Valid methods are: ${validMethods.join(', ')}`);
|
|
158
383
|
}
|
|
384
|
+
// Look up user by sub using repository directly (no AuthService dependency needed)
|
|
159
385
|
const userEntity = await this.userRepository.findOne({ where: { sub: dto.userSub } });
|
|
160
386
|
if (!userEntity) {
|
|
161
387
|
throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.NOT_FOUND, 'User entity not found');
|
|
@@ -164,29 +390,38 @@ class MFAService {
|
|
|
164
390
|
if (!userId) {
|
|
165
391
|
throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.INTERNAL_ERROR, 'User entity missing internal ID');
|
|
166
392
|
}
|
|
393
|
+
// Cast to IUser for type safety
|
|
167
394
|
const user = userEntity;
|
|
168
395
|
const preferredMethod = userEntity.preferredMfaMethod;
|
|
169
396
|
const isPreferredMethod = preferredMethod === normalizedMethod;
|
|
397
|
+
// Get all active devices for this user
|
|
170
398
|
const devicesResult = await this.getUserDevices({ sub: dto.userSub });
|
|
171
399
|
const activeDevices = devicesResult.devices.filter((d) => d.isActive);
|
|
400
|
+
// Get devices of the method type to remove
|
|
172
401
|
const devicesToRemove = activeDevices.filter((d) => d.type.toLowerCase() === normalizedMethod);
|
|
173
402
|
if (devicesToRemove.length === 0) {
|
|
174
403
|
throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.VALIDATION_FAILED, `No active ${normalizedMethod} MFA devices found for this user`);
|
|
175
404
|
}
|
|
405
|
+
// Delete all devices of this method type
|
|
176
406
|
let deletedCount = 0;
|
|
177
407
|
for (const device of devicesToRemove) {
|
|
178
408
|
const result = await this.mfaDeviceRepository.delete(device.id);
|
|
179
409
|
deletedCount += result.affected || 0;
|
|
180
410
|
}
|
|
411
|
+
// Check if any devices remain after removal
|
|
181
412
|
const remainingDevicesResult = await this.getUserDevices({ sub: dto.userSub });
|
|
182
413
|
const remainingActiveDevices = remainingDevicesResult.devices.filter((d) => d.isActive);
|
|
183
414
|
let mfaDisabled = false;
|
|
415
|
+
// If no active devices remain, disable MFA for user
|
|
184
416
|
if (remainingActiveDevices.length === 0) {
|
|
185
417
|
userEntity.mfaEnabled = false;
|
|
186
418
|
userEntity.mfaMethods = [];
|
|
187
419
|
userEntity.preferredMfaMethod = null;
|
|
188
420
|
await this.userRepository.save(userEntity);
|
|
189
421
|
mfaDisabled = true;
|
|
422
|
+
// ============================================================================
|
|
423
|
+
// Audit: Record MFA disabled (all devices removed)
|
|
424
|
+
// ============================================================================
|
|
190
425
|
if (this.auditService && this.clientInfoService) {
|
|
191
426
|
try {
|
|
192
427
|
await this.auditService?.recordEvent({
|
|
@@ -195,6 +430,7 @@ class MFAService {
|
|
|
195
430
|
eventStatus: 'INFO',
|
|
196
431
|
reason: 'all_devices_removed',
|
|
197
432
|
description: 'MFA disabled - all devices removed',
|
|
433
|
+
// Client info automatically included from context
|
|
198
434
|
metadata: {
|
|
199
435
|
removedMethod: normalizedMethod,
|
|
200
436
|
deletedCount,
|
|
@@ -202,6 +438,7 @@ class MFAService {
|
|
|
202
438
|
});
|
|
203
439
|
}
|
|
204
440
|
catch (auditError) {
|
|
441
|
+
// Non-blocking: Log but continue
|
|
205
442
|
const errorMessage = auditError instanceof Error ? auditError.message : 'Unknown error';
|
|
206
443
|
this.logger?.error?.(`Failed to record MFA_DISABLED audit event: ${errorMessage}`, {
|
|
207
444
|
error: auditError,
|
|
@@ -209,11 +446,13 @@ class MFAService {
|
|
|
209
446
|
});
|
|
210
447
|
}
|
|
211
448
|
}
|
|
449
|
+
// Automatically create MFA_SETUP_REQUIRED challenge if MFA enforcement requires it
|
|
212
450
|
if (this.challengeService && this.config?.mfa?.enabled) {
|
|
213
451
|
const enforcement = this.config.mfa.enforcement || 'OPTIONAL';
|
|
214
452
|
if (enforcement === 'REQUIRED' || enforcement === 'ADAPTIVE') {
|
|
215
453
|
const user = userEntity;
|
|
216
454
|
try {
|
|
455
|
+
// Client info (ipAddress, userAgent) automatically extracted from ClientInfoService
|
|
217
456
|
await this.challengeService.createChallengeSession(user, auth_challenge_dto_1.AuthChallenge.MFA_SETUP_REQUIRED, {
|
|
218
457
|
allowedMethods: this.config.mfa.allowedMethods || [],
|
|
219
458
|
requiresSetup: true,
|
|
@@ -221,21 +460,26 @@ class MFAService {
|
|
|
221
460
|
this.logger?.log?.(`Created MFA_SETUP_REQUIRED challenge for user ${user.sub} after MFA removal`);
|
|
222
461
|
}
|
|
223
462
|
catch (error) {
|
|
463
|
+
// Log but don't fail the removal if challenge creation fails
|
|
224
464
|
this.logger?.warn?.(`Failed to create MFA_SETUP_REQUIRED challenge after MFA removal: ${error}`);
|
|
225
465
|
}
|
|
226
466
|
}
|
|
227
467
|
}
|
|
228
468
|
}
|
|
229
469
|
else {
|
|
470
|
+
// Update mfaMethods array with remaining methods
|
|
230
471
|
const remainingMethods = [...new Set(remainingActiveDevices.map((d) => d.type))];
|
|
231
472
|
userEntity.mfaMethods = remainingMethods;
|
|
473
|
+
// If the removed method was preferred, update preferred method and device primary flags
|
|
232
474
|
if (isPreferredMethod) {
|
|
233
475
|
const newPreferredMethod = remainingActiveDevices[0].type;
|
|
234
476
|
userEntity.preferredMfaMethod = newPreferredMethod;
|
|
235
477
|
await this.userRepository.save(userEntity);
|
|
478
|
+
// Update device primary flags - set first remaining device as primary
|
|
236
479
|
if (remainingActiveDevices[0].id) {
|
|
237
480
|
await this.mfaDeviceRepository.update({ id: remainingActiveDevices[0].id }, { isPrimary: true });
|
|
238
481
|
}
|
|
482
|
+
// Unset primary flag on other devices
|
|
239
483
|
for (let i = 1; i < remainingActiveDevices.length; i++) {
|
|
240
484
|
if (remainingActiveDevices[i].id) {
|
|
241
485
|
await this.mfaDeviceRepository.update({ id: remainingActiveDevices[i].id }, { isPrimary: false });
|
|
@@ -244,9 +488,13 @@ class MFAService {
|
|
|
244
488
|
this.logger?.log?.(`Updated preferred MFA method to ${newPreferredMethod} after removing ${normalizedMethod}`);
|
|
245
489
|
}
|
|
246
490
|
else {
|
|
491
|
+
// No preferred method change needed, just update mfaMethods
|
|
247
492
|
await this.userRepository.save(userEntity);
|
|
248
493
|
}
|
|
249
494
|
}
|
|
495
|
+
// ============================================================================
|
|
496
|
+
// Audit: Record MFA device removal
|
|
497
|
+
// ============================================================================
|
|
250
498
|
if (deletedCount > 0 && this.auditService && this.clientInfoService) {
|
|
251
499
|
try {
|
|
252
500
|
const user = userEntity;
|
|
@@ -260,9 +508,11 @@ class MFAService {
|
|
|
260
508
|
remainingDevices: remainingActiveDevices.length,
|
|
261
509
|
mfaDisabled,
|
|
262
510
|
},
|
|
511
|
+
// Client info automatically included from context
|
|
263
512
|
});
|
|
264
513
|
}
|
|
265
514
|
catch (auditError) {
|
|
515
|
+
// Non-blocking: Log but continue
|
|
266
516
|
const errorMessage = auditError instanceof Error ? auditError.message : 'Unknown error';
|
|
267
517
|
this.logger?.error?.(`Failed to record MFA_DEVICE_REMOVED audit event: ${errorMessage}`, {
|
|
268
518
|
error: auditError,
|
|
@@ -273,12 +523,36 @@ class MFAService {
|
|
|
273
523
|
}
|
|
274
524
|
return { deletedCount, mfaDisabled };
|
|
275
525
|
}
|
|
526
|
+
/**
|
|
527
|
+
* Set preferred MFA method for a user
|
|
528
|
+
*
|
|
529
|
+
* Updates the user's preferred MFA method and device primary flags.
|
|
530
|
+
* Validates that the method is configured for the user before setting it as preferred.
|
|
531
|
+
*
|
|
532
|
+
* This method encapsulates all database operations related to preferred method updates,
|
|
533
|
+
* ensuring the consumer app doesn't need to directly manipulate nauth_* tables.
|
|
534
|
+
*
|
|
535
|
+
* @param dto - Request DTO with user sub and method type
|
|
536
|
+
* @returns Response DTO with success message
|
|
537
|
+
* @throws {NAuthException} If user not found, invalid method type, or method not configured
|
|
538
|
+
*
|
|
539
|
+
* @example
|
|
540
|
+
* ```typescript
|
|
541
|
+
* // Consumer app controller
|
|
542
|
+
* @Put('mfa/preferred')
|
|
543
|
+
* async setPreferredMFAMethod(@CurrentUser() user: IUser, @Body() body: { method: string }) {
|
|
544
|
+
* return await this.mfaService.setPreferredMethod({ userSub: user.sub, methodType: body.method });
|
|
545
|
+
* }
|
|
546
|
+
* ```
|
|
547
|
+
*/
|
|
276
548
|
async setPreferredMethod(dto) {
|
|
549
|
+
// Validate method type
|
|
277
550
|
const validMethods = [mfa_method_enum_1.MFAMethod.TOTP, mfa_method_enum_1.MFAMethod.SMS, mfa_method_enum_1.MFAMethod.EMAIL, mfa_method_enum_1.MFAMethod.PASSKEY];
|
|
278
551
|
const normalizedMethod = dto.methodType.toLowerCase();
|
|
279
552
|
if (!validMethods.includes(normalizedMethod)) {
|
|
280
553
|
throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.VALIDATION_FAILED, `Invalid MFA method: ${dto.methodType}. Valid methods are: ${validMethods.join(', ')}`);
|
|
281
554
|
}
|
|
555
|
+
// Look up user by sub using repository directly (no AuthService dependency needed)
|
|
282
556
|
const userEntity = await this.userRepository.findOne({ where: { sub: dto.userSub } });
|
|
283
557
|
if (!userEntity) {
|
|
284
558
|
throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.NOT_FOUND, 'User not found');
|
|
@@ -287,20 +561,28 @@ class MFAService {
|
|
|
287
561
|
if (!userId) {
|
|
288
562
|
throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.INTERNAL_ERROR, 'User entity missing internal ID');
|
|
289
563
|
}
|
|
564
|
+
// Cast to IUser for type safety
|
|
290
565
|
const user = userEntity;
|
|
566
|
+
// Verify user has this method configured
|
|
291
567
|
const devicesResult = await this.getUserDevices({ sub: dto.userSub });
|
|
568
|
+
// Normalize device types for comparison (database might store in different case)
|
|
292
569
|
const preferredDevice = devicesResult.devices.find((d) => d.type.toLowerCase() === normalizedMethod && d.isActive);
|
|
293
570
|
if (!preferredDevice) {
|
|
294
571
|
throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.VALIDATION_FAILED, `MFA method '${normalizedMethod}' is not configured for this user`);
|
|
295
572
|
}
|
|
573
|
+
// Update user's preferred method directly via repository
|
|
296
574
|
await this.userRepository.update({ id: userId }, {
|
|
297
575
|
preferredMfaMethod: normalizedMethod,
|
|
298
576
|
});
|
|
577
|
+
// Update device isPrimary flags: set preferred device as primary, unset others
|
|
299
578
|
const activeDevices = devicesResult.devices.filter((d) => d.isActive);
|
|
300
579
|
for (const device of activeDevices) {
|
|
301
580
|
await this.mfaDeviceRepository.update({ id: device.id }, { isPrimary: device.id === preferredDevice.id });
|
|
302
581
|
}
|
|
303
582
|
this.logger?.log?.(`Device ${preferredDevice.id} set as primary for user ${dto.userSub}`);
|
|
583
|
+
// ============================================================================
|
|
584
|
+
// Audit: Record preferred MFA method update
|
|
585
|
+
// ============================================================================
|
|
304
586
|
if (this.auditService && this.clientInfoService) {
|
|
305
587
|
try {
|
|
306
588
|
const previousMethod = userEntity.preferredMfaMethod;
|
|
@@ -309,6 +591,7 @@ class MFAService {
|
|
|
309
591
|
eventType: auth_audit_event_type_enum_1.AuthAuditEventType.MFA_PREFERRED_METHOD_UPDATED,
|
|
310
592
|
eventStatus: 'INFO',
|
|
311
593
|
metadata: {
|
|
594
|
+
// Client info automatically included from context
|
|
312
595
|
previousMethod: previousMethod || null,
|
|
313
596
|
newMethod: normalizedMethod,
|
|
314
597
|
deviceId: preferredDevice.id,
|
|
@@ -316,6 +599,7 @@ class MFAService {
|
|
|
316
599
|
});
|
|
317
600
|
}
|
|
318
601
|
catch (auditError) {
|
|
602
|
+
// Non-blocking: Log but continue
|
|
319
603
|
const errorMessage = auditError instanceof Error ? auditError.message : 'Unknown error';
|
|
320
604
|
this.logger?.error?.(`Failed to record MFA_PREFERRED_METHOD_UPDATED audit event: ${errorMessage}`, {
|
|
321
605
|
error: auditError,
|
|
@@ -328,22 +612,57 @@ class MFAService {
|
|
|
328
612
|
message: 'Preferred method updated',
|
|
329
613
|
};
|
|
330
614
|
}
|
|
615
|
+
/**
|
|
616
|
+
* Grant or revoke a user's exemption from multi-factor authentication (MFA) requirements.
|
|
617
|
+
*
|
|
618
|
+
* SECURITY: This admin-only operation updates the user's MFA exemption status, logs the action,
|
|
619
|
+
* and records an audit event. MFA exemption bypasses MFA at login, but all other security controls remain enforced.
|
|
620
|
+
*
|
|
621
|
+
* @param dto - Request DTO with user sub, exempt flag, reason, and grantedBy
|
|
622
|
+
* @returns Response DTO with updated exemption fields
|
|
623
|
+
* @throws {NAuthException} If the user is not found
|
|
624
|
+
*
|
|
625
|
+
* @example
|
|
626
|
+
* ```typescript
|
|
627
|
+
* // Grant MFA exemption
|
|
628
|
+
* await mfaService.setMFAExemption({
|
|
629
|
+
* userSub: 'user-uuid',
|
|
630
|
+
* exempt: true,
|
|
631
|
+
* reason: 'Business partner requires MFA bypass',
|
|
632
|
+
* grantedBy: 'admin@example.com'
|
|
633
|
+
* });
|
|
634
|
+
*
|
|
635
|
+
* // Revoke MFA exemption
|
|
636
|
+
* await mfaService.setMFAExemption({
|
|
637
|
+
* userSub: 'user-uuid',
|
|
638
|
+
* exempt: false,
|
|
639
|
+
* reason: 'MFA now mandatory for this user',
|
|
640
|
+
* grantedBy: 'admin@example.com'
|
|
641
|
+
* });
|
|
642
|
+
* ```
|
|
643
|
+
*/
|
|
331
644
|
async setMFAExemption(dto) {
|
|
645
|
+
// Find user by sub (external identifier)
|
|
332
646
|
const userEntity = await this.userRepository.findOne({ where: { sub: dto.userSub } });
|
|
333
647
|
if (!userEntity) {
|
|
334
648
|
throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.NOT_FOUND, 'User not found');
|
|
335
649
|
}
|
|
336
650
|
const user = userEntity;
|
|
651
|
+
// Prepare update
|
|
337
652
|
const updateFields = {
|
|
338
653
|
mfaExempt: dto.exempt,
|
|
339
654
|
mfaExemptReason: dto.reason || null,
|
|
340
655
|
mfaExemptGrantedAt: dto.exempt ? new Date() : null,
|
|
341
656
|
mfaExemptGrantedBy: dto.exempt ? dto.grantedBy || null : null,
|
|
342
657
|
};
|
|
658
|
+
// If revoking exemption and MFA is required, check if user needs to set up MFA
|
|
659
|
+
// Note: This is just for logging - actual MFA setup requirement is checked by state machine on next login
|
|
343
660
|
if (!dto.exempt && userEntity.mfaExempt === true && !userEntity.mfaEnabled) {
|
|
344
661
|
this.logger?.warn?.(`MFA exemption revoked for user ${dto.userSub} - MFA setup will be required on next login`);
|
|
345
662
|
}
|
|
663
|
+
// Update user in database
|
|
346
664
|
await this.userRepository.update(userEntity.id, updateFields);
|
|
665
|
+
// Log the exemption change for audit trail
|
|
347
666
|
this.logger?.log?.(`MFA exemption ${dto.exempt ? 'granted' : 'revoked'} for user ${dto.userSub}`, {
|
|
348
667
|
userSub: dto.userSub,
|
|
349
668
|
exempt: dto.exempt,
|
|
@@ -351,6 +670,9 @@ class MFAService {
|
|
|
351
670
|
grantedBy: dto.grantedBy || 'System',
|
|
352
671
|
timestamp: new Date().toISOString(),
|
|
353
672
|
});
|
|
673
|
+
// ============================================================================
|
|
674
|
+
// Audit: Record MFA exemption grant/revoke
|
|
675
|
+
// ============================================================================
|
|
354
676
|
if (this.auditService && this.clientInfoService) {
|
|
355
677
|
try {
|
|
356
678
|
await this.auditService.recordEvent({
|
|
@@ -358,6 +680,7 @@ class MFAService {
|
|
|
358
680
|
eventType: dto.exempt ? auth_audit_event_type_enum_1.AuthAuditEventType.MFA_EXEMPTION_GRANTED : auth_audit_event_type_enum_1.AuthAuditEventType.MFA_EXEMPTION_REVOKED,
|
|
359
681
|
eventStatus: 'INFO',
|
|
360
682
|
performedBy: dto.grantedBy || null,
|
|
683
|
+
// Client info automatically included from context
|
|
361
684
|
reason: dto.reason || null,
|
|
362
685
|
metadata: {
|
|
363
686
|
previousExemptStatus: userEntity.mfaExempt,
|
|
@@ -366,6 +689,7 @@ class MFAService {
|
|
|
366
689
|
});
|
|
367
690
|
}
|
|
368
691
|
catch (auditError) {
|
|
692
|
+
// Non-blocking: Log but continue
|
|
369
693
|
const errorMessage = auditError instanceof Error ? auditError.message : 'Unknown error';
|
|
370
694
|
this.logger?.error?.(`Failed to record MFA exemption audit event: ${errorMessage}`, {
|
|
371
695
|
error: auditError,
|
|
@@ -373,6 +697,7 @@ class MFAService {
|
|
|
373
697
|
});
|
|
374
698
|
}
|
|
375
699
|
}
|
|
700
|
+
// Fetch updated user to return exemption fields
|
|
376
701
|
const exemptionData = await this.userRepository.findOne({
|
|
377
702
|
where: { id: userEntity.id },
|
|
378
703
|
select: ['mfaExempt', 'mfaExemptReason', 'mfaExemptGrantedAt'],
|
|
@@ -386,19 +711,51 @@ class MFAService {
|
|
|
386
711
|
mfaExemptGrantedAt: exemptionData.mfaExemptGrantedAt || null,
|
|
387
712
|
};
|
|
388
713
|
}
|
|
714
|
+
/**
|
|
715
|
+
* Get MFA setup data during MFA_SETUP_REQUIRED challenge
|
|
716
|
+
*
|
|
717
|
+
* Returns provider-specific setup data:
|
|
718
|
+
* - TOTP: { secret, qrCode, manualEntryKey }
|
|
719
|
+
* - SMS: { maskedPhone } or error if phone required
|
|
720
|
+
* - Passkey: WebAuthn registration options
|
|
721
|
+
*
|
|
722
|
+
* @param dto - Request DTO with session token, method, and optional setup data
|
|
723
|
+
* @returns Response DTO with provider-specific setup data
|
|
724
|
+
* @throws {NAuthException} INVALID_CHALLENGE_SESSION | VALIDATION_FAILED | PHONE_REQUIRED
|
|
725
|
+
*
|
|
726
|
+
* @example
|
|
727
|
+
* ```typescript
|
|
728
|
+
* const result = await mfaService.getSetupData({
|
|
729
|
+
* session: 'session-token',
|
|
730
|
+
* method: 'totp'
|
|
731
|
+
* });
|
|
732
|
+
* // Returns: { setupData: { secret: '...', qrCode: '...', manualEntryKey: '...' } }
|
|
733
|
+
*
|
|
734
|
+
* const result = await mfaService.getSetupData({
|
|
735
|
+
* session: 'session-token',
|
|
736
|
+
* method: 'sms',
|
|
737
|
+
* setupData: { phoneNumber: '+1234567890' }
|
|
738
|
+
* });
|
|
739
|
+
* // Returns: { setupData: { maskedPhone: '***-***-7890' } }
|
|
740
|
+
* ```
|
|
741
|
+
*/
|
|
389
742
|
async getSetupData(dto) {
|
|
390
743
|
if (!this.challengeService) {
|
|
391
744
|
throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.INTERNAL_ERROR, 'Challenge service is not available');
|
|
392
745
|
}
|
|
393
746
|
this.logger?.debug?.(`Getting MFA setup data: session=${dto.session}, method=${dto.method}`);
|
|
747
|
+
// Validate session and ensure it's MFA_SETUP_REQUIRED
|
|
394
748
|
const challengeSession = await this.challengeService.validateSession(dto.session);
|
|
395
749
|
if (challengeSession.challengeName !== auth_challenge_dto_1.AuthChallenge.MFA_SETUP_REQUIRED) {
|
|
396
750
|
throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.VALIDATION_FAILED, `Cannot get setup data: expected MFA_SETUP_REQUIRED challenge, got ${challengeSession.challengeName}`);
|
|
397
751
|
}
|
|
752
|
+
// Get user from session
|
|
398
753
|
const user = challengeSession.user;
|
|
399
754
|
if (!user) {
|
|
400
755
|
throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.VALIDATION_FAILED, 'Challenge session has no associated user');
|
|
401
756
|
}
|
|
757
|
+
// Get provider and call setup
|
|
758
|
+
// Pass challenge session ID in setupData so provider can link verification tokens
|
|
402
759
|
const setupDataWithSession = {
|
|
403
760
|
...(dto.setupData || {}),
|
|
404
761
|
challengeSessionId: challengeSession.id,
|
|
@@ -411,24 +768,47 @@ class MFAService {
|
|
|
411
768
|
setupData: result,
|
|
412
769
|
};
|
|
413
770
|
}
|
|
771
|
+
/**
|
|
772
|
+
* Get MFA challenge data during MFA_REQUIRED challenge
|
|
773
|
+
*
|
|
774
|
+
* Currently only used for passkey authentication to get WebAuthn options.
|
|
775
|
+
* SMS/TOTP codes are sent automatically when the challenge is created.
|
|
776
|
+
*
|
|
777
|
+
* @param dto - Request DTO with session token and method
|
|
778
|
+
* @returns Response DTO with provider-specific challenge data
|
|
779
|
+
* @throws {NAuthException} INVALID_CHALLENGE_SESSION | VALIDATION_FAILED
|
|
780
|
+
*
|
|
781
|
+
* @example
|
|
782
|
+
* ```typescript
|
|
783
|
+
* const result = await mfaService.getChallengeData({
|
|
784
|
+
* session: 'session-token',
|
|
785
|
+
* method: 'passkey'
|
|
786
|
+
* });
|
|
787
|
+
* // Returns: { challengeData: { challenge: '...', allowCredentials: [...], ... } }
|
|
788
|
+
* ```
|
|
789
|
+
*/
|
|
414
790
|
async getChallengeData(dto) {
|
|
415
791
|
if (!this.challengeService) {
|
|
416
792
|
throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.INTERNAL_ERROR, 'Challenge service is not available');
|
|
417
793
|
}
|
|
418
794
|
this.logger?.debug?.(`Getting MFA challenge data: session=${dto.session}, method=${dto.method}`);
|
|
795
|
+
// Validate session and ensure it's MFA_REQUIRED
|
|
419
796
|
const challengeSession = await this.challengeService.validateSession(dto.session);
|
|
420
797
|
if (challengeSession.challengeName !== auth_challenge_dto_1.AuthChallenge.MFA_REQUIRED) {
|
|
421
798
|
throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.VALIDATION_FAILED, `Cannot get challenge data: expected MFA_REQUIRED challenge, got ${challengeSession.challengeName}`);
|
|
422
799
|
}
|
|
800
|
+
// Get user from session
|
|
423
801
|
const user = challengeSession.user;
|
|
424
802
|
if (!user) {
|
|
425
803
|
throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.VALIDATION_FAILED, 'Challenge session has no associated user');
|
|
426
804
|
}
|
|
805
|
+
// Get provider and send challenge
|
|
427
806
|
const provider = this.getProvider(dto.method);
|
|
428
807
|
if (!provider.sendChallenge) {
|
|
429
808
|
throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.VALIDATION_FAILED, `MFA method '${dto.method}' does not support challenge data generation`);
|
|
430
809
|
}
|
|
431
810
|
const challengeData = await provider.sendChallenge(user);
|
|
811
|
+
// For passkey, store the challenge in session metadata for verification
|
|
432
812
|
if (dto.method === 'passkey') {
|
|
433
813
|
const passkeyOptions = challengeData;
|
|
434
814
|
const passkeyChallenge = passkeyOptions.options?.challenge;
|