@rpcbase/auth 0.199.0 → 0.200.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/dist/api/resend-otp/handler.d.ts.map +1 -1
  2. package/dist/api/resend-otp/index.d.ts +5 -0
  3. package/dist/api/resend-otp/index.d.ts.map +1 -1
  4. package/dist/api/resend-otp/index.js +16 -0
  5. package/dist/api/resend-otp/index.js.map +1 -0
  6. package/dist/api/sign-in/handler.d.ts +3 -1
  7. package/dist/api/sign-in/handler.d.ts.map +1 -1
  8. package/dist/api/sign-in/index.d.ts.map +1 -1
  9. package/dist/{sign-in-DYq3bJeH.js → api/sign-in/index.js} +5 -5
  10. package/dist/api/sign-in/index.js.map +1 -0
  11. package/dist/api/sign-up/handler.d.ts.map +1 -1
  12. package/dist/api/sign-up/index.d.ts +27 -1
  13. package/dist/api/sign-up/index.d.ts.map +1 -1
  14. package/dist/api/sign-up/index.js +32 -0
  15. package/dist/api/sign-up/index.js.map +1 -0
  16. package/dist/api/verify-otp/handler.d.ts +3 -1
  17. package/dist/api/verify-otp/handler.d.ts.map +1 -1
  18. package/dist/api/verify-otp/index.js +18 -0
  19. package/dist/api/verify-otp/index.js.map +1 -0
  20. package/dist/components/PasswordInput/index.d.ts.map +1 -1
  21. package/dist/components/SignInForm/index.d.ts.map +1 -1
  22. package/dist/emailVerification-DA9pZ0N_.js +65 -0
  23. package/dist/emailVerification-DA9pZ0N_.js.map +1 -0
  24. package/dist/emailVerification.d.ts +20 -0
  25. package/dist/emailVerification.d.ts.map +1 -0
  26. package/dist/handler-0uaKeuYp.js +213 -0
  27. package/dist/handler-0uaKeuYp.js.map +1 -0
  28. package/dist/{handler-BgjYCWF4.js → handler-B6roBozS.js} +2 -2
  29. package/dist/{handler-BgjYCWF4.js.map → handler-B6roBozS.js.map} +1 -1
  30. package/dist/handler-BPoWo5Qy.js +85 -0
  31. package/dist/handler-BPoWo5Qy.js.map +1 -0
  32. package/dist/{handler-DBbjgU-f.js → handler-BenTgwfT.js} +2 -2
  33. package/dist/{handler-DBbjgU-f.js.map → handler-BenTgwfT.js.map} +1 -1
  34. package/dist/{handler-Dbw6BOmj.js → handler-BhPmGpUq.js} +3 -3
  35. package/dist/handler-BhPmGpUq.js.map +1 -0
  36. package/dist/handler-DCk7C1pI.js +80 -0
  37. package/dist/handler-DCk7C1pI.js.map +1 -0
  38. package/dist/{handler-iT2eSHpE.js → handler-DCljRBBf.js} +3 -3
  39. package/dist/handler-DCljRBBf.js.map +1 -0
  40. package/dist/{handler-CF3fSh8e.js → handler-JKQLI2iL.js} +2 -2
  41. package/dist/{handler-CF3fSh8e.js.map → handler-JKQLI2iL.js.map} +1 -1
  42. package/dist/{handler-DgIcxthg.js → handler-b6PvhATs.js} +3 -3
  43. package/dist/handler-b6PvhATs.js.map +1 -0
  44. package/dist/handler-cj7IkJB-.js +98 -0
  45. package/dist/handler-cj7IkJB-.js.map +1 -0
  46. package/dist/index.js +13 -5
  47. package/dist/index.js.map +1 -1
  48. package/dist/oauth/index.js +30 -1
  49. package/dist/oauth/index.js.map +1 -1
  50. package/dist/oauth/lib.d.ts.map +1 -1
  51. package/dist/registrationStatus-BP6yReMw.js +7 -0
  52. package/dist/registrationStatus-BP6yReMw.js.map +1 -0
  53. package/dist/registrationStatus.d.ts +16 -0
  54. package/dist/registrationStatus.d.ts.map +1 -0
  55. package/dist/routes.js +10 -10
  56. package/dist/{schemas-CMgO_xn9.js → schemas-DABNKHVA.js} +2 -2
  57. package/dist/{schemas-CMgO_xn9.js.map → schemas-DABNKHVA.js.map} +1 -1
  58. package/package.json +21 -1
  59. package/dist/handler-1Jy7G8dM.js +0 -92
  60. package/dist/handler-1Jy7G8dM.js.map +0 -1
  61. package/dist/handler-B1Z9YpER.js +0 -77
  62. package/dist/handler-B1Z9YpER.js.map +0 -1
  63. package/dist/handler-BkZLC8Bg.js +0 -92
  64. package/dist/handler-BkZLC8Bg.js.map +0 -1
  65. package/dist/handler-D4ui5jV4.js +0 -74
  66. package/dist/handler-D4ui5jV4.js.map +0 -1
  67. package/dist/handler-Dbw6BOmj.js.map +0 -1
  68. package/dist/handler-DgIcxthg.js.map +0 -1
  69. package/dist/handler-iT2eSHpE.js.map +0 -1
  70. package/dist/sign-in-DYq3bJeH.js.map +0 -1
  71. package/dist/sign-up-CYGadNUe.js +0 -18
  72. package/dist/sign-up-CYGadNUe.js.map +0 -1
@@ -0,0 +1,85 @@
1
+ import { ROUTE, requestSchema } from "./api/sign-in/index.js";
2
+ import { t as isPendingRegistration } from "./registrationStatus-BP6yReMw.js";
3
+ import { n as completeAuthSignIn } from "./session-BQ06Yo3C.js";
4
+ import { models } from "@rpcbase/db";
5
+ import { verifyPasswordFromStorage } from "@rpcbase/server";
6
+ //#region src/api/sign-in/handler.ts
7
+ var signIn = async (payload, ctx) => {
8
+ const parsed = requestSchema.safeParse(payload);
9
+ if (!parsed.success) {
10
+ ctx.res.status(400);
11
+ return {
12
+ success: false,
13
+ error: "invalid_payload"
14
+ };
15
+ }
16
+ const [User, Tenant] = await Promise.all([models.getGlobal("RBUser", ctx), models.getGlobal("RBTenant", ctx)]);
17
+ const user = await User.findOne({ email: parsed.data.email }, {
18
+ password: 1,
19
+ tenants: 1,
20
+ tenantRoles: 1,
21
+ registrationStatus: 1,
22
+ emailVerificationCode: 1
23
+ });
24
+ if (!user?.password || !await verifyPasswordFromStorage(parsed.data.password, String(user.password))) {
25
+ ctx.res.status(401);
26
+ return {
27
+ success: false,
28
+ error: "invalid_credentials"
29
+ };
30
+ }
31
+ if (isPendingRegistration({
32
+ registrationStatus: user.get("registrationStatus"),
33
+ emailVerificationCode: user.get("emailVerificationCode")
34
+ })) {
35
+ ctx.res.status(403);
36
+ return {
37
+ success: false,
38
+ error: "email_verification_required"
39
+ };
40
+ }
41
+ const signedInTenants = Array.isArray(user.tenants) ? user.tenants.map(String) : [];
42
+ const tenantId = signedInTenants[0]?.trim() ?? "";
43
+ if (!tenantId) {
44
+ ctx.res.status(500);
45
+ return {
46
+ success: false,
47
+ error: "tenant_unavailable"
48
+ };
49
+ }
50
+ const tenant = await Tenant.findOne({ tenantId }, { provisioningStatus: 1 }).lean();
51
+ if (!tenant || tenant.provisioningStatus && tenant.provisioningStatus !== "active") {
52
+ ctx.res.status(503);
53
+ return {
54
+ success: false,
55
+ error: "tenant_unavailable"
56
+ };
57
+ }
58
+ const tenantRolesMap = user.get("tenantRoles");
59
+ const tenantRoles = tenantRolesMap ? Object.fromEntries(tenantRolesMap.entries()) : void 0;
60
+ const userId = user._id.toString();
61
+ if (!completeAuthSignIn(ctx, {
62
+ userId,
63
+ currentTenantId: tenantId,
64
+ signedInTenants: signedInTenants.length ? signedInTenants : [tenantId],
65
+ tenantRoles
66
+ })) {
67
+ ctx.res.status(500);
68
+ return {
69
+ success: false,
70
+ error: "session_unavailable"
71
+ };
72
+ }
73
+ return {
74
+ success: true,
75
+ userId,
76
+ tenantId
77
+ };
78
+ };
79
+ var handler_default = (api) => {
80
+ api.post(ROUTE, signIn);
81
+ };
82
+ //#endregion
83
+ export { handler_default as default, signIn };
84
+
85
+ //# sourceMappingURL=handler-BPoWo5Qy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler-BPoWo5Qy.js","names":[],"sources":["../src/api/sign-in/handler.ts"],"sourcesContent":["import { Api, type ApiHandler, type Ctx } from \"@rpcbase/api\"\nimport { models } from \"@rpcbase/db\"\nimport { verifyPasswordFromStorage } from \"@rpcbase/server\"\n\nimport { completeAuthSignIn } from \"../../session\"\nimport { isPendingRegistration } from \"../../registrationStatus\"\nimport type { AuthSessionUser } from \"../../types\"\n\nimport * as SignIn from \"./index\"\n\n\nexport const signIn: ApiHandler<SignIn.RequestPayload, SignIn.ResponsePayload, AuthSessionUser> = async (\n payload,\n ctx: Ctx<AuthSessionUser>,\n): Promise<SignIn.ResponsePayload> => {\n const parsed = SignIn.requestSchema.safeParse(payload)\n\n if (!parsed.success) {\n ctx.res.status(400)\n return { success: false, error: \"invalid_payload\" }\n }\n\n const [User, Tenant] = await Promise.all([\n models.getGlobal(\"RBUser\", ctx),\n models.getGlobal(\"RBTenant\", ctx),\n ])\n const user = await User.findOne(\n { email: parsed.data.email },\n {\n password: 1,\n tenants: 1,\n tenantRoles: 1,\n registrationStatus: 1,\n emailVerificationCode: 1,\n },\n )\n\n if (!user?.password || !await verifyPasswordFromStorage(parsed.data.password, String(user.password))) {\n ctx.res.status(401)\n return { success: false, error: \"invalid_credentials\" }\n }\n\n if (isPendingRegistration({\n registrationStatus: user.get(\"registrationStatus\"),\n emailVerificationCode: user.get(\"emailVerificationCode\"),\n })) {\n ctx.res.status(403)\n return { success: false, error: \"email_verification_required\" }\n }\n\n const signedInTenants = Array.isArray(user.tenants) ? user.tenants.map(String) : []\n const tenantId = signedInTenants[0]?.trim() ?? \"\"\n if (!tenantId) {\n ctx.res.status(500)\n return { success: false, error: \"tenant_unavailable\" }\n }\n\n const tenant = await Tenant.findOne({ tenantId }, { provisioningStatus: 1 }).lean() as {\n provisioningStatus?: unknown\n } | null\n if (!tenant || (tenant.provisioningStatus && tenant.provisioningStatus !== \"active\")) {\n ctx.res.status(503)\n return { success: false, error: \"tenant_unavailable\" }\n }\n\n const tenantRolesMap = user.get(\"tenantRoles\") as Map<string, string[]> | undefined\n const tenantRoles = tenantRolesMap ? Object.fromEntries(tenantRolesMap.entries()) : undefined\n const userId = user._id.toString()\n\n if (!completeAuthSignIn(ctx, {\n userId,\n currentTenantId: tenantId,\n signedInTenants: signedInTenants.length ? signedInTenants : [tenantId],\n tenantRoles,\n })) {\n ctx.res.status(500)\n return { success: false, error: \"session_unavailable\" }\n }\n\n return { success: true, userId, tenantId }\n}\n\nexport default (api: Api<AuthSessionUser>) => {\n api.post(SignIn.ROUTE, signIn)\n}\n"],"mappings":";;;;;;AAWA,IAAa,SAAqF,OAChG,SACA,QACoC;CACpC,MAAM,SAAA,cAA8B,UAAU,OAAO;CAErD,IAAI,CAAC,OAAO,SAAS;EACnB,IAAI,IAAI,OAAO,GAAG;EAClB,OAAO;GAAE,SAAS;GAAO,OAAO;EAAkB;CACpD;CAEA,MAAM,CAAC,MAAM,UAAU,MAAM,QAAQ,IAAI,CACvC,OAAO,UAAU,UAAU,GAAG,GAC9B,OAAO,UAAU,YAAY,GAAG,CAAC,CAClC;CACD,MAAM,OAAO,MAAM,KAAK,QACtB,EAAE,OAAO,OAAO,KAAK,MAAM,GAC3B;EACE,UAAU;EACV,SAAS;EACT,aAAa;EACb,oBAAoB;EACpB,uBAAuB;CACzB,CACF;CAEA,IAAI,CAAC,MAAM,YAAY,CAAC,MAAM,0BAA0B,OAAO,KAAK,UAAU,OAAO,KAAK,QAAQ,CAAC,GAAG;EACpG,IAAI,IAAI,OAAO,GAAG;EAClB,OAAO;GAAE,SAAS;GAAO,OAAO;EAAsB;CACxD;CAEA,IAAI,sBAAsB;EACxB,oBAAoB,KAAK,IAAI,oBAAoB;EACjD,uBAAuB,KAAK,IAAI,uBAAuB;CACzD,CAAC,GAAG;EACF,IAAI,IAAI,OAAO,GAAG;EAClB,OAAO;GAAE,SAAS;GAAO,OAAO;EAA8B;CAChE;CAEA,MAAM,kBAAkB,MAAM,QAAQ,KAAK,OAAO,IAAI,KAAK,QAAQ,IAAI,MAAM,IAAI,CAAA;CACjF,MAAM,WAAW,gBAAgB,EAAE,EAAE,KAAK,KAAK;CAC/C,IAAI,CAAC,UAAU;EACb,IAAI,IAAI,OAAO,GAAG;EAClB,OAAO;GAAE,SAAS;GAAO,OAAO;EAAqB;CACvD;CAEA,MAAM,SAAS,MAAM,OAAO,QAAQ,EAAE,SAAS,GAAG,EAAE,oBAAoB,EAAE,CAAC,CAAC,CAAC,KAAK;CAGlF,IAAI,CAAC,UAAW,OAAO,sBAAsB,OAAO,uBAAuB,UAAW;EACpF,IAAI,IAAI,OAAO,GAAG;EAClB,OAAO;GAAE,SAAS;GAAO,OAAO;EAAqB;CACvD;CAEA,MAAM,iBAAiB,KAAK,IAAI,aAAa;CAC7C,MAAM,cAAc,iBAAiB,OAAO,YAAY,eAAe,QAAQ,CAAC,IAAI,KAAA;CACpF,MAAM,SAAS,KAAK,IAAI,SAAS;CAEjC,IAAI,CAAC,mBAAmB,KAAK;EAC3B;EACA,iBAAiB;EACjB,iBAAiB,gBAAgB,SAAS,kBAAkB,CAAC,QAAQ;EACrE;CACF,CAAC,GAAG;EACF,IAAI,IAAI,OAAO,GAAG;EAClB,OAAO;GAAE,SAAS;GAAO,OAAO;EAAsB;CACxD;CAEA,OAAO;EAAE,SAAS;EAAM;EAAQ;CAAS;AAC3C;AAEA,IAAA,mBAAgB,QAA8B;CAC5C,IAAI,KAAK,OAAc,MAAM;AAC/B"}
@@ -1,4 +1,4 @@
1
- import { i as object, n as boolean } from "./schemas-CMgO_xn9.js";
1
+ import { a as object, r as boolean } from "./schemas-DABNKHVA.js";
2
2
  //#region src/api/sign-out/index.ts
3
3
  var ROUTE = "/api/rb/auth/sign-out";
4
4
  object({});
@@ -16,4 +16,4 @@ var handler_default = (api) => {
16
16
  //#endregion
17
17
  export { handler_default as default };
18
18
 
19
- //# sourceMappingURL=handler-DBbjgU-f.js.map
19
+ //# sourceMappingURL=handler-BenTgwfT.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"handler-DBbjgU-f.js","names":[],"sources":["../src/api/sign-out/index.ts","../src/api/sign-out/handler.ts"],"sourcesContent":["import { z } from \"zod\"\n\n\nexport const ROUTE = \"/api/rb/auth/sign-out\"\n\n// No request payload needed for signout\nexport const requestSchema = z.object({})\n\nexport type RequestPayload = z.infer<typeof requestSchema>;\n\nexport const responseSchema = z.object({\n success: z.boolean()\n})\n\nexport type ResponsePayload = z.infer<typeof responseSchema>;\n","import { Api, Ctx } from \"@rpcbase/api\"\n\nimport * as SignOut from \"./index\"\n\n\nconst handleSignOut = async(_: SignOut.RequestPayload, ctx: Ctx): Promise<SignOut.ResponsePayload> => {\n // Destroy the session\n if (!ctx.req.session) {\n return { success: true }\n }\n\n await new Promise<void>((resolve) => ctx.req.session!.destroy(() => resolve()))\n\n return {\n success: true\n }\n}\n\nexport default (api: Api) => {\n api.post(SignOut.ROUTE, handleSignOut)\n}\n"],"mappings":";;AAGA,IAAa,QAAQ;AAGQ,OAAS,CAAC,CAAC;AAIV,OAAS,EACrC,SAAS,QAAU,EACrB,CAAC;;;ACPD,IAAM,gBAAgB,OAAM,GAA2B,QAA+C;CAEpG,IAAI,CAAC,IAAI,IAAI,SACX,OAAO,EAAE,SAAS,KAAK;CAGzB,MAAM,IAAI,SAAe,YAAY,IAAI,IAAI,QAAS,cAAc,QAAQ,CAAC,CAAC;CAE9E,OAAO,EACL,SAAS,KACX;AACF;AAEA,IAAA,mBAAgB,QAAa;CAC3B,IAAI,KAAK,OAAe,aAAa;AACvC"}
1
+ {"version":3,"file":"handler-BenTgwfT.js","names":[],"sources":["../src/api/sign-out/index.ts","../src/api/sign-out/handler.ts"],"sourcesContent":["import { z } from \"zod\"\n\n\nexport const ROUTE = \"/api/rb/auth/sign-out\"\n\n// No request payload needed for signout\nexport const requestSchema = z.object({})\n\nexport type RequestPayload = z.infer<typeof requestSchema>;\n\nexport const responseSchema = z.object({\n success: z.boolean()\n})\n\nexport type ResponsePayload = z.infer<typeof responseSchema>;\n","import { Api, Ctx } from \"@rpcbase/api\"\n\nimport * as SignOut from \"./index\"\n\n\nconst handleSignOut = async(_: SignOut.RequestPayload, ctx: Ctx): Promise<SignOut.ResponsePayload> => {\n // Destroy the session\n if (!ctx.req.session) {\n return { success: true }\n }\n\n await new Promise<void>((resolve) => ctx.req.session!.destroy(() => resolve()))\n\n return {\n success: true\n }\n}\n\nexport default (api: Api) => {\n api.post(SignOut.ROUTE, handleSignOut)\n}\n"],"mappings":";;AAGA,IAAa,QAAQ;AAGQ,OAAS,CAAC,CAAC;AAIV,OAAS,EACrC,SAAS,QAAU,EACrB,CAAC;;;ACPD,IAAM,gBAAgB,OAAM,GAA2B,QAA+C;CAEpG,IAAI,CAAC,IAAI,IAAI,SACX,OAAO,EAAE,SAAS,KAAK;CAGzB,MAAM,IAAI,SAAe,YAAY,IAAI,IAAI,QAAS,cAAc,QAAQ,CAAC,CAAC;CAE9E,OAAO,EACL,SAAS,KACX;AACF;AAEA,IAAA,mBAAgB,QAAa;CAC3B,IAAI,KAAK,OAAe,aAAa;AACvC"}
@@ -1,11 +1,11 @@
1
- import { a as string, i as object, n as boolean, r as number } from "./schemas-CMgO_xn9.js";
1
+ import { a as object, i as number, o as string, r as boolean } from "./schemas-DABNKHVA.js";
2
2
  import { t as email_validation_exports } from "./email-validation/index.js";
3
3
  import crypto from "crypto";
4
4
  import { models } from "@rpcbase/db";
5
5
  import { sendEmail } from "@rpcbase/server";
6
6
  //#region src/api/request-password-reset/index.ts
7
7
  var ROUTE = "/api/rb/auth/request-password-reset";
8
- var requestSchema = object({ email: string().email() });
8
+ var requestSchema = object({ email: string().trim().toLowerCase().email() });
9
9
  object({
10
10
  success: boolean(),
11
11
  error: string().optional(),
@@ -130,4 +130,4 @@ var handler_default = (api) => {
130
130
  //#endregion
131
131
  export { PASSWORD_RESET_COOLDOWN_SECONDS, handler_default as default, requestPasswordReset };
132
132
 
133
- //# sourceMappingURL=handler-Dbw6BOmj.js.map
133
+ //# sourceMappingURL=handler-BhPmGpUq.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler-BhPmGpUq.js","names":[],"sources":["../src/api/request-password-reset/index.ts","../src/api/request-password-reset/handler.ts"],"sourcesContent":["import { z } from \"zod\"\n\n\nexport const ROUTE = \"/api/rb/auth/request-password-reset\"\n\nexport const requestSchema = z.object({\n email: z.string().trim().toLowerCase().email(),\n})\n\nexport type RequestPayload = z.infer<typeof requestSchema>\n\nexport const responseSchema = z.object({\n success: z.boolean(),\n error: z.string().optional(),\n retryAfterSeconds: z.number().int().positive().optional(),\n})\n\nexport type ResponsePayload = z.infer<typeof responseSchema>\n","import crypto from \"crypto\"\n\nimport { Api, ApiHandler, Ctx } from \"@rpcbase/api\"\nimport { models } from \"@rpcbase/db\"\nimport { sendEmail } from \"@rpcbase/server\"\n\nimport { isEmailAddressValid } from \"../../email-validation\"\nimport type { AuthSessionUser } from \"../../types\"\n\nimport * as RequestPasswordReset from \"./index\"\n\n\nexport const PASSWORD_RESET_COOLDOWN_SECONDS = 60\nconst PASSWORD_RESET_CODE_TTL_SECONDS = 10 * 60\n\nconst maskEmailForLog = (email: string) => {\n const [localRaw, domainRaw = \"\"] = email.split(\"@\")\n const local = localRaw || \"\"\n const domain = domainRaw || \"\"\n const localPrefix = local.slice(0, Math.min(3, local.length))\n const localMask = \"*\".repeat(Math.max(5, local.length - localPrefix.length))\n\n if (!domain) {\n return `${localPrefix}${localMask}@****`\n }\n\n const domainTailLength = Math.min(7, domain.length)\n const domainTail = domain.slice(-domainTailLength)\n const domainMask = \"*\".repeat(Math.max(4, domain.length - domainTailLength))\n\n return `${localPrefix}${localMask}@${domainMask}${domainTail}`\n}\n\n\nexport const requestPasswordReset: ApiHandler<\n RequestPasswordReset.RequestPayload,\n RequestPasswordReset.ResponsePayload,\n AuthSessionUser\n> = async(\n payload,\n ctx: Ctx<AuthSessionUser>\n): Promise<RequestPasswordReset.ResponsePayload> => {\n const parsed = RequestPasswordReset.requestSchema.safeParse(payload)\n\n if (!parsed.success) {\n ctx.res.status(400)\n return { success: false, error: \"invalid_payload\" }\n }\n\n const { email } = parsed.data\n\n if (!await isEmailAddressValid(email)) {\n ctx.res.setHeader(\"Retry-After\", String(PASSWORD_RESET_COOLDOWN_SECONDS))\n return { success: true, retryAfterSeconds: PASSWORD_RESET_COOLDOWN_SECONDS }\n }\n\n const User = await models.getGlobal(\"RBUser\", ctx)\n const requestedAt = new Date()\n const cooldownEligibleExpiresAt = new Date(\n requestedAt.getTime() + (PASSWORD_RESET_CODE_TTL_SECONDS - PASSWORD_RESET_COOLDOWN_SECONDS) * 1000,\n )\n const passwordResetCode = crypto.randomInt(0, 1_000_000).toString().padStart(6, \"0\")\n const passwordResetCodeExpiresAt = new Date(requestedAt.getTime() + PASSWORD_RESET_CODE_TTL_SECONDS * 1000)\n\n const user = await User.findOneAndUpdate(\n {\n email,\n $or: [\n { passwordResetCodeExpiresAt: { $exists: false } },\n { passwordResetCodeExpiresAt: { $lte: cooldownEligibleExpiresAt } },\n ],\n },\n {\n $set: {\n passwordResetCode,\n passwordResetCodeExpiresAt,\n },\n $unset: {\n passwordResetToken: \"\",\n passwordResetTokenExpiresAt: \"\",\n },\n },\n { returnDocument: \"after\" },\n )\n\n ctx.res.setHeader(\"Retry-After\", String(PASSWORD_RESET_COOLDOWN_SECONDS))\n\n if (!user) {\n const existingUser = await User.findOne({ email }, { _id: 1 })\n\n if (!existingUser) {\n try {\n console.info(`sending password reset account notice to email ${maskEmailForLog(email)}`)\n await sendEmail({\n to: email,\n subject: \"Password reset request\",\n html: `\n <p>Hello,</p>\n <p>A password reset code has been requested for this email address, but there doesn't appear to be any account associated with it.</p>\n <p>If you didn't request this, you can ignore this message.</p>\n <p>If you're having trouble accessing your account, please contact support.</p>\n `,\n text: \"Hello, A password reset code has been requested for this email address, but there doesn't appear to be any account associated with it. If you didn't request this, you can ignore this message. If you're having trouble accessing your account, please contact support.\",\n })\n } catch (err) {\n console.error(\"failed to send password reset account notice email\", err)\n ctx.res.status(500)\n return { success: false, error: \"email_delivery_failed\" }\n }\n }\n\n return { success: true, retryAfterSeconds: PASSWORD_RESET_COOLDOWN_SECONDS }\n }\n\n try {\n console.info(`sending password reset to email ${maskEmailForLog(email)}`)\n await sendEmail({\n to: email,\n subject: `Your password reset code: ${passwordResetCode}`,\n html: `\n <p>Your password reset code is <strong>${passwordResetCode}</strong>. It expires in 10 minutes.</p>\n <p>If you didn't request this, you can ignore this message.</p>\n `,\n text: `Your password reset code is ${passwordResetCode}. It expires in 10 minutes. If you didn't request this, you can ignore this message.`,\n })\n } catch (err) {\n console.error(\"failed to send password reset email\", err)\n\n try {\n const releaseResult = await User.updateOne(\n { _id: user._id, passwordResetCode },\n {\n $unset: {\n passwordResetCode: \"\",\n passwordResetCodeExpiresAt: \"\",\n },\n },\n )\n if (releaseResult.modifiedCount > 0) {\n ctx.res.removeHeader(\"Retry-After\")\n }\n } catch (releaseErr) {\n console.error(\"failed to release password reset cooldown\", releaseErr)\n }\n\n ctx.res.status(500)\n return { success: false, error: \"email_delivery_failed\" }\n }\n\n return { success: true, retryAfterSeconds: PASSWORD_RESET_COOLDOWN_SECONDS }\n}\n\nexport default (api: Api<AuthSessionUser>) => {\n api.post(RequestPasswordReset.ROUTE, requestPasswordReset)\n}\n"],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ import { n as pendingRegistrationCondition } from "./registrationStatus-BP6yReMw.js";
2
+ import { ROUTE, requestSchema } from "./api/resend-otp/index.js";
3
+ import { n as issueVerificationCode, t as OTP_RESEND_COOLDOWN_MS } from "./emailVerification-DA9pZ0N_.js";
4
+ import { models } from "@rpcbase/db";
5
+ //#region src/api/resend-otp/handler.ts
6
+ var resendOtp = async (payload, ctx) => {
7
+ const parsed = requestSchema.safeParse(payload);
8
+ if (!parsed.success) {
9
+ ctx.res.status(400);
10
+ return {
11
+ success: false,
12
+ error: "invalid_payload"
13
+ };
14
+ }
15
+ const User = await models.getGlobal("RBUser", ctx);
16
+ const user = await User.findOne({
17
+ email: parsed.data.email,
18
+ ...pendingRegistrationCondition()
19
+ }, { emailVerificationSentAt: 1 });
20
+ if (!user) {
21
+ ctx.res.status(404);
22
+ return {
23
+ success: false,
24
+ error: "pending_registration_not_found"
25
+ };
26
+ }
27
+ const lastSentAt = user.get("emailVerificationSentAt");
28
+ if (lastSentAt instanceof Date) {
29
+ const remainingMs = OTP_RESEND_COOLDOWN_MS - (Date.now() - lastSentAt.getTime());
30
+ if (remainingMs > 0) {
31
+ const retryAfterSeconds = Math.max(1, Math.ceil(remainingMs / 1e3));
32
+ ctx.res.setHeader("Retry-After", String(retryAfterSeconds));
33
+ ctx.res.status(429);
34
+ return {
35
+ success: false,
36
+ error: "rate_limited",
37
+ retryAfterSeconds
38
+ };
39
+ }
40
+ }
41
+ try {
42
+ if (!await issueVerificationCode({
43
+ User,
44
+ userId: user._id,
45
+ email: parsed.data.email,
46
+ locale: parsed.data.locale,
47
+ enforceCooldown: true
48
+ })) {
49
+ const retryAfterSeconds = OTP_RESEND_COOLDOWN_MS / 1e3;
50
+ ctx.res.setHeader("Retry-After", String(retryAfterSeconds));
51
+ ctx.res.status(429);
52
+ return {
53
+ success: false,
54
+ error: "rate_limited",
55
+ retryAfterSeconds
56
+ };
57
+ }
58
+ } catch (error) {
59
+ console.error("auth::failed_to_resend_verification_email", {
60
+ userId: user._id.toString(),
61
+ error
62
+ });
63
+ ctx.res.status(500);
64
+ return {
65
+ success: false,
66
+ error: "email_delivery_failed"
67
+ };
68
+ }
69
+ return {
70
+ success: true,
71
+ retryAfterSeconds: OTP_RESEND_COOLDOWN_MS / 1e3
72
+ };
73
+ };
74
+ var handler_default = (api) => {
75
+ api.post(ROUTE, resendOtp);
76
+ };
77
+ //#endregion
78
+ export { handler_default as default, resendOtp };
79
+
80
+ //# sourceMappingURL=handler-DCk7C1pI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler-DCk7C1pI.js","names":[],"sources":["../src/api/resend-otp/handler.ts"],"sourcesContent":["import { Api, type ApiHandler, type Ctx } from \"@rpcbase/api\"\nimport { models } from \"@rpcbase/db\"\n\nimport {\n issueVerificationCode,\n OTP_RESEND_COOLDOWN_MS,\n} from \"../../emailVerification\"\nimport { pendingRegistrationCondition } from \"../../registrationStatus\"\nimport type { AuthSessionUser } from \"../../types\"\n\nimport * as ResendOtp from \"./index\"\n\n\nexport const resendOtp: ApiHandler<ResendOtp.RequestPayload, ResendOtp.ResponsePayload, AuthSessionUser> = async (\n payload,\n ctx: Ctx<AuthSessionUser>,\n): Promise<ResendOtp.ResponsePayload> => {\n const parsed = ResendOtp.requestSchema.safeParse(payload)\n\n if (!parsed.success) {\n ctx.res.status(400)\n return { success: false, error: \"invalid_payload\" }\n }\n\n const User = await models.getGlobal(\"RBUser\", ctx)\n const user = await User.findOne(\n { email: parsed.data.email, ...pendingRegistrationCondition() },\n { emailVerificationSentAt: 1 },\n )\n\n if (!user) {\n ctx.res.status(404)\n return { success: false, error: \"pending_registration_not_found\" }\n }\n\n const lastSentAt = user.get(\"emailVerificationSentAt\")\n if (lastSentAt instanceof Date) {\n const remainingMs = OTP_RESEND_COOLDOWN_MS - (Date.now() - lastSentAt.getTime())\n if (remainingMs > 0) {\n const retryAfterSeconds = Math.max(1, Math.ceil(remainingMs / 1000))\n ctx.res.setHeader(\"Retry-After\", String(retryAfterSeconds))\n ctx.res.status(429)\n return { success: false, error: \"rate_limited\", retryAfterSeconds }\n }\n }\n\n try {\n const issued = await issueVerificationCode({\n User,\n userId: user._id,\n email: parsed.data.email,\n locale: parsed.data.locale,\n enforceCooldown: true,\n })\n\n if (!issued) {\n const retryAfterSeconds = OTP_RESEND_COOLDOWN_MS / 1000\n ctx.res.setHeader(\"Retry-After\", String(retryAfterSeconds))\n ctx.res.status(429)\n return { success: false, error: \"rate_limited\", retryAfterSeconds }\n }\n } catch (error) {\n console.error(\"auth::failed_to_resend_verification_email\", {\n userId: user._id.toString(),\n error,\n })\n ctx.res.status(500)\n return { success: false, error: \"email_delivery_failed\" }\n }\n\n return { success: true, retryAfterSeconds: OTP_RESEND_COOLDOWN_MS / 1000 }\n}\n\nexport default (api: Api<AuthSessionUser>) => {\n api.post(ResendOtp.ROUTE, resendOtp)\n}\n"],"mappings":";;;;;AAaA,IAAa,YAA8F,OACzG,SACA,QACuC;CACvC,MAAM,SAAA,cAAiC,UAAU,OAAO;CAExD,IAAI,CAAC,OAAO,SAAS;EACnB,IAAI,IAAI,OAAO,GAAG;EAClB,OAAO;GAAE,SAAS;GAAO,OAAO;EAAkB;CACpD;CAEA,MAAM,OAAO,MAAM,OAAO,UAAU,UAAU,GAAG;CACjD,MAAM,OAAO,MAAM,KAAK,QACtB;EAAE,OAAO,OAAO,KAAK;EAAO,GAAG,6BAA6B;CAAE,GAC9D,EAAE,yBAAyB,EAAE,CAC/B;CAEA,IAAI,CAAC,MAAM;EACT,IAAI,IAAI,OAAO,GAAG;EAClB,OAAO;GAAE,SAAS;GAAO,OAAO;EAAiC;CACnE;CAEA,MAAM,aAAa,KAAK,IAAI,yBAAyB;CACrD,IAAI,sBAAsB,MAAM;EAC9B,MAAM,cAAc,0BAA0B,KAAK,IAAI,IAAI,WAAW,QAAQ;EAC9E,IAAI,cAAc,GAAG;GACnB,MAAM,oBAAoB,KAAK,IAAI,GAAG,KAAK,KAAK,cAAc,GAAI,CAAC;GACnE,IAAI,IAAI,UAAU,eAAe,OAAO,iBAAiB,CAAC;GAC1D,IAAI,IAAI,OAAO,GAAG;GAClB,OAAO;IAAE,SAAS;IAAO,OAAO;IAAgB;GAAkB;EACpE;CACF;CAEA,IAAI;EASF,IAAI,CAAC,MARgB,sBAAsB;GACzC;GACA,QAAQ,KAAK;GACb,OAAO,OAAO,KAAK;GACnB,QAAQ,OAAO,KAAK;GACpB,iBAAiB;EACnB,CAAC,GAEY;GACX,MAAM,oBAAoB,yBAAyB;GACnD,IAAI,IAAI,UAAU,eAAe,OAAO,iBAAiB,CAAC;GAC1D,IAAI,IAAI,OAAO,GAAG;GAClB,OAAO;IAAE,SAAS;IAAO,OAAO;IAAgB;GAAkB;EACpE;CACF,SAAS,OAAO;EACd,QAAQ,MAAM,6CAA6C;GACzD,QAAQ,KAAK,IAAI,SAAS;GAC1B;EACF,CAAC;EACD,IAAI,IAAI,OAAO,GAAG;EAClB,OAAO;GAAE,SAAS;GAAO,OAAO;EAAwB;CAC1D;CAEA,OAAO;EAAE,SAAS;EAAM,mBAAmB,yBAAyB;CAAK;AAC3E;AAEA,IAAA,mBAAgB,QAA8B;CAC5C,IAAI,KAAK,OAAiB,SAAS;AACrC"}
@@ -1,10 +1,10 @@
1
- import { a as string, i as object, n as boolean } from "./schemas-CMgO_xn9.js";
1
+ import { a as object, o as string, r as boolean } from "./schemas-DABNKHVA.js";
2
2
  import crypto from "crypto";
3
3
  import { models } from "@rpcbase/db";
4
4
  //#region src/api/verify-password-reset-otp/index.ts
5
5
  var ROUTE = "/api/rb/auth/verify-password-reset-otp";
6
6
  var requestSchema = object({
7
- email: string().email(),
7
+ email: string().trim().toLowerCase().email(),
8
8
  code: string().length(6, "Code must be 6 digits")
9
9
  });
10
10
  object({
@@ -53,4 +53,4 @@ var handler_default = (api) => {
53
53
  //#endregion
54
54
  export { handler_default as default };
55
55
 
56
- //# sourceMappingURL=handler-iT2eSHpE.js.map
56
+ //# sourceMappingURL=handler-DCljRBBf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler-DCljRBBf.js","names":[],"sources":["../src/api/verify-password-reset-otp/index.ts","../src/api/verify-password-reset-otp/handler.ts"],"sourcesContent":["import { z } from \"zod\"\n\n\nexport const ROUTE = \"/api/rb/auth/verify-password-reset-otp\"\n\nexport const requestSchema = z.object({\n email: z.string().trim().toLowerCase().email(),\n code: z.string().length(6, \"Code must be 6 digits\"),\n})\n\nexport type RequestPayload = z.infer<typeof requestSchema>\n\nexport const responseSchema = z.object({\n success: z.boolean(),\n error: z.string().optional(),\n resetToken: z.string().optional(),\n})\n\nexport type ResponsePayload = z.infer<typeof responseSchema>\n","import crypto from \"crypto\"\n\nimport { Api, ApiHandler, Ctx } from \"@rpcbase/api\"\nimport { models } from \"@rpcbase/db\"\n\nimport type { AuthSessionUser } from \"../../types\"\n\nimport * as VerifyPasswordResetOtp from \"./index\"\n\n\nconst verifyPasswordResetOtp: ApiHandler<\n VerifyPasswordResetOtp.RequestPayload,\n VerifyPasswordResetOtp.ResponsePayload,\n AuthSessionUser\n> = async(\n payload,\n ctx: Ctx<AuthSessionUser>\n): Promise<VerifyPasswordResetOtp.ResponsePayload> => {\n const User = await models.getGlobal(\"RBUser\", ctx)\n\n const parsed = VerifyPasswordResetOtp.requestSchema.safeParse(payload)\n\n if (!parsed.success) {\n ctx.res.status(400)\n return { success: false, error: \"invalid_payload\" }\n }\n\n const { email, code } = parsed.data\n const user = await User.findOne({ email })\n\n const storedCode = user?.passwordResetCode\n const expiresAt = user?.passwordResetCodeExpiresAt\n const isExpired = expiresAt instanceof Date && expiresAt.getTime() < Date.now()\n\n if (!user || !storedCode || storedCode !== code || isExpired) {\n ctx.res.status(400)\n return { success: false, error: \"invalid_code\" }\n }\n\n const resetToken = crypto.randomBytes(24).toString(\"hex\")\n\n user.passwordResetCode = undefined\n user.passwordResetCodeExpiresAt = undefined\n user.passwordResetToken = resetToken\n user.passwordResetTokenExpiresAt = new Date(Date.now() + 15 * 60 * 1000)\n await user.save()\n\n return { success: true, resetToken }\n}\n\nexport default (api: Api<AuthSessionUser>) => {\n api.post(VerifyPasswordResetOtp.ROUTE, verifyPasswordResetOtp)\n}\n"],"mappings":";;;;AAGA,IAAa,QAAQ;AAErB,IAAa,gBAAgB,OAAS;CACpC,OAAO,OAAS,CAAC,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,MAAM;CAC7C,MAAM,OAAS,CAAC,CAAC,OAAO,GAAG,uBAAuB;AACpD,CAAC;AAI6B,OAAS;CACrC,SAAS,QAAU;CACnB,OAAO,OAAS,CAAC,CAAC,SAAS;CAC3B,YAAY,OAAS,CAAC,CAAC,SAAS;AAClC,CAAC;;;ACND,IAAM,yBAIF,OACF,SACA,QACoD;CACpD,MAAM,OAAO,MAAM,OAAO,UAAU,UAAU,GAAG;CAEjD,MAAM,SAAA,cAA8C,UAAU,OAAO;CAErE,IAAI,CAAC,OAAO,SAAS;EACnB,IAAI,IAAI,OAAO,GAAG;EAClB,OAAO;GAAE,SAAS;GAAO,OAAO;EAAkB;CACpD;CAEA,MAAM,EAAE,OAAO,SAAS,OAAO;CAC/B,MAAM,OAAO,MAAM,KAAK,QAAQ,EAAE,MAAM,CAAC;CAEzC,MAAM,aAAa,MAAM;CACzB,MAAM,YAAY,MAAM;CACxB,MAAM,YAAY,qBAAqB,QAAQ,UAAU,QAAQ,IAAI,KAAK,IAAI;CAE9E,IAAI,CAAC,QAAQ,CAAC,cAAc,eAAe,QAAQ,WAAW;EAC5D,IAAI,IAAI,OAAO,GAAG;EAClB,OAAO;GAAE,SAAS;GAAO,OAAO;EAAe;CACjD;CAEA,MAAM,aAAa,OAAO,YAAY,EAAE,CAAC,CAAC,SAAS,KAAK;CAExD,KAAK,oBAAoB,KAAA;CACzB,KAAK,6BAA6B,KAAA;CAClC,KAAK,qBAAqB;CAC1B,KAAK,8BAA8B,IAAI,KAAK,KAAK,IAAI,IAAI,GAAc;CACvE,MAAM,KAAK,KAAK;CAEhB,OAAO;EAAE,SAAS;EAAM;CAAW;AACrC;AAEA,IAAA,mBAAgB,QAA8B;CAC5C,IAAI,KAAK,OAA8B,sBAAsB;AAC/D"}
@@ -1,4 +1,4 @@
1
- import { a as string, i as object, n as boolean } from "./schemas-CMgO_xn9.js";
1
+ import { a as object, o as string, r as boolean } from "./schemas-DABNKHVA.js";
2
2
  import { r as restrictSessionMiddleware } from "./middleware-BT3Uput1.js";
3
3
  import { t as AUTHENTICATED_TENANT_ID_HEADER } from "./session-BQ06Yo3C.js";
4
4
  //#region src/api/set-active-account/index.ts
@@ -64,4 +64,4 @@ var handler_default = (api) => {
64
64
  //#endregion
65
65
  export { handler_default as default };
66
66
 
67
- //# sourceMappingURL=handler-CF3fSh8e.js.map
67
+ //# sourceMappingURL=handler-JKQLI2iL.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"handler-CF3fSh8e.js","names":[],"sources":["../src/api/set-active-account/index.ts","../src/api/set-active-account/handler.ts"],"sourcesContent":["import { z } from \"zod\"\n\n\nexport const ROUTE = \"/api/rb/auth/set-active-account\"\n\nexport const requestSchema = z.object({\n tenantId: z.string().min(1, \"Tenant is required\"),\n})\n\nexport type RequestPayload = z.infer<typeof requestSchema>\n\nexport const responseSchema = z.object({\n success: z.boolean(),\n error: z.string().optional(),\n tenantId: z.string().optional(),\n})\n\nexport type ResponsePayload = z.infer<typeof responseSchema>\n","import { Api, ApiHandler, Ctx } from \"@rpcbase/api\"\n\nimport { restrictSessionMiddleware } from \"../../middleware\"\nimport { AUTHENTICATED_TENANT_ID_HEADER } from \"../../session\"\nimport type { AuthSessionUser } from \"../../types\"\n\nimport * as SetActiveAccount from \"./index\"\n\n\nconst setActiveAccount: ApiHandler<\n SetActiveAccount.RequestPayload,\n SetActiveAccount.ResponsePayload,\n AuthSessionUser\n> = async(\n payload,\n ctx: Ctx<AuthSessionUser>\n): Promise<SetActiveAccount.ResponsePayload> => {\n const parsed = SetActiveAccount.requestSchema.safeParse(payload)\n\n if (!parsed.success) {\n ctx.res.status(400)\n return { success: false, error: \"invalid_payload\" }\n }\n\n const sessionUser = ctx.req.session?.user\n if (!sessionUser?.id) {\n ctx.res.status(401)\n return { success: false, error: \"unauthorized\" }\n }\n\n const tenantId = parsed.data.tenantId.trim()\n const signedInTenants = Array.isArray(sessionUser.signedInTenants)\n ? sessionUser.signedInTenants.map(String)\n : []\n\n if (signedInTenants.length > 0 && !signedInTenants.includes(tenantId)) {\n ctx.res.status(403)\n return { success: false, error: \"invalid_account\" }\n }\n\n if (signedInTenants.length === 0) {\n const currentTenantId = typeof sessionUser.currentTenantId === \"string\" ? sessionUser.currentTenantId.trim() : \"\"\n if (!currentTenantId || currentTenantId !== tenantId) {\n ctx.res.status(403)\n return { success: false, error: \"invalid_account\" }\n }\n }\n\n ctx.req.session.user = {\n ...sessionUser,\n currentTenantId: tenantId,\n }\n ctx.res.setHeader(AUTHENTICATED_TENANT_ID_HEADER, tenantId)\n\n return { success: true, tenantId }\n}\n\nexport default (api: Api<AuthSessionUser>) => {\n api.use(SetActiveAccount.ROUTE, restrictSessionMiddleware)\n api.post(SetActiveAccount.ROUTE, setActiveAccount)\n}\n"],"mappings":";;;;AAGA,IAAa,QAAQ;AAErB,IAAa,gBAAgB,OAAS,EACpC,UAAU,OAAS,CAAC,CAAC,IAAI,GAAG,oBAAoB,EAClD,CAAC;AAI6B,OAAS;CACrC,SAAS,QAAU;CACnB,OAAO,OAAS,CAAC,CAAC,SAAS;CAC3B,UAAU,OAAS,CAAC,CAAC,SAAS;AAChC,CAAC;;;ACND,IAAM,mBAIF,OACF,SACA,QAC8C;CAC9C,MAAM,SAAA,cAAwC,UAAU,OAAO;CAE/D,IAAI,CAAC,OAAO,SAAS;EACnB,IAAI,IAAI,OAAO,GAAG;EAClB,OAAO;GAAE,SAAS;GAAO,OAAO;EAAkB;CACpD;CAEA,MAAM,cAAc,IAAI,IAAI,SAAS;CACrC,IAAI,CAAC,aAAa,IAAI;EACpB,IAAI,IAAI,OAAO,GAAG;EAClB,OAAO;GAAE,SAAS;GAAO,OAAO;EAAe;CACjD;CAEA,MAAM,WAAW,OAAO,KAAK,SAAS,KAAK;CAC3C,MAAM,kBAAkB,MAAM,QAAQ,YAAY,eAAe,IAC7D,YAAY,gBAAgB,IAAI,MAAM,IACtC,CAAA;CAEJ,IAAI,gBAAgB,SAAS,KAAK,CAAC,gBAAgB,SAAS,QAAQ,GAAG;EACrE,IAAI,IAAI,OAAO,GAAG;EAClB,OAAO;GAAE,SAAS;GAAO,OAAO;EAAkB;CACpD;CAEA,IAAI,gBAAgB,WAAW,GAAG;EAChC,MAAM,kBAAkB,OAAO,YAAY,oBAAoB,WAAW,YAAY,gBAAgB,KAAK,IAAI;EAC/G,IAAI,CAAC,mBAAmB,oBAAoB,UAAU;GACpD,IAAI,IAAI,OAAO,GAAG;GAClB,OAAO;IAAE,SAAS;IAAO,OAAO;GAAkB;EACpD;CACF;CAEA,IAAI,IAAI,QAAQ,OAAO;EACrB,GAAG;EACH,iBAAiB;CACnB;CACA,IAAI,IAAI,UAAU,gCAAgC,QAAQ;CAE1D,OAAO;EAAE,SAAS;EAAM;CAAS;AACnC;AAEA,IAAA,mBAAgB,QAA8B;CAC5C,IAAI,IAAI,OAAwB,yBAAyB;CACzD,IAAI,KAAK,OAAwB,gBAAgB;AACnD"}
1
+ {"version":3,"file":"handler-JKQLI2iL.js","names":[],"sources":["../src/api/set-active-account/index.ts","../src/api/set-active-account/handler.ts"],"sourcesContent":["import { z } from \"zod\"\n\n\nexport const ROUTE = \"/api/rb/auth/set-active-account\"\n\nexport const requestSchema = z.object({\n tenantId: z.string().min(1, \"Tenant is required\"),\n})\n\nexport type RequestPayload = z.infer<typeof requestSchema>\n\nexport const responseSchema = z.object({\n success: z.boolean(),\n error: z.string().optional(),\n tenantId: z.string().optional(),\n})\n\nexport type ResponsePayload = z.infer<typeof responseSchema>\n","import { Api, ApiHandler, Ctx } from \"@rpcbase/api\"\n\nimport { restrictSessionMiddleware } from \"../../middleware\"\nimport { AUTHENTICATED_TENANT_ID_HEADER } from \"../../session\"\nimport type { AuthSessionUser } from \"../../types\"\n\nimport * as SetActiveAccount from \"./index\"\n\n\nconst setActiveAccount: ApiHandler<\n SetActiveAccount.RequestPayload,\n SetActiveAccount.ResponsePayload,\n AuthSessionUser\n> = async(\n payload,\n ctx: Ctx<AuthSessionUser>\n): Promise<SetActiveAccount.ResponsePayload> => {\n const parsed = SetActiveAccount.requestSchema.safeParse(payload)\n\n if (!parsed.success) {\n ctx.res.status(400)\n return { success: false, error: \"invalid_payload\" }\n }\n\n const sessionUser = ctx.req.session?.user\n if (!sessionUser?.id) {\n ctx.res.status(401)\n return { success: false, error: \"unauthorized\" }\n }\n\n const tenantId = parsed.data.tenantId.trim()\n const signedInTenants = Array.isArray(sessionUser.signedInTenants)\n ? sessionUser.signedInTenants.map(String)\n : []\n\n if (signedInTenants.length > 0 && !signedInTenants.includes(tenantId)) {\n ctx.res.status(403)\n return { success: false, error: \"invalid_account\" }\n }\n\n if (signedInTenants.length === 0) {\n const currentTenantId = typeof sessionUser.currentTenantId === \"string\" ? sessionUser.currentTenantId.trim() : \"\"\n if (!currentTenantId || currentTenantId !== tenantId) {\n ctx.res.status(403)\n return { success: false, error: \"invalid_account\" }\n }\n }\n\n ctx.req.session.user = {\n ...sessionUser,\n currentTenantId: tenantId,\n }\n ctx.res.setHeader(AUTHENTICATED_TENANT_ID_HEADER, tenantId)\n\n return { success: true, tenantId }\n}\n\nexport default (api: Api<AuthSessionUser>) => {\n api.use(SetActiveAccount.ROUTE, restrictSessionMiddleware)\n api.post(SetActiveAccount.ROUTE, setActiveAccount)\n}\n"],"mappings":";;;;AAGA,IAAa,QAAQ;AAErB,IAAa,gBAAgB,OAAS,EACpC,UAAU,OAAS,CAAC,CAAC,IAAI,GAAG,oBAAoB,EAClD,CAAC;AAI6B,OAAS;CACrC,SAAS,QAAU;CACnB,OAAO,OAAS,CAAC,CAAC,SAAS;CAC3B,UAAU,OAAS,CAAC,CAAC,SAAS;AAChC,CAAC;;;ACND,IAAM,mBAIF,OACF,SACA,QAC8C;CAC9C,MAAM,SAAA,cAAwC,UAAU,OAAO;CAE/D,IAAI,CAAC,OAAO,SAAS;EACnB,IAAI,IAAI,OAAO,GAAG;EAClB,OAAO;GAAE,SAAS;GAAO,OAAO;EAAkB;CACpD;CAEA,MAAM,cAAc,IAAI,IAAI,SAAS;CACrC,IAAI,CAAC,aAAa,IAAI;EACpB,IAAI,IAAI,OAAO,GAAG;EAClB,OAAO;GAAE,SAAS;GAAO,OAAO;EAAe;CACjD;CAEA,MAAM,WAAW,OAAO,KAAK,SAAS,KAAK;CAC3C,MAAM,kBAAkB,MAAM,QAAQ,YAAY,eAAe,IAC7D,YAAY,gBAAgB,IAAI,MAAM,IACtC,CAAA;CAEJ,IAAI,gBAAgB,SAAS,KAAK,CAAC,gBAAgB,SAAS,QAAQ,GAAG;EACrE,IAAI,IAAI,OAAO,GAAG;EAClB,OAAO;GAAE,SAAS;GAAO,OAAO;EAAkB;CACpD;CAEA,IAAI,gBAAgB,WAAW,GAAG;EAChC,MAAM,kBAAkB,OAAO,YAAY,oBAAoB,WAAW,YAAY,gBAAgB,KAAK,IAAI;EAC/G,IAAI,CAAC,mBAAmB,oBAAoB,UAAU;GACpD,IAAI,IAAI,OAAO,GAAG;GAClB,OAAO;IAAE,SAAS;IAAO,OAAO;GAAkB;EACpD;CACF;CAEA,IAAI,IAAI,QAAQ,OAAO;EACrB,GAAG;EACH,iBAAiB;CACnB;CACA,IAAI,IAAI,UAAU,gCAAgC,QAAQ;CAE1D,OAAO;EAAE,SAAS;EAAM;CAAS;AACnC;AAEA,IAAA,mBAAgB,QAA8B;CAC5C,IAAI,IAAI,OAAwB,yBAAyB;CACzD,IAAI,KAAK,OAAwB,gBAAgB;AACnD"}
@@ -1,10 +1,10 @@
1
- import { a as string, i as object, n as boolean } from "./schemas-CMgO_xn9.js";
1
+ import { a as object, o as string, r as boolean } from "./schemas-DABNKHVA.js";
2
2
  import { models } from "@rpcbase/db";
3
3
  import { hashPasswordForStorage } from "@rpcbase/server";
4
4
  //#region src/api/set-new-password/index.ts
5
5
  var ROUTE = "/api/rb/auth/set-new-password";
6
6
  var requestSchema = object({
7
- email: string().email(),
7
+ email: string().trim().toLowerCase().email(),
8
8
  resetToken: string().min(1),
9
9
  password: string().min(8, { message: "Password must be at least 8 characters long." })
10
10
  });
@@ -48,4 +48,4 @@ var handler_default = (api) => {
48
48
  //#endregion
49
49
  export { handler_default as default };
50
50
 
51
- //# sourceMappingURL=handler-DgIcxthg.js.map
51
+ //# sourceMappingURL=handler-b6PvhATs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler-b6PvhATs.js","names":[],"sources":["../src/api/set-new-password/index.ts","../src/api/set-new-password/handler.ts"],"sourcesContent":["import { z } from \"zod\"\n\n\nexport const ROUTE = \"/api/rb/auth/set-new-password\"\n\nexport const requestSchema = z.object({\n email: z.string().trim().toLowerCase().email(),\n resetToken: z.string().min(1),\n password: z.string().min(8, { message: \"Password must be at least 8 characters long.\" }),\n})\n\nexport type RequestPayload = z.infer<typeof requestSchema>\n\nexport const responseSchema = z.object({\n success: z.boolean(),\n error: z.string().optional(),\n})\n\nexport type ResponsePayload = z.infer<typeof responseSchema>\n","import { Api, ApiHandler, Ctx } from \"@rpcbase/api\"\nimport { models } from \"@rpcbase/db\"\nimport { hashPasswordForStorage } from \"@rpcbase/server\"\n\nimport type { AuthSessionUser } from \"../../types\"\n\nimport * as SetNewPassword from \"./index\"\n\n\nconst setNewPassword: ApiHandler<\n SetNewPassword.RequestPayload,\n SetNewPassword.ResponsePayload,\n AuthSessionUser\n> = async(\n payload,\n ctx: Ctx<AuthSessionUser>\n): Promise<SetNewPassword.ResponsePayload> => {\n const User = await models.getGlobal(\"RBUser\", ctx)\n\n const parsed = SetNewPassword.requestSchema.safeParse(payload)\n\n if (!parsed.success) {\n ctx.res.status(400)\n return { success: false, error: \"invalid_payload\" }\n }\n\n const { email, resetToken, password } = parsed.data\n\n const user = await User.findOne({ email })\n const storedToken = user?.passwordResetToken\n const tokenExpiresAt = user?.passwordResetTokenExpiresAt\n const isExpired = tokenExpiresAt instanceof Date && tokenExpiresAt.getTime() < Date.now()\n\n if (!user || !storedToken || storedToken !== resetToken || isExpired) {\n ctx.res.status(400)\n return { success: false, error: \"invalid_token\" }\n }\n\n const hashedPassword = await hashPasswordForStorage(password)\n user.password = hashedPassword\n user.passwordResetToken = undefined\n user.passwordResetTokenExpiresAt = undefined\n await user.save()\n\n return { success: true }\n}\n\nexport default (api: Api<AuthSessionUser>) => {\n api.post(SetNewPassword.ROUTE, setNewPassword)\n}\n"],"mappings":";;;;AAGA,IAAa,QAAQ;AAErB,IAAa,gBAAgB,OAAS;CACpC,OAAO,OAAS,CAAC,CAAC,KAAK,CAAC,CAAC,YAAY,CAAC,CAAC,MAAM;CAC7C,YAAY,OAAS,CAAC,CAAC,IAAI,CAAC;CAC5B,UAAU,OAAS,CAAC,CAAC,IAAI,GAAG,EAAE,SAAS,+CAA+C,CAAC;AACzF,CAAC;AAI6B,OAAS;CACrC,SAAS,QAAU;CACnB,OAAO,OAAS,CAAC,CAAC,SAAS;AAC7B,CAAC;;;ACPD,IAAM,iBAIF,OACF,SACA,QAC4C;CAC5C,MAAM,OAAO,MAAM,OAAO,UAAU,UAAU,GAAG;CAEjD,MAAM,SAAA,cAAsC,UAAU,OAAO;CAE7D,IAAI,CAAC,OAAO,SAAS;EACnB,IAAI,IAAI,OAAO,GAAG;EAClB,OAAO;GAAE,SAAS;GAAO,OAAO;EAAkB;CACpD;CAEA,MAAM,EAAE,OAAO,YAAY,aAAa,OAAO;CAE/C,MAAM,OAAO,MAAM,KAAK,QAAQ,EAAE,MAAM,CAAC;CACzC,MAAM,cAAc,MAAM;CAC1B,MAAM,iBAAiB,MAAM;CAC7B,MAAM,YAAY,0BAA0B,QAAQ,eAAe,QAAQ,IAAI,KAAK,IAAI;CAExF,IAAI,CAAC,QAAQ,CAAC,eAAe,gBAAgB,cAAc,WAAW;EACpE,IAAI,IAAI,OAAO,GAAG;EAClB,OAAO;GAAE,SAAS;GAAO,OAAO;EAAgB;CAClD;CAGA,KAAK,WAAW,MADa,uBAAuB,QAAQ;CAE5D,KAAK,qBAAqB,KAAA;CAC1B,KAAK,8BAA8B,KAAA;CACnC,MAAM,KAAK,KAAK;CAEhB,OAAO,EAAE,SAAS,KAAK;AACzB;AAEA,IAAA,mBAAgB,QAA8B;CAC5C,IAAI,KAAK,OAAsB,cAAc;AAC/C"}
@@ -0,0 +1,98 @@
1
+ import { n as pendingRegistrationCondition } from "./registrationStatus-BP6yReMw.js";
2
+ import { n as completeAuthSignIn } from "./session-BQ06Yo3C.js";
3
+ import { ROUTE, requestSchema } from "./api/verify-otp/index.js";
4
+ import { models } from "@rpcbase/db";
5
+ //#region src/api/verify-otp/handler.ts
6
+ var verifyOtp = async (payload, ctx) => {
7
+ const parsed = requestSchema.safeParse(payload);
8
+ if (!parsed.success) {
9
+ ctx.res.status(400);
10
+ return {
11
+ success: false,
12
+ error: "invalid_payload"
13
+ };
14
+ }
15
+ if (!ctx.req.session) {
16
+ ctx.res.status(500);
17
+ return {
18
+ success: false,
19
+ error: "session_unavailable"
20
+ };
21
+ }
22
+ const [User, Tenant] = await Promise.all([models.getGlobal("RBUser", ctx), models.getGlobal("RBTenant", ctx)]);
23
+ const user = await User.findOne({
24
+ email: parsed.data.email,
25
+ ...pendingRegistrationCondition()
26
+ }, {
27
+ tenants: 1,
28
+ tenantRoles: 1,
29
+ emailVerificationCode: 1,
30
+ emailVerificationExpiresAt: 1
31
+ });
32
+ const expiresAt = user?.emailVerificationExpiresAt;
33
+ const codeMatches = user?.emailVerificationCode === parsed.data.code;
34
+ const codeIsCurrent = expiresAt instanceof Date && expiresAt.getTime() > Date.now();
35
+ if (!user || !codeMatches || !codeIsCurrent) {
36
+ ctx.res.status(400);
37
+ return {
38
+ success: false,
39
+ error: "invalid_code"
40
+ };
41
+ }
42
+ const signedInTenants = Array.isArray(user.tenants) ? user.tenants.map(String) : [];
43
+ const tenantId = signedInTenants[0]?.trim() ?? "";
44
+ const tenant = tenantId ? await Tenant.findOne({ tenantId }, { provisioningStatus: 1 }).lean() : null;
45
+ if (!tenantId || tenant?.provisioningStatus !== "active") {
46
+ ctx.res.status(503);
47
+ return {
48
+ success: false,
49
+ error: "tenant_unavailable"
50
+ };
51
+ }
52
+ if ((await User.updateOne({
53
+ _id: user._id,
54
+ ...pendingRegistrationCondition(),
55
+ emailVerificationCode: parsed.data.code,
56
+ emailVerificationExpiresAt: { $gt: /* @__PURE__ */ new Date() }
57
+ }, {
58
+ $set: { registrationStatus: "active" },
59
+ $unset: {
60
+ emailVerificationCode: "",
61
+ emailVerificationExpiresAt: "",
62
+ emailVerificationSentAt: ""
63
+ }
64
+ })).matchedCount !== 1) {
65
+ ctx.res.status(409);
66
+ return {
67
+ success: false,
68
+ error: "code_already_used"
69
+ };
70
+ }
71
+ const tenantRolesMap = user.get("tenantRoles");
72
+ const tenantRoles = tenantRolesMap ? Object.fromEntries(tenantRolesMap.entries()) : void 0;
73
+ const userId = user._id.toString();
74
+ if (!completeAuthSignIn(ctx, {
75
+ userId,
76
+ currentTenantId: tenantId,
77
+ signedInTenants: signedInTenants.length ? signedInTenants : [tenantId],
78
+ tenantRoles
79
+ })) {
80
+ ctx.res.status(500);
81
+ return {
82
+ success: false,
83
+ error: "session_unavailable"
84
+ };
85
+ }
86
+ return {
87
+ success: true,
88
+ userId,
89
+ tenantId
90
+ };
91
+ };
92
+ var handler_default = (api) => {
93
+ api.post(ROUTE, verifyOtp);
94
+ };
95
+ //#endregion
96
+ export { handler_default as default, verifyOtp };
97
+
98
+ //# sourceMappingURL=handler-cj7IkJB-.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler-cj7IkJB-.js","names":[],"sources":["../src/api/verify-otp/handler.ts"],"sourcesContent":["import { Api, type ApiHandler, type Ctx } from \"@rpcbase/api\"\nimport { models } from \"@rpcbase/db\"\n\nimport { completeAuthSignIn } from \"../../session\"\nimport { pendingRegistrationCondition } from \"../../registrationStatus\"\nimport type { AuthSessionUser } from \"../../types\"\n\nimport * as VerifyOtp from \"./index\"\n\n\nexport const verifyOtp: ApiHandler<VerifyOtp.RequestPayload, VerifyOtp.ResponsePayload, AuthSessionUser> = async (\n payload,\n ctx: Ctx<AuthSessionUser>,\n): Promise<VerifyOtp.ResponsePayload> => {\n const parsed = VerifyOtp.requestSchema.safeParse(payload)\n\n if (!parsed.success) {\n ctx.res.status(400)\n return { success: false, error: \"invalid_payload\" }\n }\n\n if (!ctx.req.session) {\n ctx.res.status(500)\n return { success: false, error: \"session_unavailable\" }\n }\n\n const [User, Tenant] = await Promise.all([\n models.getGlobal(\"RBUser\", ctx),\n models.getGlobal(\"RBTenant\", ctx),\n ])\n const user = await User.findOne(\n { email: parsed.data.email, ...pendingRegistrationCondition() },\n {\n tenants: 1,\n tenantRoles: 1,\n emailVerificationCode: 1,\n emailVerificationExpiresAt: 1,\n },\n )\n\n const expiresAt = user?.emailVerificationExpiresAt\n const codeMatches = user?.emailVerificationCode === parsed.data.code\n const codeIsCurrent = expiresAt instanceof Date && expiresAt.getTime() > Date.now()\n\n if (!user || !codeMatches || !codeIsCurrent) {\n ctx.res.status(400)\n return { success: false, error: \"invalid_code\" }\n }\n\n const signedInTenants = Array.isArray(user.tenants) ? user.tenants.map(String) : []\n const tenantId = signedInTenants[0]?.trim() ?? \"\"\n const tenant = tenantId\n ? await Tenant.findOne({ tenantId }, { provisioningStatus: 1 }).lean() as {\n provisioningStatus?: unknown\n } | null\n : null\n\n if (!tenantId || tenant?.provisioningStatus !== \"active\") {\n ctx.res.status(503)\n return { success: false, error: \"tenant_unavailable\" }\n }\n\n const activation = await User.updateOne(\n {\n _id: user._id,\n ...pendingRegistrationCondition(),\n emailVerificationCode: parsed.data.code,\n emailVerificationExpiresAt: { $gt: new Date() },\n },\n {\n $set: { registrationStatus: \"active\" },\n $unset: {\n emailVerificationCode: \"\",\n emailVerificationExpiresAt: \"\",\n emailVerificationSentAt: \"\",\n },\n },\n )\n\n if (activation.matchedCount !== 1) {\n ctx.res.status(409)\n return { success: false, error: \"code_already_used\" }\n }\n\n const tenantRolesMap = user.get(\"tenantRoles\") as Map<string, string[]> | undefined\n const tenantRoles = tenantRolesMap ? Object.fromEntries(tenantRolesMap.entries()) : undefined\n const userId = user._id.toString()\n\n if (!completeAuthSignIn(ctx, {\n userId,\n currentTenantId: tenantId,\n signedInTenants: signedInTenants.length ? signedInTenants : [tenantId],\n tenantRoles,\n })) {\n ctx.res.status(500)\n return { success: false, error: \"session_unavailable\" }\n }\n\n return { success: true, userId, tenantId }\n}\n\nexport default (api: Api<AuthSessionUser>) => {\n api.post(VerifyOtp.ROUTE, verifyOtp)\n}\n"],"mappings":";;;;;AAUA,IAAa,YAA8F,OACzG,SACA,QACuC;CACvC,MAAM,SAAA,cAAiC,UAAU,OAAO;CAExD,IAAI,CAAC,OAAO,SAAS;EACnB,IAAI,IAAI,OAAO,GAAG;EAClB,OAAO;GAAE,SAAS;GAAO,OAAO;EAAkB;CACpD;CAEA,IAAI,CAAC,IAAI,IAAI,SAAS;EACpB,IAAI,IAAI,OAAO,GAAG;EAClB,OAAO;GAAE,SAAS;GAAO,OAAO;EAAsB;CACxD;CAEA,MAAM,CAAC,MAAM,UAAU,MAAM,QAAQ,IAAI,CACvC,OAAO,UAAU,UAAU,GAAG,GAC9B,OAAO,UAAU,YAAY,GAAG,CAAC,CAClC;CACD,MAAM,OAAO,MAAM,KAAK,QACtB;EAAE,OAAO,OAAO,KAAK;EAAO,GAAG,6BAA6B;CAAE,GAC9D;EACE,SAAS;EACT,aAAa;EACb,uBAAuB;EACvB,4BAA4B;CAC9B,CACF;CAEA,MAAM,YAAY,MAAM;CACxB,MAAM,cAAc,MAAM,0BAA0B,OAAO,KAAK;CAChE,MAAM,gBAAgB,qBAAqB,QAAQ,UAAU,QAAQ,IAAI,KAAK,IAAI;CAElF,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,eAAe;EAC3C,IAAI,IAAI,OAAO,GAAG;EAClB,OAAO;GAAE,SAAS;GAAO,OAAO;EAAe;CACjD;CAEA,MAAM,kBAAkB,MAAM,QAAQ,KAAK,OAAO,IAAI,KAAK,QAAQ,IAAI,MAAM,IAAI,CAAA;CACjF,MAAM,WAAW,gBAAgB,EAAE,EAAE,KAAK,KAAK;CAC/C,MAAM,SAAS,WACX,MAAM,OAAO,QAAQ,EAAE,SAAS,GAAG,EAAE,oBAAoB,EAAE,CAAC,CAAC,CAAC,KAAK,IAGnE;CAEJ,IAAI,CAAC,YAAY,QAAQ,uBAAuB,UAAU;EACxD,IAAI,IAAI,OAAO,GAAG;EAClB,OAAO;GAAE,SAAS;GAAO,OAAO;EAAqB;CACvD;CAmBA,KAAI,MAjBqB,KAAK,UAC5B;EACE,KAAK,KAAK;EACV,GAAG,6BAA6B;EAChC,uBAAuB,OAAO,KAAK;EACnC,4BAA4B,EAAE,qBAAK,IAAI,KAAK,EAAE;CAChD,GACA;EACE,MAAM,EAAE,oBAAoB,SAAS;EACrC,QAAQ;GACN,uBAAuB;GACvB,4BAA4B;GAC5B,yBAAyB;EAC3B;CACF,CACF,EAAA,CAEe,iBAAiB,GAAG;EACjC,IAAI,IAAI,OAAO,GAAG;EAClB,OAAO;GAAE,SAAS;GAAO,OAAO;EAAoB;CACtD;CAEA,MAAM,iBAAiB,KAAK,IAAI,aAAa;CAC7C,MAAM,cAAc,iBAAiB,OAAO,YAAY,eAAe,QAAQ,CAAC,IAAI,KAAA;CACpF,MAAM,SAAS,KAAK,IAAI,SAAS;CAEjC,IAAI,CAAC,mBAAmB,KAAK;EAC3B;EACA,iBAAiB;EACjB,iBAAiB,gBAAgB,SAAS,kBAAkB,CAAC,QAAQ;EACrE;CACF,CAAC,GAAG;EACF,IAAI,IAAI,OAAO,GAAG;EAClB,OAAO;GAAE,SAAS;GAAO,OAAO;EAAsB;CACxD;CAEA,OAAO;EAAE,SAAS;EAAM;EAAQ;CAAS;AAC3C;AAEA,IAAA,mBAAgB,QAA8B;CAC5C,IAAI,KAAK,OAAiB,SAAS;AACrC"}
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { n as requestSchema } from "./sign-in-DYq3bJeH.js";
2
- import { n as requestSchema$1 } from "./sign-up-CYGadNUe.js";
1
+ import { requestSchema } from "./api/sign-in/index.js";
2
+ import { requestSchema as requestSchema$1 } from "./api/sign-up/index.js";
3
3
  import { n as requireSession, r as restrictSessionMiddleware, t as redirectAuthMiddleware } from "./middleware-BT3Uput1.js";
4
4
  import { createContext, createElement, forwardRef, useContext, useEffect, useRef, useState } from "react";
5
5
  import { Link, useLocation, useNavigate, useSearchParams } from "@rpcbase/router";
@@ -187,6 +187,13 @@ var SignInForm = ({ children, className }) => {
187
187
  });
188
188
  const json = await res.json().catch(() => null);
189
189
  if (!res.ok || !json?.success) {
190
+ if (json?.error === "email_verification_required") {
191
+ const nextPath = sanitizeNextPath(searchParams.get("next")) ?? "/";
192
+ const search = new URLSearchParams({ email: data.email.trim().toLowerCase() });
193
+ search.set("next", nextPath);
194
+ navigate(`/auth/sign-up-otp?${search.toString()}`);
195
+ return;
196
+ }
190
197
  const message = json?.error === "invalid_credentials" ? "Invalid email or password." : "Sign-in failed. Please try again.";
191
198
  methods.setError("root", {
192
199
  type: "server",
@@ -194,8 +201,8 @@ var SignInForm = ({ children, className }) => {
194
201
  });
195
202
  return;
196
203
  }
197
- const nextPath = sanitizeNextPath(searchParams.get("next")) ?? "/";
198
- navigate(nextPath, { replace: true });
204
+ const nextPath_0 = sanitizeNextPath(searchParams.get("next")) ?? "/";
205
+ navigate(nextPath_0, { replace: true });
199
206
  } catch (err) {
200
207
  methods.setError("root", {
201
208
  type: "server",
@@ -258,7 +265,7 @@ var SignUpForm = ({ children, className, otpNextPath }) => {
258
265
  });
259
266
  return;
260
267
  }
261
- const search = new URLSearchParams({ email: data.email });
268
+ const search = new URLSearchParams({ email: data.email.trim().toLowerCase() });
262
269
  if (otpNextPath) search.set("next", otpNextPath);
263
270
  navigate(`/auth/sign-up-otp?${search.toString()}`);
264
271
  setServerMessage("Account created. Check your inbox to verify your email.");
@@ -486,6 +493,7 @@ var PasswordInput = ({ id, name = "password", className, placeholder, autoComple
486
493
  ...register(name)
487
494
  }), /* @__PURE__ */ jsx("button", {
488
495
  type: "button",
496
+ tabIndex: -1,
489
497
  className: "absolute inset-y-0 right-0 flex items-center pr-3 text-gray-500",
490
498
  onClick: () => setShowPassword((prev) => !prev),
491
499
  "aria-label": showPassword ? "Hide characters" : "Show characters",