@warlock.js/auth 5.29.0 → 5.31.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 (273) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/esm/commands/auth-cleanup-command.d.mts +1 -1
  3. package/esm/commands/auth-cleanup-command.d.mts.map +1 -1
  4. package/esm/commands/auth-cleanup-command.mjs +1 -1
  5. package/esm/commands/auth-cleanup-command.mjs.map +1 -1
  6. package/esm/commands/auth-purge-never-expiring-command.d.mts +1 -1
  7. package/esm/commands/auth-purge-never-expiring-command.d.mts.map +1 -1
  8. package/esm/commands/auth-purge-never-expiring-command.mjs +1 -1
  9. package/esm/commands/auth-purge-never-expiring-command.mjs.map +1 -1
  10. package/esm/commands/jwt-secret-generator-command.d.mts +1 -1
  11. package/esm/commands/jwt-secret-generator-command.d.mts.map +1 -1
  12. package/esm/commands/jwt-secret-generator-command.mjs +1 -1
  13. package/esm/commands/jwt-secret-generator-command.mjs.map +1 -1
  14. package/esm/contracts/auth-contract.d.mts +1 -1
  15. package/esm/contracts/auth-contract.d.mts.map +1 -1
  16. package/esm/contracts/providers.d.mts +1 -1
  17. package/esm/contracts/providers.d.mts.map +1 -1
  18. package/esm/contracts/request-locals.d.mts +1 -1
  19. package/esm/contracts/request-locals.d.mts.map +1 -1
  20. package/esm/contracts/types.d.mts +1 -1
  21. package/esm/contracts/types.d.mts.map +1 -1
  22. package/esm/contracts/types.mjs +1 -1
  23. package/esm/contracts/types.mjs.map +1 -1
  24. package/esm/errors/auth-provider-sdk-missing.error.d.mts +1 -1
  25. package/esm/errors/auth-provider-sdk-missing.error.d.mts.map +1 -1
  26. package/esm/errors/auth-provider-sdk-missing.error.mjs +1 -1
  27. package/esm/errors/auth-provider-sdk-missing.error.mjs.map +1 -1
  28. package/esm/errors/email-not-verified.error.d.mts +1 -1
  29. package/esm/errors/email-not-verified.error.d.mts.map +1 -1
  30. package/esm/errors/email-not-verified.error.mjs +1 -1
  31. package/esm/errors/email-not-verified.error.mjs.map +1 -1
  32. package/esm/errors/invalid-one-time-token.error.d.mts +1 -1
  33. package/esm/errors/invalid-one-time-token.error.d.mts.map +1 -1
  34. package/esm/errors/invalid-one-time-token.error.mjs +1 -1
  35. package/esm/errors/invalid-one-time-token.error.mjs.map +1 -1
  36. package/esm/errors/invalid-passkey.error.d.mts +1 -1
  37. package/esm/errors/invalid-passkey.error.d.mts.map +1 -1
  38. package/esm/errors/invalid-passkey.error.mjs +1 -1
  39. package/esm/errors/invalid-passkey.error.mjs.map +1 -1
  40. package/esm/errors/invalid-provider-callback.error.d.mts +1 -1
  41. package/esm/errors/invalid-provider-callback.error.d.mts.map +1 -1
  42. package/esm/errors/invalid-provider-callback.error.mjs +1 -1
  43. package/esm/errors/invalid-provider-callback.error.mjs.map +1 -1
  44. package/esm/errors/notifications-unavailable.error.d.mts +1 -1
  45. package/esm/errors/notifications-unavailable.error.d.mts.map +1 -1
  46. package/esm/errors/notifications-unavailable.error.mjs +1 -1
  47. package/esm/errors/notifications-unavailable.error.mjs.map +1 -1
  48. package/esm/errors/provider-email-not-verified.error.d.mts +1 -1
  49. package/esm/errors/provider-email-not-verified.error.d.mts.map +1 -1
  50. package/esm/errors/provider-email-not-verified.error.mjs +1 -1
  51. package/esm/errors/provider-email-not-verified.error.mjs.map +1 -1
  52. package/esm/index.d.mts +2 -2
  53. package/esm/index.mjs +2 -2
  54. package/esm/localization/ar.translations.mjs +1 -1
  55. package/esm/localization/ar.translations.mjs.map +1 -1
  56. package/esm/localization/auth-default-translations.d.mts +1 -1
  57. package/esm/localization/auth-default-translations.d.mts.map +1 -1
  58. package/esm/localization/auth-default-translations.mjs +1 -1
  59. package/esm/localization/auth-default-translations.mjs.map +1 -1
  60. package/esm/localization/auth-translations.type.d.mts +1 -1
  61. package/esm/localization/auth-translations.type.d.mts.map +1 -1
  62. package/esm/localization/en.translations.mjs +1 -1
  63. package/esm/localization/en.translations.mjs.map +1 -1
  64. package/esm/localization/init.mjs +1 -1
  65. package/esm/localization/init.mjs.map +1 -1
  66. package/esm/localization/register-auth-translations.d.mts +1 -1
  67. package/esm/localization/register-auth-translations.d.mts.map +1 -1
  68. package/esm/localization/register-auth-translations.mjs +1 -1
  69. package/esm/localization/register-auth-translations.mjs.map +1 -1
  70. package/esm/middleware/auth.middleware.d.mts +1 -1
  71. package/esm/middleware/auth.middleware.d.mts.map +1 -1
  72. package/esm/middleware/auth.middleware.mjs +3 -3
  73. package/esm/middleware/auth.middleware.mjs.map +1 -1
  74. package/esm/middleware/csrf-origin-check.d.mts +1 -1
  75. package/esm/middleware/csrf-origin-check.d.mts.map +1 -1
  76. package/esm/middleware/csrf-origin-check.mjs +1 -1
  77. package/esm/middleware/csrf-origin-check.mjs.map +1 -1
  78. package/esm/middleware/index.d.mts +1 -1
  79. package/esm/middleware/index.mjs +1 -1
  80. package/esm/middleware/localized-login-path.mjs +1 -1
  81. package/esm/middleware/localized-login-path.mjs.map +1 -1
  82. package/esm/middleware/login-throttle.middleware.d.mts +17 -2
  83. package/esm/middleware/login-throttle.middleware.d.mts.map +1 -1
  84. package/esm/middleware/login-throttle.middleware.mjs +84 -6
  85. package/esm/middleware/login-throttle.middleware.mjs.map +1 -1
  86. package/esm/middleware/one-time-token-throttle.middleware.d.mts +1 -1
  87. package/esm/middleware/one-time-token-throttle.middleware.d.mts.map +1 -1
  88. package/esm/middleware/one-time-token-throttle.middleware.mjs +1 -1
  89. package/esm/middleware/one-time-token-throttle.middleware.mjs.map +1 -1
  90. package/esm/middleware/otp-throttle.middleware.d.mts +1 -1
  91. package/esm/middleware/otp-throttle.middleware.d.mts.map +1 -1
  92. package/esm/middleware/otp-throttle.middleware.mjs +1 -1
  93. package/esm/middleware/otp-throttle.middleware.mjs.map +1 -1
  94. package/esm/middleware/require-verified-email.middleware.d.mts +1 -1
  95. package/esm/middleware/require-verified-email.middleware.d.mts.map +1 -1
  96. package/esm/middleware/require-verified-email.middleware.mjs +1 -1
  97. package/esm/middleware/require-verified-email.middleware.mjs.map +1 -1
  98. package/esm/models/access-token/access-token.model.d.mts +3 -35
  99. package/esm/models/access-token/access-token.model.d.mts.map +1 -1
  100. package/esm/models/access-token/access-token.model.mjs +1 -1
  101. package/esm/models/access-token/access-token.model.mjs.map +1 -1
  102. package/esm/models/access-token/family-association.migration.mjs +1 -1
  103. package/esm/models/access-token/family-association.migration.mjs.map +1 -1
  104. package/esm/models/access-token/migration.mjs +1 -1
  105. package/esm/models/access-token/migration.mjs.map +1 -1
  106. package/esm/models/auth-token-family/auth-token-family.model.d.mts +3 -43
  107. package/esm/models/auth-token-family/auth-token-family.model.d.mts.map +1 -1
  108. package/esm/models/auth-token-family/auth-token-family.model.mjs +1 -1
  109. package/esm/models/auth-token-family/auth-token-family.model.mjs.map +1 -1
  110. package/esm/models/auth-token-family/migration.mjs +1 -1
  111. package/esm/models/auth-token-family/migration.mjs.map +1 -1
  112. package/esm/models/auth.model.d.mts +1 -1
  113. package/esm/models/auth.model.d.mts.map +1 -1
  114. package/esm/models/auth.model.mjs +1 -1
  115. package/esm/models/auth.model.mjs.map +1 -1
  116. package/esm/models/index.d.mts +1 -1
  117. package/esm/models/index.d.mts.map +1 -1
  118. package/esm/models/index.mjs +1 -1
  119. package/esm/models/index.mjs.map +1 -1
  120. package/esm/models/one-time-token/migration.mjs +1 -1
  121. package/esm/models/one-time-token/migration.mjs.map +1 -1
  122. package/esm/models/one-time-token/one-time-token.model.d.mts +3 -67
  123. package/esm/models/one-time-token/one-time-token.model.d.mts.map +1 -1
  124. package/esm/models/one-time-token/one-time-token.model.mjs +1 -1
  125. package/esm/models/one-time-token/one-time-token.model.mjs.map +1 -1
  126. package/esm/models/passkey-credential/migration.mjs +1 -1
  127. package/esm/models/passkey-credential/migration.mjs.map +1 -1
  128. package/esm/models/passkey-credential/passkey-credential.model.d.mts +3 -47
  129. package/esm/models/passkey-credential/passkey-credential.model.d.mts.map +1 -1
  130. package/esm/models/passkey-credential/passkey-credential.model.mjs +1 -1
  131. package/esm/models/passkey-credential/passkey-credential.model.mjs.map +1 -1
  132. package/esm/models/provider-account/migration.mjs +1 -1
  133. package/esm/models/provider-account/migration.mjs.map +1 -1
  134. package/esm/models/provider-account/provider-account.model.d.mts +3 -35
  135. package/esm/models/provider-account/provider-account.model.d.mts.map +1 -1
  136. package/esm/models/provider-account/provider-account.model.mjs +1 -1
  137. package/esm/models/provider-account/provider-account.model.mjs.map +1 -1
  138. package/esm/models/refresh-token/migration.mjs +1 -1
  139. package/esm/models/refresh-token/migration.mjs.map +1 -1
  140. package/esm/models/refresh-token/refresh-token.model.d.mts +7 -99
  141. package/esm/models/refresh-token/refresh-token.model.d.mts.map +1 -1
  142. package/esm/models/refresh-token/refresh-token.model.mjs +1 -1
  143. package/esm/models/refresh-token/refresh-token.model.mjs.map +1 -1
  144. package/esm/models/refresh-token/successor-association.migration.mjs +1 -1
  145. package/esm/models/refresh-token/successor-association.migration.mjs.map +1 -1
  146. package/esm/otp/otp.d.mts +1 -1
  147. package/esm/otp/otp.d.mts.map +1 -1
  148. package/esm/otp/otp.mjs +1 -1
  149. package/esm/otp/otp.mjs.map +1 -1
  150. package/esm/passkeys/passkey-authentication.d.mts +1 -1
  151. package/esm/passkeys/passkey-authentication.d.mts.map +1 -1
  152. package/esm/passkeys/passkey-authentication.mjs +1 -1
  153. package/esm/passkeys/passkey-authentication.mjs.map +1 -1
  154. package/esm/passkeys/passkey-challenges.mjs +1 -1
  155. package/esm/passkeys/passkey-challenges.mjs.map +1 -1
  156. package/esm/passkeys/passkey-registration.d.mts +1 -1
  157. package/esm/passkeys/passkey-registration.d.mts.map +1 -1
  158. package/esm/passkeys/passkey-registration.mjs +1 -1
  159. package/esm/passkeys/passkey-registration.mjs.map +1 -1
  160. package/esm/passkeys/simplewebauthn.d.mts +1 -1
  161. package/esm/passkeys/simplewebauthn.d.mts.map +1 -1
  162. package/esm/passkeys/simplewebauthn.mjs +1 -1
  163. package/esm/passkeys/simplewebauthn.mjs.map +1 -1
  164. package/esm/providers/apple-provider.d.mts +1 -1
  165. package/esm/providers/apple-provider.d.mts.map +1 -1
  166. package/esm/providers/apple-provider.mjs +1 -1
  167. package/esm/providers/apple-provider.mjs.map +1 -1
  168. package/esm/providers/discord-provider.d.mts +1 -1
  169. package/esm/providers/discord-provider.d.mts.map +1 -1
  170. package/esm/providers/discord-provider.mjs +1 -1
  171. package/esm/providers/discord-provider.mjs.map +1 -1
  172. package/esm/providers/facebook-provider.d.mts +1 -1
  173. package/esm/providers/facebook-provider.d.mts.map +1 -1
  174. package/esm/providers/facebook-provider.mjs +1 -1
  175. package/esm/providers/facebook-provider.mjs.map +1 -1
  176. package/esm/providers/github-provider.d.mts +1 -1
  177. package/esm/providers/github-provider.d.mts.map +1 -1
  178. package/esm/providers/github-provider.mjs +1 -1
  179. package/esm/providers/github-provider.mjs.map +1 -1
  180. package/esm/providers/google-provider.d.mts +1 -1
  181. package/esm/providers/google-provider.d.mts.map +1 -1
  182. package/esm/providers/google-provider.mjs +1 -1
  183. package/esm/providers/google-provider.mjs.map +1 -1
  184. package/esm/providers/linkedin-provider.d.mts +1 -1
  185. package/esm/providers/linkedin-provider.d.mts.map +1 -1
  186. package/esm/providers/linkedin-provider.mjs +1 -1
  187. package/esm/providers/linkedin-provider.mjs.map +1 -1
  188. package/esm/providers/provider-login.d.mts +1 -1
  189. package/esm/providers/provider-login.d.mts.map +1 -1
  190. package/esm/providers/provider-login.mjs +1 -1
  191. package/esm/providers/provider-login.mjs.map +1 -1
  192. package/esm/providers/provider-state-cookie.mjs +1 -1
  193. package/esm/providers/provider-state-cookie.mjs.map +1 -1
  194. package/esm/providers/resolve-provider-user.mjs +1 -1
  195. package/esm/providers/resolve-provider-user.mjs.map +1 -1
  196. package/esm/providers/x-provider.d.mts +1 -1
  197. package/esm/providers/x-provider.d.mts.map +1 -1
  198. package/esm/providers/x-provider.mjs +1 -1
  199. package/esm/providers/x-provider.mjs.map +1 -1
  200. package/esm/services/auth-config.mjs +1 -1
  201. package/esm/services/auth-config.mjs.map +1 -1
  202. package/esm/services/auth-events.d.mts +1 -1
  203. package/esm/services/auth-events.d.mts.map +1 -1
  204. package/esm/services/auth-events.mjs +1 -1
  205. package/esm/services/auth-events.mjs.map +1 -1
  206. package/esm/services/auth.service.d.mts +1 -1
  207. package/esm/services/auth.service.d.mts.map +1 -1
  208. package/esm/services/auth.service.mjs +1 -1
  209. package/esm/services/auth.service.mjs.map +1 -1
  210. package/esm/services/current-user.d.mts +1 -1
  211. package/esm/services/current-user.d.mts.map +1 -1
  212. package/esm/services/current-user.mjs +1 -1
  213. package/esm/services/current-user.mjs.map +1 -1
  214. package/esm/services/email-verification.d.mts +1 -1
  215. package/esm/services/email-verification.d.mts.map +1 -1
  216. package/esm/services/email-verification.mjs +1 -1
  217. package/esm/services/email-verification.mjs.map +1 -1
  218. package/esm/services/generate-jwt-secret.d.mts +1 -1
  219. package/esm/services/generate-jwt-secret.d.mts.map +1 -1
  220. package/esm/services/generate-jwt-secret.mjs +1 -1
  221. package/esm/services/generate-jwt-secret.mjs.map +1 -1
  222. package/esm/services/jwt.d.mts +1 -1
  223. package/esm/services/jwt.d.mts.map +1 -1
  224. package/esm/services/jwt.mjs +1 -1
  225. package/esm/services/jwt.mjs.map +1 -1
  226. package/esm/services/one-time-tokens.d.mts +1 -1
  227. package/esm/services/one-time-tokens.d.mts.map +1 -1
  228. package/esm/services/one-time-tokens.mjs +1 -1
  229. package/esm/services/one-time-tokens.mjs.map +1 -1
  230. package/esm/services/optional-peer.mjs +1 -1
  231. package/esm/services/optional-peer.mjs.map +1 -1
  232. package/esm/services/password-reset.d.mts +1 -1
  233. package/esm/services/password-reset.d.mts.map +1 -1
  234. package/esm/services/password-reset.mjs +1 -1
  235. package/esm/services/password-reset.mjs.map +1 -1
  236. package/esm/services/resolve-request-user.d.mts +1 -1
  237. package/esm/services/resolve-request-user.d.mts.map +1 -1
  238. package/esm/services/resolve-request-user.mjs +1 -1
  239. package/esm/services/resolve-request-user.mjs.map +1 -1
  240. package/esm/services/token-family-operation.mjs +1 -1
  241. package/esm/services/token-family-operation.mjs.map +1 -1
  242. package/esm/services/token-notifications.mjs +1 -1
  243. package/esm/services/token-notifications.mjs.map +1 -1
  244. package/esm/session/page-session.d.mts +1 -1
  245. package/esm/session/page-session.d.mts.map +1 -1
  246. package/esm/session/page-session.mjs +1 -1
  247. package/esm/session/page-session.mjs.map +1 -1
  248. package/esm/utils/auth-error-codes.d.mts +1 -1
  249. package/esm/utils/auth-error-codes.d.mts.map +1 -1
  250. package/esm/utils/auth-error-codes.mjs +1 -1
  251. package/esm/utils/auth-error-codes.mjs.map +1 -1
  252. package/esm/utils/safe-equal.d.mts +1 -1
  253. package/esm/utils/safe-equal.d.mts.map +1 -1
  254. package/esm/utils/safe-equal.mjs +1 -1
  255. package/esm/utils/safe-equal.mjs.map +1 -1
  256. package/esm/utils/token-expiry.d.mts +1 -1
  257. package/esm/utils/token-expiry.d.mts.map +1 -1
  258. package/esm/utils/token-expiry.mjs +1 -1
  259. package/esm/utils/token-expiry.mjs.map +1 -1
  260. package/llms-full.txt +192 -112
  261. package/package.json +10 -9
  262. package/skills/auth-basics/SKILL.md +10 -13
  263. package/skills/customize-token-storage/SKILL.md +24 -19
  264. package/skills/customize-user-type/SKILL.md +3 -1
  265. package/skills/index.md +4 -1
  266. package/skills/login-with-providers/SKILL.md +54 -29
  267. package/skills/manage-tokens/SKILL.md +2 -2
  268. package/skills/overview/SKILL.md +10 -1
  269. package/skills/protect-routes/SKILL.md +23 -14
  270. package/skills/register-user/SKILL.md +4 -4
  271. package/skills/run-auth-commands/SKILL.md +1 -1
  272. package/skills/throttle-login-attempts/SKILL.md +54 -23
  273. package/skills/verify-email-and-reset-password/SKILL.md +7 -5
@@ -1 +1 @@
1
- {"version":3,"file":"auth-events.d.mts","names":[],"sources":["../../../../../../../auth/src/services/auth-events.ts"],"mappings":";;;;;;;;;KASY,iBAAA;EAEV,eAAA,GAAkB,IAAA,EAAM,IAAA,EAAM,SAAA,EAAW,SAAA,EAAW,UAAA,GAAa,UAAA;EACjE,cAAA,GAAiB,WAAA;IAAe,KAAA;IAAgB,QAAA;EAAA,GAAqB,MAAA;EACrE,eAAA,GAAkB,WAAA;IAAe,KAAA;IAAgB,QAAA;EAAA;EAGjD,MAAA,GAAS,IAAA,EAAM,IAAA;EACf,YAAA,GAAe,IAAA,EAAM,IAAA;EACrB,iBAAA,GAAoB,IAAA,EAAM,IAAA;EAG1B,eAAA,GAAkB,IAAA,EAAM,IAAA,EAAM,SAAA,EAAW,SAAA;EACzC,iBAAA,GAAoB,IAAA,EAAM,IAAA,EAAM,YAAA,EAAc,SAAA,EAAW,eAAA,EAAiB,YAAA;EAC1E,eAAA,GAAkB,IAAA,EAAM,IAAA,EAAM,KAAA,EAAO,YAAA;EACrC,eAAA,GAAkB,KAAA,EAAO,YAAA;EACzB,qBAAA,GAAwB,QAAA,UAAkB,MAAA,EAAQ,YAAA;EAGlD,kBAAA,GAAqB,IAAA,EAAM,IAAA;EAC3B,yBAAA,GAA4B,IAAA,EAAM,IAAA,EAAM,UAAA;EACxC,gBAAA,GAAmB,IAAA,EAAM,IAAA;EAGzB,iBAAA,GAAoB,IAAA,EAAM,IAAA,EAAM,YAAA,EAAc,YAAA,EAAc,UAAA,GAAa,UAAA;EACzE,mBAAA,GAAsB,IAAA,EAAM,IAAA,EAAM,YAAA,EAAc,YAAA;EAGhD,mBAAA,GAAsB,YAAA;AAAA;;;;KAMZ,aAAA,SAAsB,iBAAiB;;;;KAKvC,iBAAA,WAA4B,aAAA,QACnC,IAAA,EAAM,iBAAA,CAAkB,CAAA,aACjB,OAAA;;;;;;;;;;;;;;;;;;;cAyBC,UAAA;EAzDe;;;eA6Db,aAAA,EAAa,KAAA,EAAS,CAAA,EAAC,QAAA,EAAY,iBAAA,CAAkB,CAAA,IAAK,iBAAA;EA1DzC;;;sBAiEV,aAAA,EAAa,KAAA,EAAS,CAAA,EAAC,QAAA,EAAY,iBAAA,CAAkB,CAAA,IAAK,iBAAA;EAhEpD;;;iBAuEX,aAAA,EAAa,KAAA,EAAS,CAAA,KAAC,IAAA,EAAW,iBAAA,CAAkB,CAAA;EAvEO;;;oBAuFxD,aAAA,EAAa,KAAA,EAAS,CAAA,KAAC,IAAA,EAAW,iBAAA,CAAkB,CAAA;EAtFxC;;;;EACL;;;cAmGb,aAAA;AAAA"}
1
+ {"version":3,"file":"auth-events.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/services/auth-events.ts"],"mappings":";;;;;;;;;KASY,iBAAA;EAEV,eAAA,GAAkB,IAAA,EAAM,IAAA,EAAM,SAAA,EAAW,SAAA,EAAW,UAAA,GAAa,UAAA;EACjE,cAAA,GAAiB,WAAA;IAAe,KAAA;IAAgB,QAAA;EAAA,GAAqB,MAAA;EACrE,eAAA,GAAkB,WAAA;IAAe,KAAA;IAAgB,QAAA;EAAA;EAGjD,MAAA,GAAS,IAAA,EAAM,IAAA;EACf,YAAA,GAAe,IAAA,EAAM,IAAA;EACrB,iBAAA,GAAoB,IAAA,EAAM,IAAA;EAG1B,eAAA,GAAkB,IAAA,EAAM,IAAA,EAAM,SAAA,EAAW,SAAA;EACzC,iBAAA,GAAoB,IAAA,EAAM,IAAA,EAAM,YAAA,EAAc,SAAA,EAAW,eAAA,EAAiB,YAAA;EAC1E,eAAA,GAAkB,IAAA,EAAM,IAAA,EAAM,KAAA,EAAO,YAAA;EACrC,eAAA,GAAkB,KAAA,EAAO,YAAA;EACzB,qBAAA,GAAwB,QAAA,UAAkB,MAAA,EAAQ,YAAA;EAGlD,kBAAA,GAAqB,IAAA,EAAM,IAAA;EAC3B,yBAAA,GAA4B,IAAA,EAAM,IAAA,EAAM,UAAA;EACxC,gBAAA,GAAmB,IAAA,EAAM,IAAA;EAGzB,iBAAA,GAAoB,IAAA,EAAM,IAAA,EAAM,YAAA,EAAc,YAAA,EAAc,UAAA,GAAa,UAAA;EACzE,mBAAA,GAAsB,IAAA,EAAM,IAAA,EAAM,YAAA,EAAc,YAAA;EAGhD,mBAAA,GAAsB,YAAA;AAAA;;;;KAMZ,aAAA,SAAsB,iBAAiB;;;;KAKvC,iBAAA,WAA4B,aAAA,QACnC,IAAA,EAAM,iBAAA,CAAkB,CAAA,aACjB,OAAA;;;;;;;;;;;;;;;;;;;cAyBC,UAAA;EAzDe;;;eA6Db,aAAA,EAAa,KAAA,EAAS,CAAA,EAAC,QAAA,EAAY,iBAAA,CAAkB,CAAA,IAAK,iBAAA;EA1DzC;;;sBAiEV,aAAA,EAAa,KAAA,EAAS,CAAA,EAAC,QAAA,EAAY,iBAAA,CAAkB,CAAA,IAAK,iBAAA;EAhEpD;;;iBAuEX,aAAA,EAAa,KAAA,EAAS,CAAA,KAAC,IAAA,EAAW,iBAAA,CAAkB,CAAA;EAvEO;;;oBAuFxD,aAAA,EAAa,KAAA,EAAS,CAAA,KAAC,IAAA,EAAW,iBAAA,CAAkB,CAAA;EAtFxC;;;;EACL;;;cAmGb,aAAA;AAAA"}
@@ -1,7 +1,7 @@
1
1
  import { log } from "@warlock.js/logger";
2
2
  import events from "@mongez/events";
3
3
 
4
- //#region ../auth/src/services/auth-events.ts
4
+ //#region ../../packages/auth/src/services/auth-events.ts
5
5
  /**
6
6
  * Event namespace prefix for auth events
7
7
  */
@@ -1 +1 @@
1
- {"version":3,"file":"auth-events.mjs","names":[],"sources":["../../../../../../../auth/src/services/auth-events.ts"],"sourcesContent":["import events, { type EventSubscription } from \"@mongez/events\";\nimport { log } from \"@warlock.js/logger\";\nimport type { DeviceInfo, TokenPair } from \"../contracts/types\";\nimport type { Auth } from \"../models/auth.model\";\nimport type { RefreshToken } from \"../models/refresh-token\";\n\n/**\n * Auth event payload types\n */\nexport type AuthEventPayloads = {\n // Login events\n \"login.success\": [user: Auth, tokenPair: TokenPair, deviceInfo?: DeviceInfo];\n \"login.failed\": [credentials: { email?: string; username?: string }, reason: string];\n \"login.attempt\": [credentials: { email?: string; username?: string }];\n\n // Logout events\n logout: [user: Auth];\n \"logout.all\": [user: Auth];\n \"logout.failsafe\": [user: Auth];\n\n // Token events\n \"token.created\": [user: Auth, tokenPair: TokenPair];\n \"token.refreshed\": [user: Auth, newTokenPair: TokenPair, oldRefreshToken: RefreshToken];\n \"token.revoked\": [user: Auth, token: RefreshToken];\n \"token.expired\": [token: RefreshToken];\n \"token.familyRevoked\": [familyId: string, tokens: RefreshToken[]];\n\n // Password events\n \"password.changed\": [user: Auth];\n \"password.resetRequested\": [user: Auth, resetToken: string];\n \"password.reset\": [user: Auth];\n\n // Session events\n \"session.created\": [user: Auth, refreshToken: RefreshToken, deviceInfo?: DeviceInfo];\n \"session.destroyed\": [user: Auth, refreshToken: RefreshToken];\n\n // Cleanup events\n \"cleanup.completed\": [expiredCount: number];\n};\n\n/**\n * Auth event names\n */\nexport type AuthEventName = keyof AuthEventPayloads;\n\n/**\n * Callback type for a specific event\n */\nexport type AuthEventCallback<T extends AuthEventName> = (\n ...args: AuthEventPayloads[T]\n) => void | Promise<void>;\n\n/**\n * Event namespace prefix for auth events\n */\nconst AUTH_EVENT_PREFIX = \"auth.\";\n\n/**\n * Type-safe auth events manager\n *\n * @example\n * ```typescript\n * // Subscribe to events with full autocomplete\n * authEvents.on(\"login.success\", (user, tokenPair, deviceInfo) => {\n * console.log(`User ${user.id} logged in`);\n * });\n *\n * authEvents.on(\"token.refreshed\", (user, newPair, oldToken) => {\n * console.log(`Token refreshed for user ${user.id}`);\n * });\n *\n * // Trigger events\n * authEvents.emit(\"login.success\", user, tokenPair, deviceInfo);\n * ```\n */\nexport const authEvents = {\n /**\n * Subscribe to an auth event\n */\n on<T extends AuthEventName>(event: T, callback: AuthEventCallback<T>): EventSubscription {\n return events.subscribe(AUTH_EVENT_PREFIX + event, callback as Function);\n },\n\n /**\n * Subscribe to an auth event (alias for `on`)\n */\n subscribe<T extends AuthEventName>(event: T, callback: AuthEventCallback<T>): EventSubscription {\n return this.on(event, callback);\n },\n\n /**\n * Emit an auth event\n */\n emit<T extends AuthEventName>(event: T, ...args: AuthEventPayloads[T]): void {\n try {\n events.trigger(AUTH_EVENT_PREFIX + event, ...args);\n } catch (error) {\n // Auth events are fire-and-forget notifications: a throwing listener must\n // never break the flow that emitted it (e.g. turn a completed login into a\n // 500). Log and swallow. NOTE: this guards synchronous listener throws;\n // async listener rejections and \"later listeners still run\" need a fix in\n // @mongez/events (tracked separately).\n log.error(\"auth\", \"event\", error);\n }\n },\n\n /**\n * Emit an auth event (alias for `emit`)\n */\n trigger<T extends AuthEventName>(event: T, ...args: AuthEventPayloads[T]): void {\n this.emit(event, ...args);\n },\n\n /**\n * Unsubscribe from all auth events\n */\n unsubscribeAll(): void {\n events.unsubscribeNamespace(AUTH_EVENT_PREFIX.slice(0, -1));\n },\n\n /**\n * Unsubscribe from a specific auth event\n */\n off(event?: AuthEventName): void {\n if (event) {\n events.unsubscribe(AUTH_EVENT_PREFIX + event);\n } else {\n this.unsubscribeAll();\n }\n },\n};\n"],"mappings":";;;;;;;AAuDA,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;AAoB1B,MAAa,aAAa;;;;CAIxB,GAA4B,OAAU,UAAmD;EACvF,OAAO,OAAO,UAAU,oBAAoB,OAAO,QAAoB;CACzE;;;;CAKA,UAAmC,OAAU,UAAmD;EAC9F,OAAO,KAAK,GAAG,OAAO,QAAQ;CAChC;;;;CAKA,KAA8B,OAAU,GAAG,MAAkC;EAC3E,IAAI;GACF,OAAO,QAAQ,oBAAoB,OAAO,GAAG,IAAI;EACnD,SAAS,OAAO;GAMd,IAAI,MAAM,QAAQ,SAAS,KAAK;EAClC;CACF;;;;CAKA,QAAiC,OAAU,GAAG,MAAkC;EAC9E,KAAK,KAAK,OAAO,GAAG,IAAI;CAC1B;;;;CAKA,iBAAuB;EACrB,OAAO,qBAAqB,kBAAkB,MAAM,GAAG,EAAE,CAAC;CAC5D;;;;CAKA,IAAI,OAA6B;EAC/B,IAAI,OACF,OAAO,YAAY,oBAAoB,KAAK;OAE5C,KAAK,eAAe;CAExB;AACF"}
1
+ {"version":3,"file":"auth-events.mjs","names":[],"sources":["../../../../../../../../packages/auth/src/services/auth-events.ts"],"sourcesContent":["import events, { type EventSubscription } from \"@mongez/events\";\nimport { log } from \"@warlock.js/logger\";\nimport type { DeviceInfo, TokenPair } from \"../contracts/types\";\nimport type { Auth } from \"../models/auth.model\";\nimport type { RefreshToken } from \"../models/refresh-token\";\n\n/**\n * Auth event payload types\n */\nexport type AuthEventPayloads = {\n // Login events\n \"login.success\": [user: Auth, tokenPair: TokenPair, deviceInfo?: DeviceInfo];\n \"login.failed\": [credentials: { email?: string; username?: string }, reason: string];\n \"login.attempt\": [credentials: { email?: string; username?: string }];\n\n // Logout events\n logout: [user: Auth];\n \"logout.all\": [user: Auth];\n \"logout.failsafe\": [user: Auth];\n\n // Token events\n \"token.created\": [user: Auth, tokenPair: TokenPair];\n \"token.refreshed\": [user: Auth, newTokenPair: TokenPair, oldRefreshToken: RefreshToken];\n \"token.revoked\": [user: Auth, token: RefreshToken];\n \"token.expired\": [token: RefreshToken];\n \"token.familyRevoked\": [familyId: string, tokens: RefreshToken[]];\n\n // Password events\n \"password.changed\": [user: Auth];\n \"password.resetRequested\": [user: Auth, resetToken: string];\n \"password.reset\": [user: Auth];\n\n // Session events\n \"session.created\": [user: Auth, refreshToken: RefreshToken, deviceInfo?: DeviceInfo];\n \"session.destroyed\": [user: Auth, refreshToken: RefreshToken];\n\n // Cleanup events\n \"cleanup.completed\": [expiredCount: number];\n};\n\n/**\n * Auth event names\n */\nexport type AuthEventName = keyof AuthEventPayloads;\n\n/**\n * Callback type for a specific event\n */\nexport type AuthEventCallback<T extends AuthEventName> = (\n ...args: AuthEventPayloads[T]\n) => void | Promise<void>;\n\n/**\n * Event namespace prefix for auth events\n */\nconst AUTH_EVENT_PREFIX = \"auth.\";\n\n/**\n * Type-safe auth events manager\n *\n * @example\n * ```typescript\n * // Subscribe to events with full autocomplete\n * authEvents.on(\"login.success\", (user, tokenPair, deviceInfo) => {\n * console.log(`User ${user.id} logged in`);\n * });\n *\n * authEvents.on(\"token.refreshed\", (user, newPair, oldToken) => {\n * console.log(`Token refreshed for user ${user.id}`);\n * });\n *\n * // Trigger events\n * authEvents.emit(\"login.success\", user, tokenPair, deviceInfo);\n * ```\n */\nexport const authEvents = {\n /**\n * Subscribe to an auth event\n */\n on<T extends AuthEventName>(event: T, callback: AuthEventCallback<T>): EventSubscription {\n return events.subscribe(AUTH_EVENT_PREFIX + event, callback as Function);\n },\n\n /**\n * Subscribe to an auth event (alias for `on`)\n */\n subscribe<T extends AuthEventName>(event: T, callback: AuthEventCallback<T>): EventSubscription {\n return this.on(event, callback);\n },\n\n /**\n * Emit an auth event\n */\n emit<T extends AuthEventName>(event: T, ...args: AuthEventPayloads[T]): void {\n try {\n events.trigger(AUTH_EVENT_PREFIX + event, ...args);\n } catch (error) {\n // Auth events are fire-and-forget notifications: a throwing listener must\n // never break the flow that emitted it (e.g. turn a completed login into a\n // 500). Log and swallow. NOTE: this guards synchronous listener throws;\n // async listener rejections and \"later listeners still run\" need a fix in\n // @mongez/events (tracked separately).\n log.error(\"auth\", \"event\", error);\n }\n },\n\n /**\n * Emit an auth event (alias for `emit`)\n */\n trigger<T extends AuthEventName>(event: T, ...args: AuthEventPayloads[T]): void {\n this.emit(event, ...args);\n },\n\n /**\n * Unsubscribe from all auth events\n */\n unsubscribeAll(): void {\n events.unsubscribeNamespace(AUTH_EVENT_PREFIX.slice(0, -1));\n },\n\n /**\n * Unsubscribe from a specific auth event\n */\n off(event?: AuthEventName): void {\n if (event) {\n events.unsubscribe(AUTH_EVENT_PREFIX + event);\n } else {\n this.unsubscribeAll();\n }\n },\n};\n"],"mappings":";;;;;;;AAuDA,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;AAoB1B,MAAa,aAAa;;;;CAIxB,GAA4B,OAAU,UAAmD;EACvF,OAAO,OAAO,UAAU,oBAAoB,OAAO,QAAoB;CACzE;;;;CAKA,UAAmC,OAAU,UAAmD;EAC9F,OAAO,KAAK,GAAG,OAAO,QAAQ;CAChC;;;;CAKA,KAA8B,OAAU,GAAG,MAAkC;EAC3E,IAAI;GACF,OAAO,QAAQ,oBAAoB,OAAO,GAAG,IAAI;EACnD,SAAS,OAAO;GAMd,IAAI,MAAM,QAAQ,SAAS,KAAK;EAClC;CACF;;;;CAKA,QAAiC,OAAU,GAAG,MAAkC;EAC9E,KAAK,KAAK,OAAO,GAAG,IAAI;CAC1B;;;;CAKA,iBAAuB;EACrB,OAAO,qBAAqB,kBAAkB,MAAM,GAAG,EAAE,CAAC;CAC5D;;;;CAKA,IAAI,OAA6B;EAC/B,IAAI,OACF,OAAO,YAAY,oBAAoB,KAAK;OAE5C,KAAK,eAAe;CAExB;AACF"}
@@ -5,7 +5,7 @@ import { RefreshToken } from "../models/refresh-token/refresh-token.model.mjs";
5
5
  import { Request, Response } from "@warlock.js/core";
6
6
  import { ChildModel } from "@warlock.js/cascade";
7
7
 
8
- //#region ../auth/src/services/auth.service.d.ts
8
+ //#region ../../packages/auth/src/services/auth.service.d.ts
9
9
  /** Thrown when a request-scoped cookie Domain cannot be resolved safely. */
10
10
  declare class CookieDomainRequestRequiredError extends Error {
11
11
  constructor(method: string);
@@ -1 +1 @@
1
- {"version":3,"file":"auth.service.d.mts","names":[],"sources":["../../../../../../../auth/src/services/auth.service.ts"],"mappings":"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"}
1
+ {"version":3,"file":"auth.service.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/services/auth.service.ts"],"mappings":"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"}
@@ -15,7 +15,7 @@ import { Random } from "@mongez/reinforcements";
15
15
  import { ForbiddenError, config, hashPassword, verifyPassword } from "@warlock.js/core";
16
16
  import { randomUUID } from "node:crypto";
17
17
 
18
- //#region ../auth/src/services/auth.service.ts
18
+ //#region ../../packages/auth/src/services/auth.service.ts
19
19
  /** Thrown when a request-scoped cookie Domain cannot be resolved safely. */
20
20
  var CookieDomainRequestRequiredError = class extends Error {
21
21
  constructor(method) {
@@ -1 +1 @@
1
- {"version":3,"file":"auth.service.mjs","names":[],"sources":["../../../../../../../auth/src/services/auth.service.ts"],"sourcesContent":["import { Random } from \"@mongez/reinforcements\";\nimport { randomUUID } from \"node:crypto\";\nimport type { ChildModel } from \"@warlock.js/cascade\";\nimport {\n config,\n ForbiddenError,\n hashPassword,\n type Request,\n type Response,\n verifyPassword,\n} from \"@warlock.js/core\";\nimport type {\n AccessTokenOutput,\n AuthCredentials,\n ClearAuthCookieOptions,\n DeviceInfo,\n LoginResult,\n SetAuthCookieOptions,\n TokenPair,\n} from \"../contracts/types\";\nimport { assertCsrfOriginAllowed } from \"../middleware/csrf-origin-check\";\nimport { AccessToken } from \"../models/access-token\";\nimport type { Auth } from \"../models/auth.model\";\nimport { AuthTokenFamily } from \"../models/auth-token-family\";\nimport { OneTimeToken } from \"../models/one-time-token\";\nimport { RefreshToken } from \"../models/refresh-token\";\nimport { authConfig } from \"./auth-config\";\nimport { authEvents } from \"./auth-events\";\nimport { isInvalidCredentialError, jwt } from \"./jwt\";\nimport {\n afterTokenFamilyOperation,\n runTokenFamilyOperation,\n TokenFamilyUnavailableError,\n} from \"./token-family-operation\";\n\n/** Thrown when a request-scoped cookie Domain cannot be resolved safely. */\nexport class CookieDomainRequestRequiredError extends Error {\n public constructor(method: string) {\n super(\n `${method} requires the request when auth.cookie.cookieDomain is configured because the cookie Domain must be resolved for that request.`,\n );\n this.name = \"CookieDomainRequestRequiredError\";\n }\n}\n\nclass AuthService {\n /**\n * Resolve the active access-token model — the package default, or a subclass\n * an app registered under `config.auth.accessToken.model` (e.g. to add a\n * tenant column). The service never references the concrete class directly so\n * an override is a pure config change.\n */\n private get accessTokenModel(): typeof AccessToken {\n return config.key(\"auth.accessToken.model\", AccessToken);\n }\n\n /**\n * Resolve the active refresh-token model (default or registered override).\n */\n private get refreshTokenModel(): typeof RefreshToken {\n return config.key(\"auth.refreshToken.model\", RefreshToken);\n }\n\n /**\n * Resolve the active one-time-token model (default or registered override).\n */\n private get oneTimeTokenModel(): typeof OneTimeToken {\n return config.key(\"auth.oneTimeToken.model\", OneTimeToken);\n }\n\n /**\n * Build the default access-token JWT payload from a user.\n */\n public buildAccessTokenPayload(user: Auth): Record<string, unknown> {\n return {\n id: user.id,\n userType: user.userType,\n created_at: Date.now(),\n };\n }\n\n /** One policy seam keeps account-state checks identical across all auth paths. */\n public async canAuthenticate(user: Auth): Promise<boolean> {\n return authConfig.canAuthenticate(user);\n }\n\n private async assertCanAuthenticate(user: Auth): Promise<void> {\n if (!(await this.canAuthenticate(user))) {\n throw new ForbiddenError(\"This user cannot authenticate.\");\n }\n }\n\n private async issueAccessToken(\n user: Auth,\n payload?: Record<string, unknown>,\n familyId?: string,\n ): Promise<AccessTokenOutput> {\n const data = { ...(payload || this.buildAccessTokenPayload(user)), jti: randomUUID() };\n // Validate before signing so an unusable lifetime cannot mint an immortal token.\n const expiresIn = authConfig.accessToken.expiresInMs();\n\n const token = await jwt.generate(data, { expiresIn });\n const expiresAt = new Date(Date.now() + expiresIn);\n\n if (familyId) {\n await this.accessTokenModel.issue(user, token, expiresAt, familyId);\n } else {\n await this.accessTokenModel.issue(user, token, expiresAt);\n }\n\n return { token, expiresAt: expiresAt.toISOString() };\n }\n\n /**\n * Sign + persist an access token for the user and return the token with its\n * expiry. The expiry is computed locally from `expiresIn` rather than by\n * re-verifying the token we just signed.\n */\n public async generateAccessToken(\n user: Auth,\n payload?: Record<string, unknown>,\n ): Promise<AccessTokenOutput> {\n await this.assertCanAuthenticate(user);\n\n return this.issueAccessToken(user, payload);\n }\n\n private async issueRefreshToken(\n user: Auth,\n deviceInfo?: DeviceInfo,\n ensuredFamily?: AuthTokenFamily,\n ): Promise<RefreshToken | undefined> {\n if (!authConfig.refreshToken.enabled()) return;\n\n // Validate the lifetime first — before the per-user cap is enforced, before\n // anything is signed — so a bad value can never revoke a user's oldest\n // session on its way to failing.\n const expiresIn = authConfig.refreshToken.expiresInMs();\n\n const familyId = deviceInfo?.familyId || Random.string(32);\n\n const family = ensuredFamily ?? (await AuthTokenFamily.ensure(user, familyId));\n\n if (family.isRevoked) {\n throw new Error(\"Cannot issue tokens for a revoked token family\");\n }\n\n const payload = {\n userId: user.id,\n userType: user.userType,\n familyId,\n jti: randomUUID(),\n };\n\n const expiresAt = new Date(Date.now() + expiresIn).toISOString();\n\n await this.refreshTokenModel.enforceMax(user, authConfig.refreshToken.maxPerUser());\n\n const token = await jwt.generateRefreshToken(payload, { expiresIn });\n\n return this.refreshTokenModel.issue(user, token, { familyId, expiresAt, deviceInfo });\n }\n\n /**\n * Keep issuance into an explicitly reused family in the same durable\n * transition boundary as rotation and revocation. A new, caller-unspecified\n * family has no concurrent session to protect and retains the lightweight\n * issuance path.\n */\n private async issueInSuppliedFamily<T>(\n user: Auth,\n familyId: string,\n issue: (family: AuthTokenFamily) => Promise<T>,\n ): Promise<T> {\n // Preserve the established error for a family that was already revoked\n // before issuance started. The coordinator covers a revoke that races\n // after this observation.\n const family = await AuthTokenFamily.ensure(user, familyId);\n\n if (family.isRevoked) {\n throw new Error(\"Cannot issue tokens for a revoked token family\");\n }\n\n try {\n return await runTokenFamilyOperation(familyId, issue);\n } catch (error) {\n // Do not expose a timing-dependent error shape to callers: a family\n // revoked between ensure() and the transaction is the same unusable\n // family condition as one found above.\n if (error instanceof TokenFamilyUnavailableError) {\n throw new Error(\"Cannot issue tokens for a revoked token family\");\n }\n\n throw error;\n }\n }\n\n /**\n * Sign + persist a refresh token for the user (enforcing the per-user cap\n * first). Resolves to `undefined` when refresh tokens are disabled in config.\n */\n public async createRefreshToken(\n user: Auth,\n deviceInfo?: DeviceInfo,\n ): Promise<RefreshToken | undefined> {\n if (!authConfig.refreshToken.enabled()) return;\n\n await this.assertCanAuthenticate(user);\n\n if (deviceInfo?.familyId) {\n return this.issueInSuppliedFamily(user, deviceInfo.familyId, (family) =>\n this.issueRefreshToken(user, deviceInfo, family),\n );\n }\n\n return this.issueRefreshToken(user, deviceInfo);\n }\n\n private async issueTokenPair(\n user: Auth,\n deviceInfo?: DeviceInfo,\n ensuredFamily?: AuthTokenFamily,\n ): Promise<TokenPair> {\n const refreshEnabled = authConfig.refreshToken.enabled();\n const familyId = deviceInfo?.familyId || Random.string(32);\n\n const family = refreshEnabled\n ? (ensuredFamily ?? (await AuthTokenFamily.ensure(user, familyId)))\n : undefined;\n\n if (family) {\n if (family.isRevoked) {\n throw new Error(\"Cannot issue tokens for a revoked token family\");\n }\n }\n\n const accessToken = await this.issueAccessToken(\n user,\n deviceInfo?.payload,\n refreshEnabled ? familyId : undefined,\n );\n const refreshToken = await this.issueRefreshToken(\n user,\n refreshEnabled ? { ...deviceInfo, familyId } : deviceInfo,\n family,\n );\n\n const tokenPair: TokenPair = {\n accessToken,\n refreshToken: refreshToken\n ? {\n token: refreshToken.get(\"token\"),\n expiresAt: refreshToken.get(\"expires_at\"),\n }\n : undefined,\n };\n\n afterTokenFamilyOperation(() => authEvents.emit(\"token.created\", user, tokenPair));\n\n if (refreshToken) {\n afterTokenFamilyOperation(() =>\n authEvents.emit(\"session.created\", user, refreshToken, deviceInfo),\n );\n }\n\n return tokenPair;\n }\n\n /**\n * Issue both an access and a refresh token, emitting the creation events.\n */\n public async createTokenPair(user: Auth, deviceInfo?: DeviceInfo): Promise<TokenPair> {\n await this.assertCanAuthenticate(user);\n\n if (authConfig.refreshToken.enabled() && deviceInfo?.familyId) {\n return this.issueInSuppliedFamily(user, deviceInfo.familyId, (family) =>\n this.issueTokenPair(user, deviceInfo, family),\n );\n }\n\n return this.issueTokenPair(user, deviceInfo);\n }\n\n /**\n * Exchange a refresh token for a new token pair, with rotation + replay\n * detection. A concurrent reuse of the same token loses the atomic revoke and\n * is treated as a breach — the whole family is revoked and the request fails.\n */\n public async refreshTokens(\n refreshTokenString: string,\n deviceInfo?: DeviceInfo,\n ): Promise<TokenPair | null> {\n let decoded: {\n userId: number;\n userType: string;\n familyId: string;\n } | null;\n\n try {\n decoded = await jwt.verifyRefreshToken<{\n userId: number;\n userType: string;\n familyId: string;\n }>(refreshTokenString);\n } catch (error) {\n if (isInvalidCredentialError(error)) return null;\n\n throw error;\n }\n\n if (!decoded) return null;\n\n const presentedToken = await this.refreshTokenModel.findByToken(refreshTokenString);\n\n if (!presentedToken || !this.refreshIdentityMatches(presentedToken, decoded)) return null;\n\n await AuthTokenFamily.ensure(\n { id: decoded.userId, userType: decoded.userType } as Auth,\n presentedToken.familyId,\n );\n return runTokenFamilyOperation(presentedToken.familyId, async () => {\n // Each serializable attempt must read the current row in its own snapshot.\n const refreshToken = await this.refreshTokenModel.findByToken(refreshTokenString);\n if (!refreshToken || !this.refreshIdentityMatches(refreshToken, decoded)) return null;\n if (!refreshToken.isValid) {\n // An already-invalid token may be a replay of a rotated credential.\n if (refreshToken) {\n await this.revokeTokenFamily(refreshToken.familyId);\n }\n\n return null;\n }\n\n const UserModel = config.key(`auth.userType.${decoded.userType}`);\n\n if (!UserModel) {\n throw new Error(`User type ${decoded.userType} is unknown type.`);\n }\n\n const user = (await UserModel.find(decoded.userId)) as Auth | null;\n\n if (!user) return null;\n\n if (!(await this.canAuthenticate(user))) return null;\n\n const rotationEnabled = authConfig.refreshToken.rotation();\n\n if (rotationEnabled) {\n const won = await refreshToken.revokeIfActive();\n\n if (!won) {\n // A concurrent request already rotated this token.\n await this.revokeTokenFamily(refreshToken.familyId);\n\n return null;\n }\n } else {\n await refreshToken.markAsUsed();\n }\n\n const newTokenPair = await this.issueTokenPair(user, {\n ...deviceInfo,\n familyId: refreshToken.familyId,\n });\n\n afterTokenFamilyOperation(() =>\n authEvents.emit(\"token.refreshed\", user, newTokenPair, refreshToken),\n );\n\n return newTokenPair;\n }).catch((error) => {\n if (error instanceof TokenFamilyUnavailableError) return null;\n throw error;\n });\n }\n\n /**\n * Opt-in browser-session renewal. Unlike {@link refreshTokens}, this permits\n * a short duplicate presentation to receive the exact committed successor\n * pair. It is deliberately not used by the legacy/public refresh API.\n */\n public async renewAutomaticSession(\n refreshTokenString: string,\n expectedUserType: string,\n options: { overlapMs?: number; deviceInfo?: DeviceInfo; maxAgeMs?: number } = {},\n ): Promise<TokenPair | null> {\n let decoded: { userId: number; userType: string; familyId: string } | null;\n try {\n decoded = await jwt.verifyRefreshToken<{\n userId: number;\n userType: string;\n familyId: string;\n }>(refreshTokenString);\n } catch (error) {\n if (isInvalidCredentialError(error)) return null;\n throw error;\n }\n\n if (!decoded || decoded.userType !== expectedUserType) return null;\n const presentedToken = await this.refreshTokenModel.findByToken(refreshTokenString);\n if (!presentedToken || !this.refreshIdentityMatches(presentedToken, decoded)) return null;\n\n await AuthTokenFamily.ensure(\n { id: decoded.userId, userType: decoded.userType } as Auth,\n presentedToken.familyId,\n );\n return runTokenFamilyOperation(presentedToken.familyId, async () => {\n // A retry can follow another process's committed rotation. Never reuse\n // a model captured before the transaction or before a failed attempt.\n const oldToken = await this.refreshTokenModel.findByToken(refreshTokenString);\n if (!oldToken || !this.refreshIdentityMatches(oldToken, decoded)) return null;\n if (!oldToken.isValid) {\n const successor = await this.readAutomaticSuccessor(oldToken, decoded, options.overlapMs);\n if (successor) return successor;\n if (\n this.isAutomaticOverlapOpen(oldToken, options.overlapMs) &&\n this.hasSuccessor(oldToken)\n ) {\n return null;\n }\n await this.revokeTokenFamily(oldToken.familyId);\n return null;\n }\n\n // Rotation never extends a family past `maxAgeMs` from its first issue.\n if (\n options.maxAgeMs !== undefined &&\n (await this.familyExceedsMaxAge(oldToken.familyId, options.maxAgeMs))\n ) {\n await this.revokeTokenFamily(oldToken.familyId);\n return null;\n }\n\n const UserModel = config.key(`auth.userType.${decoded.userType}`);\n if (!UserModel) throw new Error(`User type ${decoded.userType} is unknown type.`);\n const user = (await UserModel.find(decoded.userId)) as Auth | null;\n if (!user || !(await this.canAuthenticate(user))) return null;\n\n if (!(await oldToken.revokeIfActive())) {\n // A newer successor is never substituted here. A stale automatic\n // response must fail quietly rather than revoke a legitimate family.\n const currentToken = await this.refreshTokenModel.findByToken(refreshTokenString);\n if (!currentToken) return null;\n const successor = await this.readAutomaticSuccessor(\n currentToken,\n decoded,\n options.overlapMs,\n );\n if (successor) return successor;\n if (\n this.isAutomaticOverlapOpen(currentToken, options.overlapMs) &&\n this.hasSuccessor(currentToken)\n )\n return null;\n await this.revokeTokenFamily(oldToken.familyId);\n return null;\n }\n\n const pair = await this.issueTokenPair(user, {\n ...options.deviceInfo,\n familyId: oldToken.familyId,\n });\n const successorAccess = await this.accessTokenModel.findByToken(pair.accessToken.token);\n const successorRefresh = pair.refreshToken\n ? await this.refreshTokenModel.findByToken(pair.refreshToken.token)\n : null;\n if (!successorAccess || !successorRefresh) {\n throw new Error(\"Automatic renewal did not persist a complete successor pair\");\n }\n\n // `revokeIfActive()` updates the database, not this hydrated instance.\n // Saving that stale instance would write its old `revoked_at: null` back\n // and reopen the predecessor. Update only the successor columns instead.\n const linked = await this.refreshTokenModel.atomic(\n { id: oldToken.id },\n {\n $set: {\n successor_access_token_id: successorAccess.id,\n successor_refresh_token_id: successorRefresh.id,\n },\n },\n );\n if (linked !== 1) throw new Error(\"Automatic renewal could not link its successor pair\");\n afterTokenFamilyOperation(() => authEvents.emit(\"token.refreshed\", user, pair, oldToken));\n return pair;\n }).catch((error) => {\n if (error instanceof TokenFamilyUnavailableError) return null;\n throw error;\n });\n }\n\n /**\n * Whether the family's first issue is older than `maxAgeMs`. The start is the\n * family row's `created_at`, falling back to its oldest refresh token when\n * the row carries none (an upserted legacy family).\n */\n private async familyExceedsMaxAge(familyId: string, maxAgeMs: number): Promise<boolean> {\n const family = await AuthTokenFamily.findByFamilyId(familyId);\n let started: unknown = family?.get(\"created_at\");\n\n if (!started) {\n const oldest = await this.refreshTokenModel\n .query()\n .where({ family_id: familyId })\n .orderBy(\"created_at\", \"asc\")\n .first();\n\n started = oldest?.get(\"created_at\");\n }\n\n const startedAt = started ? new Date(started as string | number | Date).getTime() : NaN;\n\n // Unknown start: nothing to measure against, so the cap cannot apply.\n if (!Number.isFinite(startedAt)) return false;\n\n return Date.now() - startedAt > maxAgeMs;\n }\n\n private automaticOverlapMs(overlapMs: number | undefined): number {\n return typeof overlapMs === \"number\" && Number.isFinite(overlapMs)\n ? Math.max(0, Math.min(10_000, overlapMs))\n : 5000;\n }\n\n private refreshIdentityMatches(\n token: RefreshToken,\n decoded: { userId: string | number; userType: string; familyId: string },\n ): boolean {\n return (\n token.familyId === decoded.familyId &&\n token.get(\"user_id\") === decoded.userId &&\n token.get(\"user_type\") === decoded.userType\n );\n }\n\n private hasSuccessor(oldToken: RefreshToken): boolean {\n return Boolean(\n oldToken.get(\"successor_access_token_id\") && oldToken.get(\"successor_refresh_token_id\"),\n );\n }\n\n private isAutomaticOverlapOpen(oldToken: RefreshToken, overlapMs: number | undefined): boolean {\n const revokedAt = oldToken.get<Date | undefined>(\"revoked_at\");\n const window = this.automaticOverlapMs(overlapMs);\n const elapsed = revokedAt ? Date.now() - new Date(revokedAt).getTime() : NaN;\n return window > 0 && elapsed >= 0 && elapsed <= window;\n }\n\n private async readAutomaticSuccessor(\n oldToken: RefreshToken,\n decoded: { userId: number; userType: string; familyId: string },\n overlapMs: number | undefined,\n ): Promise<TokenPair | null> {\n if (!this.isAutomaticOverlapOpen(oldToken, overlapMs)) return null;\n const accessId = oldToken.get<string | undefined>(\"successor_access_token_id\");\n const refreshId = oldToken.get<string | undefined>(\"successor_refresh_token_id\");\n if (!accessId || !refreshId) return null;\n const [access, refresh] = await Promise.all([\n this.accessTokenModel.find(accessId),\n this.refreshTokenModel.find(refreshId),\n ]);\n if (!access || access.isExpired || !refresh?.isValid) return null;\n if (\n access.get(\"family_id\") !== decoded.familyId ||\n refresh.get(\"family_id\") !== decoded.familyId ||\n access.get(\"user_id\") !== decoded.userId ||\n refresh.get(\"user_id\") !== decoded.userId ||\n access.get(\"user_type\") !== decoded.userType ||\n refresh.get(\"user_type\") !== decoded.userType\n )\n return null;\n const accessExpiry = new Date(access.get(\"expires_at\")).getTime();\n if (!Number.isFinite(accessExpiry) || accessExpiry <= Date.now()) return null;\n return {\n accessToken: { token: access.get(\"token\"), expiresAt: access.get(\"expires_at\") },\n refreshToken: { token: refresh.get(\"token\"), expiresAt: refresh.get(\"expires_at\") },\n };\n }\n\n /**\n * Verify a plaintext password against a stored hash.\n */\n public async verifyPassword(plainPassword: string, hashedPassword: string): Promise<boolean> {\n return verifyPassword(plainPassword, hashedPassword);\n }\n\n /**\n * Hash a plaintext password.\n */\n public async hashPassword(password: string): Promise<string> {\n return hashPassword(password);\n }\n\n /**\n * Resolve a user by credentials, verifying the password. Returns `null` on a\n * missing user or a wrong password, emitting `login.failed` either way.\n */\n public async attemptLogin<T extends Auth>(\n Model: ChildModel<T>,\n data: AuthCredentials,\n ): Promise<T | null> {\n const { password, ...otherData } = data;\n\n authEvents.emit(\"login.attempt\", otherData);\n\n const user = (await Model.first(otherData)) as T | null;\n\n if (!user) {\n authEvents.emit(\"login.failed\", otherData, \"User not found\");\n\n return null;\n }\n\n if (!(await this.verifyPassword(password, user.string(\"password\")!))) {\n authEvents.emit(\"login.failed\", otherData, \"Invalid password\");\n\n return null;\n }\n\n if (!(await this.canAuthenticate(user))) {\n authEvents.emit(\"login.failed\", otherData, \"Authentication not allowed\");\n\n return null;\n }\n\n return user;\n }\n\n /**\n * Full login flow: validate credentials, issue tokens, emit events. Returns\n * the user + token pair on success, `null` on failure.\n */\n public async login<T extends Auth>(\n Model: ChildModel<T>,\n credentials: AuthCredentials,\n deviceInfo?: DeviceInfo,\n ): Promise<LoginResult<T> | null> {\n const user = await this.attemptLogin(Model, credentials);\n\n if (!user) {\n return null;\n }\n\n return this.finalizeLogin(user, deviceInfo);\n }\n\n /**\n * Log in a user some OTHER method already authenticated — a provider\n * callback, a passkey assertion, a one-time code. Applies the\n * `auth.canAuthenticate` policy (403 on refusal), then produces exactly\n * what {@link login} produces: the same tokens, rows and events. Pair with\n * {@link setAuthCookie} for a cookie session, as with password login.\n *\n * Never call this for a user you have not authenticated.\n */\n public async completeLogin<T extends Auth>(\n user: T,\n deviceInfo?: DeviceInfo,\n ): Promise<LoginResult<T>> {\n await this.assertCanAuthenticate(user);\n\n return this.finalizeLogin(user, deviceInfo);\n }\n\n /**\n * Cookie login for a browser: same-origin check, credential login, then both\n * session cookies. Unlike the default CSRF guard, the Origin/Referer check\n * runs even when the request carries no cookies (login CSRF): a missing or\n * cross-site Origin/Referer throws `CsrfOriginMismatchError` before any\n * credential is looked at. Returns `null` on bad credentials.\n */\n public async loginWithSessionCookies<T extends Auth>(\n request: Request,\n response: { [K in \"cookie\" | \"clearCookie\"]: (...args: Parameters<Response[K]>) => unknown },\n Model: ChildModel<T>,\n credentials: AuthCredentials,\n deviceInfo?: DeviceInfo,\n ): Promise<LoginResult<T> | null> {\n assertCsrfOriginAllowed(request);\n\n const result = await this.login(Model, credentials, deviceInfo);\n\n if (!result) return null;\n\n if (result.tokens.refreshToken) {\n this.setSessionCookies(\n response,\n {\n accessToken: result.tokens.accessToken,\n refreshToken: result.tokens.refreshToken,\n },\n request,\n );\n } else {\n this.setAuthCookie(response, result.tokens.accessToken, {\n domain: this.cookieDomainFor(\"loginWithSessionCookies\", request),\n });\n }\n\n return result;\n }\n\n /** The one token-issuing tail every login method shares. */\n private async finalizeLogin<T extends Auth>(\n user: T,\n deviceInfo?: DeviceInfo,\n ): Promise<LoginResult<T>> {\n if (!authConfig.refreshToken.enabled()) {\n const accessToken = await this.issueAccessToken(user, deviceInfo?.payload);\n\n return { user, tokens: { accessToken } };\n }\n\n const tokens = await this.issueTokenPair(user, deviceInfo);\n\n authEvents.emit(\"login.success\", user, tokens, deviceInfo);\n\n return { user, tokens };\n }\n\n /**\n * Log a user out.\n *\n * @param accessToken - access token string to revoke (optional)\n * @param refreshToken - refresh token string to revoke (optional)\n *\n * When no refresh token is supplied, `config.auth.refreshToken.logoutWithoutToken`\n * decides the behavior: `\"revoke-all\"` (default, fail-safe) revokes every\n * refresh token; `\"error\"` requires the caller to pass one.\n */\n public async logout(user: Auth, accessToken?: string, refreshToken?: string): Promise<void> {\n if (accessToken) {\n await this.removeAccessToken(user, accessToken);\n }\n\n if (refreshToken) {\n const token = await this.refreshTokenModel.findForUser(user, refreshToken);\n\n if (token) {\n await this.revokeTokenFamily(token.familyId);\n authEvents.emit(\"session.destroyed\", user, token);\n }\n } else {\n const behavior = authConfig.refreshToken.logoutWithoutToken();\n\n if (behavior === \"error\") {\n throw new Error(\"Refresh token required for logout\");\n }\n\n await this.revokeAllTokens(user);\n authEvents.emit(\"logout.failsafe\", user);\n }\n\n authEvents.emit(\"logout\", user);\n }\n\n /**\n * Remove a specific access token belonging to the user.\n */\n public async removeAccessToken(user: Auth, token: string): Promise<void> {\n await this.accessTokenModel.deleteForUser(user, token);\n }\n\n /**\n * Remove every access token belonging to the user.\n */\n public async removeAllAccessTokens(user: Auth): Promise<void> {\n await this.accessTokenModel.deleteAllForUser(user);\n }\n\n /**\n * Remove a specific refresh token belonging to the user.\n */\n public async removeRefreshToken(user: Auth, token: string): Promise<void> {\n await this.refreshTokenModel.deleteForUser(user, token);\n }\n\n /**\n * Revoke every active refresh token for the user and delete their access\n * tokens — \"log out of all devices\". Each durable family is first marked through its revision coordinator; an\n * event fires per revoked token.\n */\n public async revokeAllTokens(user: Auth): Promise<void> {\n // Upgrade existing installations lazily before the family scan. Include\n // revoked rows: a rotation may have revoked its predecessor while its\n // durable family still needs logout's marker.\n const existingRows = await this.refreshTokenModel.familiesForUser(user);\n\n for (const token of existingRows) {\n await AuthTokenFamily.ensure(user, token.familyId);\n }\n\n const families = await AuthTokenFamily.activeFor(user);\n\n for (const family of families) {\n await this.revokeTokenFamily(family.get<string>(\"family_id\"));\n }\n\n // A deployment can still have rows created before the additive migration\n // ran; retain the old bulk cleanup as a final conservative backstop.\n const legacyTokens = await this.refreshTokenModel.revokeAllFor(user);\n\n for (const token of legacyTokens) {\n authEvents.emit(\"token.revoked\", user, token);\n }\n await this.removeAllAccessTokens(user);\n\n authEvents.emit(\"logout.all\", user);\n }\n\n /**\n * Revoke an entire token family — rotation breach containment.\n */\n public async revokeTokenFamily(familyId: string): Promise<void> {\n let family = await AuthTokenFamily.findByFamilyId(familyId);\n\n if (!family) {\n const legacyToken = await this.refreshTokenModel.findInFamily(familyId);\n\n if (!legacyToken) return;\n\n family = await AuthTokenFamily.ensure(\n {\n id: legacyToken.get(\"user_id\"),\n userType: legacyToken.get<string>(\"user_type\"),\n } as Auth,\n familyId,\n );\n }\n\n const user = {\n id: family.get(\"user_id\"),\n userType: family.get<string>(\"user_type\"),\n } as Auth;\n\n if (family.isRevoked) {\n // Logout and replay containment are idempotent. Re-run cleanup because a\n // request that lost the original race may have observed stale rows.\n const revokedTokens = await this.refreshTokenModel.revokeFamily(familyId);\n await this.accessTokenModel.deleteFamilyAndLegacyForUser(user, familyId);\n afterTokenFamilyOperation(() =>\n authEvents.emit(\"token.familyRevoked\", familyId, revokedTokens),\n );\n\n return;\n }\n\n let revokedTokens: RefreshToken[];\n try {\n revokedTokens = await runTokenFamilyOperation(familyId, async (currentFamily) => {\n const familyUser = {\n id: currentFamily.get(\"user_id\"),\n userType: currentFamily.get<string>(\"user_type\"),\n } as Auth;\n const revision = currentFamily.get<number>(\"revision\");\n const revoked = await AuthTokenFamily.revoke(familyId, revision);\n\n if (revoked !== 1) {\n throw new Error(\"Token family revoke lost its revision transition\");\n }\n\n const familyTokens = await this.refreshTokenModel.revokeFamily(familyId);\n await this.accessTokenModel.deleteFamilyAndLegacyForUser(familyUser, familyId);\n\n return familyTokens;\n });\n } catch (error) {\n if (!(error instanceof TokenFamilyUnavailableError)) throw error;\n\n // Another request committed the revoke between the read above and its\n // CAS. A second logout is successful after idempotent cleanup.\n revokedTokens = await this.refreshTokenModel.revokeFamily(familyId);\n await this.accessTokenModel.deleteFamilyAndLegacyForUser(user, familyId);\n }\n\n afterTokenFamilyOperation(() =>\n authEvents.emit(\"token.familyRevoked\", familyId, revokedTokens),\n );\n }\n\n /**\n * Delete expired tokens (refresh + access) and spent one-time tokens\n * (expired or consumed verification/reset/OTP/passkey rows). Emits `token.expired` per refresh\n * token and `cleanup.completed` with the refresh count. Drives the\n * `auth.cleanup` CLI command.\n */\n public async cleanupExpiredTokens(): Promise<number> {\n const expiredTokens = await this.refreshTokenModel.purgeExpired();\n\n for (const token of expiredTokens) {\n authEvents.emit(\"token.expired\", token);\n }\n\n await this.accessTokenModel.purgeExpired();\n await this.oneTimeTokenModel.purgeSpent();\n\n authEvents.emit(\"cleanup.completed\", expiredTokens.length);\n\n return expiredTokens.length;\n }\n\n /**\n * Find every persisted token — access and refresh — that can never retire\n * itself: an unusable `expires_at`, or a token carrying no `exp` claim.\n *\n * This is the remediation read for the pre-4.12.0 `expiresIn` defect (#25).\n * Nothing else surfaces these rows: `auth.cleanup` selects `expires_at < now`,\n * which an `Invalid Date` never satisfies, and a row whose date column is\n * perfectly fine can still hold a token with no deadline in it. Read-only —\n * pair with {@link purgeNeverExpiringTokens} to act on the answer.\n */\n public async findNeverExpiringTokens(): Promise<{\n accessTokens: AccessToken[];\n refreshTokens: RefreshToken[];\n }> {\n return {\n accessTokens: await this.accessTokenModel.findNeverExpiring(),\n refreshTokens: await this.refreshTokenModel.findNeverExpiring(),\n };\n }\n\n /**\n * Revoke every never-expiring token by deleting its row, emitting\n * `token.revoked` per refresh token. Drives `warlock auth.purge-never-expiring`.\n *\n * Access tokens first: deleting one is immediate revocation, while a refresh\n * token left in place for a moment can only mint an access token through a\n * verifier that now rejects it for the missing `exp`.\n */\n public async purgeNeverExpiringTokens(): Promise<{\n accessTokens: number;\n refreshTokens: number;\n }> {\n const accessTokens = await this.accessTokenModel.purgeNeverExpiring();\n const refreshTokens = await this.refreshTokenModel.purgeNeverExpiring();\n\n // `token.expired` (the event `auth.cleanup` already emits for a removed\n // refresh token) rather than `token.revoked`, which carries a loaded `Auth`\n // this batch path has no reason to fetch a user row for.\n for (const token of refreshTokens) {\n authEvents.emit(\"token.expired\", token);\n }\n\n return { accessTokens: accessTokens.length, refreshTokens: refreshTokens.length };\n }\n\n /**\n * Active, unexpired sessions for the user, newest first.\n */\n public async getActiveSessions(user: Auth): Promise<RefreshToken[]> {\n return this.refreshTokenModel.activeFor(user);\n }\n\n /**\n * `Max-Age` (seconds) `setAuthCookie` should apply, derived from an\n * `AccessTokenOutput`'s `expiresAt` — or `undefined` for a bare token\n * string (a session cookie) or an unparseable `expiresAt`.\n */\n private cookieMaxAgeFromToken(token: string | AccessTokenOutput): number | undefined {\n if (typeof token === \"string\") return undefined;\n\n const expiresAt = new Date(token.expiresAt).getTime();\n\n if (Number.isNaN(expiresAt)) return undefined;\n\n return Math.max(0, Math.round((expiresAt - Date.now()) / 1000));\n }\n\n private cookieDomainFor(method: string, request?: Request): string | undefined {\n const resolver = authConfig.cookie.cookieDomain();\n\n if (!resolver) {\n return undefined;\n }\n\n if (!request) {\n throw new CookieDomainRequestRequiredError(method);\n }\n\n return resolver(request);\n }\n\n /**\n * Write the auth cookie on `response` — the write side of the `cookie:<name>`\n * token source `authMiddleware([], \"cookie:<name>\")` already reads. An explicit\n * app-controller call, never a side effect of {@link login}, so an existing\n * bearer-only app never starts emitting `Set-Cookie` just by upgrading.\n *\n * Attribute flags (`HttpOnly`, `SameSite=Lax`, `Secure` outside dev) come\n * from `response.cookie()`'s own `secureCookieDefaults()` floor — this\n * method never overrides them, only `name`/`path`/`maxAge`. `raw: true`\n * writes the token unquoted, so the same string a `cookie:<name>` source\n * reads back is exactly what was issued, not a JSON-wrapped copy.\n *\n * @param token - the token string, or an `AccessTokenOutput` (e.g. from\n * {@link login}) whose `expiresAt` sets `Max-Age` automatically when\n * `options.maxAge` is not given. A bare string with no `options.maxAge`\n * produces a session cookie.\n *\n * @example\n * const { user, tokens } = await authService.login(User, credentials);\n * authService.setAuthCookie(response, tokens.accessToken);\n */\n public setAuthCookie(\n response: { [K in \"cookie\" | \"clearCookie\"]: (...args: Parameters<Response[K]>) => unknown },\n token: string | AccessTokenOutput,\n options: SetAuthCookieOptions = {},\n ): void {\n const name = options.name ?? authConfig.cookie.name();\n const path = options.path ?? authConfig.cookie.path();\n const tokenValue = typeof token === \"string\" ? token : token.token;\n const maxAge = options.maxAge ?? this.cookieMaxAgeFromToken(token);\n\n response.cookie(name, tokenValue, {\n raw: true,\n path,\n ...(options.domain !== undefined ? { domain: options.domain } : {}),\n ...(maxAge !== undefined ? { maxAge } : {}),\n });\n }\n\n /**\n * Clear the auth cookie {@link setAuthCookie} wrote. An explicit\n * app-controller call — pair it with {@link logout} after the token row is\n * revoked. `path` must match what the cookie was set with, or the browser\n * silently ignores the clear (see `response.clearCookie`'s own doc comment).\n *\n * @example\n * await authService.logout(user, accessToken, refreshToken);\n * authService.clearAuthCookie(response);\n */\n public clearAuthCookie(\n response: { [K in \"cookie\" | \"clearCookie\"]: (...args: Parameters<Response[K]>) => unknown },\n options: ClearAuthCookieOptions = {},\n request?: Request,\n ): void {\n const name = options.name ?? authConfig.cookie.name();\n const path = options.path ?? authConfig.cookie.path();\n const domain = this.cookieDomainFor(\"clearAuthCookie\", request);\n\n if (domain !== undefined) {\n response.clearCookie(name, { path, domain });\n }\n\n response.clearCookie(name, { path });\n }\n\n /**\n * Write the access and refresh cookies of a browser session. Both are\n * `HttpOnly`, `SameSite=Lax`, `Path=/` (the refresh cookie must reach every\n * page) and `Secure` outside development, named by `auth.cookie.name` and\n * `auth.cookie.refreshName`. Each `Max-Age` is derived from its token's\n * `expiresAt`, so the cookie lives exactly as long as the token.\n *\n * @example\n * const { tokens } = await authService.login(User, credentials);\n * authService.setSessionCookies(response, tokens);\n */\n public setSessionCookies(\n response: { [K in \"cookie\" | \"clearCookie\"]: (...args: Parameters<Response[K]>) => unknown },\n tokens: { accessToken: AccessTokenOutput; refreshToken: AccessTokenOutput },\n request?: Request,\n ): void {\n const domain = this.cookieDomainFor(\"setSessionCookies\", request);\n const cookies = [\n [authConfig.cookie.name(), tokens.accessToken],\n [authConfig.cookie.refreshName(), tokens.refreshToken],\n ] as const;\n\n for (const [name, token] of cookies) {\n const maxAge = this.cookieMaxAgeFromToken(token);\n\n response.cookie(name, token.token, {\n raw: true,\n httpOnly: true,\n sameSite: \"lax\",\n path: \"/\",\n ...(domain !== undefined ? { domain } : {}),\n ...(maxAge !== undefined ? { maxAge } : {}),\n });\n }\n }\n\n /**\n * Clear both session cookies {@link setSessionCookies} wrote, on `Path=/`.\n * Pair it with {@link logout} after the token rows are revoked.\n */\n public clearSessionCookies(\n response: { [K in \"cookie\" | \"clearCookie\"]: (...args: Parameters<Response[K]>) => unknown },\n request?: Request,\n ): void {\n const domain = this.cookieDomainFor(\"clearSessionCookies\", request);\n\n for (const name of [authConfig.cookie.name(), authConfig.cookie.refreshName()]) {\n if (domain !== undefined) {\n response.clearCookie(name, { path: \"/\", domain });\n }\n\n response.clearCookie(name, { path: \"/\" });\n }\n }\n}\n\nexport const authService = new AuthService();\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAoCA,IAAa,mCAAb,cAAsD,MAAM;CAC1D,AAAO,YAAY,QAAgB;EACjC,MACE,GAAG,OAAO,+HACZ;EACA,KAAK,OAAO;CACd;AACF;AAEA,IAAM,cAAN,MAAkB;;;;;;;CAOhB,IAAY,mBAAuC;EACjD,OAAO,OAAO,IAAI,0BAA0B,WAAW;CACzD;;;;CAKA,IAAY,oBAAyC;EACnD,OAAO,OAAO,IAAI,2BAA2B,YAAY;CAC3D;;;;CAKA,IAAY,oBAAyC;EACnD,OAAO,OAAO,IAAI,2BAA2B,YAAY;CAC3D;;;;CAKA,AAAO,wBAAwB,MAAqC;EAClE,OAAO;GACL,IAAI,KAAK;GACT,UAAU,KAAK;GACf,YAAY,KAAK,IAAI;EACvB;CACF;;CAGA,MAAa,gBAAgB,MAA8B;EACzD,OAAO,WAAW,gBAAgB,IAAI;CACxC;CAEA,MAAc,sBAAsB,MAA2B;EAC7D,IAAI,CAAE,MAAM,KAAK,gBAAgB,IAAI,GACnC,MAAM,IAAI,eAAe,gCAAgC;CAE7D;CAEA,MAAc,iBACZ,MACA,SACA,UAC4B;EAC5B,MAAM,OAAO;GAAE,GAAI,WAAW,KAAK,wBAAwB,IAAI;GAAI,KAAK,WAAW;EAAE;EAErF,MAAM,YAAY,WAAW,YAAY,YAAY;EAErD,MAAM,QAAQ,MAAM,IAAI,SAAS,MAAM,EAAE,UAAU,CAAC;EACpD,MAAM,YAAY,IAAI,KAAK,KAAK,IAAI,IAAI,SAAS;EAEjD,IAAI,UACF,MAAM,KAAK,iBAAiB,MAAM,MAAM,OAAO,WAAW,QAAQ;OAElE,MAAM,KAAK,iBAAiB,MAAM,MAAM,OAAO,SAAS;EAG1D,OAAO;GAAE;GAAO,WAAW,UAAU,YAAY;EAAE;CACrD;;;;;;CAOA,MAAa,oBACX,MACA,SAC4B;EAC5B,MAAM,KAAK,sBAAsB,IAAI;EAErC,OAAO,KAAK,iBAAiB,MAAM,OAAO;CAC5C;CAEA,MAAc,kBACZ,MACA,YACA,eACmC;EACnC,IAAI,CAAC,WAAW,aAAa,QAAQ,GAAG;EAKxC,MAAM,YAAY,WAAW,aAAa,YAAY;EAEtD,MAAM,WAAW,YAAY,YAAY,OAAO,OAAO,EAAE;EAIzD,KAFe,iBAAkB,MAAM,gBAAgB,OAAO,MAAM,QAAQ,EAElE,CAAC,WACT,MAAM,IAAI,MAAM,gDAAgD;EAGlE,MAAM,UAAU;GACd,QAAQ,KAAK;GACb,UAAU,KAAK;GACf;GACA,KAAK,WAAW;EAClB;EAEA,MAAM,YAAY,IAAI,KAAK,KAAK,IAAI,IAAI,SAAS,CAAC,CAAC,YAAY;EAE/D,MAAM,KAAK,kBAAkB,WAAW,MAAM,WAAW,aAAa,WAAW,CAAC;EAElF,MAAM,QAAQ,MAAM,IAAI,qBAAqB,SAAS,EAAE,UAAU,CAAC;EAEnE,OAAO,KAAK,kBAAkB,MAAM,MAAM,OAAO;GAAE;GAAU;GAAW;EAAW,CAAC;CACtF;;;;;;;CAQA,MAAc,sBACZ,MACA,UACA,OACY;EAMZ,KAAI,MAFiB,gBAAgB,OAAO,MAAM,QAAQ,EAEhD,CAAC,WACT,MAAM,IAAI,MAAM,gDAAgD;EAGlE,IAAI;GACF,OAAO,MAAM,wBAAwB,UAAU,KAAK;EACtD,SAAS,OAAO;GAId,IAAI,iBAAiB,6BACnB,MAAM,IAAI,MAAM,gDAAgD;GAGlE,MAAM;EACR;CACF;;;;;CAMA,MAAa,mBACX,MACA,YACmC;EACnC,IAAI,CAAC,WAAW,aAAa,QAAQ,GAAG;EAExC,MAAM,KAAK,sBAAsB,IAAI;EAErC,IAAI,YAAY,UACd,OAAO,KAAK,sBAAsB,MAAM,WAAW,WAAW,WAC5D,KAAK,kBAAkB,MAAM,YAAY,MAAM,CACjD;EAGF,OAAO,KAAK,kBAAkB,MAAM,UAAU;CAChD;CAEA,MAAc,eACZ,MACA,YACA,eACoB;EACpB,MAAM,iBAAiB,WAAW,aAAa,QAAQ;EACvD,MAAM,WAAW,YAAY,YAAY,OAAO,OAAO,EAAE;EAEzD,MAAM,SAAS,iBACV,iBAAkB,MAAM,gBAAgB,OAAO,MAAM,QAAQ,IAC9D;EAEJ,IAAI,QACF;OAAI,OAAO,WACT,MAAM,IAAI,MAAM,gDAAgD;EAClE;EAGF,MAAM,cAAc,MAAM,KAAK,iBAC7B,MACA,YAAY,SACZ,iBAAiB,WAAW,MAC9B;EACA,MAAM,eAAe,MAAM,KAAK,kBAC9B,MACA,iBAAiB;GAAE,GAAG;GAAY;EAAS,IAAI,YAC/C,MACF;EAEA,MAAM,YAAuB;GAC3B;GACA,cAAc,eACV;IACE,OAAO,aAAa,IAAI,OAAO;IAC/B,WAAW,aAAa,IAAI,YAAY;GAC1C,IACA;EACN;EAEA,gCAAgC,WAAW,KAAK,iBAAiB,MAAM,SAAS,CAAC;EAEjF,IAAI,cACF,gCACE,WAAW,KAAK,mBAAmB,MAAM,cAAc,UAAU,CACnE;EAGF,OAAO;CACT;;;;CAKA,MAAa,gBAAgB,MAAY,YAA6C;EACpF,MAAM,KAAK,sBAAsB,IAAI;EAErC,IAAI,WAAW,aAAa,QAAQ,KAAK,YAAY,UACnD,OAAO,KAAK,sBAAsB,MAAM,WAAW,WAAW,WAC5D,KAAK,eAAe,MAAM,YAAY,MAAM,CAC9C;EAGF,OAAO,KAAK,eAAe,MAAM,UAAU;CAC7C;;;;;;CAOA,MAAa,cACX,oBACA,YAC2B;EAC3B,IAAI;EAMJ,IAAI;GACF,UAAU,MAAM,IAAI,mBAIjB,kBAAkB;EACvB,SAAS,OAAO;GACd,IAAI,yBAAyB,KAAK,GAAG,OAAO;GAE5C,MAAM;EACR;EAEA,IAAI,CAAC,SAAS,OAAO;EAErB,MAAM,iBAAiB,MAAM,KAAK,kBAAkB,YAAY,kBAAkB;EAElF,IAAI,CAAC,kBAAkB,CAAC,KAAK,uBAAuB,gBAAgB,OAAO,GAAG,OAAO;EAErF,MAAM,gBAAgB,OACpB;GAAE,IAAI,QAAQ;GAAQ,UAAU,QAAQ;EAAS,GACjD,eAAe,QACjB;EACA,OAAO,wBAAwB,eAAe,UAAU,YAAY;GAElE,MAAM,eAAe,MAAM,KAAK,kBAAkB,YAAY,kBAAkB;GAChF,IAAI,CAAC,gBAAgB,CAAC,KAAK,uBAAuB,cAAc,OAAO,GAAG,OAAO;GACjF,IAAI,CAAC,aAAa,SAAS;IAEzB,IAAI,cACF,MAAM,KAAK,kBAAkB,aAAa,QAAQ;IAGpD,OAAO;GACT;GAEA,MAAM,YAAY,OAAO,IAAI,iBAAiB,QAAQ,UAAU;GAEhE,IAAI,CAAC,WACH,MAAM,IAAI,MAAM,aAAa,QAAQ,SAAS,kBAAkB;GAGlE,MAAM,OAAQ,MAAM,UAAU,KAAK,QAAQ,MAAM;GAEjD,IAAI,CAAC,MAAM,OAAO;GAElB,IAAI,CAAE,MAAM,KAAK,gBAAgB,IAAI,GAAI,OAAO;GAIhD,IAFwB,WAAW,aAAa,SAE9B,GAGhB;QAAI,CAAC,MAFa,aAAa,eAAe,GAEpC;KAER,MAAM,KAAK,kBAAkB,aAAa,QAAQ;KAElD,OAAO;IACT;UAEA,MAAM,aAAa,WAAW;GAGhC,MAAM,eAAe,MAAM,KAAK,eAAe,MAAM;IACnD,GAAG;IACH,UAAU,aAAa;GACzB,CAAC;GAED,gCACE,WAAW,KAAK,mBAAmB,MAAM,cAAc,YAAY,CACrE;GAEA,OAAO;EACT,CAAC,CAAC,CAAC,OAAO,UAAU;GAClB,IAAI,iBAAiB,6BAA6B,OAAO;GACzD,MAAM;EACR,CAAC;CACH;;;;;;CAOA,MAAa,sBACX,oBACA,kBACA,UAA8E,CAAC,GACpD;EAC3B,IAAI;EACJ,IAAI;GACF,UAAU,MAAM,IAAI,mBAIjB,kBAAkB;EACvB,SAAS,OAAO;GACd,IAAI,yBAAyB,KAAK,GAAG,OAAO;GAC5C,MAAM;EACR;EAEA,IAAI,CAAC,WAAW,QAAQ,aAAa,kBAAkB,OAAO;EAC9D,MAAM,iBAAiB,MAAM,KAAK,kBAAkB,YAAY,kBAAkB;EAClF,IAAI,CAAC,kBAAkB,CAAC,KAAK,uBAAuB,gBAAgB,OAAO,GAAG,OAAO;EAErF,MAAM,gBAAgB,OACpB;GAAE,IAAI,QAAQ;GAAQ,UAAU,QAAQ;EAAS,GACjD,eAAe,QACjB;EACA,OAAO,wBAAwB,eAAe,UAAU,YAAY;GAGlE,MAAM,WAAW,MAAM,KAAK,kBAAkB,YAAY,kBAAkB;GAC5E,IAAI,CAAC,YAAY,CAAC,KAAK,uBAAuB,UAAU,OAAO,GAAG,OAAO;GACzE,IAAI,CAAC,SAAS,SAAS;IACrB,MAAM,YAAY,MAAM,KAAK,uBAAuB,UAAU,SAAS,QAAQ,SAAS;IACxF,IAAI,WAAW,OAAO;IACtB,IACE,KAAK,uBAAuB,UAAU,QAAQ,SAAS,KACvD,KAAK,aAAa,QAAQ,GAE1B,OAAO;IAET,MAAM,KAAK,kBAAkB,SAAS,QAAQ;IAC9C,OAAO;GACT;GAGA,IACE,QAAQ,aAAa,UACpB,MAAM,KAAK,oBAAoB,SAAS,UAAU,QAAQ,QAAQ,GACnE;IACA,MAAM,KAAK,kBAAkB,SAAS,QAAQ;IAC9C,OAAO;GACT;GAEA,MAAM,YAAY,OAAO,IAAI,iBAAiB,QAAQ,UAAU;GAChE,IAAI,CAAC,WAAW,MAAM,IAAI,MAAM,aAAa,QAAQ,SAAS,kBAAkB;GAChF,MAAM,OAAQ,MAAM,UAAU,KAAK,QAAQ,MAAM;GACjD,IAAI,CAAC,QAAQ,CAAE,MAAM,KAAK,gBAAgB,IAAI,GAAI,OAAO;GAEzD,IAAI,CAAE,MAAM,SAAS,eAAe,GAAI;IAGtC,MAAM,eAAe,MAAM,KAAK,kBAAkB,YAAY,kBAAkB;IAChF,IAAI,CAAC,cAAc,OAAO;IAC1B,MAAM,YAAY,MAAM,KAAK,uBAC3B,cACA,SACA,QAAQ,SACV;IACA,IAAI,WAAW,OAAO;IACtB,IACE,KAAK,uBAAuB,cAAc,QAAQ,SAAS,KAC3D,KAAK,aAAa,YAAY,GAE9B,OAAO;IACT,MAAM,KAAK,kBAAkB,SAAS,QAAQ;IAC9C,OAAO;GACT;GAEA,MAAM,OAAO,MAAM,KAAK,eAAe,MAAM;IAC3C,GAAG,QAAQ;IACX,UAAU,SAAS;GACrB,CAAC;GACD,MAAM,kBAAkB,MAAM,KAAK,iBAAiB,YAAY,KAAK,YAAY,KAAK;GACtF,MAAM,mBAAmB,KAAK,eAC1B,MAAM,KAAK,kBAAkB,YAAY,KAAK,aAAa,KAAK,IAChE;GACJ,IAAI,CAAC,mBAAmB,CAAC,kBACvB,MAAM,IAAI,MAAM,6DAA6D;GAe/E,IAAI,MATiB,KAAK,kBAAkB,OAC1C,EAAE,IAAI,SAAS,GAAG,GAClB,EACE,MAAM;IACJ,2BAA2B,gBAAgB;IAC3C,4BAA4B,iBAAiB;GAC/C,EACF,CACF,MACe,GAAG,MAAM,IAAI,MAAM,qDAAqD;GACvF,gCAAgC,WAAW,KAAK,mBAAmB,MAAM,MAAM,QAAQ,CAAC;GACxF,OAAO;EACT,CAAC,CAAC,CAAC,OAAO,UAAU;GAClB,IAAI,iBAAiB,6BAA6B,OAAO;GACzD,MAAM;EACR,CAAC;CACH;;;;;;CAOA,MAAc,oBAAoB,UAAkB,UAAoC;EAEtF,IAAI,WAAmB,MADF,gBAAgB,eAAe,QAAQ,EAC/B,EAAE,IAAI,YAAY;EAE/C,IAAI,CAAC,SAOH,WAAU,MANW,KAAK,kBACvB,MAAM,CAAC,CACP,MAAM,EAAE,WAAW,SAAS,CAAC,CAAC,CAC9B,QAAQ,cAAc,KAAK,CAAC,CAC5B,MAAM,EAEO,EAAE,IAAI,YAAY;EAGpC,MAAM,YAAY,UAAU,IAAI,KAAK,OAAiC,CAAC,CAAC,QAAQ,IAAI;EAGpF,IAAI,CAAC,OAAO,SAAS,SAAS,GAAG,OAAO;EAExC,OAAO,KAAK,IAAI,IAAI,YAAY;CAClC;CAEA,AAAQ,mBAAmB,WAAuC;EAChE,OAAO,OAAO,cAAc,YAAY,OAAO,SAAS,SAAS,IAC7D,KAAK,IAAI,GAAG,KAAK,IAAI,KAAQ,SAAS,CAAC,IACvC;CACN;CAEA,AAAQ,uBACN,OACA,SACS;EACT,OACE,MAAM,aAAa,QAAQ,YAC3B,MAAM,IAAI,SAAS,MAAM,QAAQ,UACjC,MAAM,IAAI,WAAW,MAAM,QAAQ;CAEvC;CAEA,AAAQ,aAAa,UAAiC;EACpD,OAAO,QACL,SAAS,IAAI,2BAA2B,KAAK,SAAS,IAAI,4BAA4B,CACxF;CACF;CAEA,AAAQ,uBAAuB,UAAwB,WAAwC;EAC7F,MAAM,YAAY,SAAS,IAAsB,YAAY;EAC7D,MAAM,SAAS,KAAK,mBAAmB,SAAS;EAChD,MAAM,UAAU,YAAY,KAAK,IAAI,IAAI,IAAI,KAAK,SAAS,CAAC,CAAC,QAAQ,IAAI;EACzE,OAAO,SAAS,KAAK,WAAW,KAAK,WAAW;CAClD;CAEA,MAAc,uBACZ,UACA,SACA,WAC2B;EAC3B,IAAI,CAAC,KAAK,uBAAuB,UAAU,SAAS,GAAG,OAAO;EAC9D,MAAM,WAAW,SAAS,IAAwB,2BAA2B;EAC7E,MAAM,YAAY,SAAS,IAAwB,4BAA4B;EAC/E,IAAI,CAAC,YAAY,CAAC,WAAW,OAAO;EACpC,MAAM,CAAC,QAAQ,WAAW,MAAM,QAAQ,IAAI,CAC1C,KAAK,iBAAiB,KAAK,QAAQ,GACnC,KAAK,kBAAkB,KAAK,SAAS,CACvC,CAAC;EACD,IAAI,CAAC,UAAU,OAAO,aAAa,CAAC,SAAS,SAAS,OAAO;EAC7D,IACE,OAAO,IAAI,WAAW,MAAM,QAAQ,YACpC,QAAQ,IAAI,WAAW,MAAM,QAAQ,YACrC,OAAO,IAAI,SAAS,MAAM,QAAQ,UAClC,QAAQ,IAAI,SAAS,MAAM,QAAQ,UACnC,OAAO,IAAI,WAAW,MAAM,QAAQ,YACpC,QAAQ,IAAI,WAAW,MAAM,QAAQ,UAErC,OAAO;EACT,MAAM,eAAe,IAAI,KAAK,OAAO,IAAI,YAAY,CAAC,CAAC,CAAC,QAAQ;EAChE,IAAI,CAAC,OAAO,SAAS,YAAY,KAAK,gBAAgB,KAAK,IAAI,GAAG,OAAO;EACzE,OAAO;GACL,aAAa;IAAE,OAAO,OAAO,IAAI,OAAO;IAAG,WAAW,OAAO,IAAI,YAAY;GAAE;GAC/E,cAAc;IAAE,OAAO,QAAQ,IAAI,OAAO;IAAG,WAAW,QAAQ,IAAI,YAAY;GAAE;EACpF;CACF;;;;CAKA,MAAa,eAAe,eAAuB,gBAA0C;EAC3F,OAAO,eAAe,eAAe,cAAc;CACrD;;;;CAKA,MAAa,aAAa,UAAmC;EAC3D,OAAO,aAAa,QAAQ;CAC9B;;;;;CAMA,MAAa,aACX,OACA,MACmB;EACnB,MAAM,EAAE,UAAU,GAAG,cAAc;EAEnC,WAAW,KAAK,iBAAiB,SAAS;EAE1C,MAAM,OAAQ,MAAM,MAAM,MAAM,SAAS;EAEzC,IAAI,CAAC,MAAM;GACT,WAAW,KAAK,gBAAgB,WAAW,gBAAgB;GAE3D,OAAO;EACT;EAEA,IAAI,CAAE,MAAM,KAAK,eAAe,UAAU,KAAK,OAAO,UAAU,CAAE,GAAI;GACpE,WAAW,KAAK,gBAAgB,WAAW,kBAAkB;GAE7D,OAAO;EACT;EAEA,IAAI,CAAE,MAAM,KAAK,gBAAgB,IAAI,GAAI;GACvC,WAAW,KAAK,gBAAgB,WAAW,4BAA4B;GAEvE,OAAO;EACT;EAEA,OAAO;CACT;;;;;CAMA,MAAa,MACX,OACA,aACA,YACgC;EAChC,MAAM,OAAO,MAAM,KAAK,aAAa,OAAO,WAAW;EAEvD,IAAI,CAAC,MACH,OAAO;EAGT,OAAO,KAAK,cAAc,MAAM,UAAU;CAC5C;;;;;;;;;;CAWA,MAAa,cACX,MACA,YACyB;EACzB,MAAM,KAAK,sBAAsB,IAAI;EAErC,OAAO,KAAK,cAAc,MAAM,UAAU;CAC5C;;;;;;;;CASA,MAAa,wBACX,SACA,UACA,OACA,aACA,YACgC;EAChC,wBAAwB,OAAO;EAE/B,MAAM,SAAS,MAAM,KAAK,MAAM,OAAO,aAAa,UAAU;EAE9D,IAAI,CAAC,QAAQ,OAAO;EAEpB,IAAI,OAAO,OAAO,cAChB,KAAK,kBACH,UACA;GACE,aAAa,OAAO,OAAO;GAC3B,cAAc,OAAO,OAAO;EAC9B,GACA,OACF;OAEA,KAAK,cAAc,UAAU,OAAO,OAAO,aAAa,EACtD,QAAQ,KAAK,gBAAgB,2BAA2B,OAAO,EACjE,CAAC;EAGH,OAAO;CACT;;CAGA,MAAc,cACZ,MACA,YACyB;EACzB,IAAI,CAAC,WAAW,aAAa,QAAQ,GAGnC,OAAO;GAAE;GAAM,QAAQ,EAAE,mBAFC,KAAK,iBAAiB,MAAM,YAAY,OAAO,EAEpC;EAAE;EAGzC,MAAM,SAAS,MAAM,KAAK,eAAe,MAAM,UAAU;EAEzD,WAAW,KAAK,iBAAiB,MAAM,QAAQ,UAAU;EAEzD,OAAO;GAAE;GAAM;EAAO;CACxB;;;;;;;;;;;CAYA,MAAa,OAAO,MAAY,aAAsB,cAAsC;EAC1F,IAAI,aACF,MAAM,KAAK,kBAAkB,MAAM,WAAW;EAGhD,IAAI,cAAc;GAChB,MAAM,QAAQ,MAAM,KAAK,kBAAkB,YAAY,MAAM,YAAY;GAEzE,IAAI,OAAO;IACT,MAAM,KAAK,kBAAkB,MAAM,QAAQ;IAC3C,WAAW,KAAK,qBAAqB,MAAM,KAAK;GAClD;EACF,OAAO;GAGL,IAFiB,WAAW,aAAa,mBAE9B,MAAM,SACf,MAAM,IAAI,MAAM,mCAAmC;GAGrD,MAAM,KAAK,gBAAgB,IAAI;GAC/B,WAAW,KAAK,mBAAmB,IAAI;EACzC;EAEA,WAAW,KAAK,UAAU,IAAI;CAChC;;;;CAKA,MAAa,kBAAkB,MAAY,OAA8B;EACvE,MAAM,KAAK,iBAAiB,cAAc,MAAM,KAAK;CACvD;;;;CAKA,MAAa,sBAAsB,MAA2B;EAC5D,MAAM,KAAK,iBAAiB,iBAAiB,IAAI;CACnD;;;;CAKA,MAAa,mBAAmB,MAAY,OAA8B;EACxE,MAAM,KAAK,kBAAkB,cAAc,MAAM,KAAK;CACxD;;;;;;CAOA,MAAa,gBAAgB,MAA2B;EAItD,MAAM,eAAe,MAAM,KAAK,kBAAkB,gBAAgB,IAAI;EAEtE,KAAK,MAAM,SAAS,cAClB,MAAM,gBAAgB,OAAO,MAAM,MAAM,QAAQ;EAGnD,MAAM,WAAW,MAAM,gBAAgB,UAAU,IAAI;EAErD,KAAK,MAAM,UAAU,UACnB,MAAM,KAAK,kBAAkB,OAAO,IAAY,WAAW,CAAC;EAK9D,MAAM,eAAe,MAAM,KAAK,kBAAkB,aAAa,IAAI;EAEnE,KAAK,MAAM,SAAS,cAClB,WAAW,KAAK,iBAAiB,MAAM,KAAK;EAE9C,MAAM,KAAK,sBAAsB,IAAI;EAErC,WAAW,KAAK,cAAc,IAAI;CACpC;;;;CAKA,MAAa,kBAAkB,UAAiC;EAC9D,IAAI,SAAS,MAAM,gBAAgB,eAAe,QAAQ;EAE1D,IAAI,CAAC,QAAQ;GACX,MAAM,cAAc,MAAM,KAAK,kBAAkB,aAAa,QAAQ;GAEtE,IAAI,CAAC,aAAa;GAElB,SAAS,MAAM,gBAAgB,OAC7B;IACE,IAAI,YAAY,IAAI,SAAS;IAC7B,UAAU,YAAY,IAAY,WAAW;GAC/C,GACA,QACF;EACF;EAEA,MAAM,OAAO;GACX,IAAI,OAAO,IAAI,SAAS;GACxB,UAAU,OAAO,IAAY,WAAW;EAC1C;EAEA,IAAI,OAAO,WAAW;GAGpB,MAAM,gBAAgB,MAAM,KAAK,kBAAkB,aAAa,QAAQ;GACxE,MAAM,KAAK,iBAAiB,6BAA6B,MAAM,QAAQ;GACvE,gCACE,WAAW,KAAK,uBAAuB,UAAU,aAAa,CAChE;GAEA;EACF;EAEA,IAAI;EACJ,IAAI;GACF,gBAAgB,MAAM,wBAAwB,UAAU,OAAO,kBAAkB;IAC/E,MAAM,aAAa;KACjB,IAAI,cAAc,IAAI,SAAS;KAC/B,UAAU,cAAc,IAAY,WAAW;IACjD;IACA,MAAM,WAAW,cAAc,IAAY,UAAU;IAGrD,IAAI,MAFkB,gBAAgB,OAAO,UAAU,QAAQ,MAE/C,GACd,MAAM,IAAI,MAAM,kDAAkD;IAGpE,MAAM,eAAe,MAAM,KAAK,kBAAkB,aAAa,QAAQ;IACvE,MAAM,KAAK,iBAAiB,6BAA6B,YAAY,QAAQ;IAE7E,OAAO;GACT,CAAC;EACH,SAAS,OAAO;GACd,IAAI,EAAE,iBAAiB,8BAA8B,MAAM;GAI3D,gBAAgB,MAAM,KAAK,kBAAkB,aAAa,QAAQ;GAClE,MAAM,KAAK,iBAAiB,6BAA6B,MAAM,QAAQ;EACzE;EAEA,gCACE,WAAW,KAAK,uBAAuB,UAAU,aAAa,CAChE;CACF;;;;;;;CAQA,MAAa,uBAAwC;EACnD,MAAM,gBAAgB,MAAM,KAAK,kBAAkB,aAAa;EAEhE,KAAK,MAAM,SAAS,eAClB,WAAW,KAAK,iBAAiB,KAAK;EAGxC,MAAM,KAAK,iBAAiB,aAAa;EACzC,MAAM,KAAK,kBAAkB,WAAW;EAExC,WAAW,KAAK,qBAAqB,cAAc,MAAM;EAEzD,OAAO,cAAc;CACvB;;;;;;;;;;;CAYA,MAAa,0BAGV;EACD,OAAO;GACL,cAAc,MAAM,KAAK,iBAAiB,kBAAkB;GAC5D,eAAe,MAAM,KAAK,kBAAkB,kBAAkB;EAChE;CACF;;;;;;;;;CAUA,MAAa,2BAGV;EACD,MAAM,eAAe,MAAM,KAAK,iBAAiB,mBAAmB;EACpE,MAAM,gBAAgB,MAAM,KAAK,kBAAkB,mBAAmB;EAKtE,KAAK,MAAM,SAAS,eAClB,WAAW,KAAK,iBAAiB,KAAK;EAGxC,OAAO;GAAE,cAAc,aAAa;GAAQ,eAAe,cAAc;EAAO;CAClF;;;;CAKA,MAAa,kBAAkB,MAAqC;EAClE,OAAO,KAAK,kBAAkB,UAAU,IAAI;CAC9C;;;;;;CAOA,AAAQ,sBAAsB,OAAuD;EACnF,IAAI,OAAO,UAAU,UAAU,OAAO;EAEtC,MAAM,YAAY,IAAI,KAAK,MAAM,SAAS,CAAC,CAAC,QAAQ;EAEpD,IAAI,OAAO,MAAM,SAAS,GAAG,OAAO;EAEpC,OAAO,KAAK,IAAI,GAAG,KAAK,OAAO,YAAY,KAAK,IAAI,KAAK,GAAI,CAAC;CAChE;CAEA,AAAQ,gBAAgB,QAAgB,SAAuC;EAC7E,MAAM,WAAW,WAAW,OAAO,aAAa;EAEhD,IAAI,CAAC,UACH;EAGF,IAAI,CAAC,SACH,MAAM,IAAI,iCAAiC,MAAM;EAGnD,OAAO,SAAS,OAAO;CACzB;;;;;;;;;;;;;;;;;;;;;;CAuBA,AAAO,cACL,UACA,OACA,UAAgC,CAAC,GAC3B;EACN,MAAM,OAAO,QAAQ,QAAQ,WAAW,OAAO,KAAK;EACpD,MAAM,OAAO,QAAQ,QAAQ,WAAW,OAAO,KAAK;EACpD,MAAM,aAAa,OAAO,UAAU,WAAW,QAAQ,MAAM;EAC7D,MAAM,SAAS,QAAQ,UAAU,KAAK,sBAAsB,KAAK;EAEjE,SAAS,OAAO,MAAM,YAAY;GAChC,KAAK;GACL;GACA,GAAI,QAAQ,WAAW,SAAY,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC;GACjE,GAAI,WAAW,SAAY,EAAE,OAAO,IAAI,CAAC;EAC3C,CAAC;CACH;;;;;;;;;;;CAYA,AAAO,gBACL,UACA,UAAkC,CAAC,GACnC,SACM;EACN,MAAM,OAAO,QAAQ,QAAQ,WAAW,OAAO,KAAK;EACpD,MAAM,OAAO,QAAQ,QAAQ,WAAW,OAAO,KAAK;EACpD,MAAM,SAAS,KAAK,gBAAgB,mBAAmB,OAAO;EAE9D,IAAI,WAAW,QACb,SAAS,YAAY,MAAM;GAAE;GAAM;EAAO,CAAC;EAG7C,SAAS,YAAY,MAAM,EAAE,KAAK,CAAC;CACrC;;;;;;;;;;;;CAaA,AAAO,kBACL,UACA,QACA,SACM;EACN,MAAM,SAAS,KAAK,gBAAgB,qBAAqB,OAAO;EAChE,MAAM,UAAU,CACd,CAAC,WAAW,OAAO,KAAK,GAAG,OAAO,WAAW,GAC7C,CAAC,WAAW,OAAO,YAAY,GAAG,OAAO,YAAY,CACvD;EAEA,KAAK,MAAM,CAAC,MAAM,UAAU,SAAS;GACnC,MAAM,SAAS,KAAK,sBAAsB,KAAK;GAE/C,SAAS,OAAO,MAAM,MAAM,OAAO;IACjC,KAAK;IACL,UAAU;IACV,UAAU;IACV,MAAM;IACN,GAAI,WAAW,SAAY,EAAE,OAAO,IAAI,CAAC;IACzC,GAAI,WAAW,SAAY,EAAE,OAAO,IAAI,CAAC;GAC3C,CAAC;EACH;CACF;;;;;CAMA,AAAO,oBACL,UACA,SACM;EACN,MAAM,SAAS,KAAK,gBAAgB,uBAAuB,OAAO;EAElE,KAAK,MAAM,QAAQ,CAAC,WAAW,OAAO,KAAK,GAAG,WAAW,OAAO,YAAY,CAAC,GAAG;GAC9E,IAAI,WAAW,QACb,SAAS,YAAY,MAAM;IAAE,MAAM;IAAK;GAAO,CAAC;GAGlD,SAAS,YAAY,MAAM,EAAE,MAAM,IAAI,CAAC;EAC1C;CACF;AACF;AAEA,MAAa,cAAc,IAAI,YAAY"}
1
+ {"version":3,"file":"auth.service.mjs","names":[],"sources":["../../../../../../../../packages/auth/src/services/auth.service.ts"],"sourcesContent":["import { Random } from \"@mongez/reinforcements\";\nimport { randomUUID } from \"node:crypto\";\nimport type { ChildModel } from \"@warlock.js/cascade\";\nimport {\n config,\n ForbiddenError,\n hashPassword,\n type Request,\n type Response,\n verifyPassword,\n} from \"@warlock.js/core\";\nimport type {\n AccessTokenOutput,\n AuthCredentials,\n ClearAuthCookieOptions,\n DeviceInfo,\n LoginResult,\n SetAuthCookieOptions,\n TokenPair,\n} from \"../contracts/types\";\nimport { assertCsrfOriginAllowed } from \"../middleware/csrf-origin-check\";\nimport { AccessToken } from \"../models/access-token\";\nimport type { Auth } from \"../models/auth.model\";\nimport { AuthTokenFamily } from \"../models/auth-token-family\";\nimport { OneTimeToken } from \"../models/one-time-token\";\nimport { RefreshToken } from \"../models/refresh-token\";\nimport { authConfig } from \"./auth-config\";\nimport { authEvents } from \"./auth-events\";\nimport { isInvalidCredentialError, jwt } from \"./jwt\";\nimport {\n afterTokenFamilyOperation,\n runTokenFamilyOperation,\n TokenFamilyUnavailableError,\n} from \"./token-family-operation\";\n\n/** Thrown when a request-scoped cookie Domain cannot be resolved safely. */\nexport class CookieDomainRequestRequiredError extends Error {\n public constructor(method: string) {\n super(\n `${method} requires the request when auth.cookie.cookieDomain is configured because the cookie Domain must be resolved for that request.`,\n );\n this.name = \"CookieDomainRequestRequiredError\";\n }\n}\n\nclass AuthService {\n /**\n * Resolve the active access-token model — the package default, or a subclass\n * an app registered under `config.auth.accessToken.model` (e.g. to add a\n * tenant column). The service never references the concrete class directly so\n * an override is a pure config change.\n */\n private get accessTokenModel(): typeof AccessToken {\n return config.key(\"auth.accessToken.model\", AccessToken);\n }\n\n /**\n * Resolve the active refresh-token model (default or registered override).\n */\n private get refreshTokenModel(): typeof RefreshToken {\n return config.key(\"auth.refreshToken.model\", RefreshToken);\n }\n\n /**\n * Resolve the active one-time-token model (default or registered override).\n */\n private get oneTimeTokenModel(): typeof OneTimeToken {\n return config.key(\"auth.oneTimeToken.model\", OneTimeToken);\n }\n\n /**\n * Build the default access-token JWT payload from a user.\n */\n public buildAccessTokenPayload(user: Auth): Record<string, unknown> {\n return {\n id: user.id,\n userType: user.userType,\n created_at: Date.now(),\n };\n }\n\n /** One policy seam keeps account-state checks identical across all auth paths. */\n public async canAuthenticate(user: Auth): Promise<boolean> {\n return authConfig.canAuthenticate(user);\n }\n\n private async assertCanAuthenticate(user: Auth): Promise<void> {\n if (!(await this.canAuthenticate(user))) {\n throw new ForbiddenError(\"This user cannot authenticate.\");\n }\n }\n\n private async issueAccessToken(\n user: Auth,\n payload?: Record<string, unknown>,\n familyId?: string,\n ): Promise<AccessTokenOutput> {\n const data = { ...(payload || this.buildAccessTokenPayload(user)), jti: randomUUID() };\n // Validate before signing so an unusable lifetime cannot mint an immortal token.\n const expiresIn = authConfig.accessToken.expiresInMs();\n\n const token = await jwt.generate(data, { expiresIn });\n const expiresAt = new Date(Date.now() + expiresIn);\n\n if (familyId) {\n await this.accessTokenModel.issue(user, token, expiresAt, familyId);\n } else {\n await this.accessTokenModel.issue(user, token, expiresAt);\n }\n\n return { token, expiresAt: expiresAt.toISOString() };\n }\n\n /**\n * Sign + persist an access token for the user and return the token with its\n * expiry. The expiry is computed locally from `expiresIn` rather than by\n * re-verifying the token we just signed.\n */\n public async generateAccessToken(\n user: Auth,\n payload?: Record<string, unknown>,\n ): Promise<AccessTokenOutput> {\n await this.assertCanAuthenticate(user);\n\n return this.issueAccessToken(user, payload);\n }\n\n private async issueRefreshToken(\n user: Auth,\n deviceInfo?: DeviceInfo,\n ensuredFamily?: AuthTokenFamily,\n ): Promise<RefreshToken | undefined> {\n if (!authConfig.refreshToken.enabled()) return;\n\n // Validate the lifetime first — before the per-user cap is enforced, before\n // anything is signed — so a bad value can never revoke a user's oldest\n // session on its way to failing.\n const expiresIn = authConfig.refreshToken.expiresInMs();\n\n const familyId = deviceInfo?.familyId || Random.string(32);\n\n const family = ensuredFamily ?? (await AuthTokenFamily.ensure(user, familyId));\n\n if (family.isRevoked) {\n throw new Error(\"Cannot issue tokens for a revoked token family\");\n }\n\n const payload = {\n userId: user.id,\n userType: user.userType,\n familyId,\n jti: randomUUID(),\n };\n\n const expiresAt = new Date(Date.now() + expiresIn).toISOString();\n\n await this.refreshTokenModel.enforceMax(user, authConfig.refreshToken.maxPerUser());\n\n const token = await jwt.generateRefreshToken(payload, { expiresIn });\n\n return this.refreshTokenModel.issue(user, token, { familyId, expiresAt, deviceInfo });\n }\n\n /**\n * Keep issuance into an explicitly reused family in the same durable\n * transition boundary as rotation and revocation. A new, caller-unspecified\n * family has no concurrent session to protect and retains the lightweight\n * issuance path.\n */\n private async issueInSuppliedFamily<T>(\n user: Auth,\n familyId: string,\n issue: (family: AuthTokenFamily) => Promise<T>,\n ): Promise<T> {\n // Preserve the established error for a family that was already revoked\n // before issuance started. The coordinator covers a revoke that races\n // after this observation.\n const family = await AuthTokenFamily.ensure(user, familyId);\n\n if (family.isRevoked) {\n throw new Error(\"Cannot issue tokens for a revoked token family\");\n }\n\n try {\n return await runTokenFamilyOperation(familyId, issue);\n } catch (error) {\n // Do not expose a timing-dependent error shape to callers: a family\n // revoked between ensure() and the transaction is the same unusable\n // family condition as one found above.\n if (error instanceof TokenFamilyUnavailableError) {\n throw new Error(\"Cannot issue tokens for a revoked token family\");\n }\n\n throw error;\n }\n }\n\n /**\n * Sign + persist a refresh token for the user (enforcing the per-user cap\n * first). Resolves to `undefined` when refresh tokens are disabled in config.\n */\n public async createRefreshToken(\n user: Auth,\n deviceInfo?: DeviceInfo,\n ): Promise<RefreshToken | undefined> {\n if (!authConfig.refreshToken.enabled()) return;\n\n await this.assertCanAuthenticate(user);\n\n if (deviceInfo?.familyId) {\n return this.issueInSuppliedFamily(user, deviceInfo.familyId, (family) =>\n this.issueRefreshToken(user, deviceInfo, family),\n );\n }\n\n return this.issueRefreshToken(user, deviceInfo);\n }\n\n private async issueTokenPair(\n user: Auth,\n deviceInfo?: DeviceInfo,\n ensuredFamily?: AuthTokenFamily,\n ): Promise<TokenPair> {\n const refreshEnabled = authConfig.refreshToken.enabled();\n const familyId = deviceInfo?.familyId || Random.string(32);\n\n const family = refreshEnabled\n ? (ensuredFamily ?? (await AuthTokenFamily.ensure(user, familyId)))\n : undefined;\n\n if (family) {\n if (family.isRevoked) {\n throw new Error(\"Cannot issue tokens for a revoked token family\");\n }\n }\n\n const accessToken = await this.issueAccessToken(\n user,\n deviceInfo?.payload,\n refreshEnabled ? familyId : undefined,\n );\n const refreshToken = await this.issueRefreshToken(\n user,\n refreshEnabled ? { ...deviceInfo, familyId } : deviceInfo,\n family,\n );\n\n const tokenPair: TokenPair = {\n accessToken,\n refreshToken: refreshToken\n ? {\n token: refreshToken.get(\"token\"),\n expiresAt: refreshToken.get(\"expires_at\"),\n }\n : undefined,\n };\n\n afterTokenFamilyOperation(() => authEvents.emit(\"token.created\", user, tokenPair));\n\n if (refreshToken) {\n afterTokenFamilyOperation(() =>\n authEvents.emit(\"session.created\", user, refreshToken, deviceInfo),\n );\n }\n\n return tokenPair;\n }\n\n /**\n * Issue both an access and a refresh token, emitting the creation events.\n */\n public async createTokenPair(user: Auth, deviceInfo?: DeviceInfo): Promise<TokenPair> {\n await this.assertCanAuthenticate(user);\n\n if (authConfig.refreshToken.enabled() && deviceInfo?.familyId) {\n return this.issueInSuppliedFamily(user, deviceInfo.familyId, (family) =>\n this.issueTokenPair(user, deviceInfo, family),\n );\n }\n\n return this.issueTokenPair(user, deviceInfo);\n }\n\n /**\n * Exchange a refresh token for a new token pair, with rotation + replay\n * detection. A concurrent reuse of the same token loses the atomic revoke and\n * is treated as a breach — the whole family is revoked and the request fails.\n */\n public async refreshTokens(\n refreshTokenString: string,\n deviceInfo?: DeviceInfo,\n ): Promise<TokenPair | null> {\n let decoded: {\n userId: number;\n userType: string;\n familyId: string;\n } | null;\n\n try {\n decoded = await jwt.verifyRefreshToken<{\n userId: number;\n userType: string;\n familyId: string;\n }>(refreshTokenString);\n } catch (error) {\n if (isInvalidCredentialError(error)) return null;\n\n throw error;\n }\n\n if (!decoded) return null;\n\n const presentedToken = await this.refreshTokenModel.findByToken(refreshTokenString);\n\n if (!presentedToken || !this.refreshIdentityMatches(presentedToken, decoded)) return null;\n\n await AuthTokenFamily.ensure(\n { id: decoded.userId, userType: decoded.userType } as Auth,\n presentedToken.familyId,\n );\n return runTokenFamilyOperation(presentedToken.familyId, async () => {\n // Each serializable attempt must read the current row in its own snapshot.\n const refreshToken = await this.refreshTokenModel.findByToken(refreshTokenString);\n if (!refreshToken || !this.refreshIdentityMatches(refreshToken, decoded)) return null;\n if (!refreshToken.isValid) {\n // An already-invalid token may be a replay of a rotated credential.\n if (refreshToken) {\n await this.revokeTokenFamily(refreshToken.familyId);\n }\n\n return null;\n }\n\n const UserModel = config.key(`auth.userType.${decoded.userType}`);\n\n if (!UserModel) {\n throw new Error(`User type ${decoded.userType} is unknown type.`);\n }\n\n const user = (await UserModel.find(decoded.userId)) as Auth | null;\n\n if (!user) return null;\n\n if (!(await this.canAuthenticate(user))) return null;\n\n const rotationEnabled = authConfig.refreshToken.rotation();\n\n if (rotationEnabled) {\n const won = await refreshToken.revokeIfActive();\n\n if (!won) {\n // A concurrent request already rotated this token.\n await this.revokeTokenFamily(refreshToken.familyId);\n\n return null;\n }\n } else {\n await refreshToken.markAsUsed();\n }\n\n const newTokenPair = await this.issueTokenPair(user, {\n ...deviceInfo,\n familyId: refreshToken.familyId,\n });\n\n afterTokenFamilyOperation(() =>\n authEvents.emit(\"token.refreshed\", user, newTokenPair, refreshToken),\n );\n\n return newTokenPair;\n }).catch((error) => {\n if (error instanceof TokenFamilyUnavailableError) return null;\n throw error;\n });\n }\n\n /**\n * Opt-in browser-session renewal. Unlike {@link refreshTokens}, this permits\n * a short duplicate presentation to receive the exact committed successor\n * pair. It is deliberately not used by the legacy/public refresh API.\n */\n public async renewAutomaticSession(\n refreshTokenString: string,\n expectedUserType: string,\n options: { overlapMs?: number; deviceInfo?: DeviceInfo; maxAgeMs?: number } = {},\n ): Promise<TokenPair | null> {\n let decoded: { userId: number; userType: string; familyId: string } | null;\n try {\n decoded = await jwt.verifyRefreshToken<{\n userId: number;\n userType: string;\n familyId: string;\n }>(refreshTokenString);\n } catch (error) {\n if (isInvalidCredentialError(error)) return null;\n throw error;\n }\n\n if (!decoded || decoded.userType !== expectedUserType) return null;\n const presentedToken = await this.refreshTokenModel.findByToken(refreshTokenString);\n if (!presentedToken || !this.refreshIdentityMatches(presentedToken, decoded)) return null;\n\n await AuthTokenFamily.ensure(\n { id: decoded.userId, userType: decoded.userType } as Auth,\n presentedToken.familyId,\n );\n return runTokenFamilyOperation(presentedToken.familyId, async () => {\n // A retry can follow another process's committed rotation. Never reuse\n // a model captured before the transaction or before a failed attempt.\n const oldToken = await this.refreshTokenModel.findByToken(refreshTokenString);\n if (!oldToken || !this.refreshIdentityMatches(oldToken, decoded)) return null;\n if (!oldToken.isValid) {\n const successor = await this.readAutomaticSuccessor(oldToken, decoded, options.overlapMs);\n if (successor) return successor;\n if (\n this.isAutomaticOverlapOpen(oldToken, options.overlapMs) &&\n this.hasSuccessor(oldToken)\n ) {\n return null;\n }\n await this.revokeTokenFamily(oldToken.familyId);\n return null;\n }\n\n // Rotation never extends a family past `maxAgeMs` from its first issue.\n if (\n options.maxAgeMs !== undefined &&\n (await this.familyExceedsMaxAge(oldToken.familyId, options.maxAgeMs))\n ) {\n await this.revokeTokenFamily(oldToken.familyId);\n return null;\n }\n\n const UserModel = config.key(`auth.userType.${decoded.userType}`);\n if (!UserModel) throw new Error(`User type ${decoded.userType} is unknown type.`);\n const user = (await UserModel.find(decoded.userId)) as Auth | null;\n if (!user || !(await this.canAuthenticate(user))) return null;\n\n if (!(await oldToken.revokeIfActive())) {\n // A newer successor is never substituted here. A stale automatic\n // response must fail quietly rather than revoke a legitimate family.\n const currentToken = await this.refreshTokenModel.findByToken(refreshTokenString);\n if (!currentToken) return null;\n const successor = await this.readAutomaticSuccessor(\n currentToken,\n decoded,\n options.overlapMs,\n );\n if (successor) return successor;\n if (\n this.isAutomaticOverlapOpen(currentToken, options.overlapMs) &&\n this.hasSuccessor(currentToken)\n )\n return null;\n await this.revokeTokenFamily(oldToken.familyId);\n return null;\n }\n\n const pair = await this.issueTokenPair(user, {\n ...options.deviceInfo,\n familyId: oldToken.familyId,\n });\n const successorAccess = await this.accessTokenModel.findByToken(pair.accessToken.token);\n const successorRefresh = pair.refreshToken\n ? await this.refreshTokenModel.findByToken(pair.refreshToken.token)\n : null;\n if (!successorAccess || !successorRefresh) {\n throw new Error(\"Automatic renewal did not persist a complete successor pair\");\n }\n\n // `revokeIfActive()` updates the database, not this hydrated instance.\n // Saving that stale instance would write its old `revoked_at: null` back\n // and reopen the predecessor. Update only the successor columns instead.\n const linked = await this.refreshTokenModel.atomic(\n { id: oldToken.id },\n {\n $set: {\n successor_access_token_id: successorAccess.id,\n successor_refresh_token_id: successorRefresh.id,\n },\n },\n );\n if (linked !== 1) throw new Error(\"Automatic renewal could not link its successor pair\");\n afterTokenFamilyOperation(() => authEvents.emit(\"token.refreshed\", user, pair, oldToken));\n return pair;\n }).catch((error) => {\n if (error instanceof TokenFamilyUnavailableError) return null;\n throw error;\n });\n }\n\n /**\n * Whether the family's first issue is older than `maxAgeMs`. The start is the\n * family row's `created_at`, falling back to its oldest refresh token when\n * the row carries none (an upserted legacy family).\n */\n private async familyExceedsMaxAge(familyId: string, maxAgeMs: number): Promise<boolean> {\n const family = await AuthTokenFamily.findByFamilyId(familyId);\n let started: unknown = family?.get(\"created_at\");\n\n if (!started) {\n const oldest = await this.refreshTokenModel\n .query()\n .where({ family_id: familyId })\n .orderBy(\"created_at\", \"asc\")\n .first();\n\n started = oldest?.get(\"created_at\");\n }\n\n const startedAt = started ? new Date(started as string | number | Date).getTime() : NaN;\n\n // Unknown start: nothing to measure against, so the cap cannot apply.\n if (!Number.isFinite(startedAt)) return false;\n\n return Date.now() - startedAt > maxAgeMs;\n }\n\n private automaticOverlapMs(overlapMs: number | undefined): number {\n return typeof overlapMs === \"number\" && Number.isFinite(overlapMs)\n ? Math.max(0, Math.min(10_000, overlapMs))\n : 5000;\n }\n\n private refreshIdentityMatches(\n token: RefreshToken,\n decoded: { userId: string | number; userType: string; familyId: string },\n ): boolean {\n return (\n token.familyId === decoded.familyId &&\n token.get(\"user_id\") === decoded.userId &&\n token.get(\"user_type\") === decoded.userType\n );\n }\n\n private hasSuccessor(oldToken: RefreshToken): boolean {\n return Boolean(\n oldToken.get(\"successor_access_token_id\") && oldToken.get(\"successor_refresh_token_id\"),\n );\n }\n\n private isAutomaticOverlapOpen(oldToken: RefreshToken, overlapMs: number | undefined): boolean {\n const revokedAt = oldToken.get<Date | undefined>(\"revoked_at\");\n const window = this.automaticOverlapMs(overlapMs);\n const elapsed = revokedAt ? Date.now() - new Date(revokedAt).getTime() : NaN;\n return window > 0 && elapsed >= 0 && elapsed <= window;\n }\n\n private async readAutomaticSuccessor(\n oldToken: RefreshToken,\n decoded: { userId: number; userType: string; familyId: string },\n overlapMs: number | undefined,\n ): Promise<TokenPair | null> {\n if (!this.isAutomaticOverlapOpen(oldToken, overlapMs)) return null;\n const accessId = oldToken.get<string | undefined>(\"successor_access_token_id\");\n const refreshId = oldToken.get<string | undefined>(\"successor_refresh_token_id\");\n if (!accessId || !refreshId) return null;\n const [access, refresh] = await Promise.all([\n this.accessTokenModel.find(accessId),\n this.refreshTokenModel.find(refreshId),\n ]);\n if (!access || access.isExpired || !refresh?.isValid) return null;\n if (\n access.get(\"family_id\") !== decoded.familyId ||\n refresh.get(\"family_id\") !== decoded.familyId ||\n access.get(\"user_id\") !== decoded.userId ||\n refresh.get(\"user_id\") !== decoded.userId ||\n access.get(\"user_type\") !== decoded.userType ||\n refresh.get(\"user_type\") !== decoded.userType\n )\n return null;\n const accessExpiry = new Date(access.get(\"expires_at\")).getTime();\n if (!Number.isFinite(accessExpiry) || accessExpiry <= Date.now()) return null;\n return {\n accessToken: { token: access.get(\"token\"), expiresAt: access.get(\"expires_at\") },\n refreshToken: { token: refresh.get(\"token\"), expiresAt: refresh.get(\"expires_at\") },\n };\n }\n\n /**\n * Verify a plaintext password against a stored hash.\n */\n public async verifyPassword(plainPassword: string, hashedPassword: string): Promise<boolean> {\n return verifyPassword(plainPassword, hashedPassword);\n }\n\n /**\n * Hash a plaintext password.\n */\n public async hashPassword(password: string): Promise<string> {\n return hashPassword(password);\n }\n\n /**\n * Resolve a user by credentials, verifying the password. Returns `null` on a\n * missing user or a wrong password, emitting `login.failed` either way.\n */\n public async attemptLogin<T extends Auth>(\n Model: ChildModel<T>,\n data: AuthCredentials,\n ): Promise<T | null> {\n const { password, ...otherData } = data;\n\n authEvents.emit(\"login.attempt\", otherData);\n\n const user = (await Model.first(otherData)) as T | null;\n\n if (!user) {\n authEvents.emit(\"login.failed\", otherData, \"User not found\");\n\n return null;\n }\n\n if (!(await this.verifyPassword(password, user.string(\"password\")!))) {\n authEvents.emit(\"login.failed\", otherData, \"Invalid password\");\n\n return null;\n }\n\n if (!(await this.canAuthenticate(user))) {\n authEvents.emit(\"login.failed\", otherData, \"Authentication not allowed\");\n\n return null;\n }\n\n return user;\n }\n\n /**\n * Full login flow: validate credentials, issue tokens, emit events. Returns\n * the user + token pair on success, `null` on failure.\n */\n public async login<T extends Auth>(\n Model: ChildModel<T>,\n credentials: AuthCredentials,\n deviceInfo?: DeviceInfo,\n ): Promise<LoginResult<T> | null> {\n const user = await this.attemptLogin(Model, credentials);\n\n if (!user) {\n return null;\n }\n\n return this.finalizeLogin(user, deviceInfo);\n }\n\n /**\n * Log in a user some OTHER method already authenticated — a provider\n * callback, a passkey assertion, a one-time code. Applies the\n * `auth.canAuthenticate` policy (403 on refusal), then produces exactly\n * what {@link login} produces: the same tokens, rows and events. Pair with\n * {@link setAuthCookie} for a cookie session, as with password login.\n *\n * Never call this for a user you have not authenticated.\n */\n public async completeLogin<T extends Auth>(\n user: T,\n deviceInfo?: DeviceInfo,\n ): Promise<LoginResult<T>> {\n await this.assertCanAuthenticate(user);\n\n return this.finalizeLogin(user, deviceInfo);\n }\n\n /**\n * Cookie login for a browser: same-origin check, credential login, then both\n * session cookies. Unlike the default CSRF guard, the Origin/Referer check\n * runs even when the request carries no cookies (login CSRF): a missing or\n * cross-site Origin/Referer throws `CsrfOriginMismatchError` before any\n * credential is looked at. Returns `null` on bad credentials.\n */\n public async loginWithSessionCookies<T extends Auth>(\n request: Request,\n response: { [K in \"cookie\" | \"clearCookie\"]: (...args: Parameters<Response[K]>) => unknown },\n Model: ChildModel<T>,\n credentials: AuthCredentials,\n deviceInfo?: DeviceInfo,\n ): Promise<LoginResult<T> | null> {\n assertCsrfOriginAllowed(request);\n\n const result = await this.login(Model, credentials, deviceInfo);\n\n if (!result) return null;\n\n if (result.tokens.refreshToken) {\n this.setSessionCookies(\n response,\n {\n accessToken: result.tokens.accessToken,\n refreshToken: result.tokens.refreshToken,\n },\n request,\n );\n } else {\n this.setAuthCookie(response, result.tokens.accessToken, {\n domain: this.cookieDomainFor(\"loginWithSessionCookies\", request),\n });\n }\n\n return result;\n }\n\n /** The one token-issuing tail every login method shares. */\n private async finalizeLogin<T extends Auth>(\n user: T,\n deviceInfo?: DeviceInfo,\n ): Promise<LoginResult<T>> {\n if (!authConfig.refreshToken.enabled()) {\n const accessToken = await this.issueAccessToken(user, deviceInfo?.payload);\n\n return { user, tokens: { accessToken } };\n }\n\n const tokens = await this.issueTokenPair(user, deviceInfo);\n\n authEvents.emit(\"login.success\", user, tokens, deviceInfo);\n\n return { user, tokens };\n }\n\n /**\n * Log a user out.\n *\n * @param accessToken - access token string to revoke (optional)\n * @param refreshToken - refresh token string to revoke (optional)\n *\n * When no refresh token is supplied, `config.auth.refreshToken.logoutWithoutToken`\n * decides the behavior: `\"revoke-all\"` (default, fail-safe) revokes every\n * refresh token; `\"error\"` requires the caller to pass one.\n */\n public async logout(user: Auth, accessToken?: string, refreshToken?: string): Promise<void> {\n if (accessToken) {\n await this.removeAccessToken(user, accessToken);\n }\n\n if (refreshToken) {\n const token = await this.refreshTokenModel.findForUser(user, refreshToken);\n\n if (token) {\n await this.revokeTokenFamily(token.familyId);\n authEvents.emit(\"session.destroyed\", user, token);\n }\n } else {\n const behavior = authConfig.refreshToken.logoutWithoutToken();\n\n if (behavior === \"error\") {\n throw new Error(\"Refresh token required for logout\");\n }\n\n await this.revokeAllTokens(user);\n authEvents.emit(\"logout.failsafe\", user);\n }\n\n authEvents.emit(\"logout\", user);\n }\n\n /**\n * Remove a specific access token belonging to the user.\n */\n public async removeAccessToken(user: Auth, token: string): Promise<void> {\n await this.accessTokenModel.deleteForUser(user, token);\n }\n\n /**\n * Remove every access token belonging to the user.\n */\n public async removeAllAccessTokens(user: Auth): Promise<void> {\n await this.accessTokenModel.deleteAllForUser(user);\n }\n\n /**\n * Remove a specific refresh token belonging to the user.\n */\n public async removeRefreshToken(user: Auth, token: string): Promise<void> {\n await this.refreshTokenModel.deleteForUser(user, token);\n }\n\n /**\n * Revoke every active refresh token for the user and delete their access\n * tokens — \"log out of all devices\". Each durable family is first marked through its revision coordinator; an\n * event fires per revoked token.\n */\n public async revokeAllTokens(user: Auth): Promise<void> {\n // Upgrade existing installations lazily before the family scan. Include\n // revoked rows: a rotation may have revoked its predecessor while its\n // durable family still needs logout's marker.\n const existingRows = await this.refreshTokenModel.familiesForUser(user);\n\n for (const token of existingRows) {\n await AuthTokenFamily.ensure(user, token.familyId);\n }\n\n const families = await AuthTokenFamily.activeFor(user);\n\n for (const family of families) {\n await this.revokeTokenFamily(family.get<string>(\"family_id\"));\n }\n\n // A deployment can still have rows created before the additive migration\n // ran; retain the old bulk cleanup as a final conservative backstop.\n const legacyTokens = await this.refreshTokenModel.revokeAllFor(user);\n\n for (const token of legacyTokens) {\n authEvents.emit(\"token.revoked\", user, token);\n }\n await this.removeAllAccessTokens(user);\n\n authEvents.emit(\"logout.all\", user);\n }\n\n /**\n * Revoke an entire token family — rotation breach containment.\n */\n public async revokeTokenFamily(familyId: string): Promise<void> {\n let family = await AuthTokenFamily.findByFamilyId(familyId);\n\n if (!family) {\n const legacyToken = await this.refreshTokenModel.findInFamily(familyId);\n\n if (!legacyToken) return;\n\n family = await AuthTokenFamily.ensure(\n {\n id: legacyToken.get(\"user_id\"),\n userType: legacyToken.get<string>(\"user_type\"),\n } as Auth,\n familyId,\n );\n }\n\n const user = {\n id: family.get(\"user_id\"),\n userType: family.get<string>(\"user_type\"),\n } as Auth;\n\n if (family.isRevoked) {\n // Logout and replay containment are idempotent. Re-run cleanup because a\n // request that lost the original race may have observed stale rows.\n const revokedTokens = await this.refreshTokenModel.revokeFamily(familyId);\n await this.accessTokenModel.deleteFamilyAndLegacyForUser(user, familyId);\n afterTokenFamilyOperation(() =>\n authEvents.emit(\"token.familyRevoked\", familyId, revokedTokens),\n );\n\n return;\n }\n\n let revokedTokens: RefreshToken[];\n try {\n revokedTokens = await runTokenFamilyOperation(familyId, async (currentFamily) => {\n const familyUser = {\n id: currentFamily.get(\"user_id\"),\n userType: currentFamily.get<string>(\"user_type\"),\n } as Auth;\n const revision = currentFamily.get<number>(\"revision\");\n const revoked = await AuthTokenFamily.revoke(familyId, revision);\n\n if (revoked !== 1) {\n throw new Error(\"Token family revoke lost its revision transition\");\n }\n\n const familyTokens = await this.refreshTokenModel.revokeFamily(familyId);\n await this.accessTokenModel.deleteFamilyAndLegacyForUser(familyUser, familyId);\n\n return familyTokens;\n });\n } catch (error) {\n if (!(error instanceof TokenFamilyUnavailableError)) throw error;\n\n // Another request committed the revoke between the read above and its\n // CAS. A second logout is successful after idempotent cleanup.\n revokedTokens = await this.refreshTokenModel.revokeFamily(familyId);\n await this.accessTokenModel.deleteFamilyAndLegacyForUser(user, familyId);\n }\n\n afterTokenFamilyOperation(() =>\n authEvents.emit(\"token.familyRevoked\", familyId, revokedTokens),\n );\n }\n\n /**\n * Delete expired tokens (refresh + access) and spent one-time tokens\n * (expired or consumed verification/reset/OTP/passkey rows). Emits `token.expired` per refresh\n * token and `cleanup.completed` with the refresh count. Drives the\n * `auth.cleanup` CLI command.\n */\n public async cleanupExpiredTokens(): Promise<number> {\n const expiredTokens = await this.refreshTokenModel.purgeExpired();\n\n for (const token of expiredTokens) {\n authEvents.emit(\"token.expired\", token);\n }\n\n await this.accessTokenModel.purgeExpired();\n await this.oneTimeTokenModel.purgeSpent();\n\n authEvents.emit(\"cleanup.completed\", expiredTokens.length);\n\n return expiredTokens.length;\n }\n\n /**\n * Find every persisted token — access and refresh — that can never retire\n * itself: an unusable `expires_at`, or a token carrying no `exp` claim.\n *\n * This is the remediation read for the pre-4.12.0 `expiresIn` defect (#25).\n * Nothing else surfaces these rows: `auth.cleanup` selects `expires_at < now`,\n * which an `Invalid Date` never satisfies, and a row whose date column is\n * perfectly fine can still hold a token with no deadline in it. Read-only —\n * pair with {@link purgeNeverExpiringTokens} to act on the answer.\n */\n public async findNeverExpiringTokens(): Promise<{\n accessTokens: AccessToken[];\n refreshTokens: RefreshToken[];\n }> {\n return {\n accessTokens: await this.accessTokenModel.findNeverExpiring(),\n refreshTokens: await this.refreshTokenModel.findNeverExpiring(),\n };\n }\n\n /**\n * Revoke every never-expiring token by deleting its row, emitting\n * `token.revoked` per refresh token. Drives `warlock auth.purge-never-expiring`.\n *\n * Access tokens first: deleting one is immediate revocation, while a refresh\n * token left in place for a moment can only mint an access token through a\n * verifier that now rejects it for the missing `exp`.\n */\n public async purgeNeverExpiringTokens(): Promise<{\n accessTokens: number;\n refreshTokens: number;\n }> {\n const accessTokens = await this.accessTokenModel.purgeNeverExpiring();\n const refreshTokens = await this.refreshTokenModel.purgeNeverExpiring();\n\n // `token.expired` (the event `auth.cleanup` already emits for a removed\n // refresh token) rather than `token.revoked`, which carries a loaded `Auth`\n // this batch path has no reason to fetch a user row for.\n for (const token of refreshTokens) {\n authEvents.emit(\"token.expired\", token);\n }\n\n return { accessTokens: accessTokens.length, refreshTokens: refreshTokens.length };\n }\n\n /**\n * Active, unexpired sessions for the user, newest first.\n */\n public async getActiveSessions(user: Auth): Promise<RefreshToken[]> {\n return this.refreshTokenModel.activeFor(user);\n }\n\n /**\n * `Max-Age` (seconds) `setAuthCookie` should apply, derived from an\n * `AccessTokenOutput`'s `expiresAt` — or `undefined` for a bare token\n * string (a session cookie) or an unparseable `expiresAt`.\n */\n private cookieMaxAgeFromToken(token: string | AccessTokenOutput): number | undefined {\n if (typeof token === \"string\") return undefined;\n\n const expiresAt = new Date(token.expiresAt).getTime();\n\n if (Number.isNaN(expiresAt)) return undefined;\n\n return Math.max(0, Math.round((expiresAt - Date.now()) / 1000));\n }\n\n private cookieDomainFor(method: string, request?: Request): string | undefined {\n const resolver = authConfig.cookie.cookieDomain();\n\n if (!resolver) {\n return undefined;\n }\n\n if (!request) {\n throw new CookieDomainRequestRequiredError(method);\n }\n\n return resolver(request);\n }\n\n /**\n * Write the auth cookie on `response` — the write side of the `cookie:<name>`\n * token source `authMiddleware([], \"cookie:<name>\")` already reads. An explicit\n * app-controller call, never a side effect of {@link login}, so an existing\n * bearer-only app never starts emitting `Set-Cookie` just by upgrading.\n *\n * Attribute flags (`HttpOnly`, `SameSite=Lax`, `Secure` outside dev) come\n * from `response.cookie()`'s own `secureCookieDefaults()` floor — this\n * method never overrides them, only `name`/`path`/`maxAge`. `raw: true`\n * writes the token unquoted, so the same string a `cookie:<name>` source\n * reads back is exactly what was issued, not a JSON-wrapped copy.\n *\n * @param token - the token string, or an `AccessTokenOutput` (e.g. from\n * {@link login}) whose `expiresAt` sets `Max-Age` automatically when\n * `options.maxAge` is not given. A bare string with no `options.maxAge`\n * produces a session cookie.\n *\n * @example\n * const { user, tokens } = await authService.login(User, credentials);\n * authService.setAuthCookie(response, tokens.accessToken);\n */\n public setAuthCookie(\n response: { [K in \"cookie\" | \"clearCookie\"]: (...args: Parameters<Response[K]>) => unknown },\n token: string | AccessTokenOutput,\n options: SetAuthCookieOptions = {},\n ): void {\n const name = options.name ?? authConfig.cookie.name();\n const path = options.path ?? authConfig.cookie.path();\n const tokenValue = typeof token === \"string\" ? token : token.token;\n const maxAge = options.maxAge ?? this.cookieMaxAgeFromToken(token);\n\n response.cookie(name, tokenValue, {\n raw: true,\n path,\n ...(options.domain !== undefined ? { domain: options.domain } : {}),\n ...(maxAge !== undefined ? { maxAge } : {}),\n });\n }\n\n /**\n * Clear the auth cookie {@link setAuthCookie} wrote. An explicit\n * app-controller call — pair it with {@link logout} after the token row is\n * revoked. `path` must match what the cookie was set with, or the browser\n * silently ignores the clear (see `response.clearCookie`'s own doc comment).\n *\n * @example\n * await authService.logout(user, accessToken, refreshToken);\n * authService.clearAuthCookie(response);\n */\n public clearAuthCookie(\n response: { [K in \"cookie\" | \"clearCookie\"]: (...args: Parameters<Response[K]>) => unknown },\n options: ClearAuthCookieOptions = {},\n request?: Request,\n ): void {\n const name = options.name ?? authConfig.cookie.name();\n const path = options.path ?? authConfig.cookie.path();\n const domain = this.cookieDomainFor(\"clearAuthCookie\", request);\n\n if (domain !== undefined) {\n response.clearCookie(name, { path, domain });\n }\n\n response.clearCookie(name, { path });\n }\n\n /**\n * Write the access and refresh cookies of a browser session. Both are\n * `HttpOnly`, `SameSite=Lax`, `Path=/` (the refresh cookie must reach every\n * page) and `Secure` outside development, named by `auth.cookie.name` and\n * `auth.cookie.refreshName`. Each `Max-Age` is derived from its token's\n * `expiresAt`, so the cookie lives exactly as long as the token.\n *\n * @example\n * const { tokens } = await authService.login(User, credentials);\n * authService.setSessionCookies(response, tokens);\n */\n public setSessionCookies(\n response: { [K in \"cookie\" | \"clearCookie\"]: (...args: Parameters<Response[K]>) => unknown },\n tokens: { accessToken: AccessTokenOutput; refreshToken: AccessTokenOutput },\n request?: Request,\n ): void {\n const domain = this.cookieDomainFor(\"setSessionCookies\", request);\n const cookies = [\n [authConfig.cookie.name(), tokens.accessToken],\n [authConfig.cookie.refreshName(), tokens.refreshToken],\n ] as const;\n\n for (const [name, token] of cookies) {\n const maxAge = this.cookieMaxAgeFromToken(token);\n\n response.cookie(name, token.token, {\n raw: true,\n httpOnly: true,\n sameSite: \"lax\",\n path: \"/\",\n ...(domain !== undefined ? { domain } : {}),\n ...(maxAge !== undefined ? { maxAge } : {}),\n });\n }\n }\n\n /**\n * Clear both session cookies {@link setSessionCookies} wrote, on `Path=/`.\n * Pair it with {@link logout} after the token rows are revoked.\n */\n public clearSessionCookies(\n response: { [K in \"cookie\" | \"clearCookie\"]: (...args: Parameters<Response[K]>) => unknown },\n request?: Request,\n ): void {\n const domain = this.cookieDomainFor(\"clearSessionCookies\", request);\n\n for (const name of [authConfig.cookie.name(), authConfig.cookie.refreshName()]) {\n if (domain !== undefined) {\n response.clearCookie(name, { path: \"/\", domain });\n }\n\n response.clearCookie(name, { path: \"/\" });\n }\n }\n}\n\nexport const authService = new AuthService();\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { Auth } from "../models/auth.model.mjs";
2
2
 
3
- //#region ../auth/src/services/current-user.d.ts
3
+ //#region ../../packages/auth/src/services/current-user.d.ts
4
4
  /**
5
5
  * Typed accessor over the request-scoped current user, for callers that want
6
6
  * `Auth`-derived typing without repeating core's `unknown` → `UserType`
@@ -1 +1 @@
1
- {"version":3,"file":"current-user.d.mts","names":[],"sources":["../../../../../../../auth/src/services/current-user.ts"],"mappings":";;;;;AAiBA;;;;;;;;;;;;iBAAgB,WAAA,kBAA6B,IAAA,GAAO,IAAA,KAAS,QAAA"}
1
+ {"version":3,"file":"current-user.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/services/current-user.ts"],"mappings":";;;;;AAiBA;;;;;;;;;;;;iBAAgB,WAAA,kBAA6B,IAAA,GAAO,IAAA,KAAS,QAAA"}
@@ -1,6 +1,6 @@
1
1
  import { useCurrentUser } from "@warlock.js/core";
2
2
 
3
- //#region ../auth/src/services/current-user.ts
3
+ //#region ../../packages/auth/src/services/current-user.ts
4
4
  /**
5
5
  * Typed accessor over the request-scoped current user, for callers that want
6
6
  * `Auth`-derived typing without repeating core's `unknown` → `UserType`
@@ -1 +1 @@
1
- {"version":3,"file":"current-user.mjs","names":[],"sources":["../../../../../../../auth/src/services/current-user.ts"],"sourcesContent":["import { useCurrentUser } from \"@warlock.js/core\";\nimport type { Auth } from \"../models/auth.model\";\n\n/**\n * Typed accessor over the request-scoped current user, for callers that want\n * `Auth`-derived typing without repeating core's `unknown` → `UserType`\n * assertion at every call site.\n *\n * Core's own `useCurrentUser()` returns `unknown` cast to the caller's\n * generic (it cannot know about `RequestUser`/`Auth` — see\n * `core/src/http/context/request-context.ts`). This wrapper narrows that\n * generic to `Auth`, matching what `@warlock.js/auth`'s middleware actually\n * writes to `request.locals.user`.\n *\n * @example\n * const user = currentUser<AdminUser>();\n */\nexport function currentUser<UserType extends Auth = Auth>(): UserType | undefined {\n return useCurrentUser<UserType>();\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAiBA,SAAgB,cAAkE;CAChF,OAAO,eAAyB;AAClC"}
1
+ {"version":3,"file":"current-user.mjs","names":[],"sources":["../../../../../../../../packages/auth/src/services/current-user.ts"],"sourcesContent":["import { useCurrentUser } from \"@warlock.js/core\";\nimport type { Auth } from \"../models/auth.model\";\n\n/**\n * Typed accessor over the request-scoped current user, for callers that want\n * `Auth`-derived typing without repeating core's `unknown` → `UserType`\n * assertion at every call site.\n *\n * Core's own `useCurrentUser()` returns `unknown` cast to the caller's\n * generic (it cannot know about `RequestUser`/`Auth` — see\n * `core/src/http/context/request-context.ts`). This wrapper narrows that\n * generic to `Auth`, matching what `@warlock.js/auth`'s middleware actually\n * writes to `request.locals.user`.\n *\n * @example\n * const user = currentUser<AdminUser>();\n */\nexport function currentUser<UserType extends Auth = Auth>(): UserType | undefined {\n return useCurrentUser<UserType>();\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAiBA,SAAgB,cAAkE;CAChF,OAAO,eAAyB;AAClC"}
@@ -1,6 +1,6 @@
1
1
  import { Auth } from "../models/auth.model.mjs";
2
2
 
3
- //#region ../auth/src/services/email-verification.d.ts
3
+ //#region ../../packages/auth/src/services/email-verification.d.ts
4
4
  /**
5
5
  * Whether the user has verified their email — the configured
6
6
  * `auth.verification.field` (default `emailVerifiedAt`) holds a value.
@@ -1 +1 @@
1
- {"version":3,"file":"email-verification.d.mts","names":[],"sources":["../../../../../../../auth/src/services/email-verification.ts"],"mappings":";;;;;AASA;;iBAAgB,eAAA,CAAgB,IAAA,EAAM,IAAI,CAAC,IAAA;;;;;;AAAY;AAiBvD;;;;;;;iBAAsB,qBAAA,CAAsB,IAAA,EAAM,IAAA,GAAO,OAAO;AAAA;AAuBhE;;;;;;;;AAvBgE,iBAuB1C,WAAA,CAAY,KAAA,WAAgB,OAAO,CAAC,IAAA"}
1
+ {"version":3,"file":"email-verification.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/services/email-verification.ts"],"mappings":";;;;;AASA;;iBAAgB,eAAA,CAAgB,IAAA,EAAM,IAAI,CAAC,IAAA;;;;;;AAAY;AAiBvD;;;;;;;iBAAsB,qBAAA,CAAsB,IAAA,EAAM,IAAA,GAAO,OAAO;AAAA;AAuBhE;;;;;;;;AAvBgE,iBAuB1C,WAAA,CAAY,KAAA,WAAgB,OAAO,CAAC,IAAA"}
@@ -2,7 +2,7 @@ import { authConfig } from "./auth-config.mjs";
2
2
  import { consumeOneTimeToken, issueOneTimeToken } from "./one-time-tokens.mjs";
3
3
  import { resolveTokenNotification } from "./token-notifications.mjs";
4
4
 
5
- //#region ../auth/src/services/email-verification.ts
5
+ //#region ../../packages/auth/src/services/email-verification.ts
6
6
  /**
7
7
  * Whether the user has verified their email — the configured
8
8
  * `auth.verification.field` (default `emailVerifiedAt`) holds a value.
@@ -1 +1 @@
1
- {"version":3,"file":"email-verification.mjs","names":[],"sources":["../../../../../../../auth/src/services/email-verification.ts"],"sourcesContent":["import type { Auth } from \"../models/auth.model\";\nimport { authConfig } from \"./auth-config\";\nimport { consumeOneTimeToken, issueOneTimeToken } from \"./one-time-tokens\";\nimport { resolveTokenNotification } from \"./token-notifications\";\n\n/**\n * Whether the user has verified their email — the configured\n * `auth.verification.field` (default `emailVerifiedAt`) holds a value.\n */\nexport function isEmailVerified(user: Pick<Auth, \"get\">): boolean {\n return !!user.get(authConfig.verification.field());\n}\n\n/**\n * Issue an email-verification token for the user and send it through the\n * verification notification (`auth.verification.notification`, else auth's\n * default mail). Earlier verification links keep working until they expire or\n * one of them is used.\n *\n * @throws NotificationsUnavailableError before any token is issued when\n * `@warlock.js/notifications` is not installed or not configured.\n *\n * @example\n * const user = await User.create(data);\n * await sendEmailVerification(user);\n */\nexport async function sendEmailVerification(user: Auth): Promise<void> {\n const notification = await resolveTokenNotification(\"email-verification\");\n\n const { token, expiresAt } = await issueOneTimeToken(\n user,\n \"email-verification\",\n authConfig.verification.expiresInMs(),\n );\n\n const url = authConfig.verification.url()?.(token, user);\n\n await notification.send(user, { token, expiresAt, ...(url ? { url } : {}) });\n}\n\n/**\n * Consume a verification token and stamp the user's verified field with the\n * current date. Resolves to the verified user.\n *\n * @throws InvalidOneTimeTokenError for an unknown, reset-purpose, expired or\n * already-used token.\n * @throws Error when the save did not keep the field — the user schema must\n * declare it (e.g. `emailVerifiedAt: v.date().optional()`).\n */\nexport async function verifyEmail(token: string): Promise<Auth> {\n const user = await consumeOneTimeToken(token, \"email-verification\");\n const field = authConfig.verification.field();\n\n await user.merge({ [field]: new Date() } as never).save();\n\n if (!isEmailVerified(user)) {\n throw new Error(\n `@warlock.js/auth: verifyEmail could not persist \"${field}\" on the user — ` +\n `declare it in the user schema (e.g. \\`${field}: v.date().optional()\\`) ` +\n \"or set `auth.verification.field` to an existing attribute.\",\n );\n }\n\n return user;\n}\n"],"mappings":";;;;;;;;;AASA,SAAgB,gBAAgB,MAAkC;CAChE,OAAO,CAAC,CAAC,KAAK,IAAI,WAAW,aAAa,MAAM,CAAC;AACnD;;;;;;;;;;;;;;AAeA,eAAsB,sBAAsB,MAA2B;CACrE,MAAM,eAAe,MAAM,yBAAyB,oBAAoB;CAExE,MAAM,EAAE,OAAO,cAAc,MAAM,kBACjC,MACA,sBACA,WAAW,aAAa,YAAY,CACtC;CAEA,MAAM,MAAM,WAAW,aAAa,IAAI,CAAC,GAAG,OAAO,IAAI;CAEvD,MAAM,aAAa,KAAK,MAAM;EAAE;EAAO;EAAW,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC;CAAG,CAAC;AAC7E;;;;;;;;;;AAWA,eAAsB,YAAY,OAA8B;CAC9D,MAAM,OAAO,MAAM,oBAAoB,OAAO,oBAAoB;CAClE,MAAM,QAAQ,WAAW,aAAa,MAAM;CAE5C,MAAM,KAAK,MAAM,GAAG,wBAAQ,IAAI,KAAK,EAAE,CAAU,CAAC,CAAC,KAAK;CAExD,IAAI,CAAC,gBAAgB,IAAI,GACvB,MAAM,IAAI,MACR,oDAAoD,MAAM,wDACf,MAAM,sFAEnD;CAGF,OAAO;AACT"}
1
+ {"version":3,"file":"email-verification.mjs","names":[],"sources":["../../../../../../../../packages/auth/src/services/email-verification.ts"],"sourcesContent":["import type { Auth } from \"../models/auth.model\";\nimport { authConfig } from \"./auth-config\";\nimport { consumeOneTimeToken, issueOneTimeToken } from \"./one-time-tokens\";\nimport { resolveTokenNotification } from \"./token-notifications\";\n\n/**\n * Whether the user has verified their email — the configured\n * `auth.verification.field` (default `emailVerifiedAt`) holds a value.\n */\nexport function isEmailVerified(user: Pick<Auth, \"get\">): boolean {\n return !!user.get(authConfig.verification.field());\n}\n\n/**\n * Issue an email-verification token for the user and send it through the\n * verification notification (`auth.verification.notification`, else auth's\n * default mail). Earlier verification links keep working until they expire or\n * one of them is used.\n *\n * @throws NotificationsUnavailableError before any token is issued when\n * `@warlock.js/notifications` is not installed or not configured.\n *\n * @example\n * const user = await User.create(data);\n * await sendEmailVerification(user);\n */\nexport async function sendEmailVerification(user: Auth): Promise<void> {\n const notification = await resolveTokenNotification(\"email-verification\");\n\n const { token, expiresAt } = await issueOneTimeToken(\n user,\n \"email-verification\",\n authConfig.verification.expiresInMs(),\n );\n\n const url = authConfig.verification.url()?.(token, user);\n\n await notification.send(user, { token, expiresAt, ...(url ? { url } : {}) });\n}\n\n/**\n * Consume a verification token and stamp the user's verified field with the\n * current date. Resolves to the verified user.\n *\n * @throws InvalidOneTimeTokenError for an unknown, reset-purpose, expired or\n * already-used token.\n * @throws Error when the save did not keep the field — the user schema must\n * declare it (e.g. `emailVerifiedAt: v.date().optional()`).\n */\nexport async function verifyEmail(token: string): Promise<Auth> {\n const user = await consumeOneTimeToken(token, \"email-verification\");\n const field = authConfig.verification.field();\n\n await user.merge({ [field]: new Date() } as never).save();\n\n if (!isEmailVerified(user)) {\n throw new Error(\n `@warlock.js/auth: verifyEmail could not persist \"${field}\" on the user — ` +\n `declare it in the user schema (e.g. \\`${field}: v.date().optional()\\`) ` +\n \"or set `auth.verification.field` to an existing attribute.\",\n );\n }\n\n return user;\n}\n"],"mappings":";;;;;;;;;AASA,SAAgB,gBAAgB,MAAkC;CAChE,OAAO,CAAC,CAAC,KAAK,IAAI,WAAW,aAAa,MAAM,CAAC;AACnD;;;;;;;;;;;;;;AAeA,eAAsB,sBAAsB,MAA2B;CACrE,MAAM,eAAe,MAAM,yBAAyB,oBAAoB;CAExE,MAAM,EAAE,OAAO,cAAc,MAAM,kBACjC,MACA,sBACA,WAAW,aAAa,YAAY,CACtC;CAEA,MAAM,MAAM,WAAW,aAAa,IAAI,CAAC,GAAG,OAAO,IAAI;CAEvD,MAAM,aAAa,KAAK,MAAM;EAAE;EAAO;EAAW,GAAI,MAAM,EAAE,IAAI,IAAI,CAAC;CAAG,CAAC;AAC7E;;;;;;;;;;AAWA,eAAsB,YAAY,OAA8B;CAC9D,MAAM,OAAO,MAAM,oBAAoB,OAAO,oBAAoB;CAClE,MAAM,QAAQ,WAAW,aAAa,MAAM;CAE5C,MAAM,KAAK,MAAM,GAAG,wBAAQ,IAAI,KAAK,EAAE,CAAU,CAAC,CAAC,KAAK;CAExD,IAAI,CAAC,gBAAgB,IAAI,GACvB,MAAM,IAAI,MACR,oDAAoD,MAAM,wDACf,MAAM,sFAEnD;CAGF,OAAO;AACT"}
@@ -1,4 +1,4 @@
1
- //#region ../auth/src/services/generate-jwt-secret.d.ts
1
+ //#region ../../packages/auth/src/services/generate-jwt-secret.d.ts
2
2
  declare function generateJWTSecret(): Promise<void>;
3
3
  //#endregion
4
4
  export { generateJWTSecret };
@@ -1 +1 @@
1
- {"version":3,"file":"generate-jwt-secret.d.mts","names":[],"sources":["../../../../../../../auth/src/services/generate-jwt-secret.ts"],"mappings":";iBAKsB,iBAAA,IAAiB,OAAA"}
1
+ {"version":3,"file":"generate-jwt-secret.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/services/generate-jwt-secret.ts"],"mappings":";iBAKsB,iBAAA,IAAiB,OAAA"}
@@ -3,7 +3,7 @@ import { environment, rootPath } from "@warlock.js/core";
3
3
  import { log } from "@warlock.js/logger";
4
4
  import { fs } from "@warlock.js/fs";
5
5
 
6
- //#region ../auth/src/services/generate-jwt-secret.ts
6
+ //#region ../../packages/auth/src/services/generate-jwt-secret.ts
7
7
  async function generateJWTSecret() {
8
8
  let envFile = rootPath(".env");
9
9
  log.info("jwt", "generating", "Generating JWT secrets");
@@ -1 +1 @@
1
- {"version":3,"file":"generate-jwt-secret.mjs","names":[],"sources":["../../../../../../../auth/src/services/generate-jwt-secret.ts"],"sourcesContent":["import { fs } from \"@warlock.js/fs\";\nimport { Random } from \"@mongez/reinforcements\";\nimport { environment, rootPath } from \"@warlock.js/core\";\nimport { log } from \"@warlock.js/logger\";\n\nexport async function generateJWTSecret() {\n let envFile = rootPath(\".env\");\n\n log.info(\"jwt\", \"generating\", \"Generating JWT secrets\");\n\n const environmentMode = environment();\n\n if (!(await fs.files.exists(envFile))) {\n const envFileType = environmentMode === \"production\" ? \".env.production\" : \".env.development\";\n envFile = rootPath(envFileType);\n }\n\n if (!(await fs.files.exists(envFile))) {\n log.error(\"jwt\", \"error\", \".env file not found\");\n return;\n }\n\n let contents = await fs.files.get(envFile);\n\n const hasJwtSecret = contents.includes(\"JWT_SECRET\");\n const hasJwtRefreshSecret = contents.includes(\"JWT_REFRESH_SECRET\");\n\n if (hasJwtSecret && hasJwtRefreshSecret) {\n log.warn(\"jwt\", \"exists\", \"JWT secrets already exist in the .env file.\");\n return;\n }\n\n let secretsToAdd = \"\";\n\n if (!hasJwtSecret) {\n const jwtSecret = Random.token(32);\n secretsToAdd += `\n# JWT Secret\nJWT_SECRET=${jwtSecret}\n`;\n log.success(\"jwt\", \"generated\", \"JWT_SECRET generated and added to the .env file.\");\n } else {\n log.info(\"jwt\", \"exists\", \"JWT_SECRET already exists in the .env file.\");\n }\n\n if (!hasJwtRefreshSecret) {\n const jwtRefreshSecret = Random.token(32);\n secretsToAdd += `\n# JWT Refresh Secret\nJWT_REFRESH_SECRET=${jwtRefreshSecret}\n`;\n log.success(\"jwt\", \"generated\", \"JWT_REFRESH_SECRET generated and added to the .env file.\");\n } else {\n log.info(\"jwt\", \"exists\", \"JWT_REFRESH_SECRET already exists in the .env file.\");\n }\n\n if (secretsToAdd) {\n contents += secretsToAdd;\n await fs.files.put(envFile, contents);\n }\n}\n"],"mappings":";;;;;;AAKA,eAAsB,oBAAoB;CACxC,IAAI,UAAU,SAAS,MAAM;CAE7B,IAAI,KAAK,OAAO,cAAc,wBAAwB;CAEtD,MAAM,kBAAkB,YAAY;CAEpC,IAAI,CAAE,MAAM,GAAG,MAAM,OAAO,OAAO,GAEjC,UAAU,SADU,oBAAoB,eAAe,oBAAoB,kBAC7C;CAGhC,IAAI,CAAE,MAAM,GAAG,MAAM,OAAO,OAAO,GAAI;EACrC,IAAI,MAAM,OAAO,SAAS,qBAAqB;EAC/C;CACF;CAEA,IAAI,WAAW,MAAM,GAAG,MAAM,IAAI,OAAO;CAEzC,MAAM,eAAe,SAAS,SAAS,YAAY;CACnD,MAAM,sBAAsB,SAAS,SAAS,oBAAoB;CAElE,IAAI,gBAAgB,qBAAqB;EACvC,IAAI,KAAK,OAAO,UAAU,6CAA6C;EACvE;CACF;CAEA,IAAI,eAAe;CAEnB,IAAI,CAAC,cAAc;EACjB,MAAM,YAAY,OAAO,MAAM,EAAE;EACjC,gBAAgB;;aAEP,UAAU;;EAEnB,IAAI,QAAQ,OAAO,aAAa,kDAAkD;CACpF,OACE,IAAI,KAAK,OAAO,UAAU,6CAA6C;CAGzE,IAAI,CAAC,qBAAqB;EACxB,MAAM,mBAAmB,OAAO,MAAM,EAAE;EACxC,gBAAgB;;qBAEC,iBAAiB;;EAElC,IAAI,QAAQ,OAAO,aAAa,0DAA0D;CAC5F,OACE,IAAI,KAAK,OAAO,UAAU,qDAAqD;CAGjF,IAAI,cAAc;EAChB,YAAY;EACZ,MAAM,GAAG,MAAM,IAAI,SAAS,QAAQ;CACtC;AACF"}
1
+ {"version":3,"file":"generate-jwt-secret.mjs","names":[],"sources":["../../../../../../../../packages/auth/src/services/generate-jwt-secret.ts"],"sourcesContent":["import { fs } from \"@warlock.js/fs\";\nimport { Random } from \"@mongez/reinforcements\";\nimport { environment, rootPath } from \"@warlock.js/core\";\nimport { log } from \"@warlock.js/logger\";\n\nexport async function generateJWTSecret() {\n let envFile = rootPath(\".env\");\n\n log.info(\"jwt\", \"generating\", \"Generating JWT secrets\");\n\n const environmentMode = environment();\n\n if (!(await fs.files.exists(envFile))) {\n const envFileType = environmentMode === \"production\" ? \".env.production\" : \".env.development\";\n envFile = rootPath(envFileType);\n }\n\n if (!(await fs.files.exists(envFile))) {\n log.error(\"jwt\", \"error\", \".env file not found\");\n return;\n }\n\n let contents = await fs.files.get(envFile);\n\n const hasJwtSecret = contents.includes(\"JWT_SECRET\");\n const hasJwtRefreshSecret = contents.includes(\"JWT_REFRESH_SECRET\");\n\n if (hasJwtSecret && hasJwtRefreshSecret) {\n log.warn(\"jwt\", \"exists\", \"JWT secrets already exist in the .env file.\");\n return;\n }\n\n let secretsToAdd = \"\";\n\n if (!hasJwtSecret) {\n const jwtSecret = Random.token(32);\n secretsToAdd += `\n# JWT Secret\nJWT_SECRET=${jwtSecret}\n`;\n log.success(\"jwt\", \"generated\", \"JWT_SECRET generated and added to the .env file.\");\n } else {\n log.info(\"jwt\", \"exists\", \"JWT_SECRET already exists in the .env file.\");\n }\n\n if (!hasJwtRefreshSecret) {\n const jwtRefreshSecret = Random.token(32);\n secretsToAdd += `\n# JWT Refresh Secret\nJWT_REFRESH_SECRET=${jwtRefreshSecret}\n`;\n log.success(\"jwt\", \"generated\", \"JWT_REFRESH_SECRET generated and added to the .env file.\");\n } else {\n log.info(\"jwt\", \"exists\", \"JWT_REFRESH_SECRET already exists in the .env file.\");\n }\n\n if (secretsToAdd) {\n contents += secretsToAdd;\n await fs.files.put(envFile, contents);\n }\n}\n"],"mappings":";;;;;;AAKA,eAAsB,oBAAoB;CACxC,IAAI,UAAU,SAAS,MAAM;CAE7B,IAAI,KAAK,OAAO,cAAc,wBAAwB;CAEtD,MAAM,kBAAkB,YAAY;CAEpC,IAAI,CAAE,MAAM,GAAG,MAAM,OAAO,OAAO,GAEjC,UAAU,SADU,oBAAoB,eAAe,oBAAoB,kBAC7C;CAGhC,IAAI,CAAE,MAAM,GAAG,MAAM,OAAO,OAAO,GAAI;EACrC,IAAI,MAAM,OAAO,SAAS,qBAAqB;EAC/C;CACF;CAEA,IAAI,WAAW,MAAM,GAAG,MAAM,IAAI,OAAO;CAEzC,MAAM,eAAe,SAAS,SAAS,YAAY;CACnD,MAAM,sBAAsB,SAAS,SAAS,oBAAoB;CAElE,IAAI,gBAAgB,qBAAqB;EACvC,IAAI,KAAK,OAAO,UAAU,6CAA6C;EACvE;CACF;CAEA,IAAI,eAAe;CAEnB,IAAI,CAAC,cAAc;EACjB,MAAM,YAAY,OAAO,MAAM,EAAE;EACjC,gBAAgB;;aAEP,UAAU;;EAEnB,IAAI,QAAQ,OAAO,aAAa,kDAAkD;CACpF,OACE,IAAI,KAAK,OAAO,UAAU,6CAA6C;CAGzE,IAAI,CAAC,qBAAqB;EACxB,MAAM,mBAAmB,OAAO,MAAM,EAAE;EACxC,gBAAgB;;qBAEC,iBAAiB;;EAElC,IAAI,QAAQ,OAAO,aAAa,0DAA0D;CAC5F,OACE,IAAI,KAAK,OAAO,UAAU,qDAAqD;CAGjF,IAAI,cAAc;EAChB,YAAY;EACZ,MAAM,GAAG,MAAM,IAAI,SAAS,QAAQ;CACtC;AACF"}
@@ -1,7 +1,7 @@
1
1
  import { AuthErrorCodes } from "../utils/auth-error-codes.mjs";
2
2
  import { SignerOptions, VerifierOptions } from "fast-jwt";
3
3
 
4
- //#region ../auth/src/services/jwt.d.ts
4
+ //#region ../../packages/auth/src/services/jwt.d.ts
5
5
  /**
6
6
  * Token class. Stamped as the `tokenType` claim on every signed token and
7
7
  * checked on verify so an access token can never be accepted where a refresh
@@ -1 +1 @@
1
- {"version":3,"file":"jwt.d.mts","names":[],"sources":["../../../../../../../auth/src/services/jwt.ts"],"mappings":";;;;;;AAoBA;;;;AAAqB;AAoCrB;KApCY,SAAA;;;AAoC2C;AAevD;;iBAfgB,wBAAA,CAAyB,KAAc;;;;;;;;;;cAe1C,cAAA,SAAuB,KAAA;EAAA,SACzB,IAAA,GAAI,cAAA,CAAA,gBAAA;cAED,QAAA,EAAU,SAAA,EAAW,MAAA;AAAA;AAAA,cA+CtB,GAAA;EA/CoC;AA+CjD;;;;IAMgB,GAAA;IAAA,SAAA;IAAA,GAAA;EAAA,IAKT,aAAA;IAAkB,GAAA;EAAA,IACpB,OAAA;EAAA;;;;;sBAaW,KAAA;IACC,GAAA;IAAA,UAAA;IAAA,cAAA;IAAA,GAAA;EAAA,IAMV,eAAA;IAAoB,GAAA;EAAA,IACtB,OAAA,CAAQ,CAAA;EAmBG;;;;IAAA,GAAA;IAAA,SAAA;IAAA,SAAA;IAAA,GAAA;EAAA,IAMT,aAAA;IAAkB,GAAA;EAAA,IACpB,OAAA;EASY;;;kCADW,KAAA;IACX,GAAA;IAAA,UAAA;IAAA,cAAA;IAAA,GAAA;EAAA,IAMV,eAAA;IAAoB,GAAA;EAAA,IACtB,OAAA,CAAQ,CAAA;AAAA"}
1
+ {"version":3,"file":"jwt.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/services/jwt.ts"],"mappings":";;;;;;AAoBA;;;;AAAqB;AAoCrB;KApCY,SAAA;;;AAoC2C;AAevD;;iBAfgB,wBAAA,CAAyB,KAAc;;;;;;;;;;cAe1C,cAAA,SAAuB,KAAA;EAAA,SACzB,IAAA,GAAI,cAAA,CAAA,gBAAA;cAED,QAAA,EAAU,SAAA,EAAW,MAAA;AAAA;AAAA,cA+CtB,GAAA;EA/CoC;AA+CjD;;;;IAMgB,GAAA;IAAA,SAAA;IAAA,GAAA;EAAA,IAKT,aAAA;IAAkB,GAAA;EAAA,IACpB,OAAA;EAAA;;;;;sBAaW,KAAA;IACC,GAAA;IAAA,UAAA;IAAA,cAAA;IAAA,GAAA;EAAA,IAMV,eAAA;IAAoB,GAAA;EAAA,IACtB,OAAA,CAAQ,CAAA;EAmBG;;;;IAAA,GAAA;IAAA,SAAA;IAAA,SAAA;IAAA,GAAA;EAAA,IAMT,aAAA;IAAkB,GAAA;EAAA,IACpB,OAAA;EASY;;;kCADW,KAAA;IACX,GAAA;IAAA,UAAA;IAAA,cAAA;IAAA,GAAA;EAAA,IAMV,eAAA;IAAoB,GAAA;EAAA,IACtB,OAAA,CAAQ,CAAA;AAAA"}
@@ -3,7 +3,7 @@ import { authConfig } from "./auth-config.mjs";
3
3
  import { randomUUID } from "node:crypto";
4
4
  import { createSigner, createVerifier } from "fast-jwt";
5
5
 
6
- //#region ../auth/src/services/jwt.ts
6
+ //#region ../../packages/auth/src/services/jwt.ts
7
7
  const getSecretKey = () => authConfig.accessToken.secret();
8
8
  const getAlgorithm = () => authConfig.accessToken.algorithm();
9
9
  const getRefreshSecretKey = () => authConfig.refreshToken.secret() || getSecretKey();
@@ -1 +1 @@
1
- {"version":3,"file":"jwt.mjs","names":[],"sources":["../../../../../../../auth/src/services/jwt.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { createSigner, createVerifier, type SignerOptions, type VerifierOptions } from \"fast-jwt\";\nimport { AuthErrorCodes } from \"../utils/auth-error-codes\";\nimport { authConfig } from \"./auth-config\";\n\nconst getSecretKey = () => authConfig.accessToken.secret();\nconst getAlgorithm = () => authConfig.accessToken.algorithm();\n\n// Refresh tokens may declare their own secret; when unset/empty we fall back to\n// the access-token secret (the documented optional behavior).\nconst getRefreshSecretKey = () => authConfig.refreshToken.secret() || getSecretKey();\n\n/**\n * Token class. Stamped as the `tokenType` claim on every signed token and\n * checked on verify so an access token can never be accepted where a refresh\n * token is expected (and vice versa) — even when both share the same secret\n * under the documented refresh-secret fallback. Legacy tokens minted before\n * this claim existed carry no `tokenType` and remain accepted; a *mismatched*\n * type is always rejected.\n */\nexport type TokenType = \"access\" | \"refresh\";\n\nconst ACCESS_TOKEN_TYPE: TokenType = \"access\";\nconst REFRESH_TOKEN_TYPE: TokenType = \"refresh\";\n\n/**\n * Error codes that mean \"the credential itself is bad\", as opposed to \"this\n * server could not check it\". This is an ALLOWLIST and must stay one. The codes\n * deliberately left out — `FAST_JWT_INVALID_KEY`, `FAST_JWT_MISSING_KEY`,\n * `FAST_JWT_KEY_FETCHING_ERROR`, `FAST_JWT_INVALID_OPTION`,\n * `FAST_JWT_VERIFY_ERROR`, and `FAST_JWT_SIGN_ERROR` — describe a broken\n * server, not a broken token, and so does every unknown future code.\n */\nconst INVALID_CREDENTIAL_ERROR_CODES = new Set<string>([\n \"FAST_JWT_MALFORMED\",\n \"FAST_JWT_INVALID_SIGNATURE\",\n \"FAST_JWT_MISSING_SIGNATURE\",\n \"FAST_JWT_INVALID_ALGORITHM\",\n \"FAST_JWT_EXPIRED\",\n \"FAST_JWT_INACTIVE\",\n // How a token carrying no `exp` is rejected — `jwt.verify` forces `exp` into\n // `requiredClaims`, so this code IS the missing-deadline guard firing.\n \"FAST_JWT_MISSING_REQUIRED_CLAIM\",\n \"FAST_JWT_INVALID_CLAIM_VALUE\",\n \"FAST_JWT_INVALID_CLAIM_TYPE\",\n \"FAST_JWT_INVALID_CRIT_HEADER\",\n \"FAST_JWT_INVALID_TYPE\",\n \"FAST_JWT_INVALID_PAYLOAD\",\n AuthErrorCodes.InvalidTokenType,\n]);\n\n/**\n * Only coded credential failures become authentication misses. Plain errors,\n * including missing-secret configuration failures, propagate to the central\n * server-error path instead of reading as \"everyone's token is bad\".\n */\nexport function isInvalidCredentialError(error: unknown): boolean {\n const code = (error as { code?: unknown } | null | undefined)?.code;\n\n return typeof code === \"string\" && INVALID_CREDENTIAL_ERROR_CODES.has(code);\n}\n\n/**\n * A `tokenType` claim that does not match what the caller expects. This IS a\n * bad credential (an access-token cookie can only hold a refresh token\n * through an app bug) — but unlike `fast-jwt`'s own rejections it was\n * previously a plain `Error`, unclassifiable by a caller that wants to answer\n * 401 without string-matching a message that could reword on any release.\n * `code` mirrors how `fast-jwt`'s `TokenError` carries its own code, so a\n * caller can classify both through `isInvalidCredentialError` above.\n */\nexport class TokenTypeError extends Error {\n readonly code = AuthErrorCodes.InvalidTokenType;\n\n constructor(expected: TokenType, actual: string) {\n super(`Invalid token type: expected \"${expected}\", received \"${actual}\".`);\n this.name = \"TokenTypeError\";\n }\n}\n\n/**\n * Reject the token when its `tokenType` claim is present and does not match the\n * expected class. Absent claim ⇒ legacy token, accepted (backward compatible).\n */\nfunction assertTokenType(decoded: unknown, expected: TokenType): void {\n const actual = (decoded as { tokenType?: unknown } | null | undefined)?.tokenType;\n\n if (typeof actual === \"string\" && actual !== expected) {\n throw new TokenTypeError(expected, actual);\n }\n}\n\n/**\n * Claims no token may be missing, whatever the caller asks for.\n *\n * A JWT with no `exp` is not \"a token with a long life\" — it is a credential\n * with *no* life, because a verifier with no deadline to check simply succeeds,\n * forever (measured against `fast-jwt@6.2.4`: a token with no `exp` verifies\n * unchanged at `clockTimestamp` + 100 years). Nothing in this package can mint\n * one as of 4.12.0, but tokens minted by an earlier version are already in the\n * wild, so the rejection lives on the *verify* side where it catches a token\n * from any version, including one signed by a service this package never ran.\n *\n * There is no legitimate source to preserve: an app that wants a token that\n * effectively never expires sets `expiresIn: NO_EXPIRATION` (`\"100y\"`), which\n * mints a real `exp` roughly a century out (measured: `ms(\"100y\")` ⇒\n * `3155760000000`, `exp - iat` ⇒ `3155760000` seconds). \"No deadline\" and \"a\n * distant deadline\" are different things, and only the second one is asked for.\n */\nconst REQUIRED_CLAIMS = [\"exp\"];\n\n/**\n * Union the caller's `requiredClaims` with the mandatory ones — a caller may\n * add requirements, never drop them.\n */\nfunction withRequiredClaims(callerClaims?: string[]): string[] {\n if (!callerClaims?.length) return REQUIRED_CLAIMS;\n\n return [...new Set([...callerClaims, ...REQUIRED_CLAIMS])];\n}\n\nexport const jwt = {\n /**\n * Generate a new JWT token for the user.\n * @param payload The payload to encode in the JWT token.\n */\n async generate(\n payload: any,\n {\n key = getSecretKey(),\n algorithm = getAlgorithm(),\n ...options\n }: SignerOptions & { key?: string } = {},\n ): Promise<string> {\n // Create a signer function with predefined options\n const sign = createSigner({ key, ...options, algorithm });\n\n const token = await sign({ jti: randomUUID(), ...payload, tokenType: ACCESS_TOKEN_TYPE });\n return token;\n },\n\n /**\n * Verify the given token.\n * @param token The JWT token to verify.\n * @returns The decoded token payload if verification is successful.\n */\n async verify<T = unknown>(\n token: string,\n {\n key = getSecretKey(),\n algorithms = [getAlgorithm()],\n requiredClaims,\n ...options\n }: VerifierOptions & { key?: string } = {},\n ): Promise<T> {\n const verify = createVerifier({\n key,\n ...options,\n algorithms,\n requiredClaims: withRequiredClaims(requiredClaims),\n });\n\n const decoded = await verify(token as string);\n\n assertTokenType(decoded, ACCESS_TOKEN_TYPE);\n\n return decoded;\n },\n\n /**\n * Generate a new refresh token for the user.\n */\n async generateRefreshToken(\n payload: any,\n {\n key = getRefreshSecretKey(),\n expiresIn,\n algorithm = getAlgorithm(),\n ...options\n }: SignerOptions & { key?: string } = {},\n ): Promise<string> {\n const sign = createSigner({ key, expiresIn, algorithm, ...options });\n return sign({ jti: randomUUID(), ...payload, tokenType: REFRESH_TOKEN_TYPE });\n },\n\n /**\n * Verify the given refresh token.\n */\n async verifyRefreshToken<T = unknown>(\n token: string,\n {\n key = getRefreshSecretKey(),\n algorithms = [getAlgorithm()],\n requiredClaims,\n ...options\n }: VerifierOptions & { key?: string } = {},\n ): Promise<T> {\n const verify = createVerifier({\n key,\n algorithms,\n ...options,\n requiredClaims: withRequiredClaims(requiredClaims),\n });\n\n const decoded = await verify(token);\n\n assertTokenType(decoded, REFRESH_TOKEN_TYPE);\n\n return decoded;\n },\n};\n"],"mappings":";;;;;;AAKA,MAAM,qBAAqB,WAAW,YAAY,OAAO;AACzD,MAAM,qBAAqB,WAAW,YAAY,UAAU;AAI5D,MAAM,4BAA4B,WAAW,aAAa,OAAO,KAAK,aAAa;AAYnF,MAAM,oBAA+B;AACrC,MAAM,qBAAgC;;;;;;;;;AAUtC,MAAM,iCAAiC,IAAI,IAAY;CACrD;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;;AAEF,CAAC;;;;;;AAOD,SAAgB,yBAAyB,OAAyB;CAChE,MAAM,OAAQ,OAAiD;CAE/D,OAAO,OAAO,SAAS,YAAY,+BAA+B,IAAI,IAAI;AAC5E;;;;;;;;;;AAWA,IAAa,iBAAb,cAAoC,MAAM;CAGxC,YAAY,UAAqB,QAAgB;EAC/C,MAAM,iCAAiC,SAAS,eAAe,OAAO,GAAG;;EACzE,KAAK,OAAO;CACd;AACF;;;;;AAMA,SAAS,gBAAgB,SAAkB,UAA2B;CACpE,MAAM,SAAU,SAAwD;CAExE,IAAI,OAAO,WAAW,YAAY,WAAW,UAC3C,MAAM,IAAI,eAAe,UAAU,MAAM;AAE7C;;;;;;;;;;;;;;;;;;AAmBA,MAAM,kBAAkB,CAAC,KAAK;;;;;AAM9B,SAAS,mBAAmB,cAAmC;CAC7D,IAAI,CAAC,cAAc,QAAQ,OAAO;CAElC,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,cAAc,GAAG,eAAe,CAAC,CAAC;AAC3D;AAEA,MAAa,MAAM;;;;;CAKjB,MAAM,SACJ,SACA,EACE,MAAM,aAAa,GACnB,YAAY,aAAa,GACzB,GAAG,YACiC,CAAC,GACtB;EAKjB,OAAO,MAHM,aAAa;GAAE;GAAK,GAAG;GAAS;EAAU,CAEhC,CAAC,CAAC;GAAE,KAAK,WAAW;GAAG,GAAG;GAAS,WAAW;EAAkB,CAAC;CAE1F;;;;;;CAOA,MAAM,OACJ,OACA,EACE,MAAM,aAAa,GACnB,aAAa,CAAC,aAAa,CAAC,GAC5B,gBACA,GAAG,YACmC,CAAC,GAC7B;EAQZ,MAAM,UAAU,MAPD,eAAe;GAC5B;GACA,GAAG;GACH;GACA,gBAAgB,mBAAmB,cAAc;EACnD,CAE2B,CAAC,CAAC,KAAe;EAE5C,gBAAgB,SAAS,iBAAiB;EAE1C,OAAO;CACT;;;;CAKA,MAAM,qBACJ,SACA,EACE,MAAM,oBAAoB,GAC1B,WACA,YAAY,aAAa,GACzB,GAAG,YACiC,CAAC,GACtB;EAEjB,OADa,aAAa;GAAE;GAAK;GAAW;GAAW,GAAG;EAAQ,CACxD,CAAC,CAAC;GAAE,KAAK,WAAW;GAAG,GAAG;GAAS,WAAW;EAAmB,CAAC;CAC9E;;;;CAKA,MAAM,mBACJ,OACA,EACE,MAAM,oBAAoB,GAC1B,aAAa,CAAC,aAAa,CAAC,GAC5B,gBACA,GAAG,YACmC,CAAC,GAC7B;EAQZ,MAAM,UAAU,MAPD,eAAe;GAC5B;GACA;GACA,GAAG;GACH,gBAAgB,mBAAmB,cAAc;EACnD,CAE2B,CAAC,CAAC,KAAK;EAElC,gBAAgB,SAAS,kBAAkB;EAE3C,OAAO;CACT;AACF"}
1
+ {"version":3,"file":"jwt.mjs","names":[],"sources":["../../../../../../../../packages/auth/src/services/jwt.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { createSigner, createVerifier, type SignerOptions, type VerifierOptions } from \"fast-jwt\";\nimport { AuthErrorCodes } from \"../utils/auth-error-codes\";\nimport { authConfig } from \"./auth-config\";\n\nconst getSecretKey = () => authConfig.accessToken.secret();\nconst getAlgorithm = () => authConfig.accessToken.algorithm();\n\n// Refresh tokens may declare their own secret; when unset/empty we fall back to\n// the access-token secret (the documented optional behavior).\nconst getRefreshSecretKey = () => authConfig.refreshToken.secret() || getSecretKey();\n\n/**\n * Token class. Stamped as the `tokenType` claim on every signed token and\n * checked on verify so an access token can never be accepted where a refresh\n * token is expected (and vice versa) — even when both share the same secret\n * under the documented refresh-secret fallback. Legacy tokens minted before\n * this claim existed carry no `tokenType` and remain accepted; a *mismatched*\n * type is always rejected.\n */\nexport type TokenType = \"access\" | \"refresh\";\n\nconst ACCESS_TOKEN_TYPE: TokenType = \"access\";\nconst REFRESH_TOKEN_TYPE: TokenType = \"refresh\";\n\n/**\n * Error codes that mean \"the credential itself is bad\", as opposed to \"this\n * server could not check it\". This is an ALLOWLIST and must stay one. The codes\n * deliberately left out — `FAST_JWT_INVALID_KEY`, `FAST_JWT_MISSING_KEY`,\n * `FAST_JWT_KEY_FETCHING_ERROR`, `FAST_JWT_INVALID_OPTION`,\n * `FAST_JWT_VERIFY_ERROR`, and `FAST_JWT_SIGN_ERROR` — describe a broken\n * server, not a broken token, and so does every unknown future code.\n */\nconst INVALID_CREDENTIAL_ERROR_CODES = new Set<string>([\n \"FAST_JWT_MALFORMED\",\n \"FAST_JWT_INVALID_SIGNATURE\",\n \"FAST_JWT_MISSING_SIGNATURE\",\n \"FAST_JWT_INVALID_ALGORITHM\",\n \"FAST_JWT_EXPIRED\",\n \"FAST_JWT_INACTIVE\",\n // How a token carrying no `exp` is rejected — `jwt.verify` forces `exp` into\n // `requiredClaims`, so this code IS the missing-deadline guard firing.\n \"FAST_JWT_MISSING_REQUIRED_CLAIM\",\n \"FAST_JWT_INVALID_CLAIM_VALUE\",\n \"FAST_JWT_INVALID_CLAIM_TYPE\",\n \"FAST_JWT_INVALID_CRIT_HEADER\",\n \"FAST_JWT_INVALID_TYPE\",\n \"FAST_JWT_INVALID_PAYLOAD\",\n AuthErrorCodes.InvalidTokenType,\n]);\n\n/**\n * Only coded credential failures become authentication misses. Plain errors,\n * including missing-secret configuration failures, propagate to the central\n * server-error path instead of reading as \"everyone's token is bad\".\n */\nexport function isInvalidCredentialError(error: unknown): boolean {\n const code = (error as { code?: unknown } | null | undefined)?.code;\n\n return typeof code === \"string\" && INVALID_CREDENTIAL_ERROR_CODES.has(code);\n}\n\n/**\n * A `tokenType` claim that does not match what the caller expects. This IS a\n * bad credential (an access-token cookie can only hold a refresh token\n * through an app bug) — but unlike `fast-jwt`'s own rejections it was\n * previously a plain `Error`, unclassifiable by a caller that wants to answer\n * 401 without string-matching a message that could reword on any release.\n * `code` mirrors how `fast-jwt`'s `TokenError` carries its own code, so a\n * caller can classify both through `isInvalidCredentialError` above.\n */\nexport class TokenTypeError extends Error {\n readonly code = AuthErrorCodes.InvalidTokenType;\n\n constructor(expected: TokenType, actual: string) {\n super(`Invalid token type: expected \"${expected}\", received \"${actual}\".`);\n this.name = \"TokenTypeError\";\n }\n}\n\n/**\n * Reject the token when its `tokenType` claim is present and does not match the\n * expected class. Absent claim ⇒ legacy token, accepted (backward compatible).\n */\nfunction assertTokenType(decoded: unknown, expected: TokenType): void {\n const actual = (decoded as { tokenType?: unknown } | null | undefined)?.tokenType;\n\n if (typeof actual === \"string\" && actual !== expected) {\n throw new TokenTypeError(expected, actual);\n }\n}\n\n/**\n * Claims no token may be missing, whatever the caller asks for.\n *\n * A JWT with no `exp` is not \"a token with a long life\" — it is a credential\n * with *no* life, because a verifier with no deadline to check simply succeeds,\n * forever (measured against `fast-jwt@6.2.4`: a token with no `exp` verifies\n * unchanged at `clockTimestamp` + 100 years). Nothing in this package can mint\n * one as of 4.12.0, but tokens minted by an earlier version are already in the\n * wild, so the rejection lives on the *verify* side where it catches a token\n * from any version, including one signed by a service this package never ran.\n *\n * There is no legitimate source to preserve: an app that wants a token that\n * effectively never expires sets `expiresIn: NO_EXPIRATION` (`\"100y\"`), which\n * mints a real `exp` roughly a century out (measured: `ms(\"100y\")` ⇒\n * `3155760000000`, `exp - iat` ⇒ `3155760000` seconds). \"No deadline\" and \"a\n * distant deadline\" are different things, and only the second one is asked for.\n */\nconst REQUIRED_CLAIMS = [\"exp\"];\n\n/**\n * Union the caller's `requiredClaims` with the mandatory ones — a caller may\n * add requirements, never drop them.\n */\nfunction withRequiredClaims(callerClaims?: string[]): string[] {\n if (!callerClaims?.length) return REQUIRED_CLAIMS;\n\n return [...new Set([...callerClaims, ...REQUIRED_CLAIMS])];\n}\n\nexport const jwt = {\n /**\n * Generate a new JWT token for the user.\n * @param payload The payload to encode in the JWT token.\n */\n async generate(\n payload: any,\n {\n key = getSecretKey(),\n algorithm = getAlgorithm(),\n ...options\n }: SignerOptions & { key?: string } = {},\n ): Promise<string> {\n // Create a signer function with predefined options\n const sign = createSigner({ key, ...options, algorithm });\n\n const token = await sign({ jti: randomUUID(), ...payload, tokenType: ACCESS_TOKEN_TYPE });\n return token;\n },\n\n /**\n * Verify the given token.\n * @param token The JWT token to verify.\n * @returns The decoded token payload if verification is successful.\n */\n async verify<T = unknown>(\n token: string,\n {\n key = getSecretKey(),\n algorithms = [getAlgorithm()],\n requiredClaims,\n ...options\n }: VerifierOptions & { key?: string } = {},\n ): Promise<T> {\n const verify = createVerifier({\n key,\n ...options,\n algorithms,\n requiredClaims: withRequiredClaims(requiredClaims),\n });\n\n const decoded = await verify(token as string);\n\n assertTokenType(decoded, ACCESS_TOKEN_TYPE);\n\n return decoded;\n },\n\n /**\n * Generate a new refresh token for the user.\n */\n async generateRefreshToken(\n payload: any,\n {\n key = getRefreshSecretKey(),\n expiresIn,\n algorithm = getAlgorithm(),\n ...options\n }: SignerOptions & { key?: string } = {},\n ): Promise<string> {\n const sign = createSigner({ key, expiresIn, algorithm, ...options });\n return sign({ jti: randomUUID(), ...payload, tokenType: REFRESH_TOKEN_TYPE });\n },\n\n /**\n * Verify the given refresh token.\n */\n async verifyRefreshToken<T = unknown>(\n token: string,\n {\n key = getRefreshSecretKey(),\n algorithms = [getAlgorithm()],\n requiredClaims,\n ...options\n }: VerifierOptions & { key?: string } = {},\n ): Promise<T> {\n const verify = createVerifier({\n key,\n algorithms,\n ...options,\n requiredClaims: withRequiredClaims(requiredClaims),\n });\n\n const decoded = await verify(token);\n\n assertTokenType(decoded, REFRESH_TOKEN_TYPE);\n\n return decoded;\n },\n};\n"],"mappings":";;;;;;AAKA,MAAM,qBAAqB,WAAW,YAAY,OAAO;AACzD,MAAM,qBAAqB,WAAW,YAAY,UAAU;AAI5D,MAAM,4BAA4B,WAAW,aAAa,OAAO,KAAK,aAAa;AAYnF,MAAM,oBAA+B;AACrC,MAAM,qBAAgC;;;;;;;;;AAUtC,MAAM,iCAAiC,IAAI,IAAY;CACrD;CACA;CACA;CACA;CACA;CACA;CAGA;CACA;CACA;CACA;CACA;CACA;;AAEF,CAAC;;;;;;AAOD,SAAgB,yBAAyB,OAAyB;CAChE,MAAM,OAAQ,OAAiD;CAE/D,OAAO,OAAO,SAAS,YAAY,+BAA+B,IAAI,IAAI;AAC5E;;;;;;;;;;AAWA,IAAa,iBAAb,cAAoC,MAAM;CAGxC,YAAY,UAAqB,QAAgB;EAC/C,MAAM,iCAAiC,SAAS,eAAe,OAAO,GAAG;;EACzE,KAAK,OAAO;CACd;AACF;;;;;AAMA,SAAS,gBAAgB,SAAkB,UAA2B;CACpE,MAAM,SAAU,SAAwD;CAExE,IAAI,OAAO,WAAW,YAAY,WAAW,UAC3C,MAAM,IAAI,eAAe,UAAU,MAAM;AAE7C;;;;;;;;;;;;;;;;;;AAmBA,MAAM,kBAAkB,CAAC,KAAK;;;;;AAM9B,SAAS,mBAAmB,cAAmC;CAC7D,IAAI,CAAC,cAAc,QAAQ,OAAO;CAElC,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,cAAc,GAAG,eAAe,CAAC,CAAC;AAC3D;AAEA,MAAa,MAAM;;;;;CAKjB,MAAM,SACJ,SACA,EACE,MAAM,aAAa,GACnB,YAAY,aAAa,GACzB,GAAG,YACiC,CAAC,GACtB;EAKjB,OAAO,MAHM,aAAa;GAAE;GAAK,GAAG;GAAS;EAAU,CAEhC,CAAC,CAAC;GAAE,KAAK,WAAW;GAAG,GAAG;GAAS,WAAW;EAAkB,CAAC;CAE1F;;;;;;CAOA,MAAM,OACJ,OACA,EACE,MAAM,aAAa,GACnB,aAAa,CAAC,aAAa,CAAC,GAC5B,gBACA,GAAG,YACmC,CAAC,GAC7B;EAQZ,MAAM,UAAU,MAPD,eAAe;GAC5B;GACA,GAAG;GACH;GACA,gBAAgB,mBAAmB,cAAc;EACnD,CAE2B,CAAC,CAAC,KAAe;EAE5C,gBAAgB,SAAS,iBAAiB;EAE1C,OAAO;CACT;;;;CAKA,MAAM,qBACJ,SACA,EACE,MAAM,oBAAoB,GAC1B,WACA,YAAY,aAAa,GACzB,GAAG,YACiC,CAAC,GACtB;EAEjB,OADa,aAAa;GAAE;GAAK;GAAW;GAAW,GAAG;EAAQ,CACxD,CAAC,CAAC;GAAE,KAAK,WAAW;GAAG,GAAG;GAAS,WAAW;EAAmB,CAAC;CAC9E;;;;CAKA,MAAM,mBACJ,OACA,EACE,MAAM,oBAAoB,GAC1B,aAAa,CAAC,aAAa,CAAC,GAC5B,gBACA,GAAG,YACmC,CAAC,GAC7B;EAQZ,MAAM,UAAU,MAPD,eAAe;GAC5B;GACA;GACA,GAAG;GACH,gBAAgB,mBAAmB,cAAc;EACnD,CAE2B,CAAC,CAAC,KAAK;EAElC,gBAAgB,SAAS,kBAAkB;EAE3C,OAAO;CACT;AACF"}
@@ -1,6 +1,6 @@
1
1
  import { Auth } from "../models/auth.model.mjs";
2
2
  import { OneTimeToken, OneTimeTokenPurpose } from "../models/one-time-token/one-time-token.model.mjs";
3
- //#region ../auth/src/services/one-time-tokens.d.ts
3
+ //#region ../../packages/auth/src/services/one-time-tokens.d.ts
4
4
  /** The raw token handed to the notification, with its expiry. */
5
5
  type IssuedOneTimeToken = {
6
6
  token: string;
@@ -1 +1 @@
1
- {"version":3,"file":"one-time-tokens.d.mts","names":[],"sources":["../../../../../../../auth/src/services/one-time-tokens.ts"],"mappings":";;;;KAOY,kBAAA;EACV,KAAA;EACA,SAAA,EAAW,IAAI;AAAA;;iBAID,iBAAA,WAA4B,YAAY;;iBAKxC,gBAAA,CAAiB,KAAa;;;AAT7B;AAIjB;;;;iBAgBsB,iBAAA,CACpB,IAAA,EAAM,IAAA,EACN,OAAA,EAAS,mBAAA,EACT,WAAA,UACA,kBAAA,aACC,OAAA,CAAQ,kBAAA;AAhBX;;;;AAA8C;AAW9C;;;AAXA,iBAuCsB,mBAAA,CACpB,QAAA,UACA,OAAA,EAAS,mBAAA,GACR,OAAA,CAAQ,IAAA"}
1
+ {"version":3,"file":"one-time-tokens.d.mts","names":[],"sources":["../../../../../../../../packages/auth/src/services/one-time-tokens.ts"],"mappings":";;;;KAOY,kBAAA;EACV,KAAA;EACA,SAAA,EAAW,IAAI;AAAA;;iBAID,iBAAA,WAA4B,YAAY;;iBAKxC,gBAAA,CAAiB,KAAa;;;AAT7B;AAIjB;;;;iBAgBsB,iBAAA,CACpB,IAAA,EAAM,IAAA,EACN,OAAA,EAAS,mBAAA,EACT,WAAA,UACA,kBAAA,aACC,OAAA,CAAQ,kBAAA;AAhBX;;;;AAA8C;AAW9C;;;AAXA,iBAuCsB,mBAAA,CACpB,QAAA,UACA,OAAA,EAAS,mBAAA,GACR,OAAA,CAAQ,IAAA"}
@@ -4,7 +4,7 @@ import { InvalidOneTimeTokenError } from "../errors/invalid-one-time-token.error
4
4
  import { config } from "@warlock.js/core";
5
5
  import { createHash, randomBytes } from "node:crypto";
6
6
 
7
- //#region ../auth/src/services/one-time-tokens.ts
7
+ //#region ../../packages/auth/src/services/one-time-tokens.ts
8
8
  /** Resolve the active one-time-token model — the default or `auth.oneTimeToken.model`. */
9
9
  function oneTimeTokenModel() {
10
10
  return config.key("auth.oneTimeToken.model", OneTimeToken);