@nauth-toolkit/core 0.1.13 → 0.1.17
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 +1774 -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 +48 -0
- package/dist/schemas/auth-config.schema.d.ts.map +1 -1
- package/dist/schemas/auth-config.schema.js +188 -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 +149 -10
- 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
|
@@ -36,6 +36,45 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.SessionService = void 0;
|
|
37
37
|
const typeorm_1 = require("typeorm");
|
|
38
38
|
const auth_audit_event_type_enum_1 = require("../enums/auth-audit-event-type.enum");
|
|
39
|
+
/**
|
|
40
|
+
* Session Service
|
|
41
|
+
*
|
|
42
|
+
* Manages user sessions and device tracking including:
|
|
43
|
+
* - Creating new sessions with device information
|
|
44
|
+
* - Finding sessions by ID or refresh token
|
|
45
|
+
* - Updating session activity and tokens (rotation)
|
|
46
|
+
* - Revoking individual or all user sessions
|
|
47
|
+
* - Token family management for reuse detection
|
|
48
|
+
* - Token reuse detection with storage tracking
|
|
49
|
+
* - Cleanup of expired sessions
|
|
50
|
+
*
|
|
51
|
+
* Security Features:
|
|
52
|
+
* - Token family tracking for reuse detection
|
|
53
|
+
* - Used refresh token tracking (prevents reuse attacks)
|
|
54
|
+
* - Session expiration management
|
|
55
|
+
* - Device fingerprinting support
|
|
56
|
+
* - Revocation with reason tracking
|
|
57
|
+
* - Activity timestamp updates
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```typescript
|
|
61
|
+
* // Create session
|
|
62
|
+
* const session = await sessionService.createSession({
|
|
63
|
+
* userId: user.id, // Internal ID (integer)
|
|
64
|
+
* accessTokenHash: 'hash1',
|
|
65
|
+
* refreshTokenHash: 'hash2',
|
|
66
|
+
* tokenFamily: 'family-abc',
|
|
67
|
+
* // Client info (ipAddress, userAgent, etc.) automatically extracted from ClientInfoService
|
|
68
|
+
* expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
|
|
69
|
+
* });
|
|
70
|
+
*
|
|
71
|
+
* // Revoke all user sessions (global signout)
|
|
72
|
+
* const revokedCount = await sessionService.revokeAllUserSessions(
|
|
73
|
+
* user.id, // Internal ID (integer)
|
|
74
|
+
* 'User requested global signout'
|
|
75
|
+
* );
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
39
78
|
class SessionService {
|
|
40
79
|
sessionRepository;
|
|
41
80
|
storageAdapter;
|
|
@@ -51,15 +90,31 @@ class SessionService {
|
|
|
51
90
|
this.logger = logger;
|
|
52
91
|
this.auditService = auditService;
|
|
53
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* Calculate session expiration date from config
|
|
95
|
+
*
|
|
96
|
+
* Parses session.maxLifetime config (e.g., '30d', '7d', '5h') and returns
|
|
97
|
+
* the expiration Date. Defaults to 30 days if not configured.
|
|
98
|
+
*
|
|
99
|
+
* @returns Session expiration date
|
|
100
|
+
*/
|
|
54
101
|
getSessionExpirationDate() {
|
|
55
102
|
const maxLifetime = this.config.session?.maxLifetime || '30d';
|
|
56
103
|
const expiresInSeconds = this.parseMaxLifetime(maxLifetime);
|
|
57
104
|
return new Date(Date.now() + expiresInSeconds * 1000);
|
|
58
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* Parse maxLifetime from string or number
|
|
108
|
+
*
|
|
109
|
+
* @param maxLifetime - Max lifetime (e.g., '30d', '7d', '5h', 2592000)
|
|
110
|
+
* @returns Max lifetime in seconds
|
|
111
|
+
* @private
|
|
112
|
+
*/
|
|
59
113
|
parseMaxLifetime(maxLifetime) {
|
|
60
114
|
if (typeof maxLifetime === 'number') {
|
|
61
115
|
return maxLifetime;
|
|
62
116
|
}
|
|
117
|
+
// Parse time strings (e.g., '15m', '1h', '30d')
|
|
63
118
|
const units = {
|
|
64
119
|
s: 1,
|
|
65
120
|
m: 60,
|
|
@@ -69,21 +124,59 @@ class SessionService {
|
|
|
69
124
|
const match = maxLifetime.match(/^(\d+)([smhd])$/);
|
|
70
125
|
if (!match) {
|
|
71
126
|
this.logger?.warn?.(`Invalid session.maxLifetime format: ${maxLifetime}. Using default 30 days.`);
|
|
72
|
-
return 30 * 86400;
|
|
127
|
+
return 30 * 86400; // Default to 30 days in seconds
|
|
73
128
|
}
|
|
74
129
|
const [, value, unit] = match;
|
|
75
130
|
return parseInt(value, 10) * units[unit];
|
|
76
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* Create a new session
|
|
134
|
+
*
|
|
135
|
+
* Creates a session record with token hashes, device information,
|
|
136
|
+
* and expiration time. Used during login and signup.
|
|
137
|
+
*
|
|
138
|
+
* @param data - Session creation data
|
|
139
|
+
* @param data.userId - Internal user ID (integer, not sub)
|
|
140
|
+
* @param data.accessTokenHash - SHA-256 hash of access token
|
|
141
|
+
* @param data.refreshTokenHash - SHA-256 hash of refresh token
|
|
142
|
+
* @param data.tokenFamily - Token family ID for rotation detection
|
|
143
|
+
* @param data.deviceId - Optional device identifier (UUID). Auto-generated if not provided.
|
|
144
|
+
* @param data.deviceName - Optional device name. Falls back to parsed value from ClientInfoService if not provided.
|
|
145
|
+
* @param data.deviceType - Optional device type (mobile, desktop, tablet). Falls back to parsed value from ClientInfoService if not provided.
|
|
146
|
+
* @param data.expiresAt - Session expiration date
|
|
147
|
+
* @remarks Client info (ipAddress, ipCountry, ipCity, userAgent, platform, browser) is automatically extracted from ClientInfoService context
|
|
148
|
+
* @param data.isRemembered - Whether session is from "remember me"
|
|
149
|
+
* @param data.authMethod - Authentication method: 'password', 'google', 'facebook', 'github', etc.
|
|
150
|
+
* @returns Created session
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```typescript
|
|
154
|
+
* const session = await sessionService.createSession({
|
|
155
|
+
* userId: user.id, // Internal ID (integer)
|
|
156
|
+
* accessTokenHash: jwtService.hashToken(accessToken),
|
|
157
|
+
* refreshTokenHash: jwtService.hashToken(refreshToken),
|
|
158
|
+
* tokenFamily: jwtService.generateTokenFamily(),
|
|
159
|
+
* // Client info (ipAddress, userAgent, etc.) automatically extracted from ClientInfoService
|
|
160
|
+
* expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000),
|
|
161
|
+
* });
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
77
164
|
async createSession(data) {
|
|
165
|
+
// ============================================================================
|
|
166
|
+
// Session Limit Enforcement (maxConcurrent)
|
|
167
|
+
// ============================================================================
|
|
78
168
|
const maxConcurrent = this.config.session?.maxConcurrent;
|
|
79
169
|
if (maxConcurrent && maxConcurrent > 0) {
|
|
170
|
+
// Count active sessions for this user (not revoked and not expired)
|
|
80
171
|
const now = new Date();
|
|
172
|
+
// Fetch only IDs of active sessions ordered by oldest activity
|
|
81
173
|
const activeIds = (await this.sessionRepository.find({
|
|
82
174
|
select: ['id'],
|
|
83
175
|
where: { userId: data.userId, isRevoked: false, expiresAt: (0, typeorm_1.MoreThan)(now) },
|
|
84
176
|
order: { lastActivityAt: 'ASC' },
|
|
85
177
|
}));
|
|
86
178
|
if (activeIds.length >= maxConcurrent) {
|
|
179
|
+
// Revoke oldest sessions to make room for new one (bulk update)
|
|
87
180
|
const toRevokeCount = activeIds.length - maxConcurrent + 1;
|
|
88
181
|
const idsToRevoke = activeIds.slice(0, toRevokeCount).map((s) => s.id);
|
|
89
182
|
if (idsToRevoke.length > 0) {
|
|
@@ -91,6 +184,7 @@ class SessionService {
|
|
|
91
184
|
const result = await this.sessionRepository.update({ id: (0, typeorm_1.In)(idsToRevoke) }, { isRevoked: true, revokedAt: nowTs, revokeReason: 'Max concurrent sessions exceeded' });
|
|
92
185
|
const affected = result.affected || idsToRevoke.length;
|
|
93
186
|
if (affected > 0) {
|
|
187
|
+
// Single summary audit event
|
|
94
188
|
try {
|
|
95
189
|
await this.auditService?.recordEvent({
|
|
96
190
|
userId: data.userId,
|
|
@@ -115,16 +209,35 @@ class SessionService {
|
|
|
115
209
|
}
|
|
116
210
|
}
|
|
117
211
|
}
|
|
212
|
+
// ============================================================================
|
|
213
|
+
// Get client info from context (transparent access - no parameters needed)
|
|
214
|
+
// ============================================================================
|
|
215
|
+
// Client info is automatically extracted by ClientInfoInterceptor and stored in context
|
|
216
|
+
// This includes parsed user agent info (platform, browser, deviceType, deviceName)
|
|
217
|
+
// This allows SessionService to access IP, userAgent, etc. without requiring them as parameters
|
|
118
218
|
const clientInfo = this.clientInfoService.get();
|
|
219
|
+
// ============================================================================
|
|
220
|
+
// Use parsed device information from ClientInfoService (already parsed by interceptor)
|
|
221
|
+
// ============================================================================
|
|
222
|
+
// ClientInfoService already parsed the user agent in the interceptor
|
|
223
|
+
// Use provided values or fall back to parsed values from context
|
|
119
224
|
const deviceType = data.deviceType || clientInfo.deviceType || null;
|
|
120
225
|
const deviceName = data.deviceName || clientInfo.deviceName || null;
|
|
121
226
|
const platform = clientInfo.platform || null;
|
|
122
227
|
const browser = clientInfo.browser || null;
|
|
228
|
+
// ============================================================================
|
|
229
|
+
// Generate deviceId if not provided
|
|
230
|
+
// ============================================================================
|
|
231
|
+
// DeviceId is a unique UUID that identifies a specific browser/device
|
|
232
|
+
// It's used for trusted device tracking and MFA "remember device" features
|
|
233
|
+
// In browser contexts, this should be stored in localStorage/sessionStorage
|
|
234
|
+
// and sent with each login request to maintain continuity
|
|
123
235
|
let deviceId = data.deviceId;
|
|
124
236
|
if (!deviceId) {
|
|
125
237
|
const crypto = await Promise.resolve().then(() => __importStar(require('crypto')));
|
|
126
238
|
deviceId = crypto.randomUUID();
|
|
127
239
|
}
|
|
240
|
+
// Debug: Log what we're about to save
|
|
128
241
|
if (!clientInfo.ipLatitude || !clientInfo.ipLongitude) {
|
|
129
242
|
this.logger?.warn?.(`[SessionService] Creating session WITHOUT coordinates: ` +
|
|
130
243
|
`IP=${clientInfo.ipAddress}, country=${clientInfo.ipCountry}, city=${clientInfo.ipCity}, ` +
|
|
@@ -143,6 +256,7 @@ class SessionService {
|
|
|
143
256
|
deviceId,
|
|
144
257
|
deviceName,
|
|
145
258
|
deviceType,
|
|
259
|
+
// Client info automatically extracted from ClientInfoService (transparent access)
|
|
146
260
|
ipAddress: clientInfo.ipAddress || null,
|
|
147
261
|
ipCountry: clientInfo.ipCountry || null,
|
|
148
262
|
ipCity: clientInfo.ipCity || null,
|
|
@@ -157,6 +271,9 @@ class SessionService {
|
|
|
157
271
|
lastActivityAt: new Date(),
|
|
158
272
|
});
|
|
159
273
|
const savedSession = (await this.sessionRepository.save(session));
|
|
274
|
+
// ============================================================================
|
|
275
|
+
// Audit: Record session creation
|
|
276
|
+
// ============================================================================
|
|
160
277
|
try {
|
|
161
278
|
await this.auditService?.recordEvent({
|
|
162
279
|
userId: data.userId,
|
|
@@ -164,6 +281,7 @@ class SessionService {
|
|
|
164
281
|
eventStatus: 'INFO',
|
|
165
282
|
sessionId: savedSession.id,
|
|
166
283
|
authMethod: data.authMethod || null,
|
|
284
|
+
// Client info automatically included from context
|
|
167
285
|
metadata: {
|
|
168
286
|
deviceId: savedSession.deviceId,
|
|
169
287
|
deviceName: savedSession.deviceName,
|
|
@@ -173,6 +291,7 @@ class SessionService {
|
|
|
173
291
|
});
|
|
174
292
|
}
|
|
175
293
|
catch (auditError) {
|
|
294
|
+
// Non-blocking: Log but continue
|
|
176
295
|
const errorMessage = auditError instanceof Error ? auditError.message : 'Unknown error';
|
|
177
296
|
this.logger?.error?.(`Failed to record SESSION_CREATED audit event: ${errorMessage}`, {
|
|
178
297
|
error: auditError,
|
|
@@ -182,13 +301,22 @@ class SessionService {
|
|
|
182
301
|
}
|
|
183
302
|
return savedSession;
|
|
184
303
|
}
|
|
304
|
+
/**
|
|
305
|
+
* Find session by ID
|
|
306
|
+
* @param sessionId - Session ID (can be string from JWT or number)
|
|
307
|
+
*/
|
|
185
308
|
async findById(sessionId) {
|
|
186
309
|
return (await this.sessionRepository.findOne({
|
|
187
310
|
where: { id: typeof sessionId === 'string' ? parseInt(sessionId, 10) : sessionId },
|
|
188
311
|
}));
|
|
189
312
|
}
|
|
313
|
+
/**
|
|
314
|
+
* Find session by ID with minimal fields (hot-path)
|
|
315
|
+
* @param sessionId - Session ID (string or number)
|
|
316
|
+
*/
|
|
190
317
|
async findByIdLight(sessionId) {
|
|
191
318
|
const id = typeof sessionId === 'string' ? parseInt(sessionId, 10) : sessionId;
|
|
319
|
+
// Select minimal session fields to reduce DB payload
|
|
192
320
|
const record = (await this.sessionRepository.findOne({
|
|
193
321
|
select: ['id', 'version', 'isRevoked', 'expiresAt', 'userId'],
|
|
194
322
|
where: { id },
|
|
@@ -205,24 +333,40 @@ class SessionService {
|
|
|
205
333
|
};
|
|
206
334
|
return light;
|
|
207
335
|
}
|
|
336
|
+
/**
|
|
337
|
+
* Find session by refresh token hash
|
|
338
|
+
*/
|
|
208
339
|
async findByRefreshToken(refreshTokenHash) {
|
|
209
340
|
return (await this.sessionRepository.findOne({
|
|
210
341
|
select: ['id', 'userId', 'isRevoked', 'tokenFamily', 'expiresAt'],
|
|
211
342
|
where: { refreshTokenHash, isRevoked: false },
|
|
212
343
|
}));
|
|
213
344
|
}
|
|
345
|
+
/**
|
|
346
|
+
* Find all active sessions for a user
|
|
347
|
+
* @param userId - Internal user ID (integer)
|
|
348
|
+
* @returns Array of active sessions
|
|
349
|
+
*/
|
|
214
350
|
async findUserSessions(userId) {
|
|
215
351
|
return (await this.sessionRepository.find({
|
|
216
352
|
where: { userId, isRevoked: false },
|
|
217
353
|
order: { createdAt: 'DESC' },
|
|
218
354
|
}));
|
|
219
355
|
}
|
|
356
|
+
/**
|
|
357
|
+
* Update session activity timestamp
|
|
358
|
+
* @param sessionId - Session ID (can be string from JWT or number)
|
|
359
|
+
*/
|
|
220
360
|
async updateActivity(sessionId) {
|
|
221
361
|
const id = typeof sessionId === 'string' ? parseInt(sessionId, 10) : sessionId;
|
|
222
362
|
await this.sessionRepository.update(id, {
|
|
223
363
|
lastActivityAt: new Date(),
|
|
224
364
|
});
|
|
225
365
|
}
|
|
366
|
+
/**
|
|
367
|
+
* Update session with new tokens (for rotation)
|
|
368
|
+
* @param sessionId - Session ID (can be string from JWT or number)
|
|
369
|
+
*/
|
|
226
370
|
async updateTokens(sessionId, accessTokenHash, refreshTokenHash) {
|
|
227
371
|
const id = typeof sessionId === 'string' ? parseInt(sessionId, 10) : sessionId;
|
|
228
372
|
await this.sessionRepository.update(id, {
|
|
@@ -231,18 +375,30 @@ class SessionService {
|
|
|
231
375
|
lastActivityAt: new Date(),
|
|
232
376
|
});
|
|
233
377
|
}
|
|
378
|
+
/**
|
|
379
|
+
* Create a session and update token hashes atomically within one transaction
|
|
380
|
+
*
|
|
381
|
+
* Uses a callback to generate token hashes after obtaining the session ID.
|
|
382
|
+
* This allows callers to embed sessionId in JWTs, then persist hashes atomically.
|
|
383
|
+
*/
|
|
234
384
|
async createSessionAtomic(data, generateHashes) {
|
|
385
|
+
// Get client info from context (transparent access)
|
|
386
|
+
// ClientInfoService already parsed the user agent in the interceptor
|
|
235
387
|
const clientInfo = this.clientInfoService.get();
|
|
236
388
|
const result = await this.sessionRepository.manager.transaction(async (trx) => {
|
|
389
|
+
// Use parsed device information from ClientInfoService (already parsed by interceptor)
|
|
390
|
+
// Use provided values or fall back to parsed values from context
|
|
237
391
|
const deviceType = data.deviceType || clientInfo.deviceType || null;
|
|
238
392
|
const deviceName = data.deviceName || clientInfo.deviceName || null;
|
|
239
393
|
const platform = clientInfo.platform || null;
|
|
240
394
|
const browser = clientInfo.browser || null;
|
|
395
|
+
// Generate deviceId if missing
|
|
241
396
|
let deviceId = data.deviceId;
|
|
242
397
|
if (!deviceId) {
|
|
243
398
|
const crypto = await Promise.resolve().then(() => __importStar(require('crypto')));
|
|
244
399
|
deviceId = crypto.randomUUID();
|
|
245
400
|
}
|
|
401
|
+
// Create with placeholder hashes (non-nullable columns)
|
|
246
402
|
const sessionEntity = this.sessionRepository.create({
|
|
247
403
|
userId: data.userId,
|
|
248
404
|
accessTokenHash: '',
|
|
@@ -251,6 +407,7 @@ class SessionService {
|
|
|
251
407
|
deviceId,
|
|
252
408
|
deviceName,
|
|
253
409
|
deviceType,
|
|
410
|
+
// Client info automatically extracted from ClientInfoService (transparent access)
|
|
254
411
|
ipAddress: clientInfo.ipAddress || null,
|
|
255
412
|
ipCountry: clientInfo.ipCountry || null,
|
|
256
413
|
ipCity: clientInfo.ipCity || null,
|
|
@@ -264,6 +421,7 @@ class SessionService {
|
|
|
264
421
|
isRemembered: data.isRemembered || false,
|
|
265
422
|
lastActivityAt: new Date(),
|
|
266
423
|
});
|
|
424
|
+
// Debug: Log what we're about to save in atomic transaction
|
|
267
425
|
if (!clientInfo.ipLatitude || !clientInfo.ipLongitude) {
|
|
268
426
|
this.logger?.warn?.(`[SessionService.createSessionAtomic] Creating session WITHOUT coordinates: ` +
|
|
269
427
|
`IP=${clientInfo.ipAddress}, country=${clientInfo.ipCountry}, city=${clientInfo.ipCity}, ` +
|
|
@@ -283,6 +441,7 @@ class SessionService {
|
|
|
283
441
|
.set({ accessTokenHash, refreshTokenHash, lastActivityAt: new Date() })
|
|
284
442
|
.where({ id: savedId })
|
|
285
443
|
.execute();
|
|
444
|
+
// Re-fetch minimal session fields to return
|
|
286
445
|
const sessionLight = (await trx.findOne(this.sessionRepository.target, {
|
|
287
446
|
where: { id: savedId },
|
|
288
447
|
}));
|
|
@@ -291,6 +450,9 @@ class SessionService {
|
|
|
291
450
|
}
|
|
292
451
|
return { session: sessionLight, extra };
|
|
293
452
|
});
|
|
453
|
+
// ============================================================================
|
|
454
|
+
// Audit: Record session creation
|
|
455
|
+
// ============================================================================
|
|
294
456
|
try {
|
|
295
457
|
await this.auditService?.recordEvent({
|
|
296
458
|
userId: data.userId,
|
|
@@ -298,6 +460,7 @@ class SessionService {
|
|
|
298
460
|
eventStatus: 'INFO',
|
|
299
461
|
sessionId: result.session.id,
|
|
300
462
|
authMethod: data.authMethod || null,
|
|
463
|
+
// Client info automatically included from context
|
|
301
464
|
metadata: {
|
|
302
465
|
deviceId: result.session.deviceId,
|
|
303
466
|
deviceName: result.session.deviceName,
|
|
@@ -307,6 +470,7 @@ class SessionService {
|
|
|
307
470
|
});
|
|
308
471
|
}
|
|
309
472
|
catch (auditError) {
|
|
473
|
+
// Non-blocking: Log but continue
|
|
310
474
|
const errorMessage = auditError instanceof Error ? auditError.message : 'Unknown error';
|
|
311
475
|
this.logger?.error?.(`Failed to record SESSION_CREATED audit event: ${errorMessage}`, {
|
|
312
476
|
error: auditError,
|
|
@@ -316,17 +480,27 @@ class SessionService {
|
|
|
316
480
|
}
|
|
317
481
|
return result;
|
|
318
482
|
}
|
|
483
|
+
/**
|
|
484
|
+
* Revoke a single session
|
|
485
|
+
* @param sessionId - Session ID (can be string from JWT or number)
|
|
486
|
+
* @param reason - Optional reason for revocation
|
|
487
|
+
* @param metadata - Optional metadata to include in audit trail
|
|
488
|
+
*/
|
|
319
489
|
async revokeSession(sessionId, reason, metadata) {
|
|
320
490
|
const id = typeof sessionId === 'string' ? parseInt(sessionId, 10) : sessionId;
|
|
491
|
+
// Get session to retrieve userId for audit logging
|
|
321
492
|
const session = await this.findById(id);
|
|
322
493
|
if (!session) {
|
|
323
|
-
return;
|
|
494
|
+
return; // Session doesn't exist, nothing to revoke
|
|
324
495
|
}
|
|
325
496
|
await this.sessionRepository.update(id, {
|
|
326
497
|
isRevoked: true,
|
|
327
498
|
revokedAt: new Date(),
|
|
328
499
|
revokeReason: reason,
|
|
329
500
|
});
|
|
501
|
+
// ============================================================================
|
|
502
|
+
// Audit: Record session revocation
|
|
503
|
+
// ============================================================================
|
|
330
504
|
try {
|
|
331
505
|
await this.auditService?.recordEvent({
|
|
332
506
|
userId: session.userId,
|
|
@@ -335,10 +509,12 @@ class SessionService {
|
|
|
335
509
|
sessionId: id,
|
|
336
510
|
reason: reason || 'User logout',
|
|
337
511
|
description: `Session revoked: ${reason || 'User logout'}`,
|
|
512
|
+
// Client info automatically included from context
|
|
338
513
|
metadata: metadata || undefined,
|
|
339
514
|
});
|
|
340
515
|
}
|
|
341
516
|
catch (auditError) {
|
|
517
|
+
// Non-blocking: Log but continue
|
|
342
518
|
const errorMessage = auditError instanceof Error ? auditError.message : 'Unknown error';
|
|
343
519
|
this.logger?.error?.(`Failed to record SESSION_REVOKED audit event: ${errorMessage}`, {
|
|
344
520
|
error: auditError,
|
|
@@ -347,7 +523,14 @@ class SessionService {
|
|
|
347
523
|
});
|
|
348
524
|
}
|
|
349
525
|
}
|
|
526
|
+
/**
|
|
527
|
+
* Revoke all sessions for a user (global signout)
|
|
528
|
+
* @param userId - Internal user ID (integer)
|
|
529
|
+
* @param reason - Optional reason for revocation
|
|
530
|
+
* @returns Number of sessions revoked
|
|
531
|
+
*/
|
|
350
532
|
async revokeAllUserSessions(userId, reason) {
|
|
533
|
+
// Get sessions before revoking for audit logging
|
|
351
534
|
const sessions = await this.findUserSessions(userId);
|
|
352
535
|
const result = await this.sessionRepository.update({ userId, isRevoked: false }, {
|
|
353
536
|
isRevoked: true,
|
|
@@ -355,10 +538,15 @@ class SessionService {
|
|
|
355
538
|
revokeReason: reason,
|
|
356
539
|
});
|
|
357
540
|
const revokedCount = result.affected || 0;
|
|
541
|
+
// ============================================================================
|
|
542
|
+
// Audit: Record session revocations (one event per session for global signout)
|
|
543
|
+
// ============================================================================
|
|
358
544
|
if (revokedCount > 0) {
|
|
359
545
|
try {
|
|
360
546
|
const isGlobalSignout = reason === 'Global signout';
|
|
361
547
|
if (isGlobalSignout) {
|
|
548
|
+
// For global signout, record individual SESSION_REVOKED event for each session
|
|
549
|
+
// AuthService.logoutAll() will record a GLOBAL_SIGNOUT event separately
|
|
362
550
|
for (const session of sessions) {
|
|
363
551
|
try {
|
|
364
552
|
await this.auditService?.recordEvent({
|
|
@@ -368,12 +556,14 @@ class SessionService {
|
|
|
368
556
|
reason: 'Global signout',
|
|
369
557
|
description: `Session revoked by global signout`,
|
|
370
558
|
sessionId: session.id,
|
|
559
|
+
// Client info automatically included from context
|
|
371
560
|
metadata: {
|
|
372
561
|
revokedBy: 'global_signout',
|
|
373
562
|
},
|
|
374
563
|
});
|
|
375
564
|
}
|
|
376
565
|
catch (sessionAuditError) {
|
|
566
|
+
// Non-blocking: Log but continue with other sessions
|
|
377
567
|
const errorMessage = sessionAuditError instanceof Error ? sessionAuditError.message : 'Unknown error';
|
|
378
568
|
this.logger?.error?.(`Failed to record SESSION_REVOKED audit event for session ${session.id}: ${errorMessage}`, {
|
|
379
569
|
error: sessionAuditError,
|
|
@@ -384,12 +574,14 @@ class SessionService {
|
|
|
384
574
|
}
|
|
385
575
|
}
|
|
386
576
|
else {
|
|
577
|
+
// For other reasons (e.g., "Login from new session"), record one summary event
|
|
387
578
|
await this.auditService?.recordEvent({
|
|
388
579
|
userId,
|
|
389
580
|
eventType: auth_audit_event_type_enum_1.AuthAuditEventType.SESSION_REVOKED,
|
|
390
581
|
eventStatus: 'INFO',
|
|
391
582
|
reason: reason || 'Session revocation',
|
|
392
583
|
description: `All user sessions revoked (${revokedCount} session(s))`,
|
|
584
|
+
// Client info automatically included from context
|
|
393
585
|
metadata: {
|
|
394
586
|
revokedCount,
|
|
395
587
|
sessionIds: sessions.map((s) => s.id),
|
|
@@ -398,6 +590,7 @@ class SessionService {
|
|
|
398
590
|
}
|
|
399
591
|
}
|
|
400
592
|
catch (auditError) {
|
|
593
|
+
// Non-blocking: Log but continue
|
|
401
594
|
const errorMessage = auditError instanceof Error ? auditError.message : 'Unknown error';
|
|
402
595
|
this.logger?.error?.(`Failed to record SESSION_REVOKED audit event (all sessions): ${errorMessage}`, {
|
|
403
596
|
error: auditError,
|
|
@@ -408,6 +601,9 @@ class SessionService {
|
|
|
408
601
|
}
|
|
409
602
|
return revokedCount;
|
|
410
603
|
}
|
|
604
|
+
/**
|
|
605
|
+
* Revoke all sessions in a token family (for reuse detection)
|
|
606
|
+
*/
|
|
411
607
|
async revokeTokenFamily(tokenFamily, reason) {
|
|
412
608
|
const result = await this.sessionRepository.update({ tokenFamily, isRevoked: false }, {
|
|
413
609
|
isRevoked: true,
|
|
@@ -416,37 +612,126 @@ class SessionService {
|
|
|
416
612
|
});
|
|
417
613
|
return result.affected || 0;
|
|
418
614
|
}
|
|
615
|
+
/**
|
|
616
|
+
* Cleanup expired sessions
|
|
617
|
+
*/
|
|
419
618
|
async cleanupExpiredSessions() {
|
|
420
619
|
const result = await this.sessionRepository.delete({
|
|
421
620
|
expiresAt: (0, typeorm_1.LessThan)(new Date()),
|
|
422
621
|
});
|
|
423
622
|
return result.affected || 0;
|
|
424
623
|
}
|
|
624
|
+
/**
|
|
625
|
+
* Count active sessions for a user
|
|
626
|
+
* @param userId - Internal user ID (integer)
|
|
627
|
+
* @returns Number of active sessions
|
|
628
|
+
*/
|
|
425
629
|
async countUserSessions(userId) {
|
|
426
630
|
return await this.sessionRepository.count({
|
|
427
631
|
where: { userId, isRevoked: false },
|
|
428
632
|
});
|
|
429
633
|
}
|
|
634
|
+
// ============================================================================
|
|
635
|
+
// Token Reuse Detection (Security Feature)
|
|
636
|
+
// ============================================================================
|
|
637
|
+
/**
|
|
638
|
+
* Mark a refresh token as used
|
|
639
|
+
*
|
|
640
|
+
* Stores the token hash in cache with expiration matching the refresh token TTL.
|
|
641
|
+
* Used to detect token reuse attacks where stolen tokens are reused multiple times.
|
|
642
|
+
*
|
|
643
|
+
* SECURITY CRITICAL: This prevents token replay attacks
|
|
644
|
+
*
|
|
645
|
+
* @param tokenHash - SHA-256 hash of the refresh token
|
|
646
|
+
* @param ttlSeconds - Time to live in seconds (should match refresh token expiry)
|
|
647
|
+
*
|
|
648
|
+
* @example
|
|
649
|
+
* ```typescript
|
|
650
|
+
* // Mark token as used during refresh
|
|
651
|
+
* await sessionService.markRefreshTokenAsUsed(tokenHash, 30 * 24 * 60 * 60);
|
|
652
|
+
* ```
|
|
653
|
+
*/
|
|
430
654
|
async markRefreshTokenAsUsed(tokenHash, ttlSeconds) {
|
|
431
655
|
const key = `used-token:${tokenHash}`;
|
|
656
|
+
// Use atomic set-if-not-exists operation
|
|
657
|
+
// Returns null if key already exists (NX failed), string if key was set
|
|
432
658
|
const result = await this.storageAdapter.set(key, 'true', ttlSeconds, { nx: true });
|
|
433
|
-
return result !== null;
|
|
659
|
+
return result !== null; // True if successfully set, false if already existed
|
|
434
660
|
}
|
|
661
|
+
/**
|
|
662
|
+
* Check if a refresh token has been used before
|
|
663
|
+
*
|
|
664
|
+
* If token has been used, it indicates a token reuse attack and the entire
|
|
665
|
+
* token family should be revoked immediately.
|
|
666
|
+
*
|
|
667
|
+
* @param tokenHash - SHA-256 hash of the refresh token
|
|
668
|
+
* @returns True if token has been used before, false otherwise
|
|
669
|
+
*
|
|
670
|
+
* @example
|
|
671
|
+
* ```typescript
|
|
672
|
+
* const isReused = await sessionService.isRefreshTokenUsed(tokenHash);
|
|
673
|
+
* if (isReused) {
|
|
674
|
+
* // TOKEN REUSE DETECTED - SECURITY BREACH!
|
|
675
|
+
* await sessionService.revokeTokenFamily(session.tokenFamily);
|
|
676
|
+
* throw new UnauthorizedException('Token reuse detected');
|
|
677
|
+
* }
|
|
678
|
+
* ```
|
|
679
|
+
*/
|
|
435
680
|
async isRefreshTokenUsed(tokenHash) {
|
|
436
681
|
const key = `used-token:${tokenHash}`;
|
|
437
682
|
return await this.storageAdapter.exists(key);
|
|
438
683
|
}
|
|
684
|
+
/**
|
|
685
|
+
* Acquire a distributed lock for token refresh
|
|
686
|
+
*
|
|
687
|
+
* Uses atomic set-if-not-exists (NX) operation to prevent concurrent refresh attempts.
|
|
688
|
+
* Lock is automatically released after TTL expires, or manually via releaseRefreshLock.
|
|
689
|
+
*
|
|
690
|
+
* @param lockKey - Lock key (e.g., `session-refresh:${sessionId}` or `refresh-lock:${tokenHash}`)
|
|
691
|
+
* @param ttlMs - Lock TTL in milliseconds (default: 10000ms)
|
|
692
|
+
* @returns True if lock was acquired, false if already locked by another request
|
|
693
|
+
*
|
|
694
|
+
* @example
|
|
695
|
+
* ```typescript
|
|
696
|
+
* const lockKey = `session-refresh:${sessionId}`;
|
|
697
|
+
* const lockAcquired = await sessionService.acquireRefreshLock(lockKey, 10000);
|
|
698
|
+
* if (!lockAcquired) {
|
|
699
|
+
* throw new Error('Refresh already in progress');
|
|
700
|
+
* }
|
|
701
|
+
* try {
|
|
702
|
+
* // ... perform refresh ...
|
|
703
|
+
* } finally {
|
|
704
|
+
* await sessionService.releaseRefreshLock(lockKey);
|
|
705
|
+
* }
|
|
706
|
+
* ```
|
|
707
|
+
*/
|
|
439
708
|
async acquireRefreshLock(lockKey, ttlMs = 10000) {
|
|
709
|
+
// ============================================================================
|
|
710
|
+
// CRITICAL FIX: Use atomic set-if-not-exists (NX) for lock acquisition
|
|
711
|
+
// ============================================================================
|
|
712
|
+
// The set() method with nx: true uses a transaction with pessimistic locking
|
|
713
|
+
// to atomically check and insert. This ensures only one request can acquire
|
|
714
|
+
// the lock even when multiple requests arrive simultaneously.
|
|
715
|
+
// Increased default TTL to 10 seconds to handle slower database operations
|
|
716
|
+
// Add small jitter (±5%) to reduce synchronized expirations under load
|
|
440
717
|
const baseTtlSeconds = Math.max(1, Math.ceil(ttlMs / 1000));
|
|
441
718
|
const jitterMax = Math.max(1, Math.floor(baseTtlSeconds * 0.05));
|
|
442
|
-
const jitter = Math.floor(Math.random() * (jitterMax * 2 + 1)) - jitterMax;
|
|
719
|
+
const jitter = Math.floor(Math.random() * (jitterMax * 2 + 1)) - jitterMax; // [-jitterMax, +jitterMax]
|
|
443
720
|
const ttlWithJitter = Math.max(1, baseTtlSeconds + jitter);
|
|
444
721
|
const result = await this.storageAdapter.set(lockKey, 'locked', ttlWithJitter, { nx: true });
|
|
445
722
|
const acquired = result !== null;
|
|
723
|
+
// Debug logging to help diagnose lock issues
|
|
446
724
|
if (!acquired) {
|
|
725
|
+
// Lock acquisition failed - another request has it
|
|
726
|
+
// This is expected behavior when multiple requests try to refresh simultaneously
|
|
447
727
|
}
|
|
448
728
|
return acquired;
|
|
449
729
|
}
|
|
730
|
+
/**
|
|
731
|
+
* Release a distributed lock for token refresh
|
|
732
|
+
*
|
|
733
|
+
* @param lockKey - Lock key (must match the key used in acquireRefreshLock)
|
|
734
|
+
*/
|
|
450
735
|
async releaseRefreshLock(lockKey) {
|
|
451
736
|
await this.storageAdapter.del(lockKey);
|
|
452
737
|
}
|