better-auth 1.2.6-beta.7 → 1.2.7-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (273) hide show
  1. package/dist/adapters/drizzle-adapter/index.cjs +186 -249
  2. package/dist/adapters/drizzle-adapter/index.d.cts +11 -49
  3. package/dist/adapters/drizzle-adapter/index.d.mts +11 -49
  4. package/dist/adapters/drizzle-adapter/index.d.ts +11 -49
  5. package/dist/adapters/drizzle-adapter/index.mjs +186 -249
  6. package/dist/adapters/index.cjs +26 -0
  7. package/dist/adapters/index.d.cts +17 -0
  8. package/dist/adapters/index.d.mts +17 -0
  9. package/dist/adapters/index.d.ts +17 -0
  10. package/dist/adapters/index.mjs +20 -0
  11. package/dist/adapters/kysely-adapter/index.cjs +7 -7
  12. package/dist/adapters/kysely-adapter/index.d.cts +17 -49
  13. package/dist/adapters/kysely-adapter/index.d.mts +17 -49
  14. package/dist/adapters/kysely-adapter/index.d.ts +17 -49
  15. package/dist/adapters/kysely-adapter/index.mjs +8 -8
  16. package/dist/adapters/memory-adapter/index.cjs +7 -8
  17. package/dist/adapters/memory-adapter/index.d.cts +9 -49
  18. package/dist/adapters/memory-adapter/index.d.mts +9 -49
  19. package/dist/adapters/memory-adapter/index.d.ts +9 -49
  20. package/dist/adapters/memory-adapter/index.mjs +8 -9
  21. package/dist/adapters/mongodb-adapter/index.cjs +2 -2
  22. package/dist/adapters/mongodb-adapter/index.d.cts +4 -4
  23. package/dist/adapters/mongodb-adapter/index.d.mts +4 -4
  24. package/dist/adapters/mongodb-adapter/index.d.ts +4 -4
  25. package/dist/adapters/mongodb-adapter/index.mjs +3 -3
  26. package/dist/adapters/prisma-adapter/index.cjs +130 -203
  27. package/dist/adapters/prisma-adapter/index.d.cts +17 -49
  28. package/dist/adapters/prisma-adapter/index.d.mts +17 -49
  29. package/dist/adapters/prisma-adapter/index.d.ts +17 -49
  30. package/dist/adapters/prisma-adapter/index.mjs +131 -204
  31. package/dist/adapters/test.cjs +710 -377
  32. package/dist/adapters/test.d.cts +64 -5
  33. package/dist/adapters/test.d.mts +64 -5
  34. package/dist/adapters/test.d.ts +64 -5
  35. package/dist/adapters/test.mjs +712 -380
  36. package/dist/api/index.cjs +61 -25
  37. package/dist/api/index.d.cts +3 -3
  38. package/dist/api/index.d.mts +3 -3
  39. package/dist/api/index.d.ts +3 -3
  40. package/dist/api/index.mjs +63 -27
  41. package/dist/client/index.d.cts +3 -3
  42. package/dist/client/index.d.mts +3 -3
  43. package/dist/client/index.d.ts +3 -3
  44. package/dist/client/plugins/index.cjs +13 -15
  45. package/dist/client/plugins/index.d.cts +80 -19
  46. package/dist/client/plugins/index.d.mts +80 -19
  47. package/dist/client/plugins/index.d.ts +80 -19
  48. package/dist/client/plugins/index.mjs +13 -16
  49. package/dist/client/react/index.cjs +4 -4
  50. package/dist/client/react/index.d.cts +3 -3
  51. package/dist/client/react/index.d.mts +3 -3
  52. package/dist/client/react/index.d.ts +3 -3
  53. package/dist/client/solid/index.d.cts +3 -3
  54. package/dist/client/solid/index.d.mts +3 -3
  55. package/dist/client/solid/index.d.ts +3 -3
  56. package/dist/client/svelte/index.d.cts +3 -3
  57. package/dist/client/svelte/index.d.mts +3 -3
  58. package/dist/client/svelte/index.d.ts +3 -3
  59. package/dist/client/vue/index.d.cts +3 -3
  60. package/dist/client/vue/index.d.mts +3 -3
  61. package/dist/client/vue/index.d.ts +3 -3
  62. package/dist/cookies/index.cjs +13 -2
  63. package/dist/cookies/index.d.cts +3 -3
  64. package/dist/cookies/index.d.mts +3 -3
  65. package/dist/cookies/index.d.ts +3 -3
  66. package/dist/cookies/index.mjs +13 -2
  67. package/dist/db/index.cjs +6 -5
  68. package/dist/db/index.d.cts +4 -4
  69. package/dist/db/index.d.mts +4 -4
  70. package/dist/db/index.d.ts +4 -4
  71. package/dist/db/index.mjs +7 -6
  72. package/dist/index.cjs +11 -7
  73. package/dist/index.d.cts +4 -4
  74. package/dist/index.d.mts +4 -4
  75. package/dist/index.d.ts +4 -4
  76. package/dist/index.mjs +14 -10
  77. package/dist/integrations/next-js.cjs +4 -5
  78. package/dist/integrations/next-js.d.cts +3 -3
  79. package/dist/integrations/next-js.d.mts +3 -3
  80. package/dist/integrations/next-js.d.ts +3 -3
  81. package/dist/integrations/next-js.mjs +5 -6
  82. package/dist/integrations/node.d.cts +3 -3
  83. package/dist/integrations/node.d.mts +3 -3
  84. package/dist/integrations/node.d.ts +3 -3
  85. package/dist/integrations/react-start.cjs +5 -6
  86. package/dist/integrations/react-start.d.cts +3 -3
  87. package/dist/integrations/react-start.d.mts +3 -3
  88. package/dist/integrations/react-start.d.ts +3 -3
  89. package/dist/integrations/react-start.mjs +6 -7
  90. package/dist/integrations/svelte-kit.d.cts +3 -3
  91. package/dist/integrations/svelte-kit.d.mts +3 -3
  92. package/dist/integrations/svelte-kit.d.ts +3 -3
  93. package/dist/oauth2/index.d.cts +5 -5
  94. package/dist/oauth2/index.d.mts +5 -5
  95. package/dist/oauth2/index.d.ts +5 -5
  96. package/dist/plugins/access/index.d.cts +1 -1
  97. package/dist/plugins/access/index.d.mts +1 -1
  98. package/dist/plugins/access/index.d.ts +1 -1
  99. package/dist/plugins/admin/access/index.d.cts +1 -1
  100. package/dist/plugins/admin/access/index.d.mts +1 -1
  101. package/dist/plugins/admin/access/index.d.ts +1 -1
  102. package/dist/plugins/admin/index.cjs +4 -4
  103. package/dist/plugins/admin/index.d.cts +74 -14
  104. package/dist/plugins/admin/index.d.mts +74 -14
  105. package/dist/plugins/admin/index.d.ts +74 -14
  106. package/dist/plugins/admin/index.mjs +5 -5
  107. package/dist/plugins/anonymous/index.cjs +4 -5
  108. package/dist/plugins/anonymous/index.d.cts +3 -3
  109. package/dist/plugins/anonymous/index.d.mts +3 -3
  110. package/dist/plugins/anonymous/index.d.ts +3 -3
  111. package/dist/plugins/anonymous/index.mjs +5 -6
  112. package/dist/plugins/bearer/index.cjs +2 -2
  113. package/dist/plugins/bearer/index.d.cts +3 -3
  114. package/dist/plugins/bearer/index.d.mts +3 -3
  115. package/dist/plugins/bearer/index.d.ts +3 -3
  116. package/dist/plugins/bearer/index.mjs +3 -3
  117. package/dist/plugins/captcha/index.cjs +110 -45
  118. package/dist/plugins/captcha/index.d.cts +26 -6
  119. package/dist/plugins/captcha/index.d.mts +26 -6
  120. package/dist/plugins/captcha/index.d.ts +26 -6
  121. package/dist/plugins/captcha/index.mjs +110 -45
  122. package/dist/plugins/custom-session/index.cjs +24 -5
  123. package/dist/plugins/custom-session/index.d.cts +25 -6
  124. package/dist/plugins/custom-session/index.d.mts +25 -6
  125. package/dist/plugins/custom-session/index.d.ts +25 -6
  126. package/dist/plugins/custom-session/index.mjs +25 -6
  127. package/dist/plugins/email-otp/index.cjs +96 -30
  128. package/dist/plugins/email-otp/index.d.cts +33 -10
  129. package/dist/plugins/email-otp/index.d.mts +33 -10
  130. package/dist/plugins/email-otp/index.d.ts +33 -10
  131. package/dist/plugins/email-otp/index.mjs +97 -31
  132. package/dist/plugins/generic-oauth/index.cjs +81 -20
  133. package/dist/plugins/generic-oauth/index.d.cts +46 -3
  134. package/dist/plugins/generic-oauth/index.d.mts +46 -3
  135. package/dist/plugins/generic-oauth/index.d.ts +46 -3
  136. package/dist/plugins/generic-oauth/index.mjs +82 -21
  137. package/dist/plugins/haveibeenpwned/index.cjs +98 -0
  138. package/dist/plugins/haveibeenpwned/index.d.cts +36 -0
  139. package/dist/plugins/haveibeenpwned/index.d.mts +36 -0
  140. package/dist/plugins/haveibeenpwned/index.d.ts +36 -0
  141. package/dist/plugins/haveibeenpwned/index.mjs +96 -0
  142. package/dist/plugins/index.cjs +583 -19
  143. package/dist/plugins/index.d.cts +7 -5
  144. package/dist/plugins/index.d.mts +7 -5
  145. package/dist/plugins/index.d.ts +7 -5
  146. package/dist/plugins/index.mjs +583 -21
  147. package/dist/plugins/jwt/index.cjs +45 -21
  148. package/dist/plugins/jwt/index.d.cts +52 -6
  149. package/dist/plugins/jwt/index.d.mts +52 -6
  150. package/dist/plugins/jwt/index.d.ts +52 -6
  151. package/dist/plugins/jwt/index.mjs +46 -22
  152. package/dist/plugins/magic-link/index.cjs +3 -3
  153. package/dist/plugins/magic-link/index.mjs +4 -4
  154. package/dist/plugins/multi-session/index.cjs +3 -3
  155. package/dist/plugins/multi-session/index.d.cts +3 -3
  156. package/dist/plugins/multi-session/index.d.mts +3 -3
  157. package/dist/plugins/multi-session/index.d.ts +3 -3
  158. package/dist/plugins/multi-session/index.mjs +4 -4
  159. package/dist/plugins/oauth-proxy/index.cjs +4 -4
  160. package/dist/plugins/oauth-proxy/index.d.cts +3 -3
  161. package/dist/plugins/oauth-proxy/index.d.mts +3 -3
  162. package/dist/plugins/oauth-proxy/index.d.ts +3 -3
  163. package/dist/plugins/oauth-proxy/index.mjs +5 -5
  164. package/dist/plugins/oidc-provider/index.cjs +227 -8
  165. package/dist/plugins/oidc-provider/index.d.cts +215 -3
  166. package/dist/plugins/oidc-provider/index.d.mts +215 -3
  167. package/dist/plugins/oidc-provider/index.d.ts +215 -3
  168. package/dist/plugins/oidc-provider/index.mjs +228 -9
  169. package/dist/plugins/one-tap/index.cjs +5 -5
  170. package/dist/plugins/one-tap/index.mjs +6 -6
  171. package/dist/plugins/one-time-token/index.cjs +119 -0
  172. package/dist/plugins/one-time-token/index.d.cts +134 -0
  173. package/dist/plugins/one-time-token/index.d.mts +134 -0
  174. package/dist/plugins/one-time-token/index.d.ts +134 -0
  175. package/dist/plugins/one-time-token/index.mjs +117 -0
  176. package/dist/plugins/open-api/index.cjs +3 -3
  177. package/dist/plugins/open-api/index.d.cts +3 -3
  178. package/dist/plugins/open-api/index.d.mts +3 -3
  179. package/dist/plugins/open-api/index.d.ts +3 -3
  180. package/dist/plugins/open-api/index.mjs +4 -4
  181. package/dist/plugins/organization/access/index.d.cts +1 -1
  182. package/dist/plugins/organization/access/index.d.mts +1 -1
  183. package/dist/plugins/organization/access/index.d.ts +1 -1
  184. package/dist/plugins/organization/index.cjs +4 -4
  185. package/dist/plugins/organization/index.d.cts +708 -55
  186. package/dist/plugins/organization/index.d.mts +708 -55
  187. package/dist/plugins/organization/index.d.ts +708 -55
  188. package/dist/plugins/organization/index.mjs +5 -5
  189. package/dist/plugins/passkey/index.cjs +82 -8
  190. package/dist/plugins/passkey/index.d.cts +72 -3
  191. package/dist/plugins/passkey/index.d.mts +72 -3
  192. package/dist/plugins/passkey/index.d.ts +72 -3
  193. package/dist/plugins/passkey/index.mjs +83 -9
  194. package/dist/plugins/phone-number/index.cjs +194 -26
  195. package/dist/plugins/phone-number/index.d.cts +132 -8
  196. package/dist/plugins/phone-number/index.d.mts +132 -8
  197. package/dist/plugins/phone-number/index.d.ts +132 -8
  198. package/dist/plugins/phone-number/index.mjs +195 -27
  199. package/dist/plugins/sso/index.cjs +190 -7
  200. package/dist/plugins/sso/index.d.cts +181 -15
  201. package/dist/plugins/sso/index.d.mts +181 -15
  202. package/dist/plugins/sso/index.d.ts +181 -15
  203. package/dist/plugins/sso/index.mjs +191 -8
  204. package/dist/plugins/two-factor/index.cjs +443 -92
  205. package/dist/plugins/two-factor/index.d.cts +230 -396
  206. package/dist/plugins/two-factor/index.d.mts +230 -396
  207. package/dist/plugins/two-factor/index.d.ts +230 -396
  208. package/dist/plugins/two-factor/index.mjs +431 -80
  209. package/dist/plugins/username/index.cjs +34 -31
  210. package/dist/plugins/username/index.d.cts +15 -12
  211. package/dist/plugins/username/index.d.mts +15 -12
  212. package/dist/plugins/username/index.d.ts +15 -12
  213. package/dist/plugins/username/index.mjs +35 -32
  214. package/dist/shared/better-auth.1DR6suCQ.mjs +307 -0
  215. package/dist/shared/{better-auth.BSsp73pg.cjs → better-auth.B7cZ2juS.cjs} +15 -14
  216. package/dist/shared/{better-auth.bKwabe3I.d.mts → better-auth.B88xucNq.d.mts} +529 -39
  217. package/dist/shared/{better-auth.CApEjVDP.cjs → better-auth.BW8BpneG.cjs} +4 -1
  218. package/dist/shared/{better-auth.BiQsvaIP.d.cts → better-auth.BcU1Kjyq.d.cts} +2051 -518
  219. package/dist/shared/better-auth.BfG24BjZ.cjs +118 -0
  220. package/dist/shared/{better-auth.A3TjrU8G.mjs → better-auth.Bk5IMdhM.mjs} +32 -12
  221. package/dist/shared/{better-auth.D9VnBkRI.mjs → better-auth.Bm9HxIzE.mjs} +47 -24
  222. package/dist/shared/{better-auth.BRf6Iynu.d.ts → better-auth.Bwc-6kOr.d.ts} +1 -1
  223. package/dist/shared/{better-auth.D-oLmHIj.d.mts → better-auth.CA2hFK4N.d.ts} +2051 -518
  224. package/dist/shared/{better-auth.Dmhe30iW.d.mts → better-auth.CGukGrxT.d.cts} +1 -1
  225. package/dist/shared/{better-auth.CsSpq0zL.cjs → better-auth.CHUzBidy.cjs} +46 -23
  226. package/dist/shared/{better-auth.DWRligF8.d.cts → better-auth.CT9J6rD-.d.cts} +539 -7
  227. package/dist/shared/better-auth.CVCo5Z2T.cjs +310 -0
  228. package/dist/shared/{better-auth.D4jH-sJA.mjs → better-auth.CWwVo_61.mjs} +458 -118
  229. package/dist/shared/{better-auth.Bi8FQwDD.d.cts → better-auth.CYegVoq1.d.cts} +1 -1
  230. package/dist/shared/{better-auth.Bi8FQwDD.d.mts → better-auth.CYegVoq1.d.mts} +1 -1
  231. package/dist/shared/{better-auth.Bi8FQwDD.d.ts → better-auth.CYegVoq1.d.ts} +1 -1
  232. package/dist/shared/{better-auth.CepcSj5H.mjs → better-auth.Cc72UxUH.mjs} +1 -2
  233. package/dist/shared/{better-auth.BWp5dztg.d.ts → better-auth.CmN4mlPh.d.ts} +539 -7
  234. package/dist/shared/{better-auth.DH3YjMQH.mjs → better-auth.Cqykj82J.mjs} +1 -1
  235. package/dist/shared/{better-auth.wcdMj2cT.d.mts → better-auth.DIt2e3lu.d.mts} +539 -7
  236. package/dist/shared/{better-auth.BANAxdkL.d.ts → better-auth.DNTAFSt1.d.ts} +529 -39
  237. package/dist/shared/{better-auth.DU2QNVc_.d.ts → better-auth.DQ7OSJbI.d.mts} +2051 -518
  238. package/dist/shared/{better-auth.DLTzKoOS.cjs → better-auth.DSVbLSt7.cjs} +4 -1
  239. package/dist/shared/{better-auth.B2Fw1vhH.d.cts → better-auth.DTiSPWEk.d.cts} +529 -39
  240. package/dist/shared/better-auth.DURsStt9.mjs +116 -0
  241. package/dist/shared/{better-auth.BIjcZ_vt.cjs → better-auth.DYoLD99C.cjs} +31 -11
  242. package/dist/shared/{better-auth.CV1L7TPV.cjs → better-auth.D_ZIX1O8.cjs} +317 -47
  243. package/dist/shared/{better-auth.C5H9XEzZ.cjs → better-auth.DcWKCjjf.cjs} +1 -2
  244. package/dist/shared/{better-auth.BDYXUcLv.cjs → better-auth.Dg0siV5C.cjs} +457 -117
  245. package/dist/shared/better-auth.DjryM8pE.cjs +760 -0
  246. package/dist/shared/{better-auth.DPBQN9Fs.mjs → better-auth.Dn_Ms1Uf.mjs} +318 -48
  247. package/dist/shared/{better-auth.DiG4KL2x.mjs → better-auth.OuYYTHC7.mjs} +4 -1
  248. package/dist/shared/{better-auth.DtC8i3pf.d.cts → better-auth.S1jimRbX.d.mts} +1 -1
  249. package/dist/shared/better-auth.SPmq4a4z.d.mts +344 -0
  250. package/dist/shared/{better-auth.cOCrlspr.mjs → better-auth.bkwPl2G4.mjs} +4 -1
  251. package/dist/shared/better-auth.cp2rC2iM.d.ts +344 -0
  252. package/dist/shared/better-auth.eVy4DZvP.d.cts +344 -0
  253. package/dist/shared/{better-auth.BrOpzmqo.mjs → better-auth.iKoUsdFE.mjs} +15 -14
  254. package/dist/shared/better-auth.rSYJCd3o.mjs +758 -0
  255. package/dist/social-providers/index.cjs +75 -3
  256. package/dist/social-providers/index.d.cts +2 -2
  257. package/dist/social-providers/index.d.mts +2 -2
  258. package/dist/social-providers/index.d.ts +2 -2
  259. package/dist/social-providers/index.mjs +77 -6
  260. package/dist/types/index.d.cts +4 -4
  261. package/dist/types/index.d.mts +4 -4
  262. package/dist/types/index.d.ts +4 -4
  263. package/package.json +42 -5
  264. package/dist/chunks/server.cjs +0 -905
  265. package/dist/chunks/server.mjs +0 -895
  266. package/dist/shared/better-auth.BcoSd9tC.mjs +0 -10
  267. package/dist/shared/better-auth.BnRFp-t0.mjs +0 -405
  268. package/dist/shared/better-auth.C1-vpKly.cjs +0 -12
  269. package/dist/shared/better-auth.ClTSOgiD.mjs +0 -140
  270. package/dist/shared/better-auth.DC8JQbiE.mjs +0 -173
  271. package/dist/shared/better-auth.DWHWPllD.cjs +0 -175
  272. package/dist/shared/better-auth.DqLjzBlO.cjs +0 -408
  273. package/dist/shared/better-auth.m575EIBC.cjs +0 -144
@@ -2,21 +2,20 @@
2
2
 
3
3
  const random = require('../../shared/better-auth.CYeOI8C-.cjs');
4
4
  const zod = require('zod');
5
- const refreshToken = require('../../shared/better-auth.BDYXUcLv.cjs');
5
+ const refreshToken = require('../../shared/better-auth.Dg0siV5C.cjs');
6
6
  const betterCall = require('better-call');
7
7
  const cookies_index = require('../../cookies/index.cjs');
8
- const schema$1 = require('../../shared/better-auth.C5H9XEzZ.cjs');
8
+ const schema$1 = require('../../shared/better-auth.DcWKCjjf.cjs');
9
9
  require('../../shared/better-auth.DiSjtgs9.cjs');
10
10
  require('../../shared/better-auth.GpOOav9x.cjs');
11
11
  require('defu');
12
12
  const crypto_index = require('../../crypto/index.cjs');
13
- const verifyMiddleware = require('../../shared/better-auth.m575EIBC.cjs');
14
- const errorCode = require('../../shared/better-auth.C1-vpKly.cjs');
13
+ require('@better-auth/utils/base64');
14
+ const hmac = require('@better-auth/utils/hmac');
15
15
  require('@better-auth/utils/hash');
16
16
  require('@noble/ciphers/chacha');
17
17
  require('@noble/ciphers/utils');
18
18
  require('@noble/ciphers/webcrypto');
19
- require('@better-auth/utils/base64');
20
19
  require('jose');
21
20
  require('@noble/hashes/scrypt');
22
21
  require('@better-auth/utils');
@@ -24,7 +23,6 @@ require('@better-auth/utils/hex');
24
23
  require('@noble/hashes/utils');
25
24
  const otp = require('@better-auth/utils/otp');
26
25
  const password = require('../../shared/better-auth.CDXNofOe.cjs');
27
- const hmac = require('@better-auth/utils/hmac');
28
26
  const client = require('../../shared/better-auth.DnER2-iT.cjs');
29
27
  require('@better-auth/utils/random');
30
28
  require('../../social-providers/index.cjs');
@@ -33,14 +31,151 @@ require('../../shared/better-auth.Cm29ZKNJ.cjs');
33
31
  require('../../shared/better-auth.C1hdVENX.cjs');
34
32
  require('../../shared/better-auth.ANpbi45u.cjs');
35
33
  require('../../shared/better-auth.QbbyHMYf.cjs');
34
+ require('../../shared/better-auth.D3mtHEZg.cjs');
36
35
  require('../../shared/better-auth.Bg6iw3ig.cjs');
37
36
  require('../../shared/better-auth.BMYo0QR-.cjs');
38
37
  require('../../shared/better-auth.C-R0J0n1.cjs');
39
38
  require('jose/errors');
40
- require('../../shared/better-auth.D3mtHEZg.cjs');
41
39
  require('@better-auth/utils/binary');
42
40
  require('../../shared/better-auth.YUF6P-PB.cjs');
43
41
 
42
+ const TWO_FACTOR_ERROR_CODES = {
43
+ OTP_NOT_ENABLED: "OTP not enabled",
44
+ OTP_HAS_EXPIRED: "OTP has expired",
45
+ TOTP_NOT_ENABLED: "TOTP not enabled",
46
+ TWO_FACTOR_NOT_ENABLED: "Two factor isn't enabled",
47
+ BACKUP_CODES_NOT_ENABLED: "Backup codes aren't enabled",
48
+ INVALID_BACKUP_CODE: "Invalid backup code",
49
+ INVALID_CODE: "Invalid code",
50
+ TOO_MANY_ATTEMPTS_REQUEST_NEW_CODE: "Too many attempts. Please request a new code.",
51
+ INVALID_TWO_FACTOR_COOKIE: "Invalid two factor cookie"
52
+ };
53
+
54
+ const TWO_FACTOR_COOKIE_NAME = "two_factor";
55
+ const TRUST_DEVICE_COOKIE_NAME = "trust_device";
56
+
57
+ async function verifyTwoFactor(ctx) {
58
+ const session = await refreshToken.getSessionFromCtx(ctx);
59
+ if (!session) {
60
+ const cookieName = ctx.context.createAuthCookie(TWO_FACTOR_COOKIE_NAME);
61
+ const twoFactorCookie = await ctx.getSignedCookie(
62
+ cookieName.name,
63
+ ctx.context.secret
64
+ );
65
+ if (!twoFactorCookie) {
66
+ throw new betterCall.APIError("UNAUTHORIZED", {
67
+ message: TWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE
68
+ });
69
+ }
70
+ const verificationToken = await ctx.context.internalAdapter.findVerificationValue(twoFactorCookie);
71
+ if (!verificationToken) {
72
+ throw new betterCall.APIError("UNAUTHORIZED", {
73
+ message: TWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE
74
+ });
75
+ }
76
+ const user = await ctx.context.internalAdapter.findUserById(
77
+ verificationToken.value
78
+ );
79
+ if (!user) {
80
+ throw new betterCall.APIError("UNAUTHORIZED", {
81
+ message: TWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE
82
+ });
83
+ }
84
+ const dontRememberMe = await ctx.getSignedCookie(
85
+ ctx.context.authCookies.dontRememberToken.name,
86
+ ctx.context.secret
87
+ );
88
+ return {
89
+ valid: async (ctx2) => {
90
+ const session2 = await ctx2.context.internalAdapter.createSession(
91
+ verificationToken.value,
92
+ ctx2.headers,
93
+ !!dontRememberMe
94
+ );
95
+ if (!session2) {
96
+ throw new betterCall.APIError("INTERNAL_SERVER_ERROR", {
97
+ message: "failed to create session"
98
+ });
99
+ }
100
+ await cookies_index.setSessionCookie(ctx2, {
101
+ session: session2,
102
+ user
103
+ });
104
+ if (ctx2.body.trustDevice) {
105
+ const trustDeviceCookie = ctx2.context.createAuthCookie(
106
+ TRUST_DEVICE_COOKIE_NAME,
107
+ {
108
+ maxAge: 30 * 24 * 60 * 60
109
+ // 30 days, it'll be refreshed on sign in requests
110
+ }
111
+ );
112
+ const token = await hmac.createHMAC("SHA-256", "base64urlnopad").sign(
113
+ ctx2.context.secret,
114
+ `${user.id}!${session2.token}`
115
+ );
116
+ await ctx2.setSignedCookie(
117
+ trustDeviceCookie.name,
118
+ `${token}!${session2.token}`,
119
+ ctx2.context.secret,
120
+ trustDeviceCookie.attributes
121
+ );
122
+ ctx2.setCookie(ctx2.context.authCookies.dontRememberToken.name, "", {
123
+ maxAge: 0
124
+ });
125
+ ctx2.setCookie(cookieName.name, "", {
126
+ maxAge: 0
127
+ });
128
+ }
129
+ return ctx2.json({
130
+ token: session2.token,
131
+ user: {
132
+ id: user.id,
133
+ email: user.email,
134
+ emailVerified: user.emailVerified,
135
+ name: user.name,
136
+ image: user.image,
137
+ createdAt: user.createdAt,
138
+ updatedAt: user.updatedAt
139
+ }
140
+ });
141
+ },
142
+ invalid: async (errorKey) => {
143
+ throw new betterCall.APIError("UNAUTHORIZED", {
144
+ message: TWO_FACTOR_ERROR_CODES[errorKey]
145
+ });
146
+ },
147
+ session: {
148
+ session: null,
149
+ user
150
+ },
151
+ key: twoFactorCookie
152
+ };
153
+ }
154
+ return {
155
+ valid: async (ctx2) => {
156
+ return ctx2.json({
157
+ token: session.session.token,
158
+ user: {
159
+ id: session.user.id,
160
+ email: session.user.email,
161
+ emailVerified: session.user.emailVerified,
162
+ name: session.user.name,
163
+ image: session.user.image,
164
+ createdAt: session.user.createdAt,
165
+ updatedAt: session.user.updatedAt
166
+ }
167
+ });
168
+ },
169
+ invalid: async () => {
170
+ throw new betterCall.APIError("UNAUTHORIZED", {
171
+ message: TWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE
172
+ });
173
+ },
174
+ session,
175
+ key: `${session.user.id}!${session.session.id}`
176
+ };
177
+ }
178
+
44
179
  function generateBackupCodesFn(options) {
45
180
  return Array.from({ length: options?.amount ?? 10 }).fill(null).map(() => random.generateRandomString(options?.length ?? 10, "a-z", "0-9", "A-Z")).map((code) => `${code.slice(0, 5)}-${code.slice(5)}`);
46
181
  }
@@ -108,10 +243,112 @@ const backupCode2fa = (options) => {
108
243
  description: "If true, the device will be trusted for 30 days. It'll be refreshed on every sign in request within this time."
109
244
  }).optional()
110
245
  }),
111
- use: [verifyMiddleware.verifyTwoFactorMiddleware]
246
+ metadata: {
247
+ openapi: {
248
+ description: "Verify a backup code for two-factor authentication",
249
+ responses: {
250
+ "200": {
251
+ description: "Backup code verified successfully",
252
+ content: {
253
+ "application/json": {
254
+ schema: {
255
+ type: "object",
256
+ properties: {
257
+ user: {
258
+ type: "object",
259
+ properties: {
260
+ id: {
261
+ type: "string",
262
+ description: "Unique identifier of the user"
263
+ },
264
+ email: {
265
+ type: "string",
266
+ format: "email",
267
+ nullable: true,
268
+ description: "User's email address"
269
+ },
270
+ emailVerified: {
271
+ type: "boolean",
272
+ nullable: true,
273
+ description: "Whether the email is verified"
274
+ },
275
+ name: {
276
+ type: "string",
277
+ nullable: true,
278
+ description: "User's name"
279
+ },
280
+ image: {
281
+ type: "string",
282
+ format: "uri",
283
+ nullable: true,
284
+ description: "User's profile image URL"
285
+ },
286
+ twoFactorEnabled: {
287
+ type: "boolean",
288
+ description: "Whether two-factor authentication is enabled for the user"
289
+ },
290
+ createdAt: {
291
+ type: "string",
292
+ format: "date-time",
293
+ description: "Timestamp when the user was created"
294
+ },
295
+ updatedAt: {
296
+ type: "string",
297
+ format: "date-time",
298
+ description: "Timestamp when the user was last updated"
299
+ }
300
+ },
301
+ required: [
302
+ "id",
303
+ "twoFactorEnabled",
304
+ "createdAt",
305
+ "updatedAt"
306
+ ],
307
+ description: "The authenticated user object with two-factor details"
308
+ },
309
+ session: {
310
+ type: "object",
311
+ properties: {
312
+ token: {
313
+ type: "string",
314
+ description: "Session token"
315
+ },
316
+ userId: {
317
+ type: "string",
318
+ description: "ID of the user associated with the session"
319
+ },
320
+ createdAt: {
321
+ type: "string",
322
+ format: "date-time",
323
+ description: "Timestamp when the session was created"
324
+ },
325
+ expiresAt: {
326
+ type: "string",
327
+ format: "date-time",
328
+ description: "Timestamp when the session expires"
329
+ }
330
+ },
331
+ required: [
332
+ "token",
333
+ "userId",
334
+ "createdAt",
335
+ "expiresAt"
336
+ ],
337
+ description: "The current session object, included unless disableSession is true"
338
+ }
339
+ },
340
+ required: ["user", "session"]
341
+ }
342
+ }
343
+ }
344
+ }
345
+ }
346
+ }
347
+ }
112
348
  },
113
349
  async (ctx) => {
114
- const user = ctx.context.session.user;
350
+ const { session, valid } = await verifyTwoFactor(ctx);
351
+ const user = session.user;
115
352
  const twoFactor = await ctx.context.adapter.findOne({
116
353
  model: twoFactorTable,
117
354
  where: [
@@ -123,7 +360,7 @@ const backupCode2fa = (options) => {
123
360
  });
124
361
  if (!twoFactor) {
125
362
  throw new betterCall.APIError("BAD_REQUEST", {
126
- message: errorCode.TWO_FACTOR_ERROR_CODES.BACKUP_CODES_NOT_ENABLED
363
+ message: TWO_FACTOR_ERROR_CODES.BACKUP_CODES_NOT_ENABLED
127
364
  });
128
365
  }
129
366
  const validate = await verifyBackupCode(
@@ -135,7 +372,7 @@ const backupCode2fa = (options) => {
135
372
  );
136
373
  if (!validate.status) {
137
374
  throw new betterCall.APIError("UNAUTHORIZED", {
138
- message: errorCode.TWO_FACTOR_ERROR_CODES.INVALID_BACKUP_CODE
375
+ message: TWO_FACTOR_ERROR_CODES.INVALID_BACKUP_CODE
139
376
  });
140
377
  }
141
378
  const updatedBackupCodes = await crypto_index.symmetricEncrypt({
@@ -155,14 +392,19 @@ const backupCode2fa = (options) => {
155
392
  ]
156
393
  });
157
394
  if (!ctx.body.disableSession) {
158
- await cookies_index.setSessionCookie(ctx, {
159
- session: ctx.context.session.session,
160
- user
161
- });
395
+ return valid(ctx);
162
396
  }
163
397
  return ctx.json({
164
- user,
165
- session: ctx.context.session
398
+ token: session.session?.token,
399
+ user: {
400
+ id: session.user?.id,
401
+ email: session.user.email,
402
+ emailVerified: session.user.emailVerified,
403
+ name: session.user.name,
404
+ image: session.user.image,
405
+ createdAt: session.user.createdAt,
406
+ updatedAt: session.user.updatedAt
407
+ }
166
408
  });
167
409
  }
168
410
  ),
@@ -173,13 +415,43 @@ const backupCode2fa = (options) => {
173
415
  body: zod.z.object({
174
416
  password: zod.z.string()
175
417
  }),
176
- use: [refreshToken.sessionMiddleware]
418
+ use: [refreshToken.sessionMiddleware],
419
+ metadata: {
420
+ openapi: {
421
+ description: "Generate new backup codes for two-factor authentication",
422
+ responses: {
423
+ "200": {
424
+ description: "Backup codes generated successfully",
425
+ content: {
426
+ "application/json": {
427
+ schema: {
428
+ type: "object",
429
+ properties: {
430
+ status: {
431
+ type: "boolean",
432
+ description: "Indicates if the backup codes were generated successfully",
433
+ enum: [true]
434
+ },
435
+ backupCodes: {
436
+ type: "array",
437
+ items: { type: "string" },
438
+ description: "Array of generated backup codes in plain text"
439
+ }
440
+ },
441
+ required: ["status", "backupCodes"]
442
+ }
443
+ }
444
+ }
445
+ }
446
+ }
447
+ }
448
+ }
177
449
  },
178
450
  async (ctx) => {
179
451
  const user = ctx.context.session.user;
180
452
  if (!user.twoFactorEnabled) {
181
453
  throw new betterCall.APIError("BAD_REQUEST", {
182
- message: errorCode.TWO_FACTOR_ERROR_CODES.TWO_FACTOR_NOT_ENABLED
454
+ message: TWO_FACTOR_ERROR_CODES.TWO_FACTOR_NOT_ENABLED
183
455
  });
184
456
  }
185
457
  await ctx.context.password.checkPassword(user.id, ctx);
@@ -237,7 +509,7 @@ const backupCode2fa = (options) => {
237
509
  );
238
510
  if (!backupCodes) {
239
511
  throw new betterCall.APIError("BAD_REQUEST", {
240
- message: errorCode.TWO_FACTOR_ERROR_CODES.BACKUP_CODES_NOT_ENABLED
512
+ message: TWO_FACTOR_ERROR_CODES.BACKUP_CODES_NOT_ENABLED
241
513
  });
242
514
  }
243
515
  return ctx.json({
@@ -269,7 +541,6 @@ const otp2fa = (options) => {
269
541
  */
270
542
  trustDevice: zod.z.boolean().optional()
271
543
  }).optional(),
272
- use: [verifyMiddleware.verifyTwoFactorMiddleware],
273
544
  metadata: {
274
545
  openapi: {
275
546
  summary: "Send two factor OTP",
@@ -303,28 +574,31 @@ const otp2fa = (options) => {
303
574
  message: "otp isn't configured"
304
575
  });
305
576
  }
306
- const user = ctx.context.session.user;
577
+ const { session, key } = await verifyTwoFactor(ctx);
307
578
  const twoFactor = await ctx.context.adapter.findOne({
308
579
  model: twoFactorTable,
309
580
  where: [
310
581
  {
311
582
  field: "userId",
312
- value: user.id
583
+ value: session.user.id
313
584
  }
314
585
  ]
315
586
  });
316
587
  if (!twoFactor) {
317
588
  throw new betterCall.APIError("BAD_REQUEST", {
318
- message: errorCode.TWO_FACTOR_ERROR_CODES.OTP_NOT_ENABLED
589
+ message: TWO_FACTOR_ERROR_CODES.OTP_NOT_ENABLED
319
590
  });
320
591
  }
321
592
  const code = random.generateRandomString(opts.digits, "0-9");
322
593
  await ctx.context.internalAdapter.createVerificationValue({
323
- value: code,
324
- identifier: `2fa-otp-${user.id}`,
594
+ value: `${code}!0`,
595
+ identifier: `2fa-otp-${key}`,
325
596
  expiresAt: new Date(Date.now() + opts.period)
326
597
  });
327
- await options.sendOTP({ user, otp: code }, ctx.request);
598
+ await options.sendOTP(
599
+ { user: session.user, otp: code },
600
+ ctx.request
601
+ );
328
602
  return ctx.json({ status: true });
329
603
  }
330
604
  );
@@ -343,23 +617,67 @@ const otp2fa = (options) => {
343
617
  */
344
618
  trustDevice: zod.z.boolean().optional()
345
619
  }),
346
- use: [verifyMiddleware.verifyTwoFactorMiddleware],
347
620
  metadata: {
348
621
  openapi: {
349
622
  summary: "Verify two factor OTP",
350
623
  description: "Verify two factor OTP",
351
624
  responses: {
352
- 200: {
353
- description: "Success",
625
+ "200": {
626
+ description: "Two-factor OTP verified successfully",
354
627
  content: {
355
628
  "application/json": {
356
629
  schema: {
357
630
  type: "object",
358
631
  properties: {
359
- status: {
360
- type: "boolean"
632
+ token: {
633
+ type: "string",
634
+ description: "Session token for the authenticated session"
635
+ },
636
+ user: {
637
+ type: "object",
638
+ properties: {
639
+ id: {
640
+ type: "string",
641
+ description: "Unique identifier of the user"
642
+ },
643
+ email: {
644
+ type: "string",
645
+ format: "email",
646
+ nullable: true,
647
+ description: "User's email address"
648
+ },
649
+ emailVerified: {
650
+ type: "boolean",
651
+ nullable: true,
652
+ description: "Whether the email is verified"
653
+ },
654
+ name: {
655
+ type: "string",
656
+ nullable: true,
657
+ description: "User's name"
658
+ },
659
+ image: {
660
+ type: "string",
661
+ format: "uri",
662
+ nullable: true,
663
+ description: "User's profile image URL"
664
+ },
665
+ createdAt: {
666
+ type: "string",
667
+ format: "date-time",
668
+ description: "Timestamp when the user was created"
669
+ },
670
+ updatedAt: {
671
+ type: "string",
672
+ format: "date-time",
673
+ description: "Timestamp when the user was last updated"
674
+ }
675
+ },
676
+ required: ["id", "createdAt", "updatedAt"],
677
+ description: "The authenticated user object"
361
678
  }
362
- }
679
+ },
680
+ required: ["token", "user"]
363
681
  }
364
682
  }
365
683
  }
@@ -369,54 +687,90 @@ const otp2fa = (options) => {
369
687
  }
370
688
  },
371
689
  async (ctx) => {
372
- const user = ctx.context.session.user;
690
+ const { session, key, valid, invalid } = await verifyTwoFactor(ctx);
373
691
  const twoFactor = await ctx.context.adapter.findOne({
374
692
  model: twoFactorTable,
375
693
  where: [
376
694
  {
377
695
  field: "userId",
378
- value: user.id
696
+ value: session.user.id
379
697
  }
380
698
  ]
381
699
  });
382
700
  if (!twoFactor) {
383
701
  throw new betterCall.APIError("BAD_REQUEST", {
384
- message: errorCode.TWO_FACTOR_ERROR_CODES.OTP_NOT_ENABLED
702
+ message: TWO_FACTOR_ERROR_CODES.OTP_NOT_ENABLED
385
703
  });
386
704
  }
387
705
  const toCheckOtp = await ctx.context.internalAdapter.findVerificationValue(
388
- `2fa-otp-${user.id}`
706
+ `2fa-otp-${key}`
389
707
  );
708
+ const [otp, counter] = toCheckOtp?.value?.split("!") ?? [];
390
709
  if (!toCheckOtp || toCheckOtp.expiresAt < /* @__PURE__ */ new Date()) {
710
+ await ctx.context.internalAdapter.deleteVerificationValue(
711
+ `2fa-otp-${key}`
712
+ );
713
+ throw new betterCall.APIError("BAD_REQUEST", {
714
+ message: TWO_FACTOR_ERROR_CODES.OTP_HAS_EXPIRED
715
+ });
716
+ }
717
+ const allowedAttempts = options?.allowedAttempts || 5;
718
+ if (parseInt(counter) >= allowedAttempts) {
719
+ await ctx.context.internalAdapter.deleteVerificationValue(
720
+ `2fa-otp-${key}`
721
+ );
391
722
  throw new betterCall.APIError("BAD_REQUEST", {
392
- message: errorCode.TWO_FACTOR_ERROR_CODES.OTP_HAS_EXPIRED
723
+ message: TWO_FACTOR_ERROR_CODES.TOO_MANY_ATTEMPTS_REQUEST_NEW_CODE
393
724
  });
394
725
  }
395
- if (toCheckOtp.value === ctx.body.code) {
396
- if (!user.twoFactorEnabled) {
726
+ if (otp === ctx.body.code) {
727
+ if (!session.user.twoFactorEnabled) {
728
+ if (!session.session) {
729
+ throw new betterCall.APIError("BAD_REQUEST", {
730
+ message: refreshToken.BASE_ERROR_CODES.FAILED_TO_CREATE_SESSION
731
+ });
732
+ }
397
733
  const updatedUser = await ctx.context.internalAdapter.updateUser(
398
- user.id,
734
+ session.user.id,
399
735
  {
400
736
  twoFactorEnabled: true
401
737
  }
402
738
  );
403
739
  const newSession = await ctx.context.internalAdapter.createSession(
404
- user.id,
405
- ctx.request,
740
+ session.user.id,
741
+ ctx.headers,
406
742
  false,
407
- ctx.context.session.session
743
+ session.session
408
744
  );
409
745
  await ctx.context.internalAdapter.deleteSession(
410
- ctx.context.session.session.token
746
+ session.session.token
411
747
  );
412
748
  await cookies_index.setSessionCookie(ctx, {
413
749
  session: newSession,
414
750
  user: updatedUser
415
751
  });
752
+ return ctx.json({
753
+ token: newSession.token,
754
+ user: {
755
+ id: updatedUser.id,
756
+ email: updatedUser.email,
757
+ emailVerified: updatedUser.emailVerified,
758
+ name: updatedUser.name,
759
+ image: updatedUser.image,
760
+ createdAt: updatedUser.createdAt,
761
+ updatedAt: updatedUser.updatedAt
762
+ }
763
+ });
416
764
  }
417
- return ctx.context.valid(ctx);
765
+ return valid(ctx);
418
766
  } else {
419
- return ctx.context.invalid();
767
+ await ctx.context.internalAdapter.updateVerificationValue(
768
+ toCheckOtp.id,
769
+ {
770
+ value: `${otp}!${parseInt(counter) + 1}`
771
+ }
772
+ );
773
+ return invalid("INVALID_CODE");
420
774
  }
421
775
  }
422
776
  );
@@ -440,7 +794,11 @@ const totp2fa = (options) => {
440
794
  "/totp/generate",
441
795
  {
442
796
  method: "POST",
443
- use: [refreshToken.sessionMiddleware],
797
+ body: zod.z.object({
798
+ secret: zod.z.string({
799
+ description: "The secret to generate the TOTP code"
800
+ })
801
+ }),
444
802
  metadata: {
445
803
  openapi: {
446
804
  summary: "Generate TOTP code",
@@ -462,7 +820,8 @@ const totp2fa = (options) => {
462
820
  }
463
821
  }
464
822
  }
465
- }
823
+ },
824
+ SERVER_ONLY: true
466
825
  }
467
826
  },
468
827
  async (ctx) => {
@@ -474,22 +833,7 @@ const totp2fa = (options) => {
474
833
  message: "totp isn't configured"
475
834
  });
476
835
  }
477
- const user = ctx.context.session.user;
478
- const twoFactor = await ctx.context.adapter.findOne({
479
- model: twoFactorTable,
480
- where: [
481
- {
482
- field: "userId",
483
- value: user.id
484
- }
485
- ]
486
- });
487
- if (!twoFactor) {
488
- throw new betterCall.APIError("BAD_REQUEST", {
489
- message: errorCode.TWO_FACTOR_ERROR_CODES.TOTP_NOT_ENABLED
490
- });
491
- }
492
- const code = await otp.createOTP(twoFactor.secret, {
836
+ const code = await otp.createOTP(ctx.body.secret, {
493
837
  period: opts.period,
494
838
  digits: opts.digits
495
839
  }).totp();
@@ -551,7 +895,7 @@ const totp2fa = (options) => {
551
895
  });
552
896
  if (!twoFactor || !user.twoFactorEnabled) {
553
897
  throw new betterCall.APIError("BAD_REQUEST", {
554
- message: errorCode.TWO_FACTOR_ERROR_CODES.TOTP_NOT_ENABLED
898
+ message: TWO_FACTOR_ERROR_CODES.TOTP_NOT_ENABLED
555
899
  });
556
900
  }
557
901
  const secret = await crypto_index.symmetricDecrypt({
@@ -585,7 +929,6 @@ const totp2fa = (options) => {
585
929
  description: "If true, the device will be trusted for 30 days. It'll be refreshed on every sign in request within this time."
586
930
  }).optional()
587
931
  }),
588
- use: [verifyMiddleware.verifyTwoFactorMiddleware],
589
932
  metadata: {
590
933
  openapi: {
591
934
  summary: "Verify two factor TOTP",
@@ -619,7 +962,8 @@ const totp2fa = (options) => {
619
962
  message: "totp isn't configured"
620
963
  });
621
964
  }
622
- const user = ctx.context.session.user;
965
+ const { session, valid, invalid } = await verifyTwoFactor(ctx);
966
+ const user = session.user;
623
967
  const twoFactor = await ctx.context.adapter.findOne({
624
968
  model: twoFactorTable,
625
969
  where: [
@@ -631,7 +975,7 @@ const totp2fa = (options) => {
631
975
  });
632
976
  if (!twoFactor) {
633
977
  throw new betterCall.APIError("BAD_REQUEST", {
634
- message: errorCode.TWO_FACTOR_ERROR_CODES.TOTP_NOT_ENABLED
978
+ message: TWO_FACTOR_ERROR_CODES.TOTP_NOT_ENABLED
635
979
  });
636
980
  }
637
981
  const decrypted = await crypto_index.symmetricDecrypt({
@@ -643,9 +987,14 @@ const totp2fa = (options) => {
643
987
  digits: opts.digits
644
988
  }).verify(ctx.body.code);
645
989
  if (!status) {
646
- return ctx.context.invalid();
990
+ return invalid("INVALID_CODE");
647
991
  }
648
992
  if (!user.twoFactorEnabled) {
993
+ if (!session.session) {
994
+ throw new betterCall.APIError("BAD_REQUEST", {
995
+ message: refreshToken.BASE_ERROR_CODES.FAILED_TO_CREATE_SESSION
996
+ });
997
+ }
649
998
  const updatedUser = await ctx.context.internalAdapter.updateUser(
650
999
  user.id,
651
1000
  {
@@ -653,23 +1002,16 @@ const totp2fa = (options) => {
653
1002
  },
654
1003
  ctx
655
1004
  );
656
- const newSession = await ctx.context.internalAdapter.createSession(
657
- user.id,
658
- ctx.request,
659
- false,
660
- ctx.context.session.session
661
- ).catch((e) => {
1005
+ const newSession = await ctx.context.internalAdapter.createSession(user.id, ctx.headers, false, session.session).catch((e) => {
662
1006
  throw e;
663
1007
  });
664
- await ctx.context.internalAdapter.deleteSession(
665
- ctx.context.session.session.token
666
- );
1008
+ await ctx.context.internalAdapter.deleteSession(session.session.token);
667
1009
  await cookies_index.setSessionCookie(ctx, {
668
1010
  session: newSession,
669
1011
  user: updatedUser
670
1012
  });
671
1013
  }
672
- return ctx.context.valid(ctx);
1014
+ return valid(ctx);
673
1015
  }
674
1016
  );
675
1017
  return {
@@ -738,7 +1080,10 @@ const twoFactor = (options) => {
738
1080
  body: zod.z.object({
739
1081
  password: zod.z.string({
740
1082
  description: "User password"
741
- })
1083
+ }),
1084
+ issuer: zod.z.string({
1085
+ description: "Custom issuer for the TOTP URI"
1086
+ }).optional()
742
1087
  }),
743
1088
  use: [refreshToken.sessionMiddleware],
744
1089
  metadata: {
@@ -775,7 +1120,7 @@ const twoFactor = (options) => {
775
1120
  },
776
1121
  async (ctx) => {
777
1122
  const user = ctx.context.session.user;
778
- const { password: password$1 } = ctx.body;
1123
+ const { password: password$1, issuer } = ctx.body;
779
1124
  const isPasswordValid = await password.validatePassword(ctx, {
780
1125
  password: password$1,
781
1126
  userId: user.id
@@ -804,7 +1149,7 @@ const twoFactor = (options) => {
804
1149
  );
805
1150
  const newSession = await ctx.context.internalAdapter.createSession(
806
1151
  updatedUser.id,
807
- ctx.request,
1152
+ ctx.headers,
808
1153
  false,
809
1154
  ctx.context.session.session
810
1155
  );
@@ -836,7 +1181,7 @@ const twoFactor = (options) => {
836
1181
  const totpURI = otp.createOTP(secret, {
837
1182
  digits: options?.totpOptions?.digits || 6,
838
1183
  period: options?.totpOptions?.period
839
- }).url(options?.issuer || ctx.context.appName, user.email);
1184
+ }).url(issuer || options?.issuer || ctx.context.appName, user.email);
840
1185
  return ctx.json({ totpURI, backupCodes: backupCodes.backupCodes });
841
1186
  }
842
1187
  ),
@@ -904,7 +1249,7 @@ const twoFactor = (options) => {
904
1249
  });
905
1250
  const newSession = await ctx.context.internalAdapter.createSession(
906
1251
  updatedUser.id,
907
- ctx.request,
1252
+ ctx.headers,
908
1253
  false,
909
1254
  ctx.context.session.session
910
1255
  );
@@ -935,7 +1280,7 @@ const twoFactor = (options) => {
935
1280
  return;
936
1281
  }
937
1282
  const trustDeviceCookieName = ctx.context.createAuthCookie(
938
- verifyMiddleware.TRUST_DEVICE_COOKIE_NAME
1283
+ TRUST_DEVICE_COOKIE_NAME
939
1284
  );
940
1285
  const trustDeviceCookie = await ctx.getSignedCookie(
941
1286
  trustDeviceCookieName.name,
@@ -963,16 +1308,22 @@ const twoFactor = (options) => {
963
1308
  }
964
1309
  cookies_index.deleteSessionCookie(ctx, true);
965
1310
  await ctx.context.internalAdapter.deleteSession(data.session.token);
1311
+ const maxAge = options?.otpOptions?.period || 60 * 5;
966
1312
  const twoFactorCookie = ctx.context.createAuthCookie(
967
- verifyMiddleware.TWO_FACTOR_COOKIE_NAME,
1313
+ TWO_FACTOR_COOKIE_NAME,
968
1314
  {
969
- maxAge: 60 * 10
970
- // 10 minutes
1315
+ maxAge
971
1316
  }
972
1317
  );
1318
+ const identifier = `2fa-${random.generateRandomString(20)}`;
1319
+ await ctx.context.internalAdapter.createVerificationValue({
1320
+ value: data.user.id,
1321
+ identifier,
1322
+ expiresAt: new Date(Date.now() + maxAge * 1e3)
1323
+ });
973
1324
  await ctx.setSignedCookie(
974
1325
  twoFactorCookie.name,
975
- data.user.id,
1326
+ identifier,
976
1327
  ctx.context.secret,
977
1328
  twoFactorCookie.attributes
978
1329
  );
@@ -993,10 +1344,10 @@ const twoFactor = (options) => {
993
1344
  max: 3
994
1345
  }
995
1346
  ],
996
- $ERROR_CODES: errorCode.TWO_FACTOR_ERROR_CODES
1347
+ $ERROR_CODES: TWO_FACTOR_ERROR_CODES
997
1348
  };
998
1349
  };
999
1350
 
1000
- exports.TWO_FACTOR_ERROR_CODES = errorCode.TWO_FACTOR_ERROR_CODES;
1001
1351
  exports.twoFactorClient = client.twoFactorClient;
1352
+ exports.TWO_FACTOR_ERROR_CODES = TWO_FACTOR_ERROR_CODES;
1002
1353
  exports.twoFactor = twoFactor;