@nauth-toolkit/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (778) hide show
  1. package/dist/adapters/database-columns.d.ts +10 -0
  2. package/dist/adapters/database-columns.d.ts.map +1 -0
  3. package/dist/adapters/database-columns.js +85 -0
  4. package/dist/adapters/database-columns.js.map +1 -0
  5. package/dist/adapters/express.adapter.d.ts +41 -0
  6. package/dist/adapters/express.adapter.d.ts.map +1 -0
  7. package/dist/adapters/express.adapter.js +188 -0
  8. package/dist/adapters/express.adapter.js.map +1 -0
  9. package/dist/adapters/fastify.adapter.d.ts +33 -0
  10. package/dist/adapters/fastify.adapter.d.ts.map +1 -0
  11. package/dist/adapters/fastify.adapter.js +223 -0
  12. package/dist/adapters/fastify.adapter.js.map +1 -0
  13. package/dist/adapters/index.d.ts +5 -0
  14. package/dist/adapters/index.d.ts.map +1 -0
  15. package/dist/adapters/index.js +25 -0
  16. package/dist/adapters/index.js.map +1 -0
  17. package/dist/adapters/storage.factory.d.ts +7 -0
  18. package/dist/adapters/storage.factory.d.ts.map +1 -0
  19. package/dist/adapters/storage.factory.js +24 -0
  20. package/dist/adapters/storage.factory.js.map +1 -0
  21. package/dist/bootstrap.d.ts +41 -0
  22. package/dist/bootstrap.d.ts.map +1 -0
  23. package/dist/bootstrap.js +113 -0
  24. package/dist/bootstrap.js.map +1 -0
  25. package/dist/dto/auth-challenge.dto.d.ts +19 -0
  26. package/dist/dto/auth-challenge.dto.d.ts.map +1 -0
  27. package/dist/dto/auth-challenge.dto.js +86 -0
  28. package/dist/dto/auth-challenge.dto.js.map +1 -0
  29. package/dist/dto/auth-response.dto.d.ts +31 -0
  30. package/dist/dto/auth-response.dto.d.ts.map +1 -0
  31. package/dist/dto/auth-response.dto.js +18 -0
  32. package/dist/dto/auth-response.dto.js.map +1 -0
  33. package/dist/dto/challenge-response.dto.d.ts +36 -0
  34. package/dist/dto/challenge-response.dto.d.ts.map +1 -0
  35. package/dist/dto/challenge-response.dto.js +3 -0
  36. package/dist/dto/challenge-response.dto.js.map +1 -0
  37. package/dist/dto/change-password-request.dto.d.ts +5 -0
  38. package/dist/dto/change-password-request.dto.d.ts.map +1 -0
  39. package/dist/dto/change-password-request.dto.js +30 -0
  40. package/dist/dto/change-password-request.dto.js.map +1 -0
  41. package/dist/dto/change-password-response.dto.d.ts +4 -0
  42. package/dist/dto/change-password-response.dto.d.ts.map +1 -0
  43. package/dist/dto/change-password-response.dto.js +8 -0
  44. package/dist/dto/change-password-response.dto.js.map +1 -0
  45. package/dist/dto/change-password.dto.d.ts +5 -0
  46. package/dist/dto/change-password.dto.d.ts.map +1 -0
  47. package/dist/dto/change-password.dto.js +29 -0
  48. package/dist/dto/change-password.dto.js.map +1 -0
  49. package/dist/dto/error-response.dto.d.ts +9 -0
  50. package/dist/dto/error-response.dto.d.ts.map +1 -0
  51. package/dist/dto/error-response.dto.js +59 -0
  52. package/dist/dto/error-response.dto.js.map +1 -0
  53. package/dist/dto/get-available-methods.dto.d.ts +7 -0
  54. package/dist/dto/get-available-methods.dto.d.ts.map +1 -0
  55. package/dist/dto/get-available-methods.dto.js +33 -0
  56. package/dist/dto/get-available-methods.dto.js.map +1 -0
  57. package/dist/dto/get-challenge-data-response.dto.d.ts +4 -0
  58. package/dist/dto/get-challenge-data-response.dto.d.ts.map +1 -0
  59. package/dist/dto/get-challenge-data-response.dto.js +8 -0
  60. package/dist/dto/get-challenge-data-response.dto.js.map +1 -0
  61. package/dist/dto/get-challenge-data.dto.d.ts +8 -0
  62. package/dist/dto/get-challenge-data.dto.d.ts.map +1 -0
  63. package/dist/dto/get-challenge-data.dto.js +40 -0
  64. package/dist/dto/get-challenge-data.dto.js.map +1 -0
  65. package/dist/dto/get-client-info.dto.d.ts +17 -0
  66. package/dist/dto/get-client-info.dto.d.ts.map +1 -0
  67. package/dist/dto/get-client-info.dto.js +20 -0
  68. package/dist/dto/get-client-info.dto.js.map +1 -0
  69. package/dist/dto/get-device-token-response.dto.d.ts +4 -0
  70. package/dist/dto/get-device-token-response.dto.d.ts.map +1 -0
  71. package/dist/dto/get-device-token-response.dto.js +8 -0
  72. package/dist/dto/get-device-token-response.dto.js.map +1 -0
  73. package/dist/dto/get-events-by-type.dto.d.ts +17 -0
  74. package/dist/dto/get-events-by-type.dto.d.ts.map +1 -0
  75. package/dist/dto/get-events-by-type.dto.js +20 -0
  76. package/dist/dto/get-events-by-type.dto.js.map +1 -0
  77. package/dist/dto/get-ip-address-response.dto.d.ts +4 -0
  78. package/dist/dto/get-ip-address-response.dto.d.ts.map +1 -0
  79. package/dist/dto/get-ip-address-response.dto.js +8 -0
  80. package/dist/dto/get-ip-address-response.dto.js.map +1 -0
  81. package/dist/dto/get-mfa-status.dto.d.ts +16 -0
  82. package/dist/dto/get-mfa-status.dto.d.ts.map +1 -0
  83. package/dist/dto/get-mfa-status.dto.js +41 -0
  84. package/dist/dto/get-mfa-status.dto.js.map +1 -0
  85. package/dist/dto/get-risk-assessment-history.dto.d.ts +9 -0
  86. package/dist/dto/get-risk-assessment-history.dto.d.ts.map +1 -0
  87. package/dist/dto/get-risk-assessment-history.dto.js +13 -0
  88. package/dist/dto/get-risk-assessment-history.dto.js.map +1 -0
  89. package/dist/dto/get-session-id-response.dto.d.ts +4 -0
  90. package/dist/dto/get-session-id-response.dto.d.ts.map +1 -0
  91. package/dist/dto/get-session-id-response.dto.js +8 -0
  92. package/dist/dto/get-session-id-response.dto.js.map +1 -0
  93. package/dist/dto/get-setup-data-response.dto.d.ts +4 -0
  94. package/dist/dto/get-setup-data-response.dto.d.ts.map +1 -0
  95. package/dist/dto/get-setup-data-response.dto.js +8 -0
  96. package/dist/dto/get-setup-data-response.dto.js.map +1 -0
  97. package/dist/dto/get-setup-data.dto.d.ts +7 -0
  98. package/dist/dto/get-setup-data.dto.d.ts.map +1 -0
  99. package/dist/dto/get-setup-data.dto.js +43 -0
  100. package/dist/dto/get-setup-data.dto.js.map +1 -0
  101. package/dist/dto/get-suspicious-activity.dto.d.ts +9 -0
  102. package/dist/dto/get-suspicious-activity.dto.d.ts.map +1 -0
  103. package/dist/dto/get-suspicious-activity.dto.js +13 -0
  104. package/dist/dto/get-suspicious-activity.dto.js.map +1 -0
  105. package/dist/dto/get-user-agent-response.dto.d.ts +4 -0
  106. package/dist/dto/get-user-agent-response.dto.d.ts.map +1 -0
  107. package/dist/dto/get-user-agent-response.dto.js +8 -0
  108. package/dist/dto/get-user-agent-response.dto.js.map +1 -0
  109. package/dist/dto/get-user-auth-history.dto.d.ts +20 -0
  110. package/dist/dto/get-user-auth-history.dto.d.ts.map +1 -0
  111. package/dist/dto/get-user-auth-history.dto.js +22 -0
  112. package/dist/dto/get-user-auth-history.dto.js.map +1 -0
  113. package/dist/dto/get-user-by-email.dto.d.ts +5 -0
  114. package/dist/dto/get-user-by-email.dto.d.ts.map +1 -0
  115. package/dist/dto/get-user-by-email.dto.js +36 -0
  116. package/dist/dto/get-user-by-email.dto.js.map +1 -0
  117. package/dist/dto/get-user-by-id.dto.d.ts +4 -0
  118. package/dist/dto/get-user-by-id.dto.d.ts.map +1 -0
  119. package/dist/dto/get-user-by-id.dto.js +29 -0
  120. package/dist/dto/get-user-by-id.dto.js.map +1 -0
  121. package/dist/dto/get-user-devices.dto.d.ts +8 -0
  122. package/dist/dto/get-user-devices.dto.d.ts.map +1 -0
  123. package/dist/dto/get-user-devices.dto.js +33 -0
  124. package/dist/dto/get-user-devices.dto.js.map +1 -0
  125. package/dist/dto/get-user-response.dto.d.ts +2 -0
  126. package/dist/dto/get-user-response.dto.d.ts.map +1 -0
  127. package/dist/dto/get-user-response.dto.js +6 -0
  128. package/dist/dto/get-user-response.dto.js.map +1 -0
  129. package/dist/dto/has-provider.dto.d.ts +7 -0
  130. package/dist/dto/has-provider.dto.d.ts.map +1 -0
  131. package/dist/dto/has-provider.dto.js +38 -0
  132. package/dist/dto/has-provider.dto.js.map +1 -0
  133. package/dist/dto/index.d.ts +51 -0
  134. package/dist/dto/index.d.ts.map +1 -0
  135. package/dist/dto/index.js +67 -0
  136. package/dist/dto/index.js.map +1 -0
  137. package/dist/dto/is-trusted-device-response.dto.d.ts +4 -0
  138. package/dist/dto/is-trusted-device-response.dto.d.ts.map +1 -0
  139. package/dist/dto/is-trusted-device-response.dto.js +8 -0
  140. package/dist/dto/is-trusted-device-response.dto.js.map +1 -0
  141. package/dist/dto/list-providers-response.dto.d.ts +4 -0
  142. package/dist/dto/list-providers-response.dto.d.ts.map +1 -0
  143. package/dist/dto/list-providers-response.dto.js +8 -0
  144. package/dist/dto/list-providers-response.dto.js.map +1 -0
  145. package/dist/dto/login.dto.d.ts +7 -0
  146. package/dist/dto/login.dto.d.ts.map +1 -0
  147. package/dist/dto/login.dto.js +68 -0
  148. package/dist/dto/login.dto.js.map +1 -0
  149. package/dist/dto/logout-all-response.dto.d.ts +4 -0
  150. package/dist/dto/logout-all-response.dto.d.ts.map +1 -0
  151. package/dist/dto/logout-all-response.dto.js +8 -0
  152. package/dist/dto/logout-all-response.dto.js.map +1 -0
  153. package/dist/dto/logout-all.dto.d.ts +5 -0
  154. package/dist/dto/logout-all.dto.d.ts.map +1 -0
  155. package/dist/dto/logout-all.dto.js +42 -0
  156. package/dist/dto/logout-all.dto.js.map +1 -0
  157. package/dist/dto/logout-response.dto.d.ts +4 -0
  158. package/dist/dto/logout-response.dto.d.ts.map +1 -0
  159. package/dist/dto/logout-response.dto.js +8 -0
  160. package/dist/dto/logout-response.dto.js.map +1 -0
  161. package/dist/dto/logout.dto.d.ts +5 -0
  162. package/dist/dto/logout.dto.d.ts.map +1 -0
  163. package/dist/dto/logout.dto.js +36 -0
  164. package/dist/dto/logout.dto.js.map +1 -0
  165. package/dist/dto/refresh-token.dto.d.ts +4 -0
  166. package/dist/dto/refresh-token.dto.d.ts.map +1 -0
  167. package/dist/dto/refresh-token.dto.js +24 -0
  168. package/dist/dto/refresh-token.dto.js.map +1 -0
  169. package/dist/dto/remove-devices.dto.d.ts +9 -0
  170. package/dist/dto/remove-devices.dto.d.ts.map +1 -0
  171. package/dist/dto/remove-devices.dto.js +50 -0
  172. package/dist/dto/remove-devices.dto.js.map +1 -0
  173. package/dist/dto/resend-code-response.dto.d.ts +4 -0
  174. package/dist/dto/resend-code-response.dto.d.ts.map +1 -0
  175. package/dist/dto/resend-code-response.dto.js +8 -0
  176. package/dist/dto/resend-code-response.dto.js.map +1 -0
  177. package/dist/dto/resend-code.dto.d.ts +4 -0
  178. package/dist/dto/resend-code.dto.d.ts.map +1 -0
  179. package/dist/dto/resend-code.dto.js +29 -0
  180. package/dist/dto/resend-code.dto.js.map +1 -0
  181. package/dist/dto/reset-password.dto.d.ts +8 -0
  182. package/dist/dto/reset-password.dto.d.ts.map +1 -0
  183. package/dist/dto/reset-password.dto.js +61 -0
  184. package/dist/dto/reset-password.dto.js.map +1 -0
  185. package/dist/dto/respond-challenge.dto.d.ts +33 -0
  186. package/dist/dto/respond-challenge.dto.d.ts.map +1 -0
  187. package/dist/dto/respond-challenge.dto.js +131 -0
  188. package/dist/dto/respond-challenge.dto.js.map +1 -0
  189. package/dist/dto/set-mfa-exemption.dto.d.ts +12 -0
  190. package/dist/dto/set-mfa-exemption.dto.d.ts.map +1 -0
  191. package/dist/dto/set-mfa-exemption.dto.js +66 -0
  192. package/dist/dto/set-mfa-exemption.dto.js.map +1 -0
  193. package/dist/dto/set-must-change-password-response.dto.d.ts +4 -0
  194. package/dist/dto/set-must-change-password-response.dto.d.ts.map +1 -0
  195. package/dist/dto/set-must-change-password-response.dto.js +8 -0
  196. package/dist/dto/set-must-change-password-response.dto.js.map +1 -0
  197. package/dist/dto/set-must-change-password.dto.d.ts +4 -0
  198. package/dist/dto/set-must-change-password.dto.d.ts.map +1 -0
  199. package/dist/dto/set-must-change-password.dto.js +29 -0
  200. package/dist/dto/set-must-change-password.dto.js.map +1 -0
  201. package/dist/dto/set-preferred-method.dto.d.ts +8 -0
  202. package/dist/dto/set-preferred-method.dto.d.ts.map +1 -0
  203. package/dist/dto/set-preferred-method.dto.js +49 -0
  204. package/dist/dto/set-preferred-method.dto.js.map +1 -0
  205. package/dist/dto/setup-mfa.dto.d.ts +9 -0
  206. package/dist/dto/setup-mfa.dto.d.ts.map +1 -0
  207. package/dist/dto/setup-mfa.dto.js +55 -0
  208. package/dist/dto/setup-mfa.dto.js.map +1 -0
  209. package/dist/dto/signup.dto.d.ts +10 -0
  210. package/dist/dto/signup.dto.d.ts.map +1 -0
  211. package/dist/dto/signup.dto.js +109 -0
  212. package/dist/dto/signup.dto.js.map +1 -0
  213. package/dist/dto/social-auth.dto.d.ts +54 -0
  214. package/dist/dto/social-auth.dto.d.ts.map +1 -0
  215. package/dist/dto/social-auth.dto.js +232 -0
  216. package/dist/dto/social-auth.dto.js.map +1 -0
  217. package/dist/dto/trust-device-response.dto.d.ts +4 -0
  218. package/dist/dto/trust-device-response.dto.d.ts.map +1 -0
  219. package/dist/dto/trust-device-response.dto.js +8 -0
  220. package/dist/dto/trust-device-response.dto.js.map +1 -0
  221. package/dist/dto/trust-device.dto.d.ts +1 -0
  222. package/dist/dto/trust-device.dto.d.ts.map +1 -0
  223. package/dist/dto/trust-device.dto.js +2 -0
  224. package/dist/dto/trust-device.dto.js.map +1 -0
  225. package/dist/dto/update-user-attributes-request.dto.d.ts +5 -0
  226. package/dist/dto/update-user-attributes-request.dto.d.ts.map +1 -0
  227. package/dist/dto/update-user-attributes-request.dto.js +30 -0
  228. package/dist/dto/update-user-attributes-request.dto.js.map +1 -0
  229. package/dist/dto/user-response.dto.d.ts +20 -0
  230. package/dist/dto/user-response.dto.d.ts.map +1 -0
  231. package/dist/dto/user-response.dto.js +42 -0
  232. package/dist/dto/user-response.dto.js.map +1 -0
  233. package/dist/dto/user-update.dto.d.ts +12 -0
  234. package/dist/dto/user-update.dto.d.ts.map +1 -0
  235. package/dist/dto/user-update.dto.js +119 -0
  236. package/dist/dto/user-update.dto.js.map +1 -0
  237. package/dist/dto/verify-email.dto.d.ts +29 -0
  238. package/dist/dto/verify-email.dto.d.ts.map +1 -0
  239. package/dist/dto/verify-email.dto.js +161 -0
  240. package/dist/dto/verify-email.dto.js.map +1 -0
  241. package/dist/dto/verify-mfa-code.dto.d.ts +10 -0
  242. package/dist/dto/verify-mfa-code.dto.d.ts.map +1 -0
  243. package/dist/dto/verify-mfa-code.dto.js +56 -0
  244. package/dist/dto/verify-mfa-code.dto.js.map +1 -0
  245. package/dist/dto/verify-phone-by-sub.dto.d.ts +6 -0
  246. package/dist/dto/verify-phone-by-sub.dto.d.ts.map +1 -0
  247. package/dist/dto/verify-phone-by-sub.dto.js +49 -0
  248. package/dist/dto/verify-phone-by-sub.dto.js.map +1 -0
  249. package/dist/dto/verify-phone.dto.d.ts +24 -0
  250. package/dist/dto/verify-phone.dto.d.ts.map +1 -0
  251. package/dist/dto/verify-phone.dto.js +124 -0
  252. package/dist/dto/verify-phone.dto.js.map +1 -0
  253. package/dist/entities/auth-audit.entity.d.ts +31 -0
  254. package/dist/entities/auth-audit.entity.d.ts.map +1 -0
  255. package/dist/entities/auth-audit.entity.js +33 -0
  256. package/dist/entities/auth-audit.entity.js.map +1 -0
  257. package/dist/entities/challenge-session.entity.d.ts +17 -0
  258. package/dist/entities/challenge-session.entity.d.ts.map +1 -0
  259. package/dist/entities/challenge-session.entity.js +21 -0
  260. package/dist/entities/challenge-session.entity.js.map +1 -0
  261. package/dist/entities/index.d.ts +12 -0
  262. package/dist/entities/index.d.ts.map +1 -0
  263. package/dist/entities/index.js +26 -0
  264. package/dist/entities/index.js.map +1 -0
  265. package/dist/entities/login-attempt.entity.d.ts +13 -0
  266. package/dist/entities/login-attempt.entity.d.ts.map +1 -0
  267. package/dist/entities/login-attempt.entity.js +17 -0
  268. package/dist/entities/login-attempt.entity.js.map +1 -0
  269. package/dist/entities/mfa-device.entity.d.ts +22 -0
  270. package/dist/entities/mfa-device.entity.d.ts.map +1 -0
  271. package/dist/entities/mfa-device.entity.js +25 -0
  272. package/dist/entities/mfa-device.entity.js.map +1 -0
  273. package/dist/entities/rate-limit.entity.d.ts +9 -0
  274. package/dist/entities/rate-limit.entity.d.ts.map +1 -0
  275. package/dist/entities/rate-limit.entity.js +13 -0
  276. package/dist/entities/rate-limit.entity.js.map +1 -0
  277. package/dist/entities/session.entity.d.ts +32 -0
  278. package/dist/entities/session.entity.d.ts.map +1 -0
  279. package/dist/entities/session.entity.js +36 -0
  280. package/dist/entities/session.entity.js.map +1 -0
  281. package/dist/entities/social-account.entity.d.ts +13 -0
  282. package/dist/entities/social-account.entity.d.ts.map +1 -0
  283. package/dist/entities/social-account.entity.js +17 -0
  284. package/dist/entities/social-account.entity.js.map +1 -0
  285. package/dist/entities/storage-lock.entity.d.ts +8 -0
  286. package/dist/entities/storage-lock.entity.d.ts.map +1 -0
  287. package/dist/entities/storage-lock.entity.js +12 -0
  288. package/dist/entities/storage-lock.entity.js.map +1 -0
  289. package/dist/entities/trusted-device.entity.d.ts +17 -0
  290. package/dist/entities/trusted-device.entity.d.ts.map +1 -0
  291. package/dist/entities/trusted-device.entity.js +21 -0
  292. package/dist/entities/trusted-device.entity.js.map +1 -0
  293. package/dist/entities/user.entity.d.ts +41 -0
  294. package/dist/entities/user.entity.d.ts.map +1 -0
  295. package/dist/entities/user.entity.js +45 -0
  296. package/dist/entities/user.entity.js.map +1 -0
  297. package/dist/entities/verification-token.entity.d.ts +19 -0
  298. package/dist/entities/verification-token.entity.d.ts.map +1 -0
  299. package/dist/entities/verification-token.entity.js +29 -0
  300. package/dist/entities/verification-token.entity.js.map +1 -0
  301. package/dist/enums/auth-audit-event-type.enum.d.ts +55 -0
  302. package/dist/enums/auth-audit-event-type.enum.d.ts.map +1 -0
  303. package/dist/enums/auth-audit-event-type.enum.js +59 -0
  304. package/dist/enums/auth-audit-event-type.enum.js.map +1 -0
  305. package/dist/enums/error-codes.enum.d.ts +53 -0
  306. package/dist/enums/error-codes.enum.d.ts.map +1 -0
  307. package/dist/enums/error-codes.enum.js +57 -0
  308. package/dist/enums/error-codes.enum.js.map +1 -0
  309. package/dist/enums/mfa-method.enum.d.ts +11 -0
  310. package/dist/enums/mfa-method.enum.d.ts.map +1 -0
  311. package/dist/enums/mfa-method.enum.js +18 -0
  312. package/dist/enums/mfa-method.enum.js.map +1 -0
  313. package/dist/enums/risk-factor.enum.d.ts +14 -0
  314. package/dist/enums/risk-factor.enum.d.ts.map +1 -0
  315. package/dist/enums/risk-factor.enum.js +18 -0
  316. package/dist/enums/risk-factor.enum.js.map +1 -0
  317. package/dist/exceptions/nauth.exception.d.ts +18 -0
  318. package/dist/exceptions/nauth.exception.d.ts.map +1 -0
  319. package/dist/exceptions/nauth.exception.js +64 -0
  320. package/dist/exceptions/nauth.exception.js.map +1 -0
  321. package/dist/handlers/auth.handler.d.ts +18 -0
  322. package/dist/handlers/auth.handler.d.ts.map +1 -0
  323. package/dist/handlers/auth.handler.js +173 -0
  324. package/dist/handlers/auth.handler.js.map +1 -0
  325. package/dist/handlers/client-info.handler.d.ts +12 -0
  326. package/dist/handlers/client-info.handler.d.ts.map +1 -0
  327. package/dist/handlers/client-info.handler.js +61 -0
  328. package/dist/handlers/client-info.handler.js.map +1 -0
  329. package/dist/handlers/csrf.handler.d.ts +13 -0
  330. package/dist/handlers/csrf.handler.d.ts.map +1 -0
  331. package/dist/handlers/csrf.handler.js +84 -0
  332. package/dist/handlers/csrf.handler.js.map +1 -0
  333. package/dist/handlers/token-delivery.handler.d.ts +12 -0
  334. package/dist/handlers/token-delivery.handler.d.ts.map +1 -0
  335. package/dist/handlers/token-delivery.handler.js +86 -0
  336. package/dist/handlers/token-delivery.handler.js.map +1 -0
  337. package/dist/index.d.ts +27 -0
  338. package/dist/index.d.ts.map +1 -0
  339. package/dist/index.js +51 -0
  340. package/dist/index.js.map +1 -0
  341. package/dist/interfaces/client-info.interface.d.ts +16 -0
  342. package/dist/interfaces/client-info.interface.d.ts.map +1 -0
  343. package/dist/interfaces/client-info.interface.js +3 -0
  344. package/dist/interfaces/client-info.interface.js.map +1 -0
  345. package/dist/interfaces/config.interface.d.ts +279 -0
  346. package/dist/interfaces/config.interface.d.ts.map +1 -0
  347. package/dist/interfaces/config.interface.js +3 -0
  348. package/dist/interfaces/config.interface.js.map +1 -0
  349. package/dist/interfaces/entities.interface.d.ts +169 -0
  350. package/dist/interfaces/entities.interface.d.ts.map +1 -0
  351. package/dist/interfaces/entities.interface.js +3 -0
  352. package/dist/interfaces/entities.interface.js.map +1 -0
  353. package/dist/interfaces/index.d.ts +11 -0
  354. package/dist/interfaces/index.d.ts.map +1 -0
  355. package/dist/interfaces/index.js +27 -0
  356. package/dist/interfaces/index.js.map +1 -0
  357. package/dist/interfaces/logger.interface.d.ts +43 -0
  358. package/dist/interfaces/logger.interface.d.ts.map +1 -0
  359. package/dist/interfaces/logger.interface.js +12 -0
  360. package/dist/interfaces/logger.interface.js.map +1 -0
  361. package/dist/interfaces/mfa-provider.interface.d.ts +12 -0
  362. package/dist/interfaces/mfa-provider.interface.d.ts.map +1 -0
  363. package/dist/interfaces/mfa-provider.interface.js +3 -0
  364. package/dist/interfaces/mfa-provider.interface.js.map +1 -0
  365. package/dist/interfaces/oauth.interface.d.ts +24 -0
  366. package/dist/interfaces/oauth.interface.d.ts.map +1 -0
  367. package/dist/interfaces/oauth.interface.js +3 -0
  368. package/dist/interfaces/oauth.interface.js.map +1 -0
  369. package/dist/interfaces/provider.interface.d.ts +12 -0
  370. package/dist/interfaces/provider.interface.d.ts.map +1 -0
  371. package/dist/interfaces/provider.interface.js +3 -0
  372. package/dist/interfaces/provider.interface.js.map +1 -0
  373. package/dist/interfaces/social-auth-provider.interface.d.ts +13 -0
  374. package/dist/interfaces/social-auth-provider.interface.d.ts.map +1 -0
  375. package/dist/interfaces/social-auth-provider.interface.js +3 -0
  376. package/dist/interfaces/social-auth-provider.interface.js.map +1 -0
  377. package/dist/interfaces/storage-adapter.interface.d.ts +39 -0
  378. package/dist/interfaces/storage-adapter.interface.d.ts.map +1 -0
  379. package/dist/interfaces/storage-adapter.interface.js +3 -0
  380. package/dist/interfaces/storage-adapter.interface.js.map +1 -0
  381. package/dist/interfaces/template.interface.d.ts +99 -0
  382. package/dist/interfaces/template.interface.d.ts.map +1 -0
  383. package/dist/interfaces/template.interface.js +15 -0
  384. package/dist/interfaces/template.interface.js.map +1 -0
  385. package/dist/interfaces/token-verifier.interface.d.ts +7 -0
  386. package/dist/interfaces/token-verifier.interface.d.ts.map +1 -0
  387. package/dist/interfaces/token-verifier.interface.js +3 -0
  388. package/dist/interfaces/token-verifier.interface.js.map +1 -0
  389. package/dist/internal.d.ts +20 -0
  390. package/dist/internal.d.ts.map +1 -0
  391. package/dist/internal.js +53 -0
  392. package/dist/internal.js.map +1 -0
  393. package/dist/platform/interfaces.d.ts +56 -0
  394. package/dist/platform/interfaces.d.ts.map +1 -0
  395. package/dist/platform/interfaces.js +3 -0
  396. package/dist/platform/interfaces.js.map +1 -0
  397. package/dist/schemas/auth-config.schema.d.ts +3411 -0
  398. package/dist/schemas/auth-config.schema.d.ts.map +1 -0
  399. package/dist/schemas/auth-config.schema.js +428 -0
  400. package/dist/schemas/auth-config.schema.js.map +1 -0
  401. package/dist/services/adaptive-mfa-decision.service.d.ts +39 -0
  402. package/dist/services/adaptive-mfa-decision.service.d.ts.map +1 -0
  403. package/dist/services/adaptive-mfa-decision.service.js +223 -0
  404. package/dist/services/adaptive-mfa-decision.service.js.map +1 -0
  405. package/dist/services/auth-audit.service.d.ts +44 -0
  406. package/dist/services/auth-audit.service.d.ts.map +1 -0
  407. package/dist/services/auth-audit.service.js +241 -0
  408. package/dist/services/auth-audit.service.js.map +1 -0
  409. package/dist/services/auth-challenge-helper.service.d.ts +48 -0
  410. package/dist/services/auth-challenge-helper.service.d.ts.map +1 -0
  411. package/dist/services/auth-challenge-helper.service.js +425 -0
  412. package/dist/services/auth-challenge-helper.service.js.map +1 -0
  413. package/dist/services/auth-flow-context-builder.service.d.ts +31 -0
  414. package/dist/services/auth-flow-context-builder.service.d.ts.map +1 -0
  415. package/dist/services/auth-flow-context-builder.service.js +253 -0
  416. package/dist/services/auth-flow-context-builder.service.js.map +1 -0
  417. package/dist/services/auth-flow-rules.d.ts +18 -0
  418. package/dist/services/auth-flow-rules.d.ts.map +1 -0
  419. package/dist/services/auth-flow-rules.js +55 -0
  420. package/dist/services/auth-flow-rules.js.map +1 -0
  421. package/dist/services/auth-flow-state-definitions.d.ts +5 -0
  422. package/dist/services/auth-flow-state-definitions.d.ts.map +1 -0
  423. package/dist/services/auth-flow-state-definitions.js +87 -0
  424. package/dist/services/auth-flow-state-definitions.js.map +1 -0
  425. package/dist/services/auth-flow-state-machine.service.d.ts +17 -0
  426. package/dist/services/auth-flow-state-machine.service.d.ts.map +1 -0
  427. package/dist/services/auth-flow-state-machine.service.js +91 -0
  428. package/dist/services/auth-flow-state-machine.service.js.map +1 -0
  429. package/dist/services/auth-flow-state-machine.types.d.ts +55 -0
  430. package/dist/services/auth-flow-state-machine.types.d.ts.map +1 -0
  431. package/dist/services/auth-flow-state-machine.types.js +16 -0
  432. package/dist/services/auth-flow-state-machine.types.js.map +1 -0
  433. package/dist/services/auth.service.d.ts +87 -0
  434. package/dist/services/auth.service.d.ts.map +1 -0
  435. package/dist/services/auth.service.js +2356 -0
  436. package/dist/services/auth.service.js.map +1 -0
  437. package/dist/services/challenge.service.d.ts +32 -0
  438. package/dist/services/challenge.service.d.ts.map +1 -0
  439. package/dist/services/challenge.service.js +293 -0
  440. package/dist/services/challenge.service.js.map +1 -0
  441. package/dist/services/client-info.service.d.ts +20 -0
  442. package/dist/services/client-info.service.d.ts.map +1 -0
  443. package/dist/services/client-info.service.js +202 -0
  444. package/dist/services/client-info.service.js.map +1 -0
  445. package/dist/services/csrf.service.d.ts +13 -0
  446. package/dist/services/csrf.service.d.ts.map +1 -0
  447. package/dist/services/csrf.service.js +67 -0
  448. package/dist/services/csrf.service.js.map +1 -0
  449. package/dist/services/email-verification.service.d.ts +30 -0
  450. package/dist/services/email-verification.service.d.ts.map +1 -0
  451. package/dist/services/email-verification.service.js +373 -0
  452. package/dist/services/email-verification.service.js.map +1 -0
  453. package/dist/services/geo-location.service.d.ts +85 -0
  454. package/dist/services/geo-location.service.d.ts.map +1 -0
  455. package/dist/services/geo-location.service.js +338 -0
  456. package/dist/services/geo-location.service.js.map +1 -0
  457. package/dist/services/index.d.ts +14 -0
  458. package/dist/services/index.d.ts.map +1 -0
  459. package/dist/services/index.js +30 -0
  460. package/dist/services/index.js.map +1 -0
  461. package/dist/services/jwt.service.d.ts +62 -0
  462. package/dist/services/jwt.service.d.ts.map +1 -0
  463. package/dist/services/jwt.service.js +261 -0
  464. package/dist/services/jwt.service.js.map +1 -0
  465. package/dist/services/mfa-base.service.d.ts +37 -0
  466. package/dist/services/mfa-base.service.d.ts.map +1 -0
  467. package/dist/services/mfa-base.service.js +297 -0
  468. package/dist/services/mfa-base.service.js.map +1 -0
  469. package/dist/services/mfa.service.d.ts +35 -0
  470. package/dist/services/mfa.service.d.ts.map +1 -0
  471. package/dist/services/mfa.service.js +449 -0
  472. package/dist/services/mfa.service.js.map +1 -0
  473. package/dist/services/password.service.d.ts +19 -0
  474. package/dist/services/password.service.d.ts.map +1 -0
  475. package/dist/services/password.service.js +150 -0
  476. package/dist/services/password.service.js.map +1 -0
  477. package/dist/services/phone-verification.service.d.ts +32 -0
  478. package/dist/services/phone-verification.service.d.ts.map +1 -0
  479. package/dist/services/phone-verification.service.js +474 -0
  480. package/dist/services/phone-verification.service.js.map +1 -0
  481. package/dist/services/risk-detection.service.d.ts +30 -0
  482. package/dist/services/risk-detection.service.d.ts.map +1 -0
  483. package/dist/services/risk-detection.service.js +518 -0
  484. package/dist/services/risk-detection.service.js.map +1 -0
  485. package/dist/services/risk-scoring.service.d.ts +12 -0
  486. package/dist/services/risk-scoring.service.d.ts.map +1 -0
  487. package/dist/services/risk-scoring.service.js +44 -0
  488. package/dist/services/risk-scoring.service.js.map +1 -0
  489. package/dist/services/session.service.d.ts +64 -0
  490. package/dist/services/session.service.d.ts.map +1 -0
  491. package/dist/services/session.service.js +455 -0
  492. package/dist/services/session.service.js.map +1 -0
  493. package/dist/services/social-auth-base.service.d.ts +57 -0
  494. package/dist/services/social-auth-base.service.d.ts.map +1 -0
  495. package/dist/services/social-auth-base.service.js +340 -0
  496. package/dist/services/social-auth-base.service.js.map +1 -0
  497. package/dist/services/social-auth.service.d.ts +31 -0
  498. package/dist/services/social-auth.service.d.ts.map +1 -0
  499. package/dist/services/social-auth.service.js +172 -0
  500. package/dist/services/social-auth.service.js.map +1 -0
  501. package/dist/services/social-provider-registry.service.d.ts +9 -0
  502. package/dist/services/social-provider-registry.service.d.ts.map +1 -0
  503. package/dist/services/social-provider-registry.service.js +30 -0
  504. package/dist/services/social-provider-registry.service.js.map +1 -0
  505. package/dist/services/trusted-device.service.d.ts +29 -0
  506. package/dist/services/trusted-device.service.d.ts.map +1 -0
  507. package/dist/services/trusted-device.service.js +190 -0
  508. package/dist/services/trusted-device.service.js.map +1 -0
  509. package/dist/storage/account-lockout-storage.service.d.ts +16 -0
  510. package/dist/storage/account-lockout-storage.service.d.ts.map +1 -0
  511. package/dist/storage/account-lockout-storage.service.js +50 -0
  512. package/dist/storage/account-lockout-storage.service.js.map +1 -0
  513. package/dist/storage/index.d.ts +4 -0
  514. package/dist/storage/index.d.ts.map +1 -0
  515. package/dist/storage/index.js +20 -0
  516. package/dist/storage/index.js.map +1 -0
  517. package/dist/storage/memory-storage.adapter.d.ts +33 -0
  518. package/dist/storage/memory-storage.adapter.d.ts.map +1 -0
  519. package/dist/storage/memory-storage.adapter.js +195 -0
  520. package/dist/storage/memory-storage.adapter.js.map +1 -0
  521. package/dist/storage/rate-limit-storage.service.d.ts +11 -0
  522. package/dist/storage/rate-limit-storage.service.d.ts.map +1 -0
  523. package/dist/storage/rate-limit-storage.service.js +33 -0
  524. package/dist/storage/rate-limit-storage.service.js.map +1 -0
  525. package/dist/templates/html-template.engine.d.ts +16 -0
  526. package/dist/templates/html-template.engine.d.ts.map +1 -0
  527. package/dist/templates/html-template.engine.js +502 -0
  528. package/dist/templates/html-template.engine.js.map +1 -0
  529. package/dist/templates/index.d.ts +2 -0
  530. package/dist/templates/index.d.ts.map +1 -0
  531. package/dist/templates/index.js +18 -0
  532. package/dist/templates/index.js.map +1 -0
  533. package/dist/utils/common-passwords.d.ts +4 -0
  534. package/dist/utils/common-passwords.d.ts.map +1 -0
  535. package/dist/utils/common-passwords.js +108 -0
  536. package/dist/utils/common-passwords.js.map +1 -0
  537. package/dist/utils/context-storage.d.ts +13 -0
  538. package/dist/utils/context-storage.d.ts.map +1 -0
  539. package/dist/utils/context-storage.js +54 -0
  540. package/dist/utils/context-storage.js.map +1 -0
  541. package/dist/utils/cookie-names.util.d.ts +7 -0
  542. package/dist/utils/cookie-names.util.d.ts.map +1 -0
  543. package/dist/utils/cookie-names.util.js +30 -0
  544. package/dist/utils/cookie-names.util.js.map +1 -0
  545. package/dist/utils/cookies.util.d.ts +12 -0
  546. package/dist/utils/cookies.util.d.ts.map +1 -0
  547. package/dist/utils/cookies.util.js +48 -0
  548. package/dist/utils/cookies.util.js.map +1 -0
  549. package/dist/utils/index.d.ts +8 -0
  550. package/dist/utils/index.d.ts.map +1 -0
  551. package/dist/utils/index.js +24 -0
  552. package/dist/utils/index.js.map +1 -0
  553. package/dist/utils/ip-extractor.d.ts +12 -0
  554. package/dist/utils/ip-extractor.d.ts.map +1 -0
  555. package/dist/utils/ip-extractor.js +88 -0
  556. package/dist/utils/ip-extractor.js.map +1 -0
  557. package/dist/utils/nauth-logger.d.ts +20 -0
  558. package/dist/utils/nauth-logger.d.ts.map +1 -0
  559. package/dist/utils/nauth-logger.js +129 -0
  560. package/dist/utils/nauth-logger.js.map +1 -0
  561. package/dist/utils/pii-redactor.d.ts +16 -0
  562. package/dist/utils/pii-redactor.d.ts.map +1 -0
  563. package/dist/utils/pii-redactor.js +147 -0
  564. package/dist/utils/pii-redactor.js.map +1 -0
  565. package/dist/utils/setup/get-repositories.d.ts +16 -0
  566. package/dist/utils/setup/get-repositories.d.ts.map +1 -0
  567. package/dist/utils/setup/get-repositories.js +36 -0
  568. package/dist/utils/setup/get-repositories.js.map +1 -0
  569. package/dist/utils/setup/init-services.d.ts +41 -0
  570. package/dist/utils/setup/init-services.d.ts.map +1 -0
  571. package/dist/utils/setup/init-services.js +107 -0
  572. package/dist/utils/setup/init-services.js.map +1 -0
  573. package/dist/utils/setup/init-social.d.ts +13 -0
  574. package/dist/utils/setup/init-social.d.ts.map +1 -0
  575. package/dist/utils/setup/init-social.js +77 -0
  576. package/dist/utils/setup/init-social.js.map +1 -0
  577. package/dist/utils/setup/init-storage.d.ts +4 -0
  578. package/dist/utils/setup/init-storage.d.ts.map +1 -0
  579. package/dist/utils/setup/init-storage.js +79 -0
  580. package/dist/utils/setup/init-storage.js.map +1 -0
  581. package/dist/utils/setup/register-mfa.d.ts +5 -0
  582. package/dist/utils/setup/register-mfa.d.ts.map +1 -0
  583. package/dist/utils/setup/register-mfa.js +85 -0
  584. package/dist/utils/setup/register-mfa.js.map +1 -0
  585. package/dist/utils/setup/run-nauth-migrations.d.ts +5 -0
  586. package/dist/utils/setup/run-nauth-migrations.d.ts.map +1 -0
  587. package/dist/utils/setup/run-nauth-migrations.js +67 -0
  588. package/dist/utils/setup/run-nauth-migrations.js.map +1 -0
  589. package/dist/utils/token-delivery-policy.d.ts +6 -0
  590. package/dist/utils/token-delivery-policy.d.ts.map +1 -0
  591. package/dist/utils/token-delivery-policy.js +15 -0
  592. package/dist/utils/token-delivery-policy.js.map +1 -0
  593. package/dist/validators/template.validator.d.ts +7 -0
  594. package/dist/validators/template.validator.d.ts.map +1 -0
  595. package/dist/validators/template.validator.js +95 -0
  596. package/dist/validators/template.validator.js.map +1 -0
  597. package/jest.config.js +15 -0
  598. package/jest.setup.ts +6 -0
  599. package/package.json +73 -0
  600. package/src/adapters/database-columns.ts +165 -0
  601. package/src/adapters/express.adapter.ts +385 -0
  602. package/src/adapters/fastify.adapter.ts +416 -0
  603. package/src/adapters/index.ts +16 -0
  604. package/src/adapters/storage.factory.ts +143 -0
  605. package/src/bootstrap.ts +374 -0
  606. package/src/dto/auth-challenge.dto.ts +231 -0
  607. package/src/dto/auth-response.dto.ts +253 -0
  608. package/src/dto/challenge-response.dto.ts +234 -0
  609. package/src/dto/change-password-request.dto.ts +50 -0
  610. package/src/dto/change-password-response.dto.ts +29 -0
  611. package/src/dto/change-password.dto.ts +57 -0
  612. package/src/dto/error-response.dto.ts +136 -0
  613. package/src/dto/get-available-methods.dto.ts +55 -0
  614. package/src/dto/get-challenge-data-response.dto.ts +28 -0
  615. package/src/dto/get-challenge-data.dto.ts +69 -0
  616. package/src/dto/get-client-info.dto.ts +104 -0
  617. package/src/dto/get-device-token-response.dto.ts +25 -0
  618. package/src/dto/get-events-by-type.dto.ts +76 -0
  619. package/src/dto/get-ip-address-response.dto.ts +24 -0
  620. package/src/dto/get-mfa-status.dto.ts +94 -0
  621. package/src/dto/get-risk-assessment-history.dto.ts +39 -0
  622. package/src/dto/get-session-id-response.dto.ts +25 -0
  623. package/src/dto/get-setup-data-response.dto.ts +31 -0
  624. package/src/dto/get-setup-data.dto.ts +75 -0
  625. package/src/dto/get-suspicious-activity.dto.ts +42 -0
  626. package/src/dto/get-user-agent-response.dto.ts +23 -0
  627. package/src/dto/get-user-auth-history.dto.ts +95 -0
  628. package/src/dto/get-user-by-email.dto.ts +61 -0
  629. package/src/dto/get-user-by-id.dto.ts +46 -0
  630. package/src/dto/get-user-devices.dto.ts +53 -0
  631. package/src/dto/get-user-response.dto.ts +17 -0
  632. package/src/dto/has-provider.dto.ts +56 -0
  633. package/src/dto/index.ts +57 -0
  634. package/src/dto/is-trusted-device-response.dto.ts +34 -0
  635. package/src/dto/list-providers-response.dto.ts +23 -0
  636. package/src/dto/login.dto.ts +95 -0
  637. package/src/dto/logout-all-response.dto.ts +24 -0
  638. package/src/dto/logout-all.dto.ts +65 -0
  639. package/src/dto/logout-response.dto.ts +25 -0
  640. package/src/dto/logout.dto.ts +64 -0
  641. package/src/dto/refresh-token.dto.ts +36 -0
  642. package/src/dto/remove-devices.dto.ts +85 -0
  643. package/src/dto/resend-code-response.dto.ts +32 -0
  644. package/src/dto/resend-code.dto.ts +51 -0
  645. package/src/dto/reset-password.dto.ts +115 -0
  646. package/src/dto/respond-challenge.dto.ts +272 -0
  647. package/src/dto/set-mfa-exemption.dto.ts +112 -0
  648. package/src/dto/set-must-change-password-response.dto.ts +27 -0
  649. package/src/dto/set-must-change-password.dto.ts +46 -0
  650. package/src/dto/set-preferred-method.dto.ts +80 -0
  651. package/src/dto/setup-mfa.dto.ts +98 -0
  652. package/src/dto/signup.dto.ts +174 -0
  653. package/src/dto/social-auth.dto.ts +422 -0
  654. package/src/dto/trust-device-response.dto.ts +30 -0
  655. package/src/dto/trust-device.dto.ts +9 -0
  656. package/src/dto/update-user-attributes-request.dto.ts +51 -0
  657. package/src/dto/user-response.dto.ts +138 -0
  658. package/src/dto/user-update.dto.ts +222 -0
  659. package/src/dto/verify-email.dto.ts +313 -0
  660. package/src/dto/verify-mfa-code.dto.ts +103 -0
  661. package/src/dto/verify-phone-by-sub.dto.ts +78 -0
  662. package/src/dto/verify-phone.dto.ts +245 -0
  663. package/src/entities/auth-audit.entity.ts +232 -0
  664. package/src/entities/challenge-session.entity.ts +116 -0
  665. package/src/entities/index.ts +29 -0
  666. package/src/entities/login-attempt.entity.ts +64 -0
  667. package/src/entities/mfa-device.entity.ts +151 -0
  668. package/src/entities/rate-limit.entity.ts +44 -0
  669. package/src/entities/session.entity.ts +180 -0
  670. package/src/entities/social-account.entity.ts +96 -0
  671. package/src/entities/storage-lock.entity.ts +39 -0
  672. package/src/entities/trusted-device.entity.ts +112 -0
  673. package/src/entities/user.entity.ts +243 -0
  674. package/src/entities/verification-token.entity.ts +141 -0
  675. package/src/enums/auth-audit-event-type.enum.ts +360 -0
  676. package/src/enums/error-codes.enum.ts +420 -0
  677. package/src/enums/mfa-method.enum.ts +97 -0
  678. package/src/enums/risk-factor.enum.ts +111 -0
  679. package/src/exceptions/nauth.exception.ts +231 -0
  680. package/src/handlers/auth.handler.ts +260 -0
  681. package/src/handlers/client-info.handler.ts +101 -0
  682. package/src/handlers/csrf.handler.ts +156 -0
  683. package/src/handlers/token-delivery.handler.ts +118 -0
  684. package/src/index.ts +118 -0
  685. package/src/interfaces/client-info.interface.ts +85 -0
  686. package/src/interfaces/config.interface.ts +2135 -0
  687. package/src/interfaces/entities.interface.ts +226 -0
  688. package/src/interfaces/index.ts +15 -0
  689. package/src/interfaces/logger.interface.ts +283 -0
  690. package/src/interfaces/mfa-provider.interface.ts +154 -0
  691. package/src/interfaces/oauth.interface.ts +148 -0
  692. package/src/interfaces/provider.interface.ts +47 -0
  693. package/src/interfaces/social-auth-provider.interface.ts +131 -0
  694. package/src/interfaces/storage-adapter.interface.ts +82 -0
  695. package/src/interfaces/template.interface.ts +510 -0
  696. package/src/interfaces/token-verifier.interface.ts +110 -0
  697. package/src/internal.ts +178 -0
  698. package/src/platform/interfaces.ts +299 -0
  699. package/src/schemas/auth-config.schema.ts +646 -0
  700. package/src/services/adaptive-mfa-decision.service.spec.ts +1058 -0
  701. package/src/services/adaptive-mfa-decision.service.ts +457 -0
  702. package/src/services/auth-audit.service.spec.ts +675 -0
  703. package/src/services/auth-audit.service.ts +558 -0
  704. package/src/services/auth-challenge-helper.service.spec.ts +3227 -0
  705. package/src/services/auth-challenge-helper.service.ts +825 -0
  706. package/src/services/auth-flow-context-builder.service.ts +520 -0
  707. package/src/services/auth-flow-rules.ts +202 -0
  708. package/src/services/auth-flow-state-definitions.ts +190 -0
  709. package/src/services/auth-flow-state-machine.service.ts +207 -0
  710. package/src/services/auth-flow-state-machine.types.ts +316 -0
  711. package/src/services/auth.service.spec.ts +4195 -0
  712. package/src/services/auth.service.ts +3727 -0
  713. package/src/services/challenge.service.spec.ts +1363 -0
  714. package/src/services/challenge.service.ts +696 -0
  715. package/src/services/client-info.service.spec.ts +572 -0
  716. package/src/services/client-info.service.ts +374 -0
  717. package/src/services/csrf.service.ts +54 -0
  718. package/src/services/email-verification.service.spec.ts +1229 -0
  719. package/src/services/email-verification.service.ts +578 -0
  720. package/src/services/geo-location.service.spec.ts +603 -0
  721. package/src/services/geo-location.service.ts +599 -0
  722. package/src/services/index.ts +13 -0
  723. package/src/services/jwt.service.spec.ts +882 -0
  724. package/src/services/jwt.service.ts +621 -0
  725. package/src/services/mfa-base.service.spec.ts +246 -0
  726. package/src/services/mfa-base.service.ts +611 -0
  727. package/src/services/mfa.service.spec.ts +693 -0
  728. package/src/services/mfa.service.ts +960 -0
  729. package/src/services/password.service.spec.ts +166 -0
  730. package/src/services/password.service.ts +309 -0
  731. package/src/services/phone-verification.service.spec.ts +1120 -0
  732. package/src/services/phone-verification.service.ts +751 -0
  733. package/src/services/risk-detection.service.spec.ts +1292 -0
  734. package/src/services/risk-detection.service.ts +1012 -0
  735. package/src/services/risk-scoring.service.spec.ts +204 -0
  736. package/src/services/risk-scoring.service.ts +131 -0
  737. package/src/services/session.service.spec.ts +1293 -0
  738. package/src/services/session.service.ts +803 -0
  739. package/src/services/social-account.service.spec.ts +725 -0
  740. package/src/services/social-auth-base.service.spec.ts +418 -0
  741. package/src/services/social-auth-base.service.ts +581 -0
  742. package/src/services/social-auth.service.spec.ts +238 -0
  743. package/src/services/social-auth.service.ts +436 -0
  744. package/src/services/social-provider-registry.service.spec.ts +238 -0
  745. package/src/services/social-provider-registry.service.ts +122 -0
  746. package/src/services/trusted-device.service.spec.ts +505 -0
  747. package/src/services/trusted-device.service.ts +339 -0
  748. package/src/storage/account-lockout-storage.service.spec.ts +310 -0
  749. package/src/storage/account-lockout-storage.service.ts +89 -0
  750. package/src/storage/index.ts +3 -0
  751. package/src/storage/memory-storage.adapter.ts +443 -0
  752. package/src/storage/rate-limit-storage.service.spec.ts +247 -0
  753. package/src/storage/rate-limit-storage.service.ts +38 -0
  754. package/src/templates/html-template.engine.spec.ts +161 -0
  755. package/src/templates/html-template.engine.ts +688 -0
  756. package/src/templates/index.ts +7 -0
  757. package/src/utils/common-passwords.spec.ts +230 -0
  758. package/src/utils/common-passwords.ts +170 -0
  759. package/src/utils/context-storage.ts +188 -0
  760. package/src/utils/cookie-names.util.ts +67 -0
  761. package/src/utils/cookies.util.ts +94 -0
  762. package/src/utils/index.ts +12 -0
  763. package/src/utils/ip-extractor.spec.ts +330 -0
  764. package/src/utils/ip-extractor.ts +220 -0
  765. package/src/utils/nauth-logger.spec.ts +388 -0
  766. package/src/utils/nauth-logger.ts +215 -0
  767. package/src/utils/pii-redactor.spec.ts +130 -0
  768. package/src/utils/pii-redactor.ts +288 -0
  769. package/src/utils/setup/get-repositories.ts +140 -0
  770. package/src/utils/setup/init-services.ts +422 -0
  771. package/src/utils/setup/init-social.ts +189 -0
  772. package/src/utils/setup/init-storage.ts +94 -0
  773. package/src/utils/setup/register-mfa.ts +165 -0
  774. package/src/utils/setup/run-nauth-migrations.ts +61 -0
  775. package/src/utils/token-delivery-policy.ts +38 -0
  776. package/src/validators/template.validator.ts +219 -0
  777. package/tsconfig.json +37 -0
  778. package/tsconfig.lint.json +6 -0
@@ -0,0 +1,1012 @@
1
+ import { Repository, IsNull, Not, MoreThan } from 'typeorm';
2
+ import { IUser, ISession } from '../interfaces/entities.interface';
3
+ import { BaseSession, BaseAuthAudit } from '../entities';
4
+ import { ClientInfo } from '../interfaces/client-info.interface';
5
+ import { NAuthConfig } from '../interfaces/config.interface';
6
+ import { NAuthLogger } from '../utils/nauth-logger';
7
+ import { AuthAuditEventType } from '../enums/auth-audit-event-type.enum';
8
+ import { RiskFactor } from '../enums/risk-factor.enum';
9
+
10
+ /**
11
+ * Risk Detection Service
12
+ *
13
+ * Analyzes authentication attempts for risk factors by comparing current
14
+ * context against user's historical behavior (sessions and audit trail).
15
+ *
16
+ * **Risk Factors Detected:**
17
+ * - `new_device`: DeviceId never seen before (check sessions table)
18
+ * - `new_ip`: IP address never seen before (check sessions + audit)
19
+ * - `new_country`: Country never seen before (check sessions where ipCountry)
20
+ * - `impossible_travel`: Geographic distance impossible in time window
21
+ * - `suspicious_activity`: Recent failed attempts, token reuse, etc.
22
+ *
23
+ * **Design Notes:**
24
+ * - All queries use userId (internal integer ID) for optimal performance
25
+ * - Queries are optimized with COUNT and LIMIT 1 for existence checks
26
+ * - Non-blocking: Errors logged but don't throw (graceful degradation)
27
+ * - Impossible travel detection requires city-level geolocation (optional)
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * const riskFactors = await riskDetectionService.detectRiskFactors(user, clientInfo);
32
+ * // Returns: ['new_device', 'new_country']
33
+ * ```
34
+ */
35
+ export class RiskDetectionService {
36
+ constructor(
37
+ private readonly sessionRepository: Repository<BaseSession>,
38
+ private readonly auditRepository: Repository<BaseAuthAudit>,
39
+ private readonly config: NAuthConfig,
40
+ private readonly logger: NAuthLogger,
41
+ private readonly trustedDeviceService?: {
42
+ isDeviceTrusted: (deviceToken: string, userId: number) => Promise<boolean>;
43
+ }, // TrustedDeviceService - optional, only available when rememberDevices is enabled
44
+ ) {}
45
+
46
+ /**
47
+ * Detect risk factors for current authentication attempt
48
+ *
49
+ * Compares current context against user's historical behavior to identify
50
+ * potential security risks. Returns array of detected risk factor strings.
51
+ *
52
+ * **Double-Counting Prevention:**
53
+ * - If `new_country` is detected, `new_ip` is NOT checked (IP is source of country data)
54
+ * - If `impossible_travel` is detected (city change), `new_ip` is NOT checked
55
+ * - This prevents double-counting the same underlying risk (location change)
56
+ *
57
+ * @param user - User being authenticated
58
+ * @param clientInfo - Current request context (IP, device, location, etc.)
59
+ * @returns Array of detected risk factor strings
60
+ *
61
+ * @example
62
+ * ```typescript
63
+ * const factors = await riskDetectionService.detectRiskFactors(user, clientInfo);
64
+ * // Returns: ['new_device', 'new_country'] // new_ip excluded if new_country detected
65
+ * ```
66
+ */
67
+ async detectRiskFactors(user: IUser, clientInfo: ClientInfo): Promise<RiskFactor[]> {
68
+ const factors: RiskFactor[] = [];
69
+
70
+ try {
71
+ // ============================================================================
72
+ // Trigger Configuration
73
+ // ============================================================================
74
+ // Check which triggers are enabled in config
75
+ // Config uses string literals, but they match RiskFactor enum values
76
+ const enabledTriggers = this.config.mfa?.adaptive?.triggers || [
77
+ RiskFactor.NEW_DEVICE,
78
+ RiskFactor.NEW_IP,
79
+ RiskFactor.NEW_COUNTRY,
80
+ ];
81
+
82
+ this.logger?.debug?.(
83
+ `Risk detection started for user ${user.sub}: enabled_triggers=[${enabledTriggers.join(', ')}], has_device_token=${!!clientInfo.deviceToken}, ip=${clientInfo.ipAddress}, location=${clientInfo.ipCity}, ${clientInfo.ipCountry}`,
84
+ );
85
+
86
+ // ============================================================================
87
+ // Device Risk Detection
88
+ // ============================================================================
89
+ // Check new_device
90
+ if (enabledTriggers.includes(RiskFactor.NEW_DEVICE)) {
91
+ if (clientInfo.deviceToken) {
92
+ // Device has a token - check if it's been seen before
93
+ // This is the most reliable method (cookie-based or header-based token)
94
+ const isNew = await this.isNewDevice(user.id, clientInfo.deviceToken);
95
+ if (isNew) {
96
+ factors.push(RiskFactor.NEW_DEVICE);
97
+ this.logger?.debug?.(
98
+ `New device detected: user=${user.sub}, deviceToken=${clientInfo.deviceToken.substring(0, 8)}...`,
99
+ );
100
+ }
101
+ } else {
102
+ // No deviceToken (incognito mode, cleared cookies, first login, etc.)
103
+ // Check if user has logged in before - if yes, missing token is suspicious
104
+ // This prevents false positives for legitimate first-time logins
105
+ const hasPreviousSessions = await this.hasUserLoggedInBefore(user.id);
106
+ if (hasPreviousSessions) {
107
+ // User has logged in before but no deviceToken - treat as new/unknown device
108
+ // This covers incognito mode, cleared cookies, and other scenarios where
109
+ // device identification is not available
110
+ factors.push(RiskFactor.NEW_DEVICE);
111
+ this.logger?.debug?.(
112
+ `Missing deviceToken for user ${user.sub} with previous sessions - treating as new_device`,
113
+ );
114
+ }
115
+ }
116
+ }
117
+
118
+ // ============================================================================
119
+ // Location Risk Detection (with Double-Counting Prevention)
120
+ // ============================================================================
121
+ // Check new_country first (before new_ip to avoid double-counting)
122
+ // IP is the source of country/city data, so if country changed, IP definitely changed
123
+ let newCountryDetected = false;
124
+ if (enabledTriggers.includes(RiskFactor.NEW_COUNTRY) && clientInfo.ipCountry) {
125
+ const isNew = await this.isNewCountry(user.id, clientInfo.ipCountry);
126
+ if (isNew) {
127
+ factors.push(RiskFactor.NEW_COUNTRY);
128
+ newCountryDetected = true;
129
+ this.logger?.debug?.(`New country detected for user ${user.sub}: ${clientInfo.ipCountry}`);
130
+ }
131
+ }
132
+
133
+ // Check impossible_travel (now works with country-only data too)
134
+ // This also indicates location change (city/country), so skip new_ip if detected
135
+ let impossibleTravelDetected = false;
136
+ if (enabledTriggers.includes(RiskFactor.IMPOSSIBLE_TRAVEL) && clientInfo.ipCountry) {
137
+ const isImpossible = await this.detectImpossibleTravel(user.id, clientInfo);
138
+ if (isImpossible) {
139
+ factors.push(RiskFactor.IMPOSSIBLE_TRAVEL);
140
+ impossibleTravelDetected = true;
141
+ this.logger?.debug?.(
142
+ `Impossible travel detected for user ${user.sub}: ${clientInfo.ipCity ?? 'unknown'}, ${clientInfo.ipCountry}`,
143
+ );
144
+ }
145
+ }
146
+
147
+ // ============================================================================
148
+ // Location Data Completeness Check
149
+ // ============================================================================
150
+ // Add risk factor if location data is incomplete (missing city or coordinates) when country exists
151
+ // AND user has previous logins (only relevant for returning users)
152
+ // This reduces confidence in risk assessment and warrants extra caution
153
+ // Only check if location-related triggers are enabled
154
+ const locationTriggersEnabled =
155
+ enabledTriggers.includes(RiskFactor.NEW_COUNTRY) || enabledTriggers.includes(RiskFactor.IMPOSSIBLE_TRAVEL);
156
+ if (
157
+ locationTriggersEnabled &&
158
+ clientInfo.ipCountry &&
159
+ (!clientInfo.ipCity || !clientInfo.ipLatitude || !clientInfo.ipLongitude) &&
160
+ (await this.hasUserLoggedInBefore(user.id))
161
+ ) {
162
+ factors.push(RiskFactor.INCOMPLETE_LOCATION_DATA);
163
+ this.logger?.warn?.(
164
+ `Incomplete location data for user ${user.sub}: country=${clientInfo.ipCountry}, ` +
165
+ `city=${clientInfo.ipCity ?? 'missing'}, coordinates=${clientInfo.ipLatitude && clientInfo.ipLongitude ? 'available' : 'missing'}. ` +
166
+ `Adding INCOMPLETE_LOCATION_DATA risk factor (+20 points) for reduced confidence in risk assessment.`,
167
+ );
168
+ }
169
+
170
+ // ============================================================================
171
+ // IP Risk Detection (only if location unchanged)
172
+ // ============================================================================
173
+ // Check new_ip only if country/city hasn't changed
174
+ // IP is the source of location data, so if country/city changed, IP definitely changed
175
+ // Avoid double-counting the same risk factor
176
+ if (
177
+ enabledTriggers.includes(RiskFactor.NEW_IP) &&
178
+ clientInfo.ipAddress &&
179
+ !newCountryDetected &&
180
+ !impossibleTravelDetected
181
+ ) {
182
+ // Normalize IP address (remove port if present, e.g., "192.168.1.1:8080" -> "192.168.1.1")
183
+ const normalizedIp = this.normalizeIpAddress(clientInfo.ipAddress);
184
+ const isNew = await this.isNewIp(user.id, normalizedIp);
185
+ if (isNew) {
186
+ factors.push(RiskFactor.NEW_IP);
187
+ this.logger?.debug?.(
188
+ `New IP detected for user ${user.sub}: ${normalizedIp} (original: ${clientInfo.ipAddress})`,
189
+ );
190
+ }
191
+ }
192
+
193
+ // ============================================================================
194
+ // Behavioral Risk Detection
195
+ // ============================================================================
196
+ // Check suspicious_activity
197
+ if (enabledTriggers.includes(RiskFactor.SUSPICIOUS_ACTIVITY)) {
198
+ const isSuspicious = await this.detectSuspiciousActivity(user.id);
199
+ if (isSuspicious) {
200
+ factors.push(RiskFactor.SUSPICIOUS_ACTIVITY);
201
+ this.logger?.debug?.(`Suspicious activity detected for user ${user.sub}`);
202
+ }
203
+ }
204
+
205
+ // ============================================================================
206
+ // Summary
207
+ // ============================================================================
208
+ if (factors.length > 0) {
209
+ this.logger?.debug?.(
210
+ `Risk detection complete for user ${user.sub}: ${factors.length} factor(s) detected [${factors.join(', ')}]`,
211
+ );
212
+ }
213
+ } catch (error) {
214
+ // Non-blocking: Log error but don't throw
215
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
216
+ this.logger?.error?.(`Risk detection failed for user ${user.sub}: ${errorMessage}`, { error, userId: user.id });
217
+ // Return empty array on error (graceful degradation)
218
+ return [];
219
+ }
220
+
221
+ return factors;
222
+ }
223
+
224
+ /**
225
+ * Check if device has been seen before
226
+ *
227
+ * Checks trusted devices first (if available), then sessions table.
228
+ * If device is trusted, it's not considered "new" even if no sessions exist yet.
229
+ *
230
+ * @param userId - Internal user ID (integer)
231
+ * @param deviceToken - Device token from client
232
+ * @returns True if device is new (never seen before and not trusted)
233
+ * @private
234
+ */
235
+ private async isNewDevice(userId: number, deviceToken: string): Promise<boolean> {
236
+ try {
237
+ // First, check if device is trusted (if trusted device service is available)
238
+ // Trusted devices should not be flagged as "new" even if no sessions exist
239
+ if (this.trustedDeviceService && typeof this.trustedDeviceService.isDeviceTrusted === 'function') {
240
+ try {
241
+ const isTrusted = await this.trustedDeviceService.isDeviceTrusted(deviceToken, userId);
242
+ if (isTrusted) {
243
+ // Device is trusted - not a new device
244
+ return false;
245
+ }
246
+ } catch (trustedError) {
247
+ // Non-blocking: If trusted device check fails, continue to session check
248
+ const errorMessage = trustedError instanceof Error ? trustedError.message : 'Unknown error';
249
+ this.logger?.warn?.(`Failed to check trusted device: ${errorMessage}`, {
250
+ error: trustedError,
251
+ userId,
252
+ deviceToken,
253
+ });
254
+ }
255
+ }
256
+
257
+ // Check if any session exists with this deviceId (short-circuit existence)
258
+ // Note: deviceToken is stored as deviceId in sessions
259
+ const exists = await this.sessionRepository.findOne({
260
+ select: ['id'],
261
+ where: { userId, deviceId: deviceToken },
262
+ });
263
+
264
+ return !exists;
265
+ } catch (error) {
266
+ // Non-blocking: Log and assume device is new (safer default)
267
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
268
+ this.logger?.warn?.(`Failed to check device history: ${errorMessage}`, { error, userId, deviceToken });
269
+ return true; // Assume new device on error (safer for security)
270
+ }
271
+ }
272
+
273
+ /**
274
+ * Check if user has logged in before (has any previous sessions)
275
+ *
276
+ * Used to determine if missing deviceToken should be treated as suspicious.
277
+ * If user has never logged in before, missing token is expected (first login).
278
+ * If user has logged in before, missing token is suspicious (incognito, cleared cookies, etc.).
279
+ *
280
+ * @param userId - Internal user ID (integer)
281
+ * @returns True if user has at least one previous session
282
+ * @private
283
+ */
284
+ private async hasUserLoggedInBefore(userId: number): Promise<boolean> {
285
+ try {
286
+ // Check if any session exists for this user (short-circuit existence check)
287
+ const exists = await this.sessionRepository.findOne({
288
+ select: ['id'],
289
+ where: { userId },
290
+ });
291
+
292
+ return !!exists;
293
+ } catch (error) {
294
+ // Non-blocking: Log and assume user has logged in before (safer default)
295
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
296
+ this.logger?.warn?.(`Failed to check user login history: ${errorMessage}`, { error, userId });
297
+ return true; // Assume user has logged in before on error (safer for security)
298
+ }
299
+ }
300
+
301
+ /**
302
+ * Normalize IP address for consistent comparison
303
+ *
304
+ * Removes port numbers and normalizes IPv6 addresses.
305
+ * This ensures IPs like "192.168.1.1:8080" and "192.168.1.1" are treated as the same.
306
+ *
307
+ * @param ipAddress - IP address to normalize
308
+ * @returns Normalized IP address
309
+ * @private
310
+ */
311
+ private normalizeIpAddress(ipAddress: string): string {
312
+ // Remove port if present (e.g., "192.168.1.1:8080" -> "192.168.1.1")
313
+ // Handle both IPv4 and IPv6 formats
314
+ if (ipAddress.includes(':')) {
315
+ // Could be IPv6 or IPv4 with port
316
+ if (ipAddress.startsWith('[')) {
317
+ // IPv6 with port: "[::1]:8080" -> "::1"
318
+ const match = ipAddress.match(/^\[(.+)\]:\d+$/);
319
+ if (match) {
320
+ return match[1];
321
+ }
322
+ } else {
323
+ // IPv4 with port: "192.168.1.1:8080" -> "192.168.1.1"
324
+ const parts = ipAddress.split(':');
325
+ if (parts.length === 2 && /^\d+$/.test(parts[1])) {
326
+ // Second part is a port number
327
+ return parts[0];
328
+ }
329
+ // Otherwise it's likely IPv6 without brackets, return as-is
330
+ }
331
+ }
332
+ return ipAddress;
333
+ }
334
+
335
+ /**
336
+ * Check if IP address has been seen before
337
+ *
338
+ * Queries sessions table first (faster), then audit table for older data.
339
+ * Uses normalized IP addresses for consistent comparison.
340
+ *
341
+ * @param userId - Internal user ID (integer)
342
+ * @param ipAddress - IP address to check (should already be normalized)
343
+ * @returns True if IP is new (never seen before)
344
+ * @private
345
+ */
346
+ private async isNewIp(userId: number, ipAddress: string): Promise<boolean> {
347
+ try {
348
+ // Normalize IP address before checking (in case it wasn't normalized upstream)
349
+ const normalizedIp = this.normalizeIpAddress(ipAddress);
350
+
351
+ // Check sessions first (faster, more recent data) - existence only
352
+ // Note: We check both normalized and original IP to handle cases where
353
+ // old records might have stored IPs with ports
354
+ const seenInSessions =
355
+ (await this.sessionRepository.findOne({
356
+ select: ['id'],
357
+ where: { userId, ipAddress: normalizedIp },
358
+ })) ||
359
+ (normalizedIp !== ipAddress &&
360
+ (await this.sessionRepository.findOne({
361
+ select: ['id'],
362
+ where: { userId, ipAddress },
363
+ })));
364
+
365
+ if (seenInSessions) {
366
+ return false; // IP seen in sessions
367
+ }
368
+
369
+ // Check audit trail for older data (only if not found in sessions)
370
+ const seenInAudit =
371
+ (await this.auditRepository.findOne({
372
+ select: ['id'],
373
+ where: { userId, ipAddress: normalizedIp },
374
+ })) ||
375
+ (normalizedIp !== ipAddress &&
376
+ (await this.auditRepository.findOne({
377
+ select: ['id'],
378
+ where: { userId, ipAddress },
379
+ })));
380
+
381
+ return !seenInAudit;
382
+ } catch (error) {
383
+ // Non-blocking: Log and assume IP is new (safer default)
384
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
385
+ this.logger?.warn?.(`Failed to check IP history: ${errorMessage}`, { error, userId, ipAddress });
386
+ return true; // Assume new IP on error (safer for security)
387
+ }
388
+ }
389
+
390
+ /**
391
+ * Check if country has been seen before
392
+ *
393
+ * Queries sessions table for any past session from this country.
394
+ * **Optimization:** Uses 1-2 queries instead of 3 by checking country existence first
395
+ * (most likely to short-circuit), then verifying country data availability if needed.
396
+ *
397
+ * **Important:**
398
+ * - On first login (no previous sessions), returns false (no history to compare)
399
+ * - If sessions exist but none have ipCountry data (null), returns false (can't determine)
400
+ * - Only flags as new if we have sessions with country data AND none match
401
+ *
402
+ * @param userId - Internal user ID (integer)
403
+ * @param country - Country code to check (e.g., 'US', 'GB')
404
+ * @returns True if country is new (never seen before), false on first login, if no country data, or if country seen before
405
+ * @private
406
+ */
407
+ private async isNewCountry(userId: number, country: string): Promise<boolean> {
408
+ try {
409
+ // ============================================================================
410
+ // Optimization: Check country existence first (most likely to short-circuit)
411
+ // ============================================================================
412
+ // This query checks if country exists in sessions with country data
413
+ // If it exists, we can return false immediately (1 query instead of 3)
414
+ const countryExists = await this.sessionRepository.findOne({
415
+ select: ['id'],
416
+ where: { userId, ipCountry: country },
417
+ });
418
+
419
+ // If country exists in any session, it's not new
420
+ if (countryExists) {
421
+ return false;
422
+ }
423
+
424
+ // ============================================================================
425
+ // Country doesn't exist - verify we have country data to compare against
426
+ // ============================================================================
427
+ // Only check if we have sessions with country data (needed to make determination)
428
+ // If no country data exists, we can't determine if it's new (first login or no geo data)
429
+ const hasAnyCountryData = await this.sessionRepository.findOne({
430
+ select: ['id'],
431
+ where: { userId, ipCountry: Not(IsNull()) },
432
+ });
433
+
434
+ // If we have sessions with country data but country doesn't exist, it's new
435
+ // If no sessions have country data, we can't determine (return false for safety)
436
+ return !!hasAnyCountryData;
437
+ } catch (error) {
438
+ // Non-blocking: Log and assume country is not new on error (safer default)
439
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
440
+ this.logger?.warn?.(`Failed to check country history: ${errorMessage}`, { error, userId, country });
441
+ return false; // Assume not new country on error (safer default)
442
+ }
443
+ }
444
+
445
+ /**
446
+ * Detect impossible travel
447
+ *
448
+ * Calculates if geographic distance between last location and current
449
+ * location is impossible given time elapsed.
450
+ *
451
+ * **Algorithm:**
452
+ * 1. Get last login location (ipCountry, ipCity, coordinates) and createdAt
453
+ * 2. Calculate distance using coordinates (Haversine) or heuristics (fallback)
454
+ * 3. Calculate max possible speed (distance / time)
455
+ * 4. If speed > threshold (default 900 km/h), flag as impossible
456
+ *
457
+ * **Edge Cases Handled:**
458
+ * - No previous location data → false (benefit of doubt)
459
+ * - Same location → false (not travel)
460
+ * - Missing city but country changed → true (suspicious, different country in short time)
461
+ * - Missing coordinates → use heuristic distance estimates
462
+ *
463
+ * @param userId - Internal user ID (integer)
464
+ * @param currentInfo - Current client info with location
465
+ * @returns True if travel is impossible
466
+ * @private
467
+ */
468
+ private async detectImpossibleTravel(userId: number, currentInfo: ClientInfo): Promise<boolean> {
469
+ // Require at least country data to perform any checks
470
+ if (!currentInfo.ipCountry) {
471
+ this.logger?.debug?.(`Skipping impossible_travel check for user ${userId}: no country data available`);
472
+ return false;
473
+ }
474
+
475
+ try {
476
+ // ============================================================================
477
+ // Find last known location from most recent login
478
+ // ============================================================================
479
+ // CRITICAL: We need the location from the PREVIOUS login, not token refreshes
480
+ // Check BOTH sessions and audit trail to ensure we get the most recent location:
481
+ // - Sessions: might be cleaned up or expired
482
+ // - Audit: permanent record of all logins
483
+ // Use the more recent of the two
484
+
485
+ // ============================================================================
486
+ // Find previous login location (exclude current login to avoid race conditions)
487
+ // ============================================================================
488
+ // SIMPLIFIED LOGIC: Just get the most recent login from EITHER sessions OR audits
489
+ // The current login hasn't been committed yet (we're in risk detection before session creation)
490
+ // So the "most recent" we find IS the previous login
491
+
492
+ // Check sessions (use createdAt for login-to-login comparison, not lastActivityAt)
493
+ const lastSession = (await this.sessionRepository.findOne({
494
+ where: {
495
+ userId,
496
+ ipCountry: Not(IsNull()),
497
+ },
498
+ order: {
499
+ createdAt: 'DESC', // Most recent first
500
+ },
501
+ })) as ISession | null;
502
+
503
+ // Check audit trail for most recent login with location data
504
+ const lastAuditLogin = (await this.auditRepository.findOne({
505
+ where: {
506
+ userId,
507
+ eventType: AuthAuditEventType.LOGIN_SUCCESS,
508
+ ipCountry: Not(IsNull()),
509
+ },
510
+ order: {
511
+ createdAt: 'DESC',
512
+ },
513
+ })) as
514
+ | (BaseAuthAudit & {
515
+ ipCountry: string;
516
+ ipCity: string | null;
517
+ ipLatitude: number | null;
518
+ ipLongitude: number | null;
519
+ createdAt: Date;
520
+ })
521
+ | null;
522
+
523
+ // ============================================================================
524
+ // Debug Dumps (optional)
525
+ // ============================================================================
526
+ // WHY: These queries are expensive and should only run when a real NAuthLogger
527
+ // instance is wired (consumer provided a logger). Unit tests frequently use
528
+ // plain object mocks that do NOT implement `isEnabled()`.
529
+ const loggerEnabled =
530
+ typeof (this.logger as unknown as { isEnabled?: () => boolean }).isEnabled === 'function'
531
+ ? (this.logger as unknown as { isEnabled: () => boolean }).isEnabled()
532
+ : false;
533
+
534
+ if (loggerEnabled && lastSession) {
535
+ const allRecentSessions = (await this.sessionRepository.find({
536
+ where: { userId },
537
+ order: { createdAt: 'DESC' },
538
+ take: 5,
539
+ select: ['id', 'ipCity', 'ipCountry', 'createdAt', 'ipAddress'],
540
+ })) as Array<{
541
+ id: number;
542
+ ipCity: string | null;
543
+ ipCountry: string | null;
544
+ createdAt: Date;
545
+ ipAddress: string | null;
546
+ }>;
547
+
548
+ this.logger?.debug?.(
549
+ `Last 5 sessions for user ${userId}: ${JSON.stringify(
550
+ allRecentSessions.map((s) => ({
551
+ id: s.id,
552
+ location: `${s.ipCity}, ${s.ipCountry}`,
553
+ ip: s.ipAddress,
554
+ created: s.createdAt.toISOString(),
555
+ ageMinutes: ((Date.now() - s.createdAt.getTime()) / (1000 * 60)).toFixed(1),
556
+ })),
557
+ )}`,
558
+ );
559
+ }
560
+
561
+ if (loggerEnabled && lastAuditLogin) {
562
+ const allRecentAudits = (await this.auditRepository.find({
563
+ where: { userId, eventType: AuthAuditEventType.LOGIN_SUCCESS },
564
+ order: { createdAt: 'DESC' },
565
+ take: 5,
566
+ select: ['id', 'ipCity', 'ipCountry', 'createdAt', 'ipAddress'],
567
+ })) as Array<{
568
+ id: number;
569
+ ipCity: string | null;
570
+ ipCountry: string | null;
571
+ createdAt: Date;
572
+ ipAddress: string | null;
573
+ }>;
574
+
575
+ this.logger?.debug?.(
576
+ `Last 5 LOGIN_SUCCESS audits for user ${userId}: ${JSON.stringify(
577
+ allRecentAudits.map((a) => ({
578
+ id: a.id,
579
+ location: `${a.ipCity}, ${a.ipCountry}`,
580
+ ip: a.ipAddress,
581
+ created: a.createdAt.toISOString(),
582
+ ageMinutes: ((Date.now() - a.createdAt.getTime()) / (1000 * 60)).toFixed(1),
583
+ })),
584
+ )}`,
585
+ );
586
+ }
587
+
588
+ // Determine which record is more recent and extract location data with coordinates
589
+ let lastLocation: {
590
+ country: string;
591
+ city: string | null;
592
+ latitude: number | null;
593
+ longitude: number | null;
594
+ time: Date;
595
+ source: 'session' | 'audit';
596
+ } | null = null;
597
+
598
+ if (lastSession && lastAuditLogin) {
599
+ // Both exist - use the more recent one as the previous login
600
+ const sessionTime = lastSession.createdAt;
601
+ const auditTime = lastAuditLogin.createdAt;
602
+
603
+ if (sessionTime > auditTime) {
604
+ lastLocation = {
605
+ country: lastSession.ipCountry!,
606
+ city: lastSession.ipCity ?? null,
607
+ latitude: (lastSession as any).ipLatitude ?? null,
608
+ longitude: (lastSession as any).ipLongitude ?? null,
609
+ time: sessionTime,
610
+ source: 'session',
611
+ };
612
+ } else {
613
+ lastLocation = {
614
+ country: lastAuditLogin.ipCountry!,
615
+ city: lastAuditLogin.ipCity ?? null,
616
+ latitude: lastAuditLogin.ipLatitude ?? null,
617
+ longitude: lastAuditLogin.ipLongitude ?? null,
618
+ time: auditTime,
619
+ source: 'audit',
620
+ };
621
+ }
622
+ } else if (lastSession) {
623
+ lastLocation = {
624
+ country: lastSession.ipCountry!,
625
+ city: lastSession.ipCity ?? null,
626
+ latitude: (lastSession as any).ipLatitude ?? null,
627
+ longitude: (lastSession as any).ipLongitude ?? null,
628
+ time: lastSession.createdAt,
629
+ source: 'session',
630
+ };
631
+ } else if (lastAuditLogin) {
632
+ lastLocation = {
633
+ country: lastAuditLogin.ipCountry!,
634
+ city: lastAuditLogin.ipCity ?? null,
635
+ latitude: lastAuditLogin.ipLatitude ?? null,
636
+ longitude: lastAuditLogin.ipLongitude ?? null,
637
+ time: lastAuditLogin.createdAt,
638
+ source: 'audit',
639
+ };
640
+ }
641
+
642
+ if (!lastLocation) {
643
+ this.logger?.debug?.(`No previous location data found for user ${userId} (no sessions or audit records)`);
644
+ return false; // No previous location data (benefit of doubt)
645
+ }
646
+
647
+ // Debug logging to help diagnose issues
648
+ const hasCoordinates = !!(
649
+ lastLocation.latitude &&
650
+ lastLocation.longitude &&
651
+ currentInfo.ipLatitude &&
652
+ currentInfo.ipLongitude
653
+ );
654
+
655
+ this.logger?.debug?.(
656
+ `Impossible travel check: user=${userId}, source=${lastLocation.source}, ` +
657
+ `last=[${lastLocation.city ?? 'unknown'}, ${lastLocation.country} @ ${lastLocation.time.toISOString()}], ` +
658
+ `current=[${currentInfo.ipCity ?? 'unknown'}, ${currentInfo.ipCountry}], ` +
659
+ `coordinates=${hasCoordinates ? 'available' : 'missing'}`,
660
+ );
661
+
662
+ // Same location → not travel (only if we have city data to compare)
663
+ if (
664
+ lastLocation.city &&
665
+ currentInfo.ipCity &&
666
+ lastLocation.country === currentInfo.ipCountry &&
667
+ lastLocation.city === currentInfo.ipCity
668
+ ) {
669
+ this.logger?.debug?.(
670
+ `Same location detected - no travel: user=${userId}, location=[${currentInfo.ipCity}, ${currentInfo.ipCountry}]`,
671
+ );
672
+ return false;
673
+ }
674
+
675
+ // Calculate time difference (hours)
676
+ // Risk detection runs BEFORE session creation, so we compare current time to previous login
677
+ // All times are in UTC (database stores timestamps in UTC)
678
+ const now = new Date();
679
+ const hoursSinceLastSeen = (now.getTime() - lastLocation.time.getTime()) / (1000 * 60 * 60);
680
+
681
+ this.logger?.debug?.(
682
+ `Time since last location: ${hoursSinceLastSeen.toFixed(2)} hours (${(hoursSinceLastSeen * 60).toFixed(1)} minutes). ` +
683
+ `Previous login: ${lastLocation.time.toISOString()} (UTC), Current: ${now.toISOString()} (UTC)`,
684
+ );
685
+
686
+ // ============================================================================
687
+ // SPECIAL CASE: Country change with missing city data
688
+ // ============================================================================
689
+ // If city data is missing for either location but countries differ,
690
+ // apply conservative threshold for country-level changes
691
+ if (lastLocation.country !== currentInfo.ipCountry) {
692
+ if (!lastLocation.city || !currentInfo.ipCity) {
693
+ // Missing city data - use conservative threshold for country changes
694
+ // If country changed in < threshold hours, flag as suspicious (can't verify exact locations)
695
+ const countryChangeThresholdHours = this.config.mfa?.adaptive?.countryChangeThreshold || 2;
696
+
697
+ if (hoursSinceLastSeen < countryChangeThresholdHours) {
698
+ this.logger?.warn?.(
699
+ `Impossible travel detected (country change without city data): ` +
700
+ `${lastLocation.country} → ${currentInfo.ipCountry} in ${(hoursSinceLastSeen * 60).toFixed(1)} minutes ` +
701
+ `(threshold: ${countryChangeThresholdHours}h). Missing city: last=${!lastLocation.city}, current=${!currentInfo.ipCity}. ` +
702
+ `Conservative detection applied due to incomplete location data.`,
703
+ );
704
+ return true;
705
+ }
706
+
707
+ this.logger?.debug?.(
708
+ `Country change acceptable: ${lastLocation.country} → ${currentInfo.ipCountry} ` +
709
+ `in ${hoursSinceLastSeen.toFixed(2)}h (> ${countryChangeThresholdHours}h threshold). ` +
710
+ `City data missing but time allows travel.`,
711
+ );
712
+ return false;
713
+ }
714
+ }
715
+
716
+ // ============================================================================
717
+ // NORMAL CASE: Calculate distance using coordinates or heuristics
718
+ // ============================================================================
719
+ let distance: number;
720
+ let distanceMethod: 'haversine' | 'heuristic' = 'heuristic';
721
+
722
+ // Try to use actual coordinates for precise distance calculation
723
+ if (hasCoordinates) {
724
+ distance = this.calculateHaversineDistance(
725
+ lastLocation.latitude!,
726
+ lastLocation.longitude!,
727
+ currentInfo.ipLatitude!,
728
+ currentInfo.ipLongitude!,
729
+ );
730
+ distanceMethod = 'haversine';
731
+
732
+ this.logger?.debug?.(
733
+ `Using Haversine formula with coordinates: distance=${distance.toFixed(0)}km ` +
734
+ `(${lastLocation.latitude},${lastLocation.longitude} → ${currentInfo.ipLatitude},${currentInfo.ipLongitude})`,
735
+ );
736
+ } else {
737
+ // Fallback to heuristic distance estimation
738
+ distance = await this.calculateDistance(
739
+ lastLocation.city,
740
+ lastLocation.country,
741
+ currentInfo.ipCity ?? null,
742
+ currentInfo.ipCountry,
743
+ );
744
+
745
+ this.logger?.debug?.(`Using heuristic distance estimation (coordinates unavailable): distance=${distance}km`);
746
+ }
747
+
748
+ if (distance === 0) {
749
+ return false; // Same location (shouldn't happen, but safety check)
750
+ }
751
+
752
+ // Max realistic speed: 900 km/h (commercial airliner speed)
753
+ const maxTravelSpeed = this.config.mfa?.adaptive?.maxTravelSpeed || 900;
754
+ const requiredSpeed = distance / hoursSinceLastSeen;
755
+
756
+ this.logger?.debug?.(
757
+ `Travel speed calculation (${distanceMethod}): distance=${distance.toFixed(0)}km, ` +
758
+ `time=${hoursSinceLastSeen.toFixed(2)}h, required_speed=${requiredSpeed.toFixed(0)}km/h, ` +
759
+ `max_allowed=${maxTravelSpeed}km/h`,
760
+ );
761
+
762
+ const isImpossible = requiredSpeed > maxTravelSpeed;
763
+ if (isImpossible) {
764
+ this.logger?.warn?.(
765
+ `Impossible travel detected: ${requiredSpeed.toFixed(0)}km/h > ${maxTravelSpeed}km/h ` +
766
+ `(${lastLocation.city ?? lastLocation.country}, ${lastLocation.country} → ` +
767
+ `${currentInfo.ipCity ?? currentInfo.ipCountry}, ${currentInfo.ipCountry})`,
768
+ );
769
+ } else {
770
+ this.logger?.debug?.(
771
+ `Travel is possible: ${requiredSpeed.toFixed(0)}km/h <= ${maxTravelSpeed}km/h ` +
772
+ `(${lastLocation.city ?? lastLocation.country}, ${lastLocation.country} → ` +
773
+ `${currentInfo.ipCity ?? currentInfo.ipCountry}, ${currentInfo.ipCountry})`,
774
+ );
775
+ }
776
+
777
+ return isImpossible;
778
+ } catch (error) {
779
+ // Non-blocking: Log and assume not impossible travel (safer default)
780
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
781
+ this.logger?.warn?.(`Failed to detect impossible travel: ${errorMessage}`, { error, userId });
782
+ return false; // Assume not impossible travel on error
783
+ }
784
+ }
785
+
786
+ /**
787
+ * Calculate distance between two points using Haversine formula
788
+ *
789
+ * Accurate distance calculation using geographic coordinates (latitude/longitude).
790
+ * This is the preferred method when coordinates are available.
791
+ *
792
+ * **Haversine Formula:**
793
+ * - Accounts for Earth's spherical shape
794
+ * - Returns great-circle distance in kilometers
795
+ * - Accuracy: ~0.5% for most distances
796
+ *
797
+ * @param lat1 - Latitude of first point (degrees)
798
+ * @param lon1 - Longitude of first point (degrees)
799
+ * @param lat2 - Latitude of second point (degrees)
800
+ * @param lon2 - Longitude of second point (degrees)
801
+ * @returns Distance in kilometers (accurate)
802
+ * @private
803
+ */
804
+ private calculateHaversineDistance(lat1: number, lon1: number, lat2: number, lon2: number): number {
805
+ const R = 6371; // Earth's radius in kilometers
806
+ const dLat = this.toRadians(lat2 - lat1);
807
+ const dLon = this.toRadians(lon2 - lon1);
808
+
809
+ const a =
810
+ Math.sin(dLat / 2) * Math.sin(dLat / 2) +
811
+ Math.cos(this.toRadians(lat1)) * Math.cos(this.toRadians(lat2)) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
812
+
813
+ const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
814
+ const distance = R * c;
815
+
816
+ return distance;
817
+ }
818
+
819
+ /**
820
+ * Convert degrees to radians
821
+ *
822
+ * @param degrees - Angle in degrees
823
+ * @returns Angle in radians
824
+ * @private
825
+ */
826
+ private toRadians(degrees: number): number {
827
+ return degrees * (Math.PI / 180);
828
+ }
829
+
830
+ /**
831
+ * Calculate distance between two cities (heuristic fallback)
832
+ *
833
+ * Heuristic-based implementation for estimating travel distance when
834
+ * precise coordinates are not available. Uses continent and regional
835
+ * groupings for realistic estimates.
836
+ *
837
+ * **Fallback approach:**
838
+ * - Same city: 0 km
839
+ * - Same country, different city: 500 km (average domestic travel)
840
+ * - Different country, same continent: 1,500 km (regional travel)
841
+ * - Different continent (intercontinental): 8,000 km (long-haul flight)
842
+ * - Missing city data: use country-level comparison
843
+ *
844
+ * @param city1 - First city name (nullable)
845
+ * @param country1 - First country code (ISO 2-letter)
846
+ * @param city2 - Second city name (nullable)
847
+ * @param country2 - Second country code (ISO 2-letter)
848
+ * @returns Distance in kilometers (estimated)
849
+ * @private
850
+ */
851
+ private async calculateDistance(
852
+ city1: string | null,
853
+ country1: string,
854
+ city2: string | null,
855
+ country2: string,
856
+ ): Promise<number> {
857
+ // Same city and country → 0 km (only if both cities are known)
858
+ if (city1 && city2 && city1 === city2 && country1 === country2) {
859
+ return 0;
860
+ }
861
+
862
+ // Same country → estimate 500 km (average domestic travel)
863
+ if (country1 === country2) {
864
+ return 500;
865
+ }
866
+
867
+ // Different country - determine if same continent or intercontinental
868
+ const continent1 = this.getContinent(country1);
869
+ const continent2 = this.getContinent(country2);
870
+
871
+ if (continent1 === continent2 && continent1 !== 'unknown') {
872
+ // Same continent, different country → estimate 1,500 km (regional travel)
873
+ // Examples: Paris-Berlin (880km), London-Rome (1,400km), Sydney-Auckland (2,160km)
874
+ return 1500;
875
+ }
876
+
877
+ // Different continent → estimate 8,000 km (intercontinental long-haul)
878
+ // Examples: NYC-London (5,570km), Tokyo-Sydney (7,800km), Auckland-Karachi (9,700km)
879
+ // Using 8,000km as realistic average for transcontinental flights
880
+ return 8000;
881
+ }
882
+
883
+ /**
884
+ * Get continent for a country code
885
+ *
886
+ * Maps ISO 2-letter country codes to continents for distance estimation.
887
+ * This is used to differentiate between regional and intercontinental travel.
888
+ *
889
+ * @param countryCode - ISO 2-letter country code
890
+ * @returns Continent name
891
+ * @private
892
+ */
893
+ private getContinent(countryCode: string): string {
894
+ const continentMap: Record<string, string> = {
895
+ // North America
896
+ US: 'north_america',
897
+ CA: 'north_america',
898
+ MX: 'north_america',
899
+ // Europe
900
+ GB: 'europe',
901
+ FR: 'europe',
902
+ DE: 'europe',
903
+ IT: 'europe',
904
+ ES: 'europe',
905
+ NL: 'europe',
906
+ BE: 'europe',
907
+ CH: 'europe',
908
+ AT: 'europe',
909
+ PT: 'europe',
910
+ SE: 'europe',
911
+ NO: 'europe',
912
+ DK: 'europe',
913
+ FI: 'europe',
914
+ PL: 'europe',
915
+ CZ: 'europe',
916
+ GR: 'europe',
917
+ IE: 'europe',
918
+ RO: 'europe',
919
+ HU: 'europe',
920
+ BG: 'europe',
921
+ SK: 'europe',
922
+ HR: 'europe',
923
+ SI: 'europe',
924
+ LT: 'europe',
925
+ LV: 'europe',
926
+ EE: 'europe',
927
+ // Asia
928
+ CN: 'asia',
929
+ JP: 'asia',
930
+ IN: 'asia',
931
+ KR: 'asia',
932
+ TH: 'asia',
933
+ VN: 'asia',
934
+ PH: 'asia',
935
+ MY: 'asia',
936
+ SG: 'asia',
937
+ ID: 'asia',
938
+ PK: 'asia',
939
+ BD: 'asia',
940
+ TR: 'asia',
941
+ SA: 'asia',
942
+ AE: 'asia',
943
+ IL: 'asia',
944
+ HK: 'asia',
945
+ TW: 'asia',
946
+ // Oceania
947
+ AU: 'oceania',
948
+ NZ: 'oceania',
949
+ // South America
950
+ BR: 'south_america',
951
+ AR: 'south_america',
952
+ CL: 'south_america',
953
+ CO: 'south_america',
954
+ PE: 'south_america',
955
+ VE: 'south_america',
956
+ // Africa
957
+ ZA: 'africa',
958
+ EG: 'africa',
959
+ NG: 'africa',
960
+ KE: 'africa',
961
+ MA: 'africa',
962
+ ET: 'africa',
963
+ };
964
+
965
+ return continentMap[countryCode.toUpperCase()] || 'unknown';
966
+ }
967
+
968
+ /**
969
+ * Detect suspicious activity patterns
970
+ *
971
+ * Checks for:
972
+ * - Recent failed login attempts (last 1 hour)
973
+ * - Token reuse detected (SUSPICIOUS_ACTIVITY audit events)
974
+ * - Multiple MFA failures
975
+ * - Account lockout attempts
976
+ *
977
+ * @param userId - Internal user ID (integer)
978
+ * @returns True if suspicious activity detected
979
+ * @private
980
+ */
981
+ private async detectSuspiciousActivity(userId: number): Promise<boolean> {
982
+ try {
983
+ const windowHours = this.config.mfa?.adaptive?.suspiciousActivityWindow || 1;
984
+ const oneHourAgo = new Date(Date.now() - windowHours * 60 * 60 * 1000);
985
+
986
+ // Check for recent suspicious audit events (existence only)
987
+ const hasSuspicious = await this.auditRepository.findOne({
988
+ select: ['id'],
989
+ where: { userId, eventStatus: 'SUSPICIOUS', createdAt: MoreThan(oneHourAgo) },
990
+ });
991
+
992
+ if (hasSuspicious) {
993
+ return true;
994
+ }
995
+
996
+ // Check for failed login attempts (3+ in last hour) using limited IDs
997
+ const failedLogins = await this.auditRepository.find({
998
+ select: ['id'],
999
+ where: { userId, eventType: AuthAuditEventType.LOGIN_FAILED, createdAt: MoreThan(oneHourAgo) },
1000
+ order: { createdAt: 'DESC' },
1001
+ take: 3,
1002
+ });
1003
+
1004
+ return failedLogins.length >= 3;
1005
+ } catch (error) {
1006
+ // Non-blocking: Log and assume not suspicious (safer default)
1007
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
1008
+ this.logger?.warn?.(`Failed to detect suspicious activity: ${errorMessage}`, { error, userId });
1009
+ return false; // Assume not suspicious on error
1010
+ }
1011
+ }
1012
+ }