better-auth 1.4.9 → 1.5.0-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 (228) hide show
  1. package/dist/api/index.d.mts +396 -395
  2. package/dist/api/index.mjs +6 -4
  3. package/dist/api/index.mjs.map +1 -1
  4. package/dist/api/middlewares/origin-check.d.mts +3 -3
  5. package/dist/api/middlewares/origin-check.mjs +14 -4
  6. package/dist/api/middlewares/origin-check.mjs.map +1 -1
  7. package/dist/api/routes/account.d.mts +11 -11
  8. package/dist/api/routes/account.mjs +59 -30
  9. package/dist/api/routes/account.mjs.map +1 -1
  10. package/dist/api/routes/callback.d.mts +2 -2
  11. package/dist/api/routes/email-verification.d.mts +4 -4
  12. package/dist/api/routes/email-verification.mjs +14 -14
  13. package/dist/api/routes/email-verification.mjs.map +1 -1
  14. package/dist/api/routes/error.d.mts +2 -2
  15. package/dist/api/routes/ok.d.mts +2 -2
  16. package/dist/api/routes/reset-password.d.mts +5 -5
  17. package/dist/api/routes/reset-password.mjs +9 -7
  18. package/dist/api/routes/reset-password.mjs.map +1 -1
  19. package/dist/api/routes/session.d.mts +14 -14
  20. package/dist/api/routes/session.mjs +31 -11
  21. package/dist/api/routes/session.mjs.map +1 -1
  22. package/dist/api/routes/sign-in.d.mts +3 -3
  23. package/dist/api/routes/sign-in.mjs +22 -17
  24. package/dist/api/routes/sign-in.mjs.map +1 -1
  25. package/dist/api/routes/sign-out.d.mts +2 -2
  26. package/dist/api/routes/sign-up.d.mts +2 -2
  27. package/dist/api/routes/sign-up.mjs +15 -12
  28. package/dist/api/routes/sign-up.mjs.map +1 -1
  29. package/dist/api/routes/update-user.d.mts +13 -13
  30. package/dist/api/routes/update-user.mjs +29 -24
  31. package/dist/api/routes/update-user.mjs.map +1 -1
  32. package/dist/api/to-auth-endpoints.mjs +7 -6
  33. package/dist/api/to-auth-endpoints.mjs.map +1 -1
  34. package/dist/client/lynx/index.d.mts +15 -15
  35. package/dist/client/plugins/index.d.mts +12 -2
  36. package/dist/client/plugins/index.mjs +11 -1
  37. package/dist/client/react/index.d.mts +13 -13
  38. package/dist/client/solid/index.d.mts +13 -13
  39. package/dist/client/svelte/index.d.mts +15 -15
  40. package/dist/client/types.d.mts +4 -1
  41. package/dist/client/vue/index.d.mts +13 -13
  42. package/dist/context/create-context.mjs +2 -2
  43. package/dist/context/create-context.mjs.map +1 -1
  44. package/dist/context/helpers.mjs +2 -2
  45. package/dist/context/helpers.mjs.map +1 -1
  46. package/dist/db/field.d.mts +6 -6
  47. package/dist/db/schema.mjs +14 -5
  48. package/dist/db/schema.mjs.map +1 -1
  49. package/dist/index.d.mts +1 -1
  50. package/dist/integrations/next-js.d.mts +4 -4
  51. package/dist/integrations/svelte-kit.d.mts +2 -2
  52. package/dist/integrations/tanstack-start.d.mts +4 -4
  53. package/dist/oauth2/link-account.mjs +3 -2
  54. package/dist/oauth2/link-account.mjs.map +1 -1
  55. package/dist/oauth2/state.mjs +3 -3
  56. package/dist/oauth2/state.mjs.map +1 -1
  57. package/dist/plugins/admin/admin.d.mts +200 -137
  58. package/dist/plugins/admin/admin.mjs +3 -4
  59. package/dist/plugins/admin/admin.mjs.map +1 -1
  60. package/dist/plugins/admin/client.d.mts +87 -0
  61. package/dist/plugins/admin/client.mjs +3 -1
  62. package/dist/plugins/admin/client.mjs.map +1 -1
  63. package/dist/plugins/admin/error-codes.d.mts +90 -0
  64. package/dist/plugins/admin/error-codes.mjs.map +1 -1
  65. package/dist/plugins/admin/routes.mjs +40 -46
  66. package/dist/plugins/admin/routes.mjs.map +1 -1
  67. package/dist/plugins/anonymous/client.d.mts +19 -0
  68. package/dist/plugins/anonymous/client.mjs +4 -1
  69. package/dist/plugins/anonymous/client.mjs.map +1 -1
  70. package/dist/plugins/anonymous/error-codes.d.mts +22 -0
  71. package/dist/plugins/anonymous/index.d.mts +21 -9
  72. package/dist/plugins/anonymous/index.mjs +5 -5
  73. package/dist/plugins/anonymous/index.mjs.map +1 -1
  74. package/dist/plugins/api-key/client.d.mts +103 -0
  75. package/dist/plugins/api-key/client.mjs +4 -1
  76. package/dist/plugins/api-key/client.mjs.map +1 -1
  77. package/dist/plugins/api-key/error-codes.d.mts +106 -0
  78. package/dist/plugins/api-key/error-codes.mjs +34 -0
  79. package/dist/plugins/api-key/error-codes.mjs.map +1 -0
  80. package/dist/plugins/api-key/index.d.mts +181 -112
  81. package/dist/plugins/api-key/index.mjs +7 -34
  82. package/dist/plugins/api-key/index.mjs.map +1 -1
  83. package/dist/plugins/api-key/rate-limit.mjs +3 -2
  84. package/dist/plugins/api-key/rate-limit.mjs.map +1 -1
  85. package/dist/plugins/api-key/routes/create-api-key.mjs +19 -17
  86. package/dist/plugins/api-key/routes/create-api-key.mjs.map +1 -1
  87. package/dist/plugins/api-key/routes/delete-api-key.mjs +7 -5
  88. package/dist/plugins/api-key/routes/delete-api-key.mjs.map +1 -1
  89. package/dist/plugins/api-key/routes/get-api-key.mjs +5 -3
  90. package/dist/plugins/api-key/routes/get-api-key.mjs.map +1 -1
  91. package/dist/plugins/api-key/routes/update-api-key.mjs +18 -16
  92. package/dist/plugins/api-key/routes/update-api-key.mjs.map +1 -1
  93. package/dist/plugins/api-key/routes/verify-api-key.mjs +16 -35
  94. package/dist/plugins/api-key/routes/verify-api-key.mjs.map +1 -1
  95. package/dist/plugins/bearer/index.d.mts +3 -3
  96. package/dist/plugins/captcha/index.d.mts +2 -2
  97. package/dist/plugins/captcha/index.mjs +3 -3
  98. package/dist/plugins/captcha/index.mjs.map +1 -1
  99. package/dist/plugins/captcha/verify-handlers/captchafox.mjs +2 -2
  100. package/dist/plugins/captcha/verify-handlers/captchafox.mjs.map +1 -1
  101. package/dist/plugins/captcha/verify-handlers/cloudflare-turnstile.mjs +2 -2
  102. package/dist/plugins/captcha/verify-handlers/cloudflare-turnstile.mjs.map +1 -1
  103. package/dist/plugins/captcha/verify-handlers/google-recaptcha.mjs +2 -2
  104. package/dist/plugins/captcha/verify-handlers/google-recaptcha.mjs.map +1 -1
  105. package/dist/plugins/captcha/verify-handlers/h-captcha.mjs +2 -2
  106. package/dist/plugins/captcha/verify-handlers/h-captcha.mjs.map +1 -1
  107. package/dist/plugins/custom-session/index.d.mts +5 -5
  108. package/dist/plugins/device-authorization/index.d.mts +54 -18
  109. package/dist/plugins/device-authorization/routes.mjs +18 -18
  110. package/dist/plugins/device-authorization/routes.mjs.map +1 -1
  111. package/dist/plugins/email-otp/client.d.mts +15 -0
  112. package/dist/plugins/email-otp/client.mjs +4 -1
  113. package/dist/plugins/email-otp/client.mjs.map +1 -1
  114. package/dist/plugins/email-otp/error-codes.d.mts +18 -0
  115. package/dist/plugins/email-otp/error-codes.mjs +12 -0
  116. package/dist/plugins/email-otp/error-codes.mjs.map +1 -0
  117. package/dist/plugins/email-otp/index.d.mts +64 -55
  118. package/dist/plugins/email-otp/index.mjs +4 -3
  119. package/dist/plugins/email-otp/index.mjs.map +1 -1
  120. package/dist/plugins/email-otp/routes.mjs +30 -35
  121. package/dist/plugins/email-otp/routes.mjs.map +1 -1
  122. package/dist/plugins/generic-oauth/client.d.mts +27 -0
  123. package/dist/plugins/generic-oauth/client.mjs +4 -1
  124. package/dist/plugins/generic-oauth/client.mjs.map +1 -1
  125. package/dist/plugins/generic-oauth/error-codes.d.mts +30 -0
  126. package/dist/plugins/generic-oauth/index.d.mts +55 -37
  127. package/dist/plugins/generic-oauth/index.mjs +4 -4
  128. package/dist/plugins/generic-oauth/index.mjs.map +1 -1
  129. package/dist/plugins/generic-oauth/routes.mjs +11 -12
  130. package/dist/plugins/generic-oauth/routes.mjs.map +1 -1
  131. package/dist/plugins/haveibeenpwned/index.d.mts +7 -4
  132. package/dist/plugins/haveibeenpwned/index.mjs +5 -4
  133. package/dist/plugins/haveibeenpwned/index.mjs.map +1 -1
  134. package/dist/plugins/index.d.mts +4 -2
  135. package/dist/plugins/index.mjs +6 -4
  136. package/dist/plugins/jwt/index.d.mts +9 -9
  137. package/dist/plugins/jwt/index.mjs +2 -2
  138. package/dist/plugins/jwt/index.mjs.map +1 -1
  139. package/dist/plugins/last-login-method/index.d.mts +4 -4
  140. package/dist/plugins/magic-link/index.d.mts +4 -4
  141. package/dist/plugins/mcp/authorize.mjs +1 -1
  142. package/dist/plugins/mcp/authorize.mjs.map +1 -1
  143. package/dist/plugins/mcp/index.d.mts +10 -10
  144. package/dist/plugins/multi-session/client.d.mts +10 -14
  145. package/dist/plugins/multi-session/client.mjs +5 -2
  146. package/dist/plugins/multi-session/client.mjs.map +1 -1
  147. package/dist/plugins/multi-session/error-codes.d.mts +10 -0
  148. package/dist/plugins/multi-session/error-codes.mjs +8 -0
  149. package/dist/plugins/multi-session/error-codes.mjs.map +1 -0
  150. package/dist/plugins/multi-session/index.d.mts +18 -14
  151. package/dist/plugins/multi-session/index.mjs +6 -7
  152. package/dist/plugins/multi-session/index.mjs.map +1 -1
  153. package/dist/plugins/oauth-proxy/index.d.mts +8 -8
  154. package/dist/plugins/oidc-provider/authorize.mjs +1 -1
  155. package/dist/plugins/oidc-provider/authorize.mjs.map +1 -1
  156. package/dist/plugins/oidc-provider/error.mjs +1 -1
  157. package/dist/plugins/oidc-provider/error.mjs.map +1 -1
  158. package/dist/plugins/oidc-provider/index.d.mts +15 -15
  159. package/dist/plugins/one-tap/client.d.mts +2 -2
  160. package/dist/plugins/one-tap/index.d.mts +2 -2
  161. package/dist/plugins/one-time-token/index.d.mts +5 -5
  162. package/dist/plugins/open-api/index.d.mts +3 -3
  163. package/dist/plugins/organization/client.d.mts +229 -2
  164. package/dist/plugins/organization/client.mjs +3 -1
  165. package/dist/plugins/organization/client.mjs.map +1 -1
  166. package/dist/plugins/organization/error-codes.d.mts +224 -56
  167. package/dist/plugins/organization/organization.d.mts +7 -7
  168. package/dist/plugins/organization/organization.mjs +4 -4
  169. package/dist/plugins/organization/organization.mjs.map +1 -1
  170. package/dist/plugins/organization/routes/crud-access-control.d.mts +22 -22
  171. package/dist/plugins/organization/routes/crud-access-control.mjs +40 -39
  172. package/dist/plugins/organization/routes/crud-access-control.mjs.map +1 -1
  173. package/dist/plugins/organization/routes/crud-invites.d.mts +58 -58
  174. package/dist/plugins/organization/routes/crud-invites.mjs +42 -40
  175. package/dist/plugins/organization/routes/crud-invites.mjs.map +1 -1
  176. package/dist/plugins/organization/routes/crud-members.d.mts +67 -67
  177. package/dist/plugins/organization/routes/crud-members.mjs +41 -54
  178. package/dist/plugins/organization/routes/crud-members.mjs.map +1 -1
  179. package/dist/plugins/organization/routes/crud-org.d.mts +51 -51
  180. package/dist/plugins/organization/routes/crud-org.mjs +28 -25
  181. package/dist/plugins/organization/routes/crud-org.mjs.map +1 -1
  182. package/dist/plugins/organization/routes/crud-team.d.mts +77 -77
  183. package/dist/plugins/organization/routes/crud-team.mjs +41 -47
  184. package/dist/plugins/organization/routes/crud-team.mjs.map +1 -1
  185. package/dist/plugins/phone-number/client.d.mts +51 -0
  186. package/dist/plugins/phone-number/client.mjs +4 -1
  187. package/dist/plugins/phone-number/client.mjs.map +1 -1
  188. package/dist/plugins/phone-number/error-codes.d.mts +54 -0
  189. package/dist/plugins/phone-number/index.d.mts +81 -45
  190. package/dist/plugins/phone-number/index.mjs +2 -2
  191. package/dist/plugins/phone-number/index.mjs.map +1 -1
  192. package/dist/plugins/phone-number/routes.mjs +27 -28
  193. package/dist/plugins/phone-number/routes.mjs.map +1 -1
  194. package/dist/plugins/siwe/index.d.mts +3 -3
  195. package/dist/plugins/siwe/index.mjs +7 -6
  196. package/dist/plugins/siwe/index.mjs.map +1 -1
  197. package/dist/plugins/two-factor/backup-codes/index.mjs +7 -7
  198. package/dist/plugins/two-factor/backup-codes/index.mjs.map +1 -1
  199. package/dist/plugins/two-factor/client.d.mts +39 -0
  200. package/dist/plugins/two-factor/client.mjs +4 -1
  201. package/dist/plugins/two-factor/client.mjs.map +1 -1
  202. package/dist/plugins/two-factor/error-code.d.mts +36 -9
  203. package/dist/plugins/two-factor/index.d.mts +54 -27
  204. package/dist/plugins/two-factor/index.mjs +4 -5
  205. package/dist/plugins/two-factor/index.mjs.map +1 -1
  206. package/dist/plugins/two-factor/otp/index.mjs +8 -6
  207. package/dist/plugins/two-factor/otp/index.mjs.map +1 -1
  208. package/dist/plugins/two-factor/totp/index.mjs +16 -8
  209. package/dist/plugins/two-factor/totp/index.mjs.map +1 -1
  210. package/dist/plugins/two-factor/verify-two-factor.mjs +9 -6
  211. package/dist/plugins/two-factor/verify-two-factor.mjs.map +1 -1
  212. package/dist/plugins/username/client.d.mts +35 -0
  213. package/dist/plugins/username/client.mjs +4 -1
  214. package/dist/plugins/username/client.mjs.map +1 -1
  215. package/dist/plugins/username/error-codes.d.mts +32 -8
  216. package/dist/plugins/username/index.d.mts +41 -17
  217. package/dist/plugins/username/index.mjs +21 -31
  218. package/dist/plugins/username/index.mjs.map +1 -1
  219. package/dist/plugins/username/schema.d.mts +3 -3
  220. package/dist/test-utils/test-instance.d.mts +1349 -1198
  221. package/dist/utils/is-api-error.d.mts +7 -0
  222. package/dist/utils/is-api-error.mjs +11 -0
  223. package/dist/utils/is-api-error.mjs.map +1 -0
  224. package/dist/utils/password.mjs +3 -3
  225. package/dist/utils/password.mjs.map +1 -1
  226. package/dist/utils/plugin-helper.mjs +2 -2
  227. package/dist/utils/plugin-helper.mjs.map +1 -1
  228. package/package.json +3 -3
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/plugins/two-factor/totp/index.ts"],"sourcesContent":["import { createAuthEndpoint } from \"@better-auth/core/api\";\nimport { BASE_ERROR_CODES } from \"@better-auth/core/error\";\nimport { createOTP } from \"@better-auth/utils/otp\";\nimport { APIError } from \"better-call\";\nimport * as z from \"zod\";\nimport { sessionMiddleware } from \"../../../api\";\nimport { setSessionCookie } from \"../../../cookies\";\nimport { symmetricDecrypt } from \"../../../crypto\";\nimport type { BackupCodeOptions } from \"../backup-codes\";\nimport { TWO_FACTOR_ERROR_CODES } from \"../error-code\";\nimport type {\n\tTwoFactorProvider,\n\tTwoFactorTable,\n\tUserWithTwoFactor,\n} from \"../types\";\nimport { verifyTwoFactor } from \"../verify-two-factor\";\n\nexport type TOTPOptions = {\n\t/**\n\t * Issuer\n\t */\n\tissuer?: string | undefined;\n\t/**\n\t * How many digits the otp to be\n\t *\n\t * @default 6\n\t */\n\tdigits?: (6 | 8) | undefined;\n\t/**\n\t * Period for otp in seconds.\n\t * @default 30\n\t */\n\tperiod?: number | undefined;\n\t/**\n\t * Backup codes configuration\n\t */\n\tbackupCodes?: BackupCodeOptions | undefined;\n\t/**\n\t * Disable totp\n\t */\n\tdisable?: boolean | undefined;\n};\n\nconst generateTOTPBodySchema = z.object({\n\tsecret: z.string().meta({\n\t\tdescription: \"The secret to generate the TOTP code\",\n\t}),\n});\n\nconst getTOTPURIBodySchema = z.object({\n\tpassword: z.string().meta({\n\t\tdescription: \"User password\",\n\t}),\n});\n\nconst verifyTOTPBodySchema = z.object({\n\tcode: z.string().meta({\n\t\tdescription: 'The otp code to verify. Eg: \"012345\"',\n\t}),\n\t/**\n\t * if true, the device will be trusted\n\t * for 30 days. It'll be refreshed on\n\t * every sign in request within this time.\n\t */\n\ttrustDevice: z\n\t\t.boolean()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"If true, the device will be trusted for 30 days. It'll be refreshed on every sign in request within this time. Eg: true\",\n\t\t})\n\t\t.optional(),\n});\n\nexport const totp2fa = (options?: TOTPOptions | undefined) => {\n\tconst opts = {\n\t\t...options,\n\t\tdigits: options?.digits || 6,\n\t\tperiod: options?.period || 30,\n\t};\n\n\tconst twoFactorTable = \"twoFactor\";\n\n\tconst generateTOTP = createAuthEndpoint(\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: generateTOTPBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tsummary: \"Generate TOTP code\",\n\t\t\t\t\tdescription: \"Use this endpoint to generate a TOTP code\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t200: {\n\t\t\t\t\t\t\tdescription: \"Successful response\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tcode: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tif (options?.disable) {\n\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\"totp isn't configured. please pass totp option on two factor plugin to enable totp\",\n\t\t\t\t);\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"totp isn't configured\",\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst code = await createOTP(ctx.body.secret, {\n\t\t\t\tperiod: opts.period,\n\t\t\t\tdigits: opts.digits,\n\t\t\t}).totp();\n\t\t\treturn { code };\n\t\t},\n\t);\n\n\tconst getTOTPURI = createAuthEndpoint(\n\t\t\"/two-factor/get-totp-uri\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tuse: [sessionMiddleware],\n\t\t\tbody: getTOTPURIBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tsummary: \"Get TOTP URI\",\n\t\t\t\t\tdescription: \"Use this endpoint to get the TOTP URI\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t200: {\n\t\t\t\t\t\t\tdescription: \"Successful response\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\ttotpURI: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tif (options?.disable) {\n\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\"totp isn't configured. please pass totp option on two factor plugin to enable totp\",\n\t\t\t\t);\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"totp isn't configured\",\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst user = ctx.context.session.user as UserWithTwoFactor;\n\t\t\tconst twoFactor = await ctx.context.adapter.findOne<TwoFactorTable>({\n\t\t\t\tmodel: twoFactorTable,\n\t\t\t\twhere: [\n\t\t\t\t\t{\n\t\t\t\t\t\tfield: \"userId\",\n\t\t\t\t\t\tvalue: user.id,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\t\t\tif (!twoFactor) {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: TWO_FACTOR_ERROR_CODES.TOTP_NOT_ENABLED,\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst secret = await symmetricDecrypt({\n\t\t\t\tkey: ctx.context.secret,\n\t\t\t\tdata: twoFactor.secret,\n\t\t\t});\n\t\t\tawait ctx.context.password.checkPassword(user.id, ctx);\n\t\t\tconst totpURI = createOTP(secret, {\n\t\t\t\tdigits: opts.digits,\n\t\t\t\tperiod: opts.period,\n\t\t\t}).url(options?.issuer || ctx.context.appName, user.email);\n\t\t\treturn {\n\t\t\t\ttotpURI,\n\t\t\t};\n\t\t},\n\t);\n\n\tconst verifyTOTP = createAuthEndpoint(\n\t\t\"/two-factor/verify-totp\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: verifyTOTPBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tsummary: \"Verify two factor TOTP\",\n\t\t\t\t\tdescription: \"Verify two factor TOTP\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t200: {\n\t\t\t\t\t\t\tdescription: \"Successful response\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tstatus: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"boolean\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tif (options?.disable) {\n\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\"totp isn't configured. please pass totp option on two factor plugin to enable totp\",\n\t\t\t\t);\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"totp isn't configured\",\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst { session, valid, invalid } = await verifyTwoFactor(ctx);\n\t\t\tconst user = session.user as UserWithTwoFactor;\n\t\t\tconst twoFactor = await ctx.context.adapter.findOne<TwoFactorTable>({\n\t\t\t\tmodel: twoFactorTable,\n\t\t\t\twhere: [\n\t\t\t\t\t{\n\t\t\t\t\t\tfield: \"userId\",\n\t\t\t\t\t\tvalue: user.id,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\n\t\t\tif (!twoFactor) {\n\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: TWO_FACTOR_ERROR_CODES.TOTP_NOT_ENABLED,\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst decrypted = await symmetricDecrypt({\n\t\t\t\tkey: ctx.context.secret,\n\t\t\t\tdata: twoFactor.secret,\n\t\t\t});\n\t\t\tconst status = await createOTP(decrypted, {\n\t\t\t\tperiod: opts.period,\n\t\t\t\tdigits: opts.digits,\n\t\t\t}).verify(ctx.body.code);\n\t\t\tif (!status) {\n\t\t\t\treturn invalid(\"INVALID_CODE\");\n\t\t\t}\n\n\t\t\tif (!user.twoFactorEnabled) {\n\t\t\t\tif (!session.session) {\n\t\t\t\t\tthrow new APIError(\"BAD_REQUEST\", {\n\t\t\t\t\t\tmessage: BASE_ERROR_CODES.FAILED_TO_CREATE_SESSION,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tconst updatedUser = await ctx.context.internalAdapter.updateUser(\n\t\t\t\t\tuser.id,\n\t\t\t\t\t{\n\t\t\t\t\t\ttwoFactorEnabled: true,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t\tconst newSession = await ctx.context.internalAdapter\n\t\t\t\t\t.createSession(user.id, false, session.session)\n\t\t\t\t\t.catch((e) => {\n\t\t\t\t\t\tthrow e;\n\t\t\t\t\t});\n\n\t\t\t\tawait ctx.context.internalAdapter.deleteSession(session.session.token);\n\t\t\t\tawait setSessionCookie(ctx, {\n\t\t\t\t\tsession: newSession,\n\t\t\t\t\tuser: updatedUser,\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn valid(ctx);\n\t\t},\n\t);\n\n\treturn {\n\t\tid: \"totp\",\n\t\tendpoints: {\n\t\t\t/**\n\t\t\t * ### Endpoint\n\t\t\t *\n\t\t\t * POST `/totp/generate`\n\t\t\t *\n\t\t\t * ### API Methods\n\t\t\t *\n\t\t\t * **server:**\n\t\t\t * `auth.api.generateTOTP`\n\t\t\t *\n\t\t\t * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/2fa#totp)\n\t\t\t */\n\t\t\tgenerateTOTP: generateTOTP,\n\t\t\t/**\n\t\t\t * ### Endpoint\n\t\t\t *\n\t\t\t * POST `/two-factor/get-totp-uri`\n\t\t\t *\n\t\t\t * ### API Methods\n\t\t\t *\n\t\t\t * **server:**\n\t\t\t * `auth.api.getTOTPURI`\n\t\t\t *\n\t\t\t * **client:**\n\t\t\t * `authClient.twoFactor.getTotpUri`\n\t\t\t *\n\t\t\t * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/2fa#getting-totp-uri)\n\t\t\t */\n\t\t\tgetTOTPURI: getTOTPURI,\n\t\t\t/**\n\t\t\t * ### Endpoint\n\t\t\t *\n\t\t\t * POST `/two-factor/verify-totp`\n\t\t\t *\n\t\t\t * ### API Methods\n\t\t\t *\n\t\t\t * **server:**\n\t\t\t * `auth.api.verifyTOTP`\n\t\t\t *\n\t\t\t * **client:**\n\t\t\t * `authClient.twoFactor.verifyTotp`\n\t\t\t *\n\t\t\t * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/2fa#verifying-totp)\n\t\t\t */\n\t\t\tverifyTOTP,\n\t\t},\n\t} satisfies TwoFactorProvider;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/plugins/two-factor/totp/index.ts"],"sourcesContent":["import { createAuthEndpoint } from \"@better-auth/core/api\";\nimport { APIError, BASE_ERROR_CODES } from \"@better-auth/core/error\";\nimport { createOTP } from \"@better-auth/utils/otp\";\nimport * as z from \"zod\";\nimport { sessionMiddleware } from \"../../../api\";\nimport { setSessionCookie } from \"../../../cookies\";\nimport { symmetricDecrypt } from \"../../../crypto\";\nimport type { BackupCodeOptions } from \"../backup-codes\";\nimport { TWO_FACTOR_ERROR_CODES } from \"../error-code\";\nimport type {\n\tTwoFactorProvider,\n\tTwoFactorTable,\n\tUserWithTwoFactor,\n} from \"../types\";\nimport { verifyTwoFactor } from \"../verify-two-factor\";\n\nexport type TOTPOptions = {\n\t/**\n\t * Issuer\n\t */\n\tissuer?: string | undefined;\n\t/**\n\t * How many digits the otp to be\n\t *\n\t * @default 6\n\t */\n\tdigits?: (6 | 8) | undefined;\n\t/**\n\t * Period for otp in seconds.\n\t * @default 30\n\t */\n\tperiod?: number | undefined;\n\t/**\n\t * Backup codes configuration\n\t */\n\tbackupCodes?: BackupCodeOptions | undefined;\n\t/**\n\t * Disable totp\n\t */\n\tdisable?: boolean | undefined;\n};\n\nconst generateTOTPBodySchema = z.object({\n\tsecret: z.string().meta({\n\t\tdescription: \"The secret to generate the TOTP code\",\n\t}),\n});\n\nconst getTOTPURIBodySchema = z.object({\n\tpassword: z.string().meta({\n\t\tdescription: \"User password\",\n\t}),\n});\n\nconst verifyTOTPBodySchema = z.object({\n\tcode: z.string().meta({\n\t\tdescription: 'The otp code to verify. Eg: \"012345\"',\n\t}),\n\t/**\n\t * if true, the device will be trusted\n\t * for 30 days. It'll be refreshed on\n\t * every sign in request within this time.\n\t */\n\ttrustDevice: z\n\t\t.boolean()\n\t\t.meta({\n\t\t\tdescription:\n\t\t\t\t\"If true, the device will be trusted for 30 days. It'll be refreshed on every sign in request within this time. Eg: true\",\n\t\t})\n\t\t.optional(),\n});\n\nexport const totp2fa = (options?: TOTPOptions | undefined) => {\n\tconst opts = {\n\t\t...options,\n\t\tdigits: options?.digits || 6,\n\t\tperiod: options?.period || 30,\n\t};\n\n\tconst twoFactorTable = \"twoFactor\";\n\n\tconst generateTOTP = createAuthEndpoint(\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: generateTOTPBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tsummary: \"Generate TOTP code\",\n\t\t\t\t\tdescription: \"Use this endpoint to generate a TOTP code\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t200: {\n\t\t\t\t\t\t\tdescription: \"Successful response\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tcode: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tif (options?.disable) {\n\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\"totp isn't configured. please pass totp option on two factor plugin to enable totp\",\n\t\t\t\t);\n\t\t\t\tthrow APIError.from(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"totp isn't configured\",\n\t\t\t\t\tcode: \"TOTP_NOT_CONFIGURED\",\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst code = await createOTP(ctx.body.secret, {\n\t\t\t\tperiod: opts.period,\n\t\t\t\tdigits: opts.digits,\n\t\t\t}).totp();\n\t\t\treturn { code };\n\t\t},\n\t);\n\n\tconst getTOTPURI = createAuthEndpoint(\n\t\t\"/two-factor/get-totp-uri\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tuse: [sessionMiddleware],\n\t\t\tbody: getTOTPURIBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tsummary: \"Get TOTP URI\",\n\t\t\t\t\tdescription: \"Use this endpoint to get the TOTP URI\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t200: {\n\t\t\t\t\t\t\tdescription: \"Successful response\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\ttotpURI: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tif (options?.disable) {\n\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\"totp isn't configured. please pass totp option on two factor plugin to enable totp\",\n\t\t\t\t);\n\t\t\t\tthrow APIError.from(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"totp isn't configured\",\n\t\t\t\t\tcode: \"TOTP_NOT_CONFIGURED\",\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst user = ctx.context.session.user as UserWithTwoFactor;\n\t\t\tconst twoFactor = await ctx.context.adapter.findOne<TwoFactorTable>({\n\t\t\t\tmodel: twoFactorTable,\n\t\t\t\twhere: [\n\t\t\t\t\t{\n\t\t\t\t\t\tfield: \"userId\",\n\t\t\t\t\t\tvalue: user.id,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\t\t\tif (!twoFactor) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tTWO_FACTOR_ERROR_CODES.TOTP_NOT_ENABLED,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst secret = await symmetricDecrypt({\n\t\t\t\tkey: ctx.context.secret,\n\t\t\t\tdata: twoFactor.secret,\n\t\t\t});\n\t\t\tawait ctx.context.password.checkPassword(user.id, ctx);\n\t\t\tconst totpURI = createOTP(secret, {\n\t\t\t\tdigits: opts.digits,\n\t\t\t\tperiod: opts.period,\n\t\t\t}).url(options?.issuer || ctx.context.appName, user.email);\n\t\t\treturn {\n\t\t\t\ttotpURI,\n\t\t\t};\n\t\t},\n\t);\n\n\tconst verifyTOTP = createAuthEndpoint(\n\t\t\"/two-factor/verify-totp\",\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\tbody: verifyTOTPBodySchema,\n\t\t\tmetadata: {\n\t\t\t\topenapi: {\n\t\t\t\t\tsummary: \"Verify two factor TOTP\",\n\t\t\t\t\tdescription: \"Verify two factor TOTP\",\n\t\t\t\t\tresponses: {\n\t\t\t\t\t\t200: {\n\t\t\t\t\t\t\tdescription: \"Successful response\",\n\t\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\tstatus: {\n\t\t\t\t\t\t\t\t\t\t\t\ttype: \"boolean\",\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\tasync (ctx) => {\n\t\t\tif (options?.disable) {\n\t\t\t\tctx.context.logger.error(\n\t\t\t\t\t\"totp isn't configured. please pass totp option on two factor plugin to enable totp\",\n\t\t\t\t);\n\t\t\t\tthrow APIError.from(\"BAD_REQUEST\", {\n\t\t\t\t\tmessage: \"totp isn't configured\",\n\t\t\t\t\tcode: \"TOTP_NOT_CONFIGURED\",\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst { session, valid, invalid } = await verifyTwoFactor(ctx);\n\t\t\tconst user = session.user as UserWithTwoFactor;\n\t\t\tconst twoFactor = await ctx.context.adapter.findOne<TwoFactorTable>({\n\t\t\t\tmodel: twoFactorTable,\n\t\t\t\twhere: [\n\t\t\t\t\t{\n\t\t\t\t\t\tfield: \"userId\",\n\t\t\t\t\t\tvalue: user.id,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t});\n\n\t\t\tif (!twoFactor) {\n\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\tTWO_FACTOR_ERROR_CODES.TOTP_NOT_ENABLED,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst decrypted = await symmetricDecrypt({\n\t\t\t\tkey: ctx.context.secret,\n\t\t\t\tdata: twoFactor.secret,\n\t\t\t});\n\t\t\tconst status = await createOTP(decrypted, {\n\t\t\t\tperiod: opts.period,\n\t\t\t\tdigits: opts.digits,\n\t\t\t}).verify(ctx.body.code);\n\t\t\tif (!status) {\n\t\t\t\treturn invalid(\"INVALID_CODE\");\n\t\t\t}\n\n\t\t\tif (!user.twoFactorEnabled) {\n\t\t\t\tif (!session.session) {\n\t\t\t\t\tthrow APIError.from(\n\t\t\t\t\t\t\"BAD_REQUEST\",\n\t\t\t\t\t\tBASE_ERROR_CODES.FAILED_TO_CREATE_SESSION,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst updatedUser = await ctx.context.internalAdapter.updateUser(\n\t\t\t\t\tuser.id,\n\t\t\t\t\t{\n\t\t\t\t\t\ttwoFactorEnabled: true,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t\tconst newSession = await ctx.context.internalAdapter\n\t\t\t\t\t.createSession(user.id, false, session.session)\n\t\t\t\t\t.catch((e) => {\n\t\t\t\t\t\tthrow e;\n\t\t\t\t\t});\n\n\t\t\t\tawait ctx.context.internalAdapter.deleteSession(session.session.token);\n\t\t\t\tawait setSessionCookie(ctx, {\n\t\t\t\t\tsession: newSession,\n\t\t\t\t\tuser: updatedUser,\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn valid(ctx);\n\t\t},\n\t);\n\n\treturn {\n\t\tid: \"totp\",\n\t\tendpoints: {\n\t\t\t/**\n\t\t\t * ### Endpoint\n\t\t\t *\n\t\t\t * POST `/totp/generate`\n\t\t\t *\n\t\t\t * ### API Methods\n\t\t\t *\n\t\t\t * **server:**\n\t\t\t * `auth.api.generateTOTP`\n\t\t\t *\n\t\t\t * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/2fa#totp)\n\t\t\t */\n\t\t\tgenerateTOTP: generateTOTP,\n\t\t\t/**\n\t\t\t * ### Endpoint\n\t\t\t *\n\t\t\t * POST `/two-factor/get-totp-uri`\n\t\t\t *\n\t\t\t * ### API Methods\n\t\t\t *\n\t\t\t * **server:**\n\t\t\t * `auth.api.getTOTPURI`\n\t\t\t *\n\t\t\t * **client:**\n\t\t\t * `authClient.twoFactor.getTotpUri`\n\t\t\t *\n\t\t\t * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/2fa#getting-totp-uri)\n\t\t\t */\n\t\t\tgetTOTPURI: getTOTPURI,\n\t\t\t/**\n\t\t\t * ### Endpoint\n\t\t\t *\n\t\t\t * POST `/two-factor/verify-totp`\n\t\t\t *\n\t\t\t * ### API Methods\n\t\t\t *\n\t\t\t * **server:**\n\t\t\t * `auth.api.verifyTOTP`\n\t\t\t *\n\t\t\t * **client:**\n\t\t\t * `authClient.twoFactor.verifyTotp`\n\t\t\t *\n\t\t\t * @see [Read our docs to learn more.](https://better-auth.com/docs/plugins/2fa#verifying-totp)\n\t\t\t */\n\t\t\tverifyTOTP,\n\t\t},\n\t} satisfies TwoFactorProvider;\n};\n"],"mappings":"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"}
@@ -3,28 +3,31 @@ import { getSessionFromCtx } from "../../api/routes/session.mjs";
3
3
  import "../../api/index.mjs";
4
4
  import { TWO_FACTOR_ERROR_CODES } from "./error-code.mjs";
5
5
  import { TRUST_DEVICE_COOKIE_MAX_AGE, TRUST_DEVICE_COOKIE_NAME, TWO_FACTOR_COOKIE_NAME } from "./constant.mjs";
6
- import { APIError } from "better-call";
6
+ import { APIError } from "@better-auth/core/error";
7
7
  import { createHMAC } from "@better-auth/utils/hmac";
8
8
 
9
9
  //#region src/plugins/two-factor/verify-two-factor.ts
10
10
  async function verifyTwoFactor(ctx) {
11
11
  const invalid = (errorKey) => {
12
- throw new APIError("UNAUTHORIZED", { message: TWO_FACTOR_ERROR_CODES[errorKey] });
12
+ throw APIError.from("UNAUTHORIZED", TWO_FACTOR_ERROR_CODES[errorKey]);
13
13
  };
14
14
  const session = await getSessionFromCtx(ctx);
15
15
  if (!session) {
16
16
  const cookieName = ctx.context.createAuthCookie(TWO_FACTOR_COOKIE_NAME);
17
17
  const twoFactorCookie = await ctx.getSignedCookie(cookieName.name, ctx.context.secret);
18
- if (!twoFactorCookie) throw new APIError("UNAUTHORIZED", { message: TWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE });
18
+ if (!twoFactorCookie) throw APIError.from("UNAUTHORIZED", TWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE);
19
19
  const verificationToken = await ctx.context.internalAdapter.findVerificationValue(twoFactorCookie);
20
- if (!verificationToken) throw new APIError("UNAUTHORIZED", { message: TWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE });
20
+ if (!verificationToken) throw APIError.from("UNAUTHORIZED", TWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE);
21
21
  const user = await ctx.context.internalAdapter.findUserById(verificationToken.value);
22
- if (!user) throw new APIError("UNAUTHORIZED", { message: TWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE });
22
+ if (!user) throw APIError.from("UNAUTHORIZED", TWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE);
23
23
  const dontRememberMe = await ctx.getSignedCookie(ctx.context.authCookies.dontRememberToken.name, ctx.context.secret);
24
24
  return {
25
25
  valid: async (ctx$1) => {
26
26
  const session$1 = await ctx$1.context.internalAdapter.createSession(verificationToken.value, !!dontRememberMe);
27
- if (!session$1) throw new APIError("INTERNAL_SERVER_ERROR", { message: "failed to create session" });
27
+ if (!session$1) throw APIError.from("INTERNAL_SERVER_ERROR", {
28
+ message: "failed to create session",
29
+ code: "FAILED_TO_CREATE_SESSION"
30
+ });
28
31
  await ctx$1.context.internalAdapter.deleteVerificationValue(verificationToken.id);
29
32
  await setSessionCookie(ctx$1, {
30
33
  session: session$1,
@@ -1 +1 @@
1
- {"version":3,"file":"verify-two-factor.mjs","names":["session","ctx"],"sources":["../../../src/plugins/two-factor/verify-two-factor.ts"],"sourcesContent":["import type { GenericEndpointContext } from \"@better-auth/core\";\nimport { createHMAC } from \"@better-auth/utils/hmac\";\nimport { APIError } from \"better-call\";\nimport { getSessionFromCtx } from \"../../api\";\nimport { setSessionCookie } from \"../../cookies\";\nimport {\n\tTRUST_DEVICE_COOKIE_MAX_AGE,\n\tTRUST_DEVICE_COOKIE_NAME,\n\tTWO_FACTOR_COOKIE_NAME,\n} from \"./constant\";\nimport { TWO_FACTOR_ERROR_CODES } from \"./error-code\";\nimport type { UserWithTwoFactor } from \"./types\";\n\nexport async function verifyTwoFactor(ctx: GenericEndpointContext) {\n\tconst invalid = (errorKey: keyof typeof TWO_FACTOR_ERROR_CODES) => {\n\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\tmessage: TWO_FACTOR_ERROR_CODES[errorKey],\n\t\t});\n\t};\n\n\tconst session = await getSessionFromCtx(ctx);\n\tif (!session) {\n\t\tconst cookieName = ctx.context.createAuthCookie(TWO_FACTOR_COOKIE_NAME);\n\t\tconst twoFactorCookie = await ctx.getSignedCookie(\n\t\t\tcookieName.name,\n\t\t\tctx.context.secret,\n\t\t);\n\t\tif (!twoFactorCookie) {\n\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\tmessage: TWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE,\n\t\t\t});\n\t\t}\n\t\tconst verificationToken =\n\t\t\tawait ctx.context.internalAdapter.findVerificationValue(twoFactorCookie);\n\t\tif (!verificationToken) {\n\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\tmessage: TWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE,\n\t\t\t});\n\t\t}\n\t\tconst user = (await ctx.context.internalAdapter.findUserById(\n\t\t\tverificationToken.value,\n\t\t)) as UserWithTwoFactor;\n\t\tif (!user) {\n\t\t\tthrow new APIError(\"UNAUTHORIZED\", {\n\t\t\t\tmessage: TWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE,\n\t\t\t});\n\t\t}\n\t\tconst dontRememberMe = await ctx.getSignedCookie(\n\t\t\tctx.context.authCookies.dontRememberToken.name,\n\t\t\tctx.context.secret,\n\t\t);\n\t\treturn {\n\t\t\tvalid: async (ctx: GenericEndpointContext) => {\n\t\t\t\tconst session = await ctx.context.internalAdapter.createSession(\n\t\t\t\t\tverificationToken.value,\n\t\t\t\t\t!!dontRememberMe,\n\t\t\t\t);\n\t\t\t\tif (!session) {\n\t\t\t\t\tthrow new APIError(\"INTERNAL_SERVER_ERROR\", {\n\t\t\t\t\t\tmessage: \"failed to create session\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\t// Delete the verification token from the database after successful verification\n\t\t\t\tawait ctx.context.internalAdapter.deleteVerificationValue(\n\t\t\t\t\tverificationToken.id,\n\t\t\t\t);\n\t\t\t\tawait setSessionCookie(ctx, {\n\t\t\t\t\tsession,\n\t\t\t\t\tuser,\n\t\t\t\t});\n\t\t\t\t// Always clear the two factor cookie after successful verification\n\t\t\t\tctx.setCookie(cookieName.name, \"\", {\n\t\t\t\t\tmaxAge: 0,\n\t\t\t\t});\n\t\t\t\tif (ctx.body.trustDevice) {\n\t\t\t\t\tconst trustDeviceCookie = ctx.context.createAuthCookie(\n\t\t\t\t\t\tTRUST_DEVICE_COOKIE_NAME,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmaxAge: TRUST_DEVICE_COOKIE_MAX_AGE,\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t\t/**\n\t\t\t\t\t * create a token that will be used to\n\t\t\t\t\t * verify the device\n\t\t\t\t\t */\n\t\t\t\t\tconst token = await createHMAC(\"SHA-256\", \"base64urlnopad\").sign(\n\t\t\t\t\t\tctx.context.secret,\n\t\t\t\t\t\t`${user.id}!${session.token}`,\n\t\t\t\t\t);\n\t\t\t\t\tawait ctx.setSignedCookie(\n\t\t\t\t\t\ttrustDeviceCookie.name,\n\t\t\t\t\t\t`${token}!${session.token}`,\n\t\t\t\t\t\tctx.context.secret,\n\t\t\t\t\t\ttrustDeviceCookie.attributes,\n\t\t\t\t\t);\n\t\t\t\t\t// delete the dont remember me cookie\n\t\t\t\t\tctx.setCookie(ctx.context.authCookies.dontRememberToken.name, \"\", {\n\t\t\t\t\t\tmaxAge: 0,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn ctx.json({\n\t\t\t\t\ttoken: session.token,\n\t\t\t\t\tuser: {\n\t\t\t\t\t\tid: user.id,\n\t\t\t\t\t\temail: user.email,\n\t\t\t\t\t\temailVerified: user.emailVerified,\n\t\t\t\t\t\tname: user.name,\n\t\t\t\t\t\timage: user.image,\n\t\t\t\t\t\tcreatedAt: user.createdAt,\n\t\t\t\t\t\tupdatedAt: user.updatedAt,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t},\n\t\t\tinvalid,\n\t\t\tsession: {\n\t\t\t\tsession: null,\n\t\t\t\tuser,\n\t\t\t},\n\t\t\tkey: twoFactorCookie,\n\t\t};\n\t}\n\treturn {\n\t\tvalid: async (ctx: GenericEndpointContext) => {\n\t\t\treturn ctx.json({\n\t\t\t\ttoken: session.session.token,\n\t\t\t\tuser: {\n\t\t\t\t\tid: session.user.id,\n\t\t\t\t\temail: session.user.email,\n\t\t\t\t\temailVerified: session.user.emailVerified,\n\t\t\t\t\tname: session.user.name,\n\t\t\t\t\timage: session.user.image,\n\t\t\t\t\tcreatedAt: session.user.createdAt,\n\t\t\t\t\tupdatedAt: session.user.updatedAt,\n\t\t\t\t},\n\t\t\t});\n\t\t},\n\t\tinvalid,\n\t\tsession,\n\t\tkey: `${session.user.id}!${session.session.id}`,\n\t};\n}\n"],"mappings":";;;;;;;;;AAaA,eAAsB,gBAAgB,KAA6B;CAClE,MAAM,WAAW,aAAkD;AAClE,QAAM,IAAI,SAAS,gBAAgB,EAClC,SAAS,uBAAuB,WAChC,CAAC;;CAGH,MAAM,UAAU,MAAM,kBAAkB,IAAI;AAC5C,KAAI,CAAC,SAAS;EACb,MAAM,aAAa,IAAI,QAAQ,iBAAiB,uBAAuB;EACvE,MAAM,kBAAkB,MAAM,IAAI,gBACjC,WAAW,MACX,IAAI,QAAQ,OACZ;AACD,MAAI,CAAC,gBACJ,OAAM,IAAI,SAAS,gBAAgB,EAClC,SAAS,uBAAuB,2BAChC,CAAC;EAEH,MAAM,oBACL,MAAM,IAAI,QAAQ,gBAAgB,sBAAsB,gBAAgB;AACzE,MAAI,CAAC,kBACJ,OAAM,IAAI,SAAS,gBAAgB,EAClC,SAAS,uBAAuB,2BAChC,CAAC;EAEH,MAAM,OAAQ,MAAM,IAAI,QAAQ,gBAAgB,aAC/C,kBAAkB,MAClB;AACD,MAAI,CAAC,KACJ,OAAM,IAAI,SAAS,gBAAgB,EAClC,SAAS,uBAAuB,2BAChC,CAAC;EAEH,MAAM,iBAAiB,MAAM,IAAI,gBAChC,IAAI,QAAQ,YAAY,kBAAkB,MAC1C,IAAI,QAAQ,OACZ;AACD,SAAO;GACN,OAAO,OAAO,UAAgC;IAC7C,MAAMA,YAAU,MAAMC,MAAI,QAAQ,gBAAgB,cACjD,kBAAkB,OAClB,CAAC,CAAC,eACF;AACD,QAAI,CAACD,UACJ,OAAM,IAAI,SAAS,yBAAyB,EAC3C,SAAS,4BACT,CAAC;AAGH,UAAMC,MAAI,QAAQ,gBAAgB,wBACjC,kBAAkB,GAClB;AACD,UAAM,iBAAiBA,OAAK;KAC3B;KACA;KACA,CAAC;AAEF,UAAI,UAAU,WAAW,MAAM,IAAI,EAClC,QAAQ,GACR,CAAC;AACF,QAAIA,MAAI,KAAK,aAAa;KACzB,MAAM,oBAAoBA,MAAI,QAAQ,iBACrC,0BACA,EACC,QAAQ,6BACR,CACD;;;;;KAKD,MAAM,QAAQ,MAAM,WAAW,WAAW,iBAAiB,CAAC,KAC3DA,MAAI,QAAQ,QACZ,GAAG,KAAK,GAAG,GAAGD,UAAQ,QACtB;AACD,WAAMC,MAAI,gBACT,kBAAkB,MAClB,GAAG,MAAM,GAAGD,UAAQ,SACpBC,MAAI,QAAQ,QACZ,kBAAkB,WAClB;AAED,WAAI,UAAUA,MAAI,QAAQ,YAAY,kBAAkB,MAAM,IAAI,EACjE,QAAQ,GACR,CAAC;;AAEH,WAAOA,MAAI,KAAK;KACf,OAAOD,UAAQ;KACf,MAAM;MACL,IAAI,KAAK;MACT,OAAO,KAAK;MACZ,eAAe,KAAK;MACpB,MAAM,KAAK;MACX,OAAO,KAAK;MACZ,WAAW,KAAK;MAChB,WAAW,KAAK;MAChB;KACD,CAAC;;GAEH;GACA,SAAS;IACR,SAAS;IACT;IACA;GACD,KAAK;GACL;;AAEF,QAAO;EACN,OAAO,OAAO,UAAgC;AAC7C,UAAOC,MAAI,KAAK;IACf,OAAO,QAAQ,QAAQ;IACvB,MAAM;KACL,IAAI,QAAQ,KAAK;KACjB,OAAO,QAAQ,KAAK;KACpB,eAAe,QAAQ,KAAK;KAC5B,MAAM,QAAQ,KAAK;KACnB,OAAO,QAAQ,KAAK;KACpB,WAAW,QAAQ,KAAK;KACxB,WAAW,QAAQ,KAAK;KACxB;IACD,CAAC;;EAEH;EACA;EACA,KAAK,GAAG,QAAQ,KAAK,GAAG,GAAG,QAAQ,QAAQ;EAC3C"}
1
+ {"version":3,"file":"verify-two-factor.mjs","names":["session","ctx"],"sources":["../../../src/plugins/two-factor/verify-two-factor.ts"],"sourcesContent":["import type { GenericEndpointContext } from \"@better-auth/core\";\nimport { APIError } from \"@better-auth/core/error\";\nimport { createHMAC } from \"@better-auth/utils/hmac\";\nimport { getSessionFromCtx } from \"../../api\";\nimport { setSessionCookie } from \"../../cookies\";\nimport {\n\tTRUST_DEVICE_COOKIE_MAX_AGE,\n\tTRUST_DEVICE_COOKIE_NAME,\n\tTWO_FACTOR_COOKIE_NAME,\n} from \"./constant\";\nimport { TWO_FACTOR_ERROR_CODES } from \"./error-code\";\nimport type { UserWithTwoFactor } from \"./types\";\n\nexport async function verifyTwoFactor(ctx: GenericEndpointContext) {\n\tconst invalid = (errorKey: keyof typeof TWO_FACTOR_ERROR_CODES) => {\n\t\tthrow APIError.from(\"UNAUTHORIZED\", TWO_FACTOR_ERROR_CODES[errorKey]);\n\t};\n\n\tconst session = await getSessionFromCtx(ctx);\n\tif (!session) {\n\t\tconst cookieName = ctx.context.createAuthCookie(TWO_FACTOR_COOKIE_NAME);\n\t\tconst twoFactorCookie = await ctx.getSignedCookie(\n\t\t\tcookieName.name,\n\t\t\tctx.context.secret,\n\t\t);\n\t\tif (!twoFactorCookie) {\n\t\t\tthrow APIError.from(\n\t\t\t\t\"UNAUTHORIZED\",\n\t\t\t\tTWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE,\n\t\t\t);\n\t\t}\n\t\tconst verificationToken =\n\t\t\tawait ctx.context.internalAdapter.findVerificationValue(twoFactorCookie);\n\t\tif (!verificationToken) {\n\t\t\tthrow APIError.from(\n\t\t\t\t\"UNAUTHORIZED\",\n\t\t\t\tTWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE,\n\t\t\t);\n\t\t}\n\t\tconst user = (await ctx.context.internalAdapter.findUserById(\n\t\t\tverificationToken.value,\n\t\t)) as UserWithTwoFactor;\n\t\tif (!user) {\n\t\t\tthrow APIError.from(\n\t\t\t\t\"UNAUTHORIZED\",\n\t\t\t\tTWO_FACTOR_ERROR_CODES.INVALID_TWO_FACTOR_COOKIE,\n\t\t\t);\n\t\t}\n\t\tconst dontRememberMe = await ctx.getSignedCookie(\n\t\t\tctx.context.authCookies.dontRememberToken.name,\n\t\t\tctx.context.secret,\n\t\t);\n\t\treturn {\n\t\t\tvalid: async (ctx: GenericEndpointContext) => {\n\t\t\t\tconst session = await ctx.context.internalAdapter.createSession(\n\t\t\t\t\tverificationToken.value,\n\t\t\t\t\t!!dontRememberMe,\n\t\t\t\t);\n\t\t\t\tif (!session) {\n\t\t\t\t\tthrow APIError.from(\"INTERNAL_SERVER_ERROR\", {\n\t\t\t\t\t\tmessage: \"failed to create session\",\n\t\t\t\t\t\tcode: \"FAILED_TO_CREATE_SESSION\",\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\t// Delete the verification token from the database after successful verification\n\t\t\t\tawait ctx.context.internalAdapter.deleteVerificationValue(\n\t\t\t\t\tverificationToken.id,\n\t\t\t\t);\n\t\t\t\tawait setSessionCookie(ctx, {\n\t\t\t\t\tsession,\n\t\t\t\t\tuser,\n\t\t\t\t});\n\t\t\t\t// Always clear the two factor cookie after successful verification\n\t\t\t\tctx.setCookie(cookieName.name, \"\", {\n\t\t\t\t\tmaxAge: 0,\n\t\t\t\t});\n\t\t\t\tif (ctx.body.trustDevice) {\n\t\t\t\t\tconst trustDeviceCookie = ctx.context.createAuthCookie(\n\t\t\t\t\t\tTRUST_DEVICE_COOKIE_NAME,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tmaxAge: TRUST_DEVICE_COOKIE_MAX_AGE,\n\t\t\t\t\t\t},\n\t\t\t\t\t);\n\t\t\t\t\t/**\n\t\t\t\t\t * create a token that will be used to\n\t\t\t\t\t * verify the device\n\t\t\t\t\t */\n\t\t\t\t\tconst token = await createHMAC(\"SHA-256\", \"base64urlnopad\").sign(\n\t\t\t\t\t\tctx.context.secret,\n\t\t\t\t\t\t`${user.id}!${session.token}`,\n\t\t\t\t\t);\n\t\t\t\t\tawait ctx.setSignedCookie(\n\t\t\t\t\t\ttrustDeviceCookie.name,\n\t\t\t\t\t\t`${token}!${session.token}`,\n\t\t\t\t\t\tctx.context.secret,\n\t\t\t\t\t\ttrustDeviceCookie.attributes,\n\t\t\t\t\t);\n\t\t\t\t\t// delete the dont remember me cookie\n\t\t\t\t\tctx.setCookie(ctx.context.authCookies.dontRememberToken.name, \"\", {\n\t\t\t\t\t\tmaxAge: 0,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn ctx.json({\n\t\t\t\t\ttoken: session.token,\n\t\t\t\t\tuser: {\n\t\t\t\t\t\tid: user.id,\n\t\t\t\t\t\temail: user.email,\n\t\t\t\t\t\temailVerified: user.emailVerified,\n\t\t\t\t\t\tname: user.name,\n\t\t\t\t\t\timage: user.image,\n\t\t\t\t\t\tcreatedAt: user.createdAt,\n\t\t\t\t\t\tupdatedAt: user.updatedAt,\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t},\n\t\t\tinvalid,\n\t\t\tsession: {\n\t\t\t\tsession: null,\n\t\t\t\tuser,\n\t\t\t},\n\t\t\tkey: twoFactorCookie,\n\t\t};\n\t}\n\treturn {\n\t\tvalid: async (ctx: GenericEndpointContext) => {\n\t\t\treturn ctx.json({\n\t\t\t\ttoken: session.session.token,\n\t\t\t\tuser: {\n\t\t\t\t\tid: session.user.id,\n\t\t\t\t\temail: session.user.email,\n\t\t\t\t\temailVerified: session.user.emailVerified,\n\t\t\t\t\tname: session.user.name,\n\t\t\t\t\timage: session.user.image,\n\t\t\t\t\tcreatedAt: session.user.createdAt,\n\t\t\t\t\tupdatedAt: session.user.updatedAt,\n\t\t\t\t},\n\t\t\t});\n\t\t},\n\t\tinvalid,\n\t\tsession,\n\t\tkey: `${session.user.id}!${session.session.id}`,\n\t};\n}\n"],"mappings":";;;;;;;;;AAaA,eAAsB,gBAAgB,KAA6B;CAClE,MAAM,WAAW,aAAkD;AAClE,QAAM,SAAS,KAAK,gBAAgB,uBAAuB,UAAU;;CAGtE,MAAM,UAAU,MAAM,kBAAkB,IAAI;AAC5C,KAAI,CAAC,SAAS;EACb,MAAM,aAAa,IAAI,QAAQ,iBAAiB,uBAAuB;EACvE,MAAM,kBAAkB,MAAM,IAAI,gBACjC,WAAW,MACX,IAAI,QAAQ,OACZ;AACD,MAAI,CAAC,gBACJ,OAAM,SAAS,KACd,gBACA,uBAAuB,0BACvB;EAEF,MAAM,oBACL,MAAM,IAAI,QAAQ,gBAAgB,sBAAsB,gBAAgB;AACzE,MAAI,CAAC,kBACJ,OAAM,SAAS,KACd,gBACA,uBAAuB,0BACvB;EAEF,MAAM,OAAQ,MAAM,IAAI,QAAQ,gBAAgB,aAC/C,kBAAkB,MAClB;AACD,MAAI,CAAC,KACJ,OAAM,SAAS,KACd,gBACA,uBAAuB,0BACvB;EAEF,MAAM,iBAAiB,MAAM,IAAI,gBAChC,IAAI,QAAQ,YAAY,kBAAkB,MAC1C,IAAI,QAAQ,OACZ;AACD,SAAO;GACN,OAAO,OAAO,UAAgC;IAC7C,MAAMA,YAAU,MAAMC,MAAI,QAAQ,gBAAgB,cACjD,kBAAkB,OAClB,CAAC,CAAC,eACF;AACD,QAAI,CAACD,UACJ,OAAM,SAAS,KAAK,yBAAyB;KAC5C,SAAS;KACT,MAAM;KACN,CAAC;AAGH,UAAMC,MAAI,QAAQ,gBAAgB,wBACjC,kBAAkB,GAClB;AACD,UAAM,iBAAiBA,OAAK;KAC3B;KACA;KACA,CAAC;AAEF,UAAI,UAAU,WAAW,MAAM,IAAI,EAClC,QAAQ,GACR,CAAC;AACF,QAAIA,MAAI,KAAK,aAAa;KACzB,MAAM,oBAAoBA,MAAI,QAAQ,iBACrC,0BACA,EACC,QAAQ,6BACR,CACD;;;;;KAKD,MAAM,QAAQ,MAAM,WAAW,WAAW,iBAAiB,CAAC,KAC3DA,MAAI,QAAQ,QACZ,GAAG,KAAK,GAAG,GAAGD,UAAQ,QACtB;AACD,WAAMC,MAAI,gBACT,kBAAkB,MAClB,GAAG,MAAM,GAAGD,UAAQ,SACpBC,MAAI,QAAQ,QACZ,kBAAkB,WAClB;AAED,WAAI,UAAUA,MAAI,QAAQ,YAAY,kBAAkB,MAAM,IAAI,EACjE,QAAQ,GACR,CAAC;;AAEH,WAAOA,MAAI,KAAK;KACf,OAAOD,UAAQ;KACf,MAAM;MACL,IAAI,KAAK;MACT,OAAO,KAAK;MACZ,eAAe,KAAK;MACpB,MAAM,KAAK;MACX,OAAO,KAAK;MACZ,WAAW,KAAK;MAChB,WAAW,KAAK;MAChB;KACD,CAAC;;GAEH;GACA,SAAS;IACR,SAAS;IACT;IACA;GACD,KAAK;GACL;;AAEF,QAAO;EACN,OAAO,OAAO,UAAgC;AAC7C,UAAOC,MAAI,KAAK;IACf,OAAO,QAAQ,QAAQ;IACvB,MAAM;KACL,IAAI,QAAQ,KAAK;KACjB,OAAO,QAAQ,KAAK;KACpB,eAAe,QAAQ,KAAK;KAC5B,MAAM,QAAQ,KAAK;KACnB,OAAO,QAAQ,KAAK;KACpB,WAAW,QAAQ,KAAK;KACxB,WAAW,QAAQ,KAAK;KACxB;IACD,CAAC;;EAEH;EACA;EACA,KAAK,GAAG,QAAQ,KAAK,GAAG,GAAG,QAAQ,QAAQ;EAC3C"}
@@ -1,3 +1,4 @@
1
+ import { USERNAME_ERROR_CODES } from "./error-codes.mjs";
1
2
  import { username } from "./index.mjs";
2
3
 
3
4
  //#region src/plugins/username/client.d.ts
@@ -8,6 +9,40 @@ declare const usernameClient: () => {
8
9
  matcher: (path: string) => path is "/sign-in/username";
9
10
  signal: "$sessionSignal";
10
11
  }[];
12
+ $ERROR_CODES: {
13
+ readonly INVALID_USERNAME_OR_PASSWORD: {
14
+ code: "INVALID_USERNAME_OR_PASSWORD";
15
+ message: "Invalid username or password";
16
+ };
17
+ readonly EMAIL_NOT_VERIFIED: {
18
+ code: "EMAIL_NOT_VERIFIED";
19
+ message: "Email not verified";
20
+ };
21
+ readonly UNEXPECTED_ERROR: {
22
+ code: "UNEXPECTED_ERROR";
23
+ message: "Unexpected error";
24
+ };
25
+ readonly USERNAME_IS_ALREADY_TAKEN: {
26
+ code: "USERNAME_IS_ALREADY_TAKEN";
27
+ message: "Username is already taken. Please try another.";
28
+ };
29
+ readonly USERNAME_TOO_SHORT: {
30
+ code: "USERNAME_TOO_SHORT";
31
+ message: "Username is too short";
32
+ };
33
+ readonly USERNAME_TOO_LONG: {
34
+ code: "USERNAME_TOO_LONG";
35
+ message: "Username is too long";
36
+ };
37
+ readonly INVALID_USERNAME: {
38
+ code: "INVALID_USERNAME";
39
+ message: "Username is invalid";
40
+ };
41
+ readonly INVALID_DISPLAY_USERNAME: {
42
+ code: "INVALID_DISPLAY_USERNAME";
43
+ message: "Display username is invalid";
44
+ };
45
+ };
11
46
  };
12
47
  //#endregion
13
48
  export { usernameClient };
@@ -1,3 +1,5 @@
1
+ import { USERNAME_ERROR_CODES } from "./error-codes.mjs";
2
+
1
3
  //#region src/plugins/username/client.ts
2
4
  const usernameClient = () => {
3
5
  return {
@@ -6,7 +8,8 @@ const usernameClient = () => {
6
8
  atomListeners: [{
7
9
  matcher: (path) => path === "/sign-in/username",
8
10
  signal: "$sessionSignal"
9
- }]
11
+ }],
12
+ $ERROR_CODES: USERNAME_ERROR_CODES
10
13
  };
11
14
  };
12
15
 
@@ -1 +1 @@
1
- {"version":3,"file":"client.mjs","names":[],"sources":["../../../src/plugins/username/client.ts"],"sourcesContent":["import type { BetterAuthClientPlugin } from \"@better-auth/core\";\nimport type { username } from \".\";\n\nexport const usernameClient = () => {\n\treturn {\n\t\tid: \"username\",\n\t\t$InferServerPlugin: {} as ReturnType<typeof username>,\n\t\tatomListeners: [\n\t\t\t{\n\t\t\t\tmatcher: (path) => path === \"/sign-in/username\",\n\t\t\t\tsignal: \"$sessionSignal\",\n\t\t\t},\n\t\t],\n\t} satisfies BetterAuthClientPlugin;\n};\n"],"mappings":";AAGA,MAAa,uBAAuB;AACnC,QAAO;EACN,IAAI;EACJ,oBAAoB,EAAE;EACtB,eAAe,CACd;GACC,UAAU,SAAS,SAAS;GAC5B,QAAQ;GACR,CACD;EACD"}
1
+ {"version":3,"file":"client.mjs","names":[],"sources":["../../../src/plugins/username/client.ts"],"sourcesContent":["import type { BetterAuthClientPlugin } from \"@better-auth/core\";\nimport type { username } from \".\";\n\nimport { USERNAME_ERROR_CODES } from \"./error-codes\";\n\nexport * from \"./error-codes\";\n\nexport const usernameClient = () => {\n\treturn {\n\t\tid: \"username\",\n\t\t$InferServerPlugin: {} as ReturnType<typeof username>,\n\t\tatomListeners: [\n\t\t\t{\n\t\t\t\tmatcher: (path) => path === \"/sign-in/username\",\n\t\t\t\tsignal: \"$sessionSignal\",\n\t\t\t},\n\t\t],\n\t\t$ERROR_CODES: USERNAME_ERROR_CODES,\n\t} satisfies BetterAuthClientPlugin;\n};\n"],"mappings":";;;AAOA,MAAa,uBAAuB;AACnC,QAAO;EACN,IAAI;EACJ,oBAAoB,EAAE;EACtB,eAAe,CACd;GACC,UAAU,SAAS,SAAS;GAC5B,QAAQ;GACR,CACD;EACD,cAAc;EACd"}
@@ -1,13 +1,37 @@
1
1
  //#region src/plugins/username/error-codes.d.ts
2
2
  declare const USERNAME_ERROR_CODES: {
3
- readonly INVALID_USERNAME_OR_PASSWORD: "Invalid username or password";
4
- readonly EMAIL_NOT_VERIFIED: "Email not verified";
5
- readonly UNEXPECTED_ERROR: "Unexpected error";
6
- readonly USERNAME_IS_ALREADY_TAKEN: "Username is already taken. Please try another.";
7
- readonly USERNAME_TOO_SHORT: "Username is too short";
8
- readonly USERNAME_TOO_LONG: "Username is too long";
9
- readonly INVALID_USERNAME: "Username is invalid";
10
- readonly INVALID_DISPLAY_USERNAME: "Display username is invalid";
3
+ readonly INVALID_USERNAME_OR_PASSWORD: {
4
+ code: "INVALID_USERNAME_OR_PASSWORD";
5
+ message: "Invalid username or password";
6
+ };
7
+ readonly EMAIL_NOT_VERIFIED: {
8
+ code: "EMAIL_NOT_VERIFIED";
9
+ message: "Email not verified";
10
+ };
11
+ readonly UNEXPECTED_ERROR: {
12
+ code: "UNEXPECTED_ERROR";
13
+ message: "Unexpected error";
14
+ };
15
+ readonly USERNAME_IS_ALREADY_TAKEN: {
16
+ code: "USERNAME_IS_ALREADY_TAKEN";
17
+ message: "Username is already taken. Please try another.";
18
+ };
19
+ readonly USERNAME_TOO_SHORT: {
20
+ code: "USERNAME_TOO_SHORT";
21
+ message: "Username is too short";
22
+ };
23
+ readonly USERNAME_TOO_LONG: {
24
+ code: "USERNAME_TOO_LONG";
25
+ message: "Username is too long";
26
+ };
27
+ readonly INVALID_USERNAME: {
28
+ code: "INVALID_USERNAME";
29
+ message: "Username is invalid";
30
+ };
31
+ readonly INVALID_DISPLAY_USERNAME: {
32
+ code: "INVALID_DISPLAY_USERNAME";
33
+ message: "Display username is invalid";
34
+ };
11
35
  };
12
36
  //#endregion
13
37
  export { USERNAME_ERROR_CODES };
@@ -1,10 +1,10 @@
1
1
  import { InferOptionSchema } from "../../types/plugins.mjs";
2
2
  import { UsernameSchema } from "./schema.mjs";
3
3
  import { USERNAME_ERROR_CODES } from "./error-codes.mjs";
4
- import * as _better_auth_core14 from "@better-auth/core";
4
+ import * as _better_auth_core16 from "@better-auth/core";
5
5
  import * as _better_auth_core_db5 from "@better-auth/core/db";
6
6
  import * as z from "zod";
7
- import * as better_call238 from "better-call";
7
+ import * as better_call112 from "better-call";
8
8
 
9
9
  //#region src/plugins/username/index.d.ts
10
10
  type UsernameOptions = {
@@ -67,7 +67,7 @@ type UsernameOptions = {
67
67
  };
68
68
  declare const username: (options?: UsernameOptions | undefined) => {
69
69
  id: "username";
70
- init(ctx: _better_auth_core14.AuthContext): {
70
+ init(ctx: _better_auth_core16.AuthContext): {
71
71
  options: {
72
72
  databaseHooks: {
73
73
  user: {
@@ -80,7 +80,7 @@ declare const username: (options?: UsernameOptions | undefined) => {
80
80
  emailVerified: boolean;
81
81
  name: string;
82
82
  image?: string | null | undefined;
83
- } & Record<string, unknown>, context: _better_auth_core14.GenericEndpointContext | null): Promise<{
83
+ } & Record<string, unknown>, context: _better_auth_core16.GenericEndpointContext | null): Promise<{
84
84
  data: {
85
85
  displayUsername?: string | undefined;
86
86
  username?: string | undefined;
@@ -103,7 +103,7 @@ declare const username: (options?: UsernameOptions | undefined) => {
103
103
  emailVerified: boolean;
104
104
  name: string;
105
105
  image?: string | null | undefined;
106
- }> & Record<string, unknown>, context: _better_auth_core14.GenericEndpointContext | null): Promise<{
106
+ }> & Record<string, unknown>, context: _better_auth_core16.GenericEndpointContext | null): Promise<{
107
107
  data: {
108
108
  displayUsername?: string | undefined;
109
109
  username?: string | undefined;
@@ -122,7 +122,7 @@ declare const username: (options?: UsernameOptions | undefined) => {
122
122
  };
123
123
  };
124
124
  endpoints: {
125
- signInUsername: better_call238.StrictEndpoint<"/sign-in/username", {
125
+ signInUsername: better_call112.StrictEndpoint<"/sign-in/username", {
126
126
  method: "POST";
127
127
  body: z.ZodObject<{
128
128
  username: z.ZodString;
@@ -187,7 +187,7 @@ declare const username: (options?: UsernameOptions | undefined) => {
187
187
  updatedAt: Date;
188
188
  };
189
189
  } | null>;
190
- isUsernameAvailable: better_call238.StrictEndpoint<"/is-username-available", {
190
+ isUsernameAvailable: better_call112.StrictEndpoint<"/is-username-available", {
191
191
  method: "POST";
192
192
  body: z.ZodObject<{
193
193
  username: z.ZodString;
@@ -221,20 +221,44 @@ declare const username: (options?: UsernameOptions | undefined) => {
221
221
  };
222
222
  hooks: {
223
223
  before: {
224
- matcher(context: _better_auth_core14.HookEndpointContext): boolean;
225
- handler: (inputContext: better_call238.MiddlewareInputContext<better_call238.MiddlewareOptions>) => Promise<void>;
224
+ matcher(context: _better_auth_core16.HookEndpointContext): boolean;
225
+ handler: (inputContext: better_call112.MiddlewareInputContext<better_call112.MiddlewareOptions>) => Promise<void>;
226
226
  }[];
227
227
  };
228
228
  options: UsernameOptions | undefined;
229
229
  $ERROR_CODES: {
230
- readonly INVALID_USERNAME_OR_PASSWORD: "Invalid username or password";
231
- readonly EMAIL_NOT_VERIFIED: "Email not verified";
232
- readonly UNEXPECTED_ERROR: "Unexpected error";
233
- readonly USERNAME_IS_ALREADY_TAKEN: "Username is already taken. Please try another.";
234
- readonly USERNAME_TOO_SHORT: "Username is too short";
235
- readonly USERNAME_TOO_LONG: "Username is too long";
236
- readonly INVALID_USERNAME: "Username is invalid";
237
- readonly INVALID_DISPLAY_USERNAME: "Display username is invalid";
230
+ readonly INVALID_USERNAME_OR_PASSWORD: {
231
+ code: "INVALID_USERNAME_OR_PASSWORD";
232
+ message: "Invalid username or password";
233
+ };
234
+ readonly EMAIL_NOT_VERIFIED: {
235
+ code: "EMAIL_NOT_VERIFIED";
236
+ message: "Email not verified";
237
+ };
238
+ readonly UNEXPECTED_ERROR: {
239
+ code: "UNEXPECTED_ERROR";
240
+ message: "Unexpected error";
241
+ };
242
+ readonly USERNAME_IS_ALREADY_TAKEN: {
243
+ code: "USERNAME_IS_ALREADY_TAKEN";
244
+ message: "Username is already taken. Please try another.";
245
+ };
246
+ readonly USERNAME_TOO_SHORT: {
247
+ code: "USERNAME_TOO_SHORT";
248
+ message: "Username is too short";
249
+ };
250
+ readonly USERNAME_TOO_LONG: {
251
+ code: "USERNAME_TOO_LONG";
252
+ message: "Username is too long";
253
+ };
254
+ readonly INVALID_USERNAME: {
255
+ code: "INVALID_USERNAME";
256
+ message: "Username is invalid";
257
+ };
258
+ readonly INVALID_DISPLAY_USERNAME: {
259
+ code: "INVALID_DISPLAY_USERNAME";
260
+ message: "Display username is invalid";
261
+ };
238
262
  };
239
263
  };
240
264
  //#endregion
@@ -5,9 +5,8 @@ import { createEmailVerificationToken } from "../../api/routes/email-verificatio
5
5
  import "../../api/index.mjs";
6
6
  import { USERNAME_ERROR_CODES } from "./error-codes.mjs";
7
7
  import { getSchema } from "./schema.mjs";
8
- import { BASE_ERROR_CODES } from "@better-auth/core/error";
8
+ import { APIError, BASE_ERROR_CODES } from "@better-auth/core/error";
9
9
  import * as z from "zod";
10
- import { APIError } from "better-call";
11
10
  import { createAuthEndpoint, createAuthMiddleware } from "@better-auth/core/api";
12
11
 
13
12
  //#region src/plugins/username/index.ts
@@ -88,23 +87,20 @@ const username = (options) => {
88
87
  }, async (ctx) => {
89
88
  if (!ctx.body.username || !ctx.body.password) {
90
89
  ctx.context.logger.error("Username or password not found");
91
- throw new APIError("UNAUTHORIZED", { message: USERNAME_ERROR_CODES.INVALID_USERNAME_OR_PASSWORD });
90
+ throw APIError.from("UNAUTHORIZED", USERNAME_ERROR_CODES.INVALID_USERNAME_OR_PASSWORD);
92
91
  }
93
92
  const username$1 = options?.validationOrder?.username === "pre-normalization" ? normalizer(ctx.body.username) : ctx.body.username;
94
93
  const minUsernameLength = options?.minUsernameLength || 3;
95
94
  const maxUsernameLength = options?.maxUsernameLength || 30;
96
95
  if (username$1.length < minUsernameLength) {
97
96
  ctx.context.logger.error("Username too short", { username: username$1 });
98
- throw new APIError("UNPROCESSABLE_ENTITY", {
99
- code: "USERNAME_TOO_SHORT",
100
- message: USERNAME_ERROR_CODES.USERNAME_TOO_SHORT
101
- });
97
+ throw APIError.from("UNPROCESSABLE_ENTITY", USERNAME_ERROR_CODES.USERNAME_TOO_SHORT);
102
98
  }
103
99
  if (username$1.length > maxUsernameLength) {
104
100
  ctx.context.logger.error("Username too long", { username: username$1 });
105
- throw new APIError("UNPROCESSABLE_ENTITY", { message: USERNAME_ERROR_CODES.USERNAME_TOO_LONG });
101
+ throw APIError.from("UNPROCESSABLE_ENTITY", USERNAME_ERROR_CODES.USERNAME_TOO_LONG);
106
102
  }
107
- if (!await (options?.usernameValidator || defaultUsernameValidator)(username$1)) throw new APIError("UNPROCESSABLE_ENTITY", { message: USERNAME_ERROR_CODES.INVALID_USERNAME });
103
+ if (!await (options?.usernameValidator || defaultUsernameValidator)(username$1)) throw APIError.from("UNPROCESSABLE_ENTITY", USERNAME_ERROR_CODES.INVALID_USERNAME);
108
104
  const user = await ctx.context.adapter.findOne({
109
105
  model: "user",
110
106
  where: [{
@@ -115,7 +111,7 @@ const username = (options) => {
115
111
  if (!user) {
116
112
  await ctx.context.password.hash(ctx.body.password);
117
113
  ctx.context.logger.error("User not found", { username: username$1 });
118
- throw new APIError("UNAUTHORIZED", { message: USERNAME_ERROR_CODES.INVALID_USERNAME_OR_PASSWORD });
114
+ throw APIError.from("UNAUTHORIZED", USERNAME_ERROR_CODES.INVALID_USERNAME_OR_PASSWORD);
119
115
  }
120
116
  const account = await ctx.context.adapter.findOne({
121
117
  model: "account",
@@ -127,21 +123,21 @@ const username = (options) => {
127
123
  value: "credential"
128
124
  }]
129
125
  });
130
- if (!account) throw new APIError("UNAUTHORIZED", { message: USERNAME_ERROR_CODES.INVALID_USERNAME_OR_PASSWORD });
126
+ if (!account) throw APIError.from("UNAUTHORIZED", USERNAME_ERROR_CODES.INVALID_USERNAME_OR_PASSWORD);
131
127
  const currentPassword = account?.password;
132
128
  if (!currentPassword) {
133
129
  ctx.context.logger.error("Password not found", { username: username$1 });
134
- throw new APIError("UNAUTHORIZED", { message: USERNAME_ERROR_CODES.INVALID_USERNAME_OR_PASSWORD });
130
+ throw APIError.from("UNAUTHORIZED", USERNAME_ERROR_CODES.INVALID_USERNAME_OR_PASSWORD);
135
131
  }
136
132
  if (!await ctx.context.password.verify({
137
133
  hash: currentPassword,
138
134
  password: ctx.body.password
139
135
  })) {
140
136
  ctx.context.logger.error("Invalid password");
141
- throw new APIError("UNAUTHORIZED", { message: USERNAME_ERROR_CODES.INVALID_USERNAME_OR_PASSWORD });
137
+ throw APIError.from("UNAUTHORIZED", USERNAME_ERROR_CODES.INVALID_USERNAME_OR_PASSWORD);
142
138
  }
143
139
  if (ctx.context.options?.emailAndPassword?.requireEmailVerification && !user.emailVerified) {
144
- if (!ctx.context.options?.emailVerification?.sendVerificationEmail) throw new APIError("FORBIDDEN", { message: USERNAME_ERROR_CODES.EMAIL_NOT_VERIFIED });
140
+ if (!ctx.context.options?.emailVerification?.sendVerificationEmail) throw APIError.from("FORBIDDEN", USERNAME_ERROR_CODES.EMAIL_NOT_VERIFIED);
145
141
  if (ctx.context.options?.emailVerification?.sendOnSignIn) {
146
142
  const token = await createEmailVerificationToken(ctx.context.secret, user.email, void 0, ctx.context.options.emailVerification?.expiresIn);
147
143
  const url = `${ctx.context.baseURL}/verify-email?token=${token}&callbackURL=${ctx.body.callbackURL || "/"}`;
@@ -151,12 +147,12 @@ const username = (options) => {
151
147
  token
152
148
  }, ctx.request));
153
149
  }
154
- throw new APIError("FORBIDDEN", { message: USERNAME_ERROR_CODES.EMAIL_NOT_VERIFIED });
150
+ throw APIError.from("FORBIDDEN", USERNAME_ERROR_CODES.EMAIL_NOT_VERIFIED);
155
151
  }
156
152
  const session = await ctx.context.internalAdapter.createSession(user.id, ctx.body.rememberMe === false);
157
153
  if (!session) return ctx.json(null, {
158
154
  status: 500,
159
- body: { message: BASE_ERROR_CODES.FAILED_TO_CREATE_SESSION }
155
+ body: { message: BASE_ERROR_CODES.FAILED_TO_CREATE_SESSION.message }
160
156
  });
161
157
  await setSessionCookie(ctx, {
162
158
  session,
@@ -182,15 +178,12 @@ const username = (options) => {
182
178
  body: isUsernameAvailableBodySchema
183
179
  }, async (ctx) => {
184
180
  const username$1 = ctx.body.username;
185
- if (!username$1) throw new APIError("UNPROCESSABLE_ENTITY", { message: USERNAME_ERROR_CODES.INVALID_USERNAME });
181
+ if (!username$1) throw APIError.from("UNPROCESSABLE_ENTITY", USERNAME_ERROR_CODES.INVALID_USERNAME);
186
182
  const minUsernameLength = options?.minUsernameLength || 3;
187
183
  const maxUsernameLength = options?.maxUsernameLength || 30;
188
- if (username$1.length < minUsernameLength) throw new APIError("UNPROCESSABLE_ENTITY", {
189
- code: "USERNAME_TOO_SHORT",
190
- message: USERNAME_ERROR_CODES.USERNAME_TOO_SHORT
191
- });
192
- if (username$1.length > maxUsernameLength) throw new APIError("UNPROCESSABLE_ENTITY", { message: USERNAME_ERROR_CODES.USERNAME_TOO_LONG });
193
- if (!await (options?.usernameValidator || defaultUsernameValidator)(username$1)) throw new APIError("UNPROCESSABLE_ENTITY", { message: USERNAME_ERROR_CODES.INVALID_USERNAME });
184
+ if (username$1.length < minUsernameLength) throw APIError.from("UNPROCESSABLE_ENTITY", USERNAME_ERROR_CODES.USERNAME_TOO_SHORT);
185
+ if (username$1.length > maxUsernameLength) throw APIError.from("UNPROCESSABLE_ENTITY", USERNAME_ERROR_CODES.USERNAME_TOO_LONG);
186
+ if (!await (options?.usernameValidator || defaultUsernameValidator)(username$1)) throw APIError.from("UNPROCESSABLE_ENTITY", USERNAME_ERROR_CODES.INVALID_USERNAME);
194
187
  if (await ctx.context.adapter.findOne({
195
188
  model: "user",
196
189
  where: [{
@@ -214,12 +207,9 @@ const username = (options) => {
214
207
  if (username$1 !== void 0 && typeof username$1 === "string") {
215
208
  const minUsernameLength = options?.minUsernameLength || 3;
216
209
  const maxUsernameLength = options?.maxUsernameLength || 30;
217
- if (username$1.length < minUsernameLength) throw new APIError("BAD_REQUEST", {
218
- code: "USERNAME_TOO_SHORT",
219
- message: USERNAME_ERROR_CODES.USERNAME_TOO_SHORT
220
- });
221
- if (username$1.length > maxUsernameLength) throw new APIError("BAD_REQUEST", { message: USERNAME_ERROR_CODES.USERNAME_TOO_LONG });
222
- if (!await (options?.usernameValidator || defaultUsernameValidator)(username$1)) throw new APIError("BAD_REQUEST", { message: USERNAME_ERROR_CODES.INVALID_USERNAME });
210
+ if (username$1.length < minUsernameLength) throw APIError.from("BAD_REQUEST", USERNAME_ERROR_CODES.USERNAME_TOO_SHORT);
211
+ if (username$1.length > maxUsernameLength) throw APIError.from("BAD_REQUEST", USERNAME_ERROR_CODES.USERNAME_TOO_LONG);
212
+ if (!await (options?.usernameValidator || defaultUsernameValidator)(username$1)) throw APIError.from("BAD_REQUEST", USERNAME_ERROR_CODES.INVALID_USERNAME);
223
213
  const user = await ctx.context.adapter.findOne({
224
214
  model: "user",
225
215
  where: [{
@@ -229,12 +219,12 @@ const username = (options) => {
229
219
  });
230
220
  const blockChangeSignUp = ctx.path === "/sign-up/email" && user;
231
221
  const blockChangeUpdateUser = ctx.path === "/update-user" && user && ctx.context.session && user.id !== ctx.context.session.session.userId;
232
- if (blockChangeSignUp || blockChangeUpdateUser) throw new APIError("BAD_REQUEST", { message: USERNAME_ERROR_CODES.USERNAME_IS_ALREADY_TAKEN });
222
+ if (blockChangeSignUp || blockChangeUpdateUser) throw APIError.from("BAD_REQUEST", USERNAME_ERROR_CODES.USERNAME_IS_ALREADY_TAKEN);
233
223
  }
234
224
  const displayUsername = typeof ctx.body.displayUsername === "string" && options?.validationOrder?.displayUsername === "post-normalization" ? displayUsernameNormalizer(ctx.body.displayUsername) : ctx.body.displayUsername;
235
225
  if (displayUsername !== void 0 && typeof displayUsername === "string") {
236
226
  if (options?.displayUsernameValidator) {
237
- if (!await options.displayUsernameValidator(displayUsername)) throw new APIError("BAD_REQUEST", { message: USERNAME_ERROR_CODES.INVALID_DISPLAY_USERNAME });
227
+ if (!await options.displayUsernameValidator(displayUsername)) throw APIError.from("BAD_REQUEST", USERNAME_ERROR_CODES.INVALID_DISPLAY_USERNAME);
238
228
  }
239
229
  }
240
230
  })